import { C as KEEPS_PROCESS_ALIVE, S as ActionableError, T as isRemotePeerError, b as writeDirect, m as blankLineBefore, p as writeNotice, s as debug, u as logger } from "../startup-clock-DXZlxuSA.mjs"; import { n as withNodePath } from "../resolve-nuxt-VKCDOC-A.mjs"; import { t as configureProjectConsola } from "../console-BgmzEcbV.mjs"; import { n as resolveDotenvFileNames } from "../args-DGaOocsH.mjs"; import { t as overrideEnv } from "../env-BkMPEX1D.mjs"; import { i as stopCpuProfile, r as startCpuProfile, t as createPhaseReporter } from "../phase-reporter-xcVDefHV.mjs"; import { a as withConfigKeys, c as openInspector, i as mergeRestartReasons, n as formatRestartReason, r as formatSkippedReload, t as formatChangedKeys } from "../reason-BzYs8eZ4.mjs"; import { t as openBroadcast } from "../broadcast-mciEtrFQ.mjs"; import { n as isLoopbackAddress, t as isAllowedHost } from "../host-check-DW342-PP.mjs"; import { n as READY_MESSAGE, t as PhaseTimeline } from "../progress-snapshot-BsUn_089.mjs"; import { n as currentRequest, r as runWithRequest, t as attachRequest } from "../serving-state-Cts7VBoa.mjs"; import { r as showBanner } from "../banner-lMLtjrKS.mjs"; import { t as clearBuildDir } from "../fs-CSqg02ri.mjs"; import { t as loadKit } from "../kit-CbhDo9Zs.mjs"; import { a as formatLockError, o as getTakeoverPid, p as updateLock, t as acquireLock } from "../lockfile-BZHfAuKg.mjs"; import { a as resolveNuxtManifest, n as loadNuxtManifest, o as writeNuxtManifest } from "../nuxt-CCjsh7BJ.mjs"; import { t as resolveServerBuild } from "../server-build-Cga7EB-K.mjs"; import { a as matchesBoundTarget, f as resolvePortlessURLs, l as resolveOpenURL, o as openBrowser, r as createListener, t as bindListener } from "../listen-BjyzPJ2X.mjs"; import process from "node:process"; import { formatWithOptions, styleText } from "node:util"; import { isCI, provider } from "std-env"; import { consola } from "consola"; import { fileURLToPath, pathToFileURL } from "node:url"; import { resolveModulePath } from "exsolve"; import { dirname, isAbsolute, join, relative, resolve } from "pathe"; import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, statSync, watch, writeFileSync } from "node:fs"; import defu from "defu"; import { hash } from "node:crypto"; import EventEmitter from "node:events"; import { mkdir } from "node:fs/promises"; import { Buffer as Buffer$1 } from "node:buffer"; import { debounce } from "perfect-debounce"; import { toNodeHandler } from "srvx/node"; //#region src/dev/force-tty.ts /** * Present a piped fork's stdio as a terminal. * * When the parent runs the interactive dev UI it pipes this fork's stdio so the * panel can stay below all output. Everything that formats output (consola's * fancy reporter, colour detection) reads `isTTY` at module load, so this must * run first and must not import anything that loads `std-env` or `consola`. */ if (process.env.__NUXT_DEV_PIPED_TTY__) for (const stream of [process.stdout, process.stderr]) { if (stream.isTTY) continue; Object.defineProperty(stream, "isTTY", { value: true, configurable: true }); Object.defineProperty(stream, "columns", { get: () => Number(process.env.__NUXT_DEV_COLUMNS__) || 80, configurable: true }); const depth = Number(process.env.__NUXT_DEV_COLOR_DEPTH__) || forcedColorDepth(); Object.defineProperty(stream, "getColorDepth", { value: () => depth, configurable: true }); Object.defineProperty(stream, "hasColors", { value: (count) => depth >= 4 && (typeof count !== "number" || count <= 2 ** depth), configurable: true }); for (const method of [ "cursorTo", "moveCursor", "clearLine", "clearScreenDown" ]) if (typeof stream[method] !== "function") Object.defineProperty(stream, method, { value: (...args) => { args.find((arg) => typeof arg === "function")?.(); return true; }, configurable: true }); } /** The depth `FORCE_COLOR` asks for, read the way Node reads it. */ function forcedColorDepth() { switch (process.env.FORCE_COLOR?.trim().toLowerCase()) { case void 0: case "": case "0": case "false": return 1; case "1": case "true": return 4; case "3": return 24; default: return 8; } } //#endregion //#region src/dev/progress.ts /** Path prefix reserved for the CLI's own dev-time endpoints. */ const DEV_INTERNAL_PREFIX = "/__nuxt_dev__/"; const PROGRESS_PATH = `${DEV_INTERNAL_PREFIX}progress`; const HEARTBEAT_INTERVAL = 15e3; /** * Startup phases, in the order they are reached. The index doubles as the * progress fraction shown to clients, so the list is deliberately coarse and * monotonic: a phase is never re-entered during a single load. */ const DEV_PHASES = [ { id: "config", message: "Loading Nuxt config" }, { id: "modules", message: "Setting up modules" }, { id: "app", message: "Preparing app" }, { id: "types", message: "Generating types" }, { id: "bundle", message: "Bundling app" }, { id: "server", message: "Building server" }, { id: "ready", message: READY_MESSAGE } ]; /** * Fraction reserved for the first render. `nuxt:ready` means the server can * accept a request, not answer one, and compiling the first document is the * longest single wait on a large project. */ const READY_PROGRESS = .95; /** Shown between the server accepting requests and it answering one. */ const WARMUP_MESSAGE = "Compiling the first request"; /** * How long a request has to be in flight before it is reported. A page a dev * server has already compiled is answered in milliseconds, and a status line * that appears and disappears within a frame is noise. */ const REQUEST_DWELL = 400; const HOOK_PHASES = { "modules:before": "modules", "builder:generateApp": "app", "prepare:types": "types", "build:before": "bundle", "vite:serverCreated": "bundle", "webpack:compile": "bundle", "nitro:build:before": "server" }; /** Index of the phase a module count belongs to. */ const MODULES_INDEX = DEV_PHASES.findIndex((phase) => phase.id === "modules"); /** * Both carry a `name` that is always renderable. A Nuxt without them shows * the phase label, as before. */ const MODULE_STARTED = "module:before"; const MODULE_FINISHED = "module:done"; /** * Nitro builds the server on its own hooks, which Nuxt's never see, so this is * the only way the server phase says anything. The prefix keeps a name like * `rollup:before` from reading as if Nuxt were the one busy. */ const NITRO_HOOK = "nitro:init"; const NITRO_PREFIX = "nitro:"; /** * How long a module has to stay busy before it is named. Most install in a few * milliseconds, and a label flickering through dozens of names a second is * worse than a still one, especially as a browser tab title. */ const MODULE_DWELL = 150; /** * How long any other hook has to run before it is named, set where a hook has * stopped being a step of the build and started being the reason for the wait. * Nuxt calls thousands of them, nearly all sub-millisecond. */ const HOOK_DWELL = 1e3; /** * How long a single phase has to run before the dwells above are dropped to * {@link IMPATIENT_MODULE_DWELL} and {@link IMPATIENT_HOOK_DWELL}. A phase this * far in has stopped being described by its own label, so anything concrete is * better than a still one, flicker included. */ const PHASE_PATIENCE = 3e3; const IMPATIENT_MODULE_DWELL = 50; const IMPATIENT_HOOK_DWELL = 150; /** How often the narration looks at what is currently running. */ const NARRATION_INTERVAL = 250; /** Friendlier wording for hooks whose raw name would not explain the wait. */ const HOOK_LABELS = { "devtools:before": "Setting up Nuxt DevTools", "modules:done": "Finishing module setup", "app:resolve": "Resolving the app", "app:templates": "Collecting app templates", "app:templatesGenerated": "Writing app templates", "pages:extend": "Scanning pages", "pages:resolved": "Resolving pages", "components:dirs": "Scanning component directories", "components:extend": "Resolving components", "imports:extend": "Resolving auto-imports", "imports:sources": "Collecting auto-imports", "schema:extend": "Extending the config schema", "schema:resolved": "Resolving the config schema", "nitro:prepare:types": "Generating server types", "nitro:config": "Configuring the server", "nitro:init": "Starting the server build", "nitro:rollup:before": "Bundling the server", "nitro:compiled": "Finishing the server build", "nitro:dev:reload": "Reloading the server", "nitro:types:extend": "Generating server types", "vite:extendConfig": "Configuring Vite", "vite:configResolved": "Configuring Vite", "vite:serverCreated": "Starting Vite", "webpack:config": "Configuring webpack" }; /** * Hook namespaces owned by Nuxt, Nitro or a bundler. A name in one of these is * an implementation detail: it is narrated only through {@link HOOK_LABELS}, * because the raw name describes the build's internals rather than the wait. * Anything else was named by the project or one of its modules, and reads as * something its author would recognise. */ const INTERNAL_HOOK_NAMESPACES = /* @__PURE__ */ new Set([ "app", "build", "builder", "components", "devtools", "imports", "kit", "modules", "module", "nitro", "pages", "prepare", "schema", "vite", "webpack", "rspack" ]); /** * The label for a hook that has been running long enough to be the reason for * the wait, or nothing where its name would say less than the phase it is in. */ function hookLabel(name) { const label = HOOK_LABELS[name]; if (label) return label; return INTERNAL_HOOK_NAMESPACES.has(name.split(":")[0]) ? void 0 : `Running ${name}`; } /** * Past this depth the stack is dropped rather than grown, so a hook that * never settles cannot retain an entry for the rest of the load. */ const HOOK_STACK_LIMIT = 64; /** Module names share their line with the status badge, and paths get long. */ const MODULE_NAME_LIMIT = 32; /** * Local modules keep only their basename; package specifiers, scope included, * are left alone. */ function shortenModuleName(name) { const short = name.startsWith("@") || !/[\\/]/.test(name) ? name : name.split(/[\\/]/).pop().replace(/\.[cm]?[jt]sx?$/, ""); return short.length > MODULE_NAME_LIMIT ? `${short.slice(0, 31)}\u2026` : short; } function moduleName(module) { const name = module?.name; return typeof name === "string" && name ? name : void 0; } /** * Tracks how far a `nuxt dev` load has got and fans that out to the terminal * reporter and to any loading pages connected over SSE. */ var DevProgress = class extends PhaseTimeline { #clients = /* @__PURE__ */ new Set(); #heartbeat; #message = DEV_PHASES[0].message; #status = "loading"; #error; #reload = false; #baseMessage = DEV_PHASES[0].message; #module; #hooks = []; #narrating = false; #serving = false; #inflight = /* @__PURE__ */ new Map(); #pending; #dwell; #nextRequest = 0; #observing = false; #installedModules; #observed = /* @__PURE__ */ new WeakSet(); #ticker; constructor() { super(DEV_PHASES); } get snapshot() { const snapshot = super.snapshot; return { ...snapshot, status: this.#status, message: this.#message, progress: this.#status === "ready" ? this.#serving ? 1 : READY_PROGRESS : snapshot.progress, reload: this.#reload, serving: this.#serving, pending: this.#pending, error: this.#error && { name: this.#error.name, message: this.#error.message } }; } start(message, reload = false) { this.#hooks = []; this.#module = void 0; this.#narrating = false; if (this.#observing) this.#startNarrating(); this.restart(); this.#status = "loading"; this.#error = void 0; this.#reload = reload; this.#serving = false; this.#clearPending(); this.#baseMessage = message || DEV_PHASES[0].message; this.#message = this.#baseMessage; this.#emit(); } setMessage(message) { if (this.#status === "error") return; this.#message = message; this.#emit(); } setPhase(id, message) { this.#advance(id, message, true); } setReady() { if (this.#status === "ready") return; this.#stopNarrating(); this.#advance("ready", void 0, false); this.#status = "ready"; this.#error = void 0; if (this.#clients.size > 0 || this.#pending || this.#inflight.size > 0) this.#message = WARMUP_MESSAGE; this.#emit(); } /** * Record a request the app is now rendering, returning the handle to settle * it with. Reported only once it has been in flight for {@link REQUEST_DWELL}, * so a page that is already compiled passes without comment. */ startRequest(label) { const id = ++this.#nextRequest; this.#inflight.set(id, { label, startedAt: Date.now() }); this.#scheduleDwell(); return id; } /** Settle the request {@link startRequest} handed back. */ finishRequest(id) { const request = this.#inflight.get(id); if (!request) return; this.#inflight.delete(id); if (this.#pending !== request) return; this.#pending = this.#oldest(Date.now() - REQUEST_DWELL); this.#scheduleDwell(); this.#emit(); } /** The oldest request in flight since before `since`, if any. */ #oldest(since) { let oldest; for (const request of this.#inflight.values()) if (request.startedAt <= since && (!oldest || request.startedAt < oldest.startedAt)) oldest = request; return oldest; } /** Report the oldest request in flight once it has dwelt long enough. */ #scheduleDwell() { if (this.#dwell || this.#pending) return; let next; for (const { startedAt } of this.#inflight.values()) next = next === void 0 ? startedAt : Math.min(next, startedAt); if (next === void 0) return; this.#dwell = setTimeout(() => { this.#dwell = void 0; this.#pending = this.#oldest(Date.now() - REQUEST_DWELL); if (this.#pending) this.#emit(); this.#scheduleDwell(); }, Math.max(0, next + REQUEST_DWELL - Date.now())); this.#dwell.unref?.(); } #clearPending() { clearTimeout(this.#dwell); this.#dwell = void 0; this.#pending = void 0; this.#inflight.clear(); } /** The app has answered a request, so the wait is genuinely over. */ setServing() { if (this.#serving || this.#status !== "ready") return; this.#serving = true; this.#message = READY_MESSAGE; this.#emit(); } #advance(id, message, emit) { if (this.#status === "error") return; const advanced = this.enter(id); if (advanced === void 0) return; if (advanced) this.#module = void 0; const next = message || DEV_PHASES[this.index].message; if (!advanced && next === this.#baseMessage) return; this.#baseMessage = next; this.#message = next; this.#narrating = false; if (emit) this.#emit(); } /** * Replace the phase label with whatever the load is actually waiting on, or * put the phase label back once it is waiting on nothing in particular. The * phase index is untouched, so the fraction clients render stays monotonic. */ #narrate() { if (this.#status !== "loading") return; const now = Date.now(); const impatient = now - this.phaseStartedAt >= PHASE_PATIENCE; let text; if (this.#module && now - this.#module.at >= (impatient ? IMPATIENT_MODULE_DWELL : MODULE_DWELL)) text = `Setting up ${shortenModuleName(this.#module.name)}`; else for (let index = this.#hooks.length - 1; index >= 0; index--) { const hook = this.#hooks[index]; if (hook.at < this.phaseStartedAt) break; if (now - hook.at < (impatient ? IMPATIENT_HOOK_DWELL : HOOK_DWELL)) continue; if (!HOOK_PHASES[hook.name]) text = hookLabel(hook.name); break; } if (!text && impatient && this.index === MODULES_INDEX) { const installed = this.#installedModules?.() ?? 0; if (installed) text = `${this.#baseMessage} \u00B7 ${installed} installed`; } if (!text && !this.#narrating) return; this.#narrating = !!text; const next = text ?? this.#baseMessage; if (next !== this.#message) { this.#message = next; this.#emit(); } } #startNarrating() { if (this.#ticker) return; this.#ticker = setInterval(() => this.#narrate(), NARRATION_INTERVAL); this.#ticker.unref?.(); } #stopNarrating() { clearInterval(this.#ticker); this.#ticker = void 0; this.#hooks = []; this.#module = void 0; } /** * Record a hook as running. Hooks nest, but they also run concurrently, so * this is a stack only by convention: a hook that finishes out of order is * removed from wherever it sits. An entry whose hook never completes would * otherwise sit at the bottom of the stack for the rest of the load, so the * oldest is dropped once the stack stops looking like one. */ #pushHook(name) { if (this.#hooks.length >= HOOK_STACK_LIMIT) this.#hooks.shift(); this.#hooks.push({ name, at: Date.now() }); } #popHook(name) { const last = this.#hooks.length - 1; if (last >= 0 && this.#hooks[last].name === name) { this.#hooks.length = last; return; } for (let index = last; index >= 0; index--) if (this.#hooks[index].name === name) { this.#hooks.splice(index, 1); return; } } setError(error) { this.#stopNarrating(); this.#error = error; this.#status = "error"; this.#message = error.message || "Nuxt failed to start."; this.#emit(); } /** * Derive phases from the hooks Nuxt calls, so the granularity follows the * project's own build rather than a schedule guessed by the CLI, and name * whatever is taking long enough that the phase alone stops explaining the * wait. Both the modules a project installs and the hooks its own code * registers are only visible from here. */ attachNuxt(hooks, options = {}) { this.#installedModules ||= options.installedModules; if (this.#observed.has(hooks)) return; const timed = !!(hooks.beforeEach && hooks.afterEach); hooks.beforeEach?.(({ name, args }) => { if (timed) this.#pushHook(name); const phase = HOOK_PHASES[name]; if (phase) this.setPhase(phase); else if (name === MODULE_STARTED) { const module = moduleName(args?.[0]); if (module) this.#module = { name: module, at: Date.now() }; } else if (name === MODULE_FINISHED && moduleName(args?.[0]) === this.#module?.name) this.#module = void 0; else if (name === NITRO_HOOK) this.#observe((args?.[0])?.hooks, NITRO_PREFIX); }); this.#observe(hooks, ""); } /** * Time the hooks of one system. Entries carry the prefix they were pushed * with, so a name shared by two systems cannot pop the other's entry, and the * innermost-wins rule still holds across both: Nitro builds inside a Nuxt * hook that is awaiting it. */ #observe(hooks, prefix) { if (!hooks?.beforeEach || !hooks.afterEach || this.#observed.has(hooks)) return; this.#observed.add(hooks); if (prefix) hooks.beforeEach(({ name }) => this.#pushHook(prefix + name)); hooks.afterEach(({ name }) => this.#popHook(prefix + name)); this.#observing = true; this.#startNarrating(); } handleRequest(req, res) { if ((req.url || "").split("?")[0] !== PROGRESS_PATH) return false; this.#subscribe(res); return true; } #subscribe(res) { res.statusCode = 200; res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-store"); res.setHeader("Connection", "keep-alive"); res.setHeader("X-Accel-Buffering", "no"); res.flushHeaders?.(); res.write("retry: 1000\n\n"); this.#clients.add(res); res.once("close", () => { this.#clients.delete(res); if (this.#clients.size === 0) { clearInterval(this.#heartbeat); this.#heartbeat = void 0; this.setServing(); } }); if (!this.#heartbeat) { this.#heartbeat = setInterval(() => { for (const client of this.#clients) client.write(": ping\n\n"); }, HEARTBEAT_INTERVAL); this.#heartbeat.unref?.(); } this.#send(res, this.snapshot); } close() { this.#stopNarrating(); this.#clearPending(); clearInterval(this.#heartbeat); this.#heartbeat = void 0; for (const client of this.#clients) client.end(); this.#clients.clear(); } #emit() { const snapshot = this.emit(); for (const client of this.#clients) this.#send(client, snapshot); } #send(res, snapshot) { if (res.writableEnded) return; res.write(`event: nuxt:${snapshot.status}\ndata: ${JSON.stringify(snapshot)}\n\n`); } }; //#endregion //#region src/dev/error-channel.ts /** Base path of the live error channel, before `nuxt.config` is known. */ const DEFAULT_ERROR_CHANNEL = `${DEV_INTERNAL_PREFIX}error`; /** Where the app reads the path the CLI mounted the channel on. */ const ERROR_CHANNEL_ENV = "NUXT_DEV_ERROR_CHANNEL"; /** Where the app forwards its reports when the CLI owns the channel. */ const ERROR_BROADCAST_CHANNEL = "nuxt:dev:error"; /** Asks whoever holds a current report to post it again. */ const SYNC_MESSAGE = { type: "nuxt:dev:error:sync" }; const THREAD_RUNNERS = /* @__PURE__ */ new Set(["node-worker"]); /** * Whether `runner` (unset means Nitro's default) evaluates the app in a thread * of this process, and so can reach the bridge. Anywhere else the app serves * the channel itself. */ function isThreadRunner(runner) { return !runner || THREAD_RUNNERS.has(runner); } let channel; /** * The live error channel, created on first use. Owned by the process that owns * the listener, so it outlives the app being rebuilt, crashing or not having * started. */ function useErrorChannel(options = {}) { if (!channel) { const pending = channel = import("my-bad/channel").then(async ({ createChannel }) => createChannel({ open: true, root: options.cwd || process.cwd(), allowedHosts: true, sink: await resolveSink(options) })); pending.catch(() => { if (channel === pending) channel = void 0; }); } return channel; } /** Record every channel event as JSON lines, when `NUXT_DEV_ERROR_LOG` asks for it. */ async function resolveSink(options) { const requested = process.env.NUXT_DEV_ERROR_LOG; if (!requested) return; const path = requested === "1" || requested === "true" ? join(options.buildDir || join(options.cwd || process.cwd(), ".nuxt"), "errors.jsonl") : requested; const { fileSink } = await import("my-bad/sinks"); return fileSink(path); } /** * The channel path a config asks for, or nothing when it cannot be served * there: a bare or trailing slash would intercept the app's own routes. */ function resolveChannelPath(value) { if (typeof value !== "string") return; const path = value.replace(/\/+$/, ""); return path.startsWith("/") && path.length > 1 ? path : void 0; } /** Whether `path` is served by the channel mounted at `base`. */ function isErrorChannelRequest(path, base) { return path === base || path.startsWith(`${base}/`); } /** Whether `req` came from this machine, by socket address since headers are forgeable. */ function isLocalPeer(req) { return isLoopbackAddress(req.socket?.remoteAddress); } async function handleErrorChannelRequest(req, res, options = {}) { if (await (await useErrorChannel(options)).handler(req, res, { trusted: isLocalPeer(req) })) return; res.statusCode = 404; res.setHeader("Content-Type", "application/json"); res.setHeader("Cache-Control", "no-store"); res.end("{}"); } /** * Build a report for something the CLI itself failed at: the config, a module's * setup, a build. Frames are mapped from disk; the app holds the sourcemaps of * its own bundle and maps its own. */ async function createCliReport(error, options = {}) { const [{ createReport, fsLoader }, { nuxtPreset }] = await Promise.all([import("my-bad"), import("my-bad/presets")]); return createReport(error, { cwd: options.cwd || process.cwd(), loaders: [fsLoader()], presets: [nuxtPreset()], context: options.req ? { req: options.req } : void 0 }); } /** Render `report` as a standalone page, subscribed to the live channel. */ async function renderErrorPage(report, options) { const [{ renderPage }, { nuxtTheme }] = await Promise.all([import("my-bad"), import("my-bad/presets")]); return renderPage(report, { cwd: options.cwd, channel: options.channel, history: options.history, environment: "Build", theme: nuxtTheme }); } /** The CLI's own phase sequence, as the channel knows it apart from the app's. */ const CLI_PROGRESS_SOURCE = "cli"; /** Progress the app forwarded for work the CLI cannot see. */ const APP_PROGRESS_SOURCE = "app"; /** Progress for the bar an open error page draws; `100` retires it. */ function toBuildProgress(snapshot) { return { phase: snapshot.phase, percent: snapshot.status === "error" ? void 0 : Math.round(snapshot.progress * 100), message: snapshot.message, source: CLI_PROGRESS_SOURCE }; } /** Publish the CLI's own progress, resolved against whatever the app is reporting. */ async function publishCliProgress(snapshot) { const progress = toBuildProgress(snapshot); await withErrorChannel((channel) => channel.progress(progress)); } /** Publish to the channel if one exists, without creating it. */ async function withErrorChannel(run) { if (!channel) return; try { run(await channel); } catch (error) { debug("Could not publish to the error channel:", error); } } /** Whether `message` is a report forwarded by the app. */ function isDevErrorMessage(message) { const candidate = message; if (candidate?.request !== void 0 && typeof candidate.request !== "string") return false; const type = candidate?.type; if (type === "nuxt:dev:error:log") return isLogEntry(message.entry); if (type === "nuxt:dev:error:progress") return isBuildProgress(message.progress); return type === "nuxt:dev:error:report" || type === "nuxt:dev:error:clear" || type === "nuxt:dev:error:warning"; } const LOG_LEVELS = /* @__PURE__ */ new Set([ "trace", "debug", "info", "log", "warn", "error", "fatal" ]); /** Whether `entry` is a log the channel's drawer can show. */ function isLogEntry(entry) { if (typeof entry !== "object" || entry === null) return false; const candidate = entry; return typeof candidate.text === "string" && typeof candidate.level === "string" && LOG_LEVELS.has(candidate.level); } /** Whether `progress` is an update the error page's bar can draw. */ function isBuildProgress(progress) { if (typeof progress !== "object" || progress === null) return false; const candidate = progress; if (typeof candidate.phase !== "string") return false; if (candidate.percent !== void 0 && (typeof candidate.percent !== "number" || !Number.isFinite(candidate.percent))) return false; return candidate.message === void 0 || typeof candidate.message === "string"; } /** The compile error a report was caused by, when there is one. */ function findCompileReport(report) { if (report.kind === "compile") return report; for (const cause of report.causes) { const compile = findCompileReport(cause); if (compile) return compile; } } /** Everything the supervisor needs to present `report`, rendered for a terminal. */ async function summariseReport(report, context = {}, cwd = process.cwd()) { const { renderAnsi } = await import("my-bad"); const named = findCompileReport(report) ?? report; const frames = named.frames.some((frame) => frame.file) ? named.frames : report.frames; const frame = frames.find((frame) => frame.type === "app" && frame.file) ?? frames.find((frame) => frame.file); return { id: report.id, name: named.name, message: named.message, file: frame?.file, line: frame?.line, location: frame?.file && formatLocation(frame.file, frame.line, frame.column, cwd), requestId: context.requestId, request: context.request, ansi: renderAnsi(report, { cwd }) }; } /** `file:line:column`, relative to the project where it sits inside it. */ function formatLocation(file, line, column, cwd) { const relativePath = relative(cwd, file); return [ !relativePath || relativePath.startsWith("..") || isAbsolute(relativePath) ? file : `./${relativePath}`, line, column ].filter((part) => part !== void 0).join(":"); } /** The request that hit `report`, then its rendering. */ function formatReportForTerminal(report) { const body = report.ansi; if (!report.request) return body; const separator = report.request.indexOf(" "); return `[request error] [${separator === -1 ? report.request : report.request.slice(0, separator)}]${separator === -1 ? "" : ` ${report.request.slice(separator + 1)}`}\n\n ${body.replaceAll("\n", "\n ")}`; } /** * Receive the reports the app forwards, publishing them on the CLI's channel, * until the returned function is called. */ function openErrorBridge(handlers = {}, options = {}) { const broadcast = openBroadcast(ERROR_BROADCAST_CHANNEL, (message) => { if (!isDevErrorMessage(message)) return; useErrorChannel(options).then((instance) => { switch (message.type) { case "nuxt:dev:error:report": instance.setError(message.report, message.requestId, message.request); handlers.onReport?.(message.report, { requestId: message.requestId, request: message.request }); break; case "nuxt:dev:error:log": instance.log(message.entry); break; case "nuxt:dev:error:progress": instance.progress({ ...message.progress, source: message.progress.source ?? APP_PROGRESS_SOURCE }); break; case "nuxt:dev:error:warning": instance.warn(message.report); break; case "nuxt:dev:error:clear": instance.clearError(message.id); handlers.onClear?.(message.id); } }).catch((error) => debug("Could not handle a forwarded error report:", error)); }); broadcast?.postMessage(SYNC_MESSAGE); return () => broadcast?.close(); } async function closeErrorChannel() { const instance = channel; channel = void 0; await instance?.then((open) => open.close()).catch(() => {}); } //#endregion //#region ../../node_modules/.pnpm/h3@1.15.11/node_modules/h3/dist/index.mjs function hasProp(obj, prop) { try { return prop in obj; } catch { return false; } } var H3Error = class extends Error { static __h3_error__ = true; statusCode = 500; fatal = false; unhandled = false; statusMessage; data; cause; constructor(message, opts = {}) { super(message, opts); if (opts.cause && !this.cause) this.cause = opts.cause; } toJSON() { const obj = { message: this.message, statusCode: sanitizeStatusCode(this.statusCode, 500) }; if (this.statusMessage) obj.statusMessage = sanitizeStatusMessage(this.statusMessage); if (this.data !== void 0) obj.data = this.data; return obj; } }; function createError(input) { if (typeof input === "string") return new H3Error(input); if (isError(input)) return input; const err = new H3Error(input.message ?? input.statusMessage ?? "", { cause: input.cause || input }); if (hasProp(input, "stack")) try { Object.defineProperty(err, "stack", { get() { return input.stack; } }); } catch { try { err.stack = input.stack; } catch {} } if (input.data) err.data = input.data; if (input.statusCode) err.statusCode = sanitizeStatusCode(input.statusCode, err.statusCode); else if (input.status) err.statusCode = sanitizeStatusCode(input.status, err.statusCode); if (input.statusMessage) err.statusMessage = input.statusMessage; else if (input.statusText) err.statusMessage = input.statusText; if (err.statusMessage) { const originalMessage = err.statusMessage; if (sanitizeStatusMessage(err.statusMessage) !== originalMessage) console.warn("[h3] Please prefer using `message` for longer error messages instead of `statusMessage`. In the future, `statusMessage` will be sanitized by default."); } if (input.fatal !== void 0) err.fatal = input.fatal; if (input.unhandled !== void 0) err.unhandled = input.unhandled; return err; } function sendError(event, error, debug) { if (event.handled) return; const h3Error = isError(error) ? error : createError(error); const responseBody = { statusCode: h3Error.statusCode, statusMessage: h3Error.statusMessage, stack: [], data: h3Error.data }; if (debug) responseBody.stack = (h3Error.stack || "").split("\n").map((l) => l.trim()); if (event.handled) return; setResponseStatus(event, Number.parseInt(h3Error.statusCode), h3Error.statusMessage); event.node.res.setHeader("content-type", MIMES.json); event.node.res.end(JSON.stringify(responseBody, void 0, 2)); } function isError(input) { return input?.constructor?.__h3_error__ === true; } const MIMES = { html: "text/html", json: "application/json" }; const DISALLOWED_STATUS_CHARS = /[^\u0009\u0020-\u007E]/g; function sanitizeStatusMessage(statusMessage = "") { return statusMessage.replace(DISALLOWED_STATUS_CHARS, ""); } function sanitizeStatusCode(statusCode, defaultStatusCode = 200) { if (!statusCode) return defaultStatusCode; if (typeof statusCode === "string") statusCode = Number.parseInt(statusCode, 10); if (statusCode < 100 || statusCode > 999) return defaultStatusCode; return statusCode; } function splitCookiesString(cookiesString) { if (Array.isArray(cookiesString)) return cookiesString.flatMap((c) => splitCookiesString(c)); if (typeof cookiesString !== "string") return []; const cookiesStrings = []; let pos = 0; let start; let ch; let lastComma; let nextStart; let cookiesSeparatorFound; const skipWhitespace = () => { while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) pos += 1; return pos < cookiesString.length; }; const notSpecialChar = () => { ch = cookiesString.charAt(pos); return ch !== "=" && ch !== ";" && ch !== ","; }; while (pos < cookiesString.length) { start = pos; cookiesSeparatorFound = false; while (skipWhitespace()) { ch = cookiesString.charAt(pos); if (ch === ",") { lastComma = pos; pos += 1; skipWhitespace(); nextStart = pos; while (pos < cookiesString.length && notSpecialChar()) pos += 1; if (pos < cookiesString.length && cookiesString.charAt(pos) === "=") { cookiesSeparatorFound = true; pos = nextStart; cookiesStrings.push(cookiesString.slice(start, lastComma)); start = pos; } else pos = lastComma + 1; } else pos += 1; } if (!cookiesSeparatorFound || pos >= cookiesString.length) cookiesStrings.push(cookiesString.slice(start)); } return cookiesStrings; } function setResponseStatus(event, code, text) { if (code) event.node.res.statusCode = sanitizeStatusCode(code, event.node.res.statusCode); if (text) event.node.res.statusMessage = sanitizeStatusMessage(text); } function sendStream(event, stream) { if (!stream || typeof stream !== "object") throw new Error("[h3] Invalid stream provided."); event.node.res._data = stream; if (!event.node.res.socket) { event._handled = true; return Promise.resolve(); } if (hasProp(stream, "pipeTo") && typeof stream.pipeTo === "function") return stream.pipeTo(new WritableStream({ write(chunk) { event.node.res.write(chunk); } })).then(() => { event.node.res.end(); }); if (hasProp(stream, "pipe") && typeof stream.pipe === "function") return new Promise((resolve, reject) => { stream.pipe(event.node.res); if (stream.on) { stream.on("end", () => { event.node.res.end(); resolve(); }); stream.on("error", (error) => { reject(error); }); } event.node.res.on("close", () => { if (stream.abort) stream.abort(); }); }); throw new Error("[h3] Invalid or incompatible stream provided."); } function sendWebResponse(event, response) { for (const [key, value] of response.headers) if (key === "set-cookie") event.node.res.appendHeader(key, splitCookiesString(value)); else event.node.res.setHeader(key, value); if (response.status) event.node.res.statusCode = sanitizeStatusCode(response.status, event.node.res.statusCode); if (response.statusText) event.node.res.statusMessage = sanitizeStatusMessage(response.statusText); if (response.redirected) event.node.res.setHeader("location", response.url); if (!response.body) { event.node.res.end(); return; } return sendStream(event, response.body); } var H3Event = class { "__is_event__" = true; node; web; context = {}; _method; _path; _headers; _requestBody; _handled = false; _onBeforeResponseCalled; _onAfterResponseCalled; constructor(req, res) { this.node = { req, res }; } get method() { if (!this._method) this._method = (this.node.req.method || "GET").toUpperCase(); return this._method; } get path() { return this._path || this.node.req.url || "/"; } get headers() { if (!this._headers) this._headers = _normalizeNodeHeaders(this.node.req.headers); return this._headers; } get handled() { return this._handled || this.node.res.writableEnded || this.node.res.headersSent; } respondWith(response) { return Promise.resolve(response).then((_response) => sendWebResponse(this, _response)); } toString() { return `[${this.method}] ${this.path}`; } toJSON() { return this.toString(); } /** @deprecated Please use `event.node.req` instead. */ get req() { return this.node.req; } /** @deprecated Please use `event.node.res` instead. */ get res() { return this.node.res; } }; function createEvent(req, res) { return new H3Event(req, res); } function _normalizeNodeHeaders(nodeHeaders) { const headers = new Headers(); for (const [name, value] of Object.entries(nodeHeaders)) if (Array.isArray(value)) for (const item of value) headers.append(name, item); else if (value) headers.set(name, value); return headers; } globalThis.Headers; globalThis.Response; function toNodeListener(app) { const toNodeHandle = async function(req, res) { const event = createEvent(req, res); try { await app.handler(event); } catch (_error) { const error = createError(_error); if (!isError(_error)) error.unhandled = true; setResponseStatus(event, error.statusCode, error.statusMessage); if (app.options.onError) await app.options.onError(error, event); if (event.handled) return; if (error.unhandled || error.fatal) console.error("[h3]", error.fatal ? "[fatal]" : "[unhandled]", error); if (app.options.onBeforeResponse && !event._onBeforeResponseCalled) await app.options.onBeforeResponse(event, { body: error }); await sendError(event, error, !!app.options.debug); if (app.options.onAfterResponse && !event._onAfterResponseCalled) await app.options.onAfterResponse(event, { body: error }); } }; return toNodeHandle; } //#endregion //#region src/utils/dev-hint.ts function hintPath(cwd) { return join(cwd, "node_modules/.cache/nuxt/dev-server.json"); } function loadDevServerHint(cwd) { try { const hint = JSON.parse(readFileSync(hintPath(cwd), "utf-8")); return typeof hint === "object" && hint ? hint : void 0; } catch { return; } } /** Persist the resolved address. Never throws: the hint is only an optimisation. */ function saveDevServerHint(cwd, hint) { const path = hintPath(cwd); if (!existsSync(join(cwd, "node_modules"))) return; try { if (JSON.stringify(loadDevServerHint(cwd)) === JSON.stringify(hint)) return; mkdirSync(dirname(path), { recursive: true }); writeFileSync(path, JSON.stringify(hint), "utf-8"); } catch (error) { debug("Could not persist dev server hint:", error); } } //#endregion //#region src/dev/config-watcher.ts const RESTART_RE = /^(?:nuxt\.config\.[a-z0-9]+|\.nuxtignore|\.nuxtrc|\.config\/nuxt(?:\.config)?\.[a-z0-9]+)$/; /** Files above this size are tracked by mtime alone. */ const MAX_HASHED_FILE_SIZE = 262144; function hashFileContents(path, size) { if (size > MAX_HASHED_FILE_SIZE) return; let fd; try { fd = openSync(path, "r"); const buffer = Buffer$1.allocUnsafe(262145); let read = 0; while (read < buffer.length) { const bytes = readSync(fd, buffer, read, buffer.length - read, read); if (bytes === 0) break; read += bytes; } if (read > MAX_HASHED_FILE_SIZE) return; return hash("sha1", buffer.subarray(0, read), "hex"); } catch { return; } finally { if (fd !== void 0) try { closeSync(fd); } catch {} } } function trackFile(path, stats) { if (stats.isDirectory()) return { mtimeMs: stats.mtimeMs }; return { mtimeMs: stats.mtimeMs, contentHash: hashFileContents(path, stats.size) }; } var FileChangeTracker = class { entries = /* @__PURE__ */ new Map(); /** Whether `filePath` changed: by content for regular files, by mtime otherwise. */ shouldEmitChange(filePath) { const resolved = resolve(filePath); try { const stats = statSync(resolved); const previous = this.entries.get(resolved); const current = trackFile(resolved, stats); this.entries.set(resolved, current); if (previous === void 0) return true; if (previous.contentHash !== void 0 && current.contentHash !== void 0) return previous.contentHash !== current.contentHash; return previous.mtimeMs !== current.mtimeMs; } catch { this.entries.delete(resolved); return true; } } prime(filePath, recursive = false) { const resolved = resolve(filePath); const stat = statSync(resolved); this.entries.set(resolved, trackFile(resolved, stat)); if (stat.isDirectory()) { const entries = readdirSync(resolved); for (const entry of entries) { const fullPath = resolve(resolved, entry); try { const stats = statSync(fullPath); this.entries.set(fullPath, trackFile(fullPath, stats)); if (recursive && stats.isDirectory()) this.prime(fullPath, recursive); } catch {} } } } }; /** Local layer directories to watch, excluding the root and layers in `node_modules` or outside it. */ function getLocalLayerDirs(layers, cwd) { const root = resolve(cwd); const dirs = /* @__PURE__ */ new Set(); for (const layer of layers) { const dir = layer.cwd || layer.config?.rootDir; const resolved = dir && resolve(dir); if (resolved && resolved !== root && resolved.startsWith(`${root}/`) && !resolved.includes("/node_modules/")) dirs.add(resolved); } return [...dirs]; } function createConfigWatcher(cwd, dotenvFileName = ".env", onRestart, onReload, layerDirs = []) { const dotenvFileNames = new Set(Array.isArray(dotenvFileName) ? dotenvFileName : [dotenvFileName]); const closers = [watchConfigDir(cwd, onReload, (file, path) => dotenvFileNames.has(file) && onRestart(path)), ...layerDirs.map((dir) => watchConfigDir(dir, onReload))]; return () => { for (const close of closers) close(); }; } /** Collapse the events of a single save into one call per file, so a half-written file is never evaluated. */ function perFile(handler, delay = 30) { const timers = /* @__PURE__ */ new Map(); return { listener: (_event, file) => { if (!file) return; clearTimeout(timers.get(file)); const timer = setTimeout(() => { timers.delete(file); handler(file); }, delay); timer.unref?.(); timers.set(file, timer); }, cancel: () => { for (const timer of timers.values()) clearTimeout(timer); timers.clear(); } }; } function watchConfigDir(dir, onReload, onFile) { const fileWatcher = new FileChangeTracker(); fileWatcher.prime(dir); const watcher = watch(dir); let configDirWatcher = existsSync(join(dir, ".config")) ? createConfigDirWatcher(dir, onReload) : void 0; const { listener, cancel } = perFile((file) => { if (!fileWatcher.shouldEmitChange(resolve(dir, file))) return; onFile?.(file, resolve(dir, file)); if (RESTART_RE.test(file)) onReload(resolve(dir, file)); if (file === ".config") configDirWatcher ||= createConfigDirWatcher(dir, onReload); }); watcher.on("change", listener); return () => { cancel(); watcher.close(); configDirWatcher?.(); }; } function createConfigDirWatcher(cwd, onReload) { const configDir = join(cwd, ".config"); const fileWatcher = new FileChangeTracker(); fileWatcher.prime(configDir); const configDirWatcher = watch(configDir); const { listener, cancel } = perFile((file) => { if (!fileWatcher.shouldEmitChange(resolve(configDir, file))) return; if (RESTART_RE.test(file)) onReload(resolve(configDir, file)); }); configDirWatcher.on("change", listener); return () => { cancel(); configDirWatcher.close(); }; } //#endregion //#region src/dev/error-response.ts const ESCAPES = { "&": "&", "<": "<", ">": ">", "\"": """, "'": "'" }; function escapeHtml(text) { return text.replace(/[&<>"']/g, (character) => ESCAPES[character]); } /** * Answer a request with the little that can be said without a report: the * message, and the stack where there is one. */ async function sendErrorResponse(req, res, error, options = {}) { if (res.headersSent) { if (!res.writableEnded) res.end(); return; } const err = error; const useJSON = !req.headers.accept?.includes("text/html"); res.statusCode = 500; res.setHeader("Content-Type", useJSON ? "application/json" : "text/html"); res.setHeader("Cache-Control", "no-store"); res.setHeader("X-Content-Type-Options", "nosniff"); res.setHeader("X-Frame-Options", "DENY"); res.setHeader("Referrer-Policy", "no-referrer"); if (!options.inject) res.setHeader("Refresh", "3"); if (useJSON) { res.end(JSON.stringify({ error: true, url: req.url, status: 500, message: err?.message || "Unknown error", data: err?.data, stack: err?.stack?.split("\n").map((line) => line.trim()) }, null, 2)); return; } const message = escapeHtml(err?.message || "Unknown error"); const stack = err?.stack ? `
${escapeHtml(err.stack)}
` : ""; res.end(`${message}

${message}

${stack}${options.inject ?? ""}`); } //#endregion //#region src/dev/loading-client.ts function progressClient(options) { const caption = document.createElement("div"); caption.id = options.captionId; document.body.append(caption); let start = Date.now() - options.elapsed; let label = ""; function paint() { const seconds = `${((Date.now() - start) / 1e3).toFixed(1)}s`; caption.textContent = label ? `${label} \u00B7 ${seconds}` : seconds; } function apply(snapshot) { start = Date.now() - snapshot.elapsed; label = `${/^[A-Z][a-z]/.test(snapshot.message) ? snapshot.message[0].toLowerCase() + snapshot.message.slice(1) : snapshot.message} \u00B7 step ${snapshot.index + 1}/${snapshot.total + 1}`; const percent = Math.max(4, Math.round(snapshot.progress * 100)); document.documentElement.style.setProperty(options.progressProperty, `${percent}%`); if (snapshot.message && !document.title.startsWith(snapshot.message)) document.title = snapshot.message; paint(); } function read(event) { try { return JSON.parse(event.data); } catch { return; } } setInterval(paint, 100); const source = new EventSource(options.progressPath); let polling = false; source.addEventListener("nuxt:loading", (event) => { const snapshot = read(event); if (snapshot) apply(snapshot); }); source.addEventListener("nuxt:error", () => { source.close(); location.reload(); }); source.addEventListener("nuxt:ready", (event) => { if (polling) { if (read(event)?.reload) { source.close(); location.reload(); } return; } const snapshot = read(event); const message = snapshot && !snapshot.serving && snapshot.message ? snapshot.message : "Starting the app"; label = message.charAt(0).toLowerCase() + message.slice(1); document.title = message; if (snapshot?.reload) { source.close(); document.documentElement.style.setProperty(options.progressProperty, "100%"); paint(); location.reload(); return; } const fraction = typeof snapshot?.progress === "number" && snapshot.progress > 0 ? Math.min(snapshot.progress, 1) : 1; document.documentElement.style.setProperty(options.progressProperty, `${Math.round(fraction * 100)}%`); paint(); polling = true; const controller = new AbortController(); let wait = options.pollInterval; const attempt = async () => { try { if (!(await (await fetch(location.href, { headers: { accept: "text/html" }, signal: controller.signal })).text()).includes(options.captionId)) { controller.abort(); source.close(); location.reload(); return; } } catch {} if (controller.signal.aborted) return; setTimeout(attempt, wait); wait = Math.min(Math.round(wait * 1.5), options.maxPollInterval); }; attempt(); }); } /** Reloads the error page as soon as the next load starts or succeeds. */ function recoveryClient(options) { const source = new EventSource(options.progressPath); let reloading = false; const reload = () => { if (reloading) return; reloading = true; source.close(); location.reload(); }; source.addEventListener("nuxt:ready", reload); source.addEventListener("nuxt:loading", reload); } /** Serialise a client function and its options into an inline `