nitropack-nightly 2.11.0-20250227-194814.c3d0e737 → 2.11.0-20250228-190633.b15db0a7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,7 @@ import { watchConfig, loadConfig } from 'c12';
8
8
  import { resolveCompatibilityDatesFromEnv, formatDate, resolveCompatibilityDates, formatCompatibilityDate } from 'compatx';
9
9
  import { klona } from 'klona/full';
10
10
  import { isDebug, isTest, nodeMajorVersion, provider } from 'std-env';
11
- import { existsSync, promises, accessSync } from 'node:fs';
11
+ import { existsSync, promises } from 'node:fs';
12
12
  import defu$1, { defu } from 'defu';
13
13
  import { withLeadingSlash, withoutTrailingSlash, withTrailingSlash, withBase, parseURL, joinURL, withoutBase } from 'ufo';
14
14
  import { colors } from 'consola/utils';
@@ -19,7 +19,7 @@ export { defineNitroPreset } from 'nitropack/kit';
19
19
  import { findWorkspaceDir } from 'pkg-types';
20
20
  import { createJiti } from 'jiti';
21
21
  import { globby } from 'globby';
22
- import fsp, { readFile, writeFile as writeFile$1, rm } from 'node:fs/promises';
22
+ import fsp, { readFile, rm, writeFile as writeFile$1 } from 'node:fs/promises';
23
23
  import { ofetch } from 'ofetch';
24
24
  import { klona as klona$1 } from 'klona';
25
25
  import { createStorage as createStorage$1, builtinDrivers } from 'unstorage';
@@ -40,16 +40,16 @@ import { gzipSize } from 'gzip-size';
40
40
  import prettyBytes from 'pretty-bytes';
41
41
  import zlib from 'node:zlib';
42
42
  import { walk, parse } from 'ultrahtml';
43
+ import { createError, setResponseHeaders, getRequestURL, setResponseStatus, getResponseHeader, setResponseHeader, send, getRequestHeader, getRequestHeaders, eventHandler, toNodeListener, createApp, fromNodeMiddleware } from 'h3';
43
44
  import { Worker } from 'node:worker_threads';
44
- import { setResponseHeaders, getRequestURL, setResponseStatus, getResponseHeader, setResponseHeader, send, getRequestHeader, getRequestHeaders, eventHandler, createError, createApp, fromNodeMiddleware, toNodeListener } from 'h3';
45
45
  import { createProxyServer } from 'httpxy';
46
- import { listen } from 'listhen';
47
- import { servePlaceholder } from 'serve-placeholder';
48
- import serveStatic from 'serve-static';
49
46
  import { resolve as resolve$1, dirname as dirname$1 } from 'node:path';
50
47
  import { ErrorParser } from 'youch-core';
51
48
  import { Youch } from 'youch';
52
49
  import { SourceMapConsumer } from 'source-map';
50
+ import serveStatic from 'serve-static';
51
+ import { listen } from 'listhen';
52
+ import { servePlaceholder } from 'serve-placeholder';
53
53
 
54
54
  const NitroDefaults = {
55
55
  // General
@@ -1479,17 +1479,12 @@ function startRollupWatcher(nitro, rollupConfig) {
1479
1479
  let start;
1480
1480
  watcher.on("event", (event) => {
1481
1481
  switch (event.code) {
1482
- // The watcher is (re)starting
1483
1482
  case "START": {
1484
- return;
1485
- }
1486
- // Building an individual bundle
1487
- case "BUNDLE_START": {
1488
1483
  start = Date.now();
1489
- return;
1484
+ nitro.hooks.callHook("dev:start");
1485
+ break;
1490
1486
  }
1491
- // Finished building all bundles
1492
- case "END": {
1487
+ case "BUNDLE_END": {
1493
1488
  nitro.hooks.callHook("compiled", nitro);
1494
1489
  if (nitro.options.logging.buildSuccess) {
1495
1490
  nitro.logger.success(
@@ -1498,11 +1493,11 @@ function startRollupWatcher(nitro, rollupConfig) {
1498
1493
  );
1499
1494
  }
1500
1495
  nitro.hooks.callHook("dev:reload");
1501
- return;
1496
+ break;
1502
1497
  }
1503
- // Encountered an error while bundling
1504
1498
  case "ERROR": {
1505
1499
  nitro.logger.error(formatRollupError(event.error));
1500
+ nitro.hooks.callHook("dev:error", event.error);
1506
1501
  }
1507
1502
  }
1508
1503
  });
@@ -2101,6 +2096,169 @@ ${[...parents.values()].map((link) => colors.gray(` \u2502 \u2514\u2500\u2500 L
2101
2096
  }
2102
2097
  }
2103
2098
 
2099
+ function createHTTPProxy(defaults = {}) {
2100
+ const proxy = createProxyServer(defaults);
2101
+ proxy.on("proxyReq", (proxyReq, req) => {
2102
+ if (!proxyReq.hasHeader("x-forwarded-for")) {
2103
+ const address = req.socket.remoteAddress;
2104
+ if (address) {
2105
+ proxyReq.appendHeader("x-forwarded-for", address);
2106
+ }
2107
+ }
2108
+ if (!proxyReq.hasHeader("x-forwarded-port")) {
2109
+ const localPort = req?.socket?.localPort;
2110
+ if (localPort) {
2111
+ proxyReq.setHeader("x-forwarded-port", req.socket.localPort);
2112
+ }
2113
+ }
2114
+ if (!proxyReq.hasHeader("x-forwarded-Proto")) {
2115
+ const encrypted = req?.connection?.encrypted;
2116
+ proxyReq.setHeader("x-forwarded-proto", encrypted ? "https" : "http");
2117
+ }
2118
+ });
2119
+ const handleEvent = async (event, opts = {}) => {
2120
+ try {
2121
+ await proxy.web(event.node.req, event.node.res, opts);
2122
+ } catch (error) {
2123
+ if (error?.code !== "ECONNRESET") {
2124
+ throw error;
2125
+ }
2126
+ }
2127
+ };
2128
+ return {
2129
+ proxy,
2130
+ handleEvent
2131
+ };
2132
+ }
2133
+
2134
+ class NodeDevWorker {
2135
+ closed = false;
2136
+ #id;
2137
+ #workerDir;
2138
+ #hooks;
2139
+ #address;
2140
+ #proxy;
2141
+ #worker;
2142
+ constructor(id, workerDir, hooks = {}) {
2143
+ this.#id = id;
2144
+ this.#workerDir = workerDir;
2145
+ this.#hooks = hooks;
2146
+ this.#proxy = createHTTPProxy();
2147
+ this.#initWorker();
2148
+ }
2149
+ get ready() {
2150
+ return Boolean(
2151
+ !this.closed && this.#address && this.#proxy && this.#worker
2152
+ );
2153
+ }
2154
+ handleEvent(event) {
2155
+ if (!this.#address || !this.#proxy) {
2156
+ throw createError({
2157
+ statusCode: 503,
2158
+ message: "worker is not ready yet"
2159
+ });
2160
+ }
2161
+ return this.#proxy.handleEvent(event, { target: this.#address });
2162
+ }
2163
+ handleUpgrade(req, socket, head) {
2164
+ if (!this.ready) {
2165
+ return;
2166
+ }
2167
+ return this.#proxy.proxy.ws(
2168
+ req,
2169
+ socket,
2170
+ { target: this.#address, xfwd: true },
2171
+ head
2172
+ );
2173
+ }
2174
+ #initWorker() {
2175
+ const workerEntryPath = join(this.#workerDir, "index.mjs");
2176
+ if (!existsSync(workerEntryPath)) {
2177
+ this.close(`worker entry not found in "${workerEntryPath}".`);
2178
+ return;
2179
+ }
2180
+ const worker = new Worker(workerEntryPath, {
2181
+ env: {
2182
+ ...process.env,
2183
+ NITRO_DEV_WORKER_ID: String(this.#id),
2184
+ NITRO_DEV_WORKER_DIR: this.#workerDir
2185
+ }
2186
+ });
2187
+ worker.once("exit", (code) => {
2188
+ worker._exitCode = code;
2189
+ this.close(`worker exited with code ${code}`);
2190
+ });
2191
+ worker.once("error", (error) => {
2192
+ this.close(error);
2193
+ });
2194
+ worker.on("message", (message) => {
2195
+ if (message?.address) {
2196
+ this.#address = message.address;
2197
+ this.#hooks.onReady?.(this, this.#address);
2198
+ }
2199
+ });
2200
+ this.#worker = worker;
2201
+ }
2202
+ async close(cause) {
2203
+ if (this.closed) {
2204
+ return;
2205
+ }
2206
+ this.closed = true;
2207
+ this.#hooks.onClose?.(this, cause);
2208
+ this.#hooks = {};
2209
+ await Promise.all(
2210
+ [this.#closeProxy(), this.#closeSocket(), this.#closeWorker()].map(
2211
+ (p) => p.catch((error) => consola.error(error))
2212
+ )
2213
+ );
2214
+ }
2215
+ async #closeProxy() {
2216
+ this.#proxy?.proxy?.close(() => {
2217
+ });
2218
+ this.#proxy = void 0;
2219
+ }
2220
+ async #closeSocket() {
2221
+ const socketPath = this.#address?.socketPath;
2222
+ if (socketPath && socketPath[0] !== "\0" && !socketPath.startsWith(String.raw`\\.\pipe`)) {
2223
+ await rm(socketPath).catch(() => {
2224
+ });
2225
+ }
2226
+ this.#address = void 0;
2227
+ }
2228
+ async #closeWorker() {
2229
+ if (!this.#worker) {
2230
+ return;
2231
+ }
2232
+ this.#worker.postMessage({ event: "shutdown" });
2233
+ if (!this.#worker._exitCode) {
2234
+ await new Promise((resolve) => {
2235
+ const gracefulShutdownTimeoutSec = Number.parseInt(process.env.NITRO_SHUTDOWN_TIMEOUT || "", 10) || 5;
2236
+ const timeout = setTimeout(() => {
2237
+ consola.warn(
2238
+ `force closing dev worker after ${gracefulShutdownTimeoutSec} seconds...`
2239
+ );
2240
+ resolve();
2241
+ }, gracefulShutdownTimeoutSec * 1e3);
2242
+ this.#worker?.on("message", (message) => {
2243
+ if (message.event === "exit") {
2244
+ clearTimeout(timeout);
2245
+ resolve();
2246
+ }
2247
+ });
2248
+ });
2249
+ }
2250
+ this.#worker.removeAllListeners();
2251
+ await this.#worker.terminate().catch((error) => {
2252
+ consola.error(error);
2253
+ });
2254
+ this.#worker = void 0;
2255
+ }
2256
+ [Symbol.for("nodejs.util.inspect.custom")]() {
2257
+ const status = this.closed ? "closed" : this.ready ? "ready" : "pending";
2258
+ return `NodeDevWorker#${this.#id}(${status})`;
2259
+ }
2260
+ }
2261
+
2104
2262
  function defineNitroErrorHandler(handler) {
2105
2263
  return handler;
2106
2264
  }
@@ -2361,262 +2519,243 @@ const editorTemplate = (options) => `
2361
2519
  `;
2362
2520
 
2363
2521
  function createDevServer(nitro) {
2364
- const workerDir = resolve(
2365
- nitro.options.output.dir,
2366
- nitro.options.output.serverDir
2367
- );
2368
- const errorHandler = nitro.options.devErrorHandler || defaultErrorHandler;
2369
- let lastError;
2370
- let reloadPromise;
2371
- let currentWorker;
2372
- const reloadWorker = async () => {
2373
- const oldWorker = currentWorker;
2374
- currentWorker = void 0;
2375
- await killWorker(oldWorker, nitro);
2376
- currentWorker = await initWorker(workerDir);
2377
- if (!currentWorker) {
2378
- return;
2522
+ const devServer = new DevServer(nitro);
2523
+ return {
2524
+ reload: () => devServer.reload(),
2525
+ listen: (port, opts) => devServer.listen(port, opts),
2526
+ close: () => devServer.close(),
2527
+ upgrade: (req, socket, head) => devServer.handleUpgrade(req, socket, head),
2528
+ get app() {
2529
+ return devServer.app;
2530
+ },
2531
+ get watcher() {
2532
+ return devServer.watcher;
2379
2533
  }
2380
- const buildInfoPath = resolve(nitro.options.buildDir, "nitro.json");
2381
- const buildInfo = {
2382
- date: (/* @__PURE__ */ new Date()).toJSON(),
2383
- preset: nitro.options.preset,
2384
- framework: nitro.options.framework,
2385
- versions: {
2386
- nitro: version
2387
- },
2388
- dev: {
2389
- pid: process.pid,
2390
- workerAddress: currentWorker?.address
2391
- }
2392
- };
2393
- await writeFile$1(buildInfoPath, JSON.stringify(buildInfo, null, 2));
2394
2534
  };
2395
- const reloadWorkerDebounced = debounce(() => {
2396
- reloadPromise = reloadWorker().then(() => {
2397
- lastError = void 0;
2398
- }).catch(async (error) => {
2399
- await loadStackTrace(error).catch(() => {
2400
- });
2401
- consola.error(error);
2402
- lastError = error;
2403
- }).finally(() => {
2404
- reloadPromise = void 0;
2535
+ }
2536
+ class DevServer {
2537
+ nitro;
2538
+ workerDir;
2539
+ app;
2540
+ listeners = [];
2541
+ reloadPromise;
2542
+ watcher;
2543
+ workers = [];
2544
+ workerIdCtr = 0;
2545
+ workerError;
2546
+ building = true;
2547
+ buildError;
2548
+ constructor(nitro) {
2549
+ this.nitro = nitro;
2550
+ this.workerDir = resolve(
2551
+ nitro.options.output.dir,
2552
+ nitro.options.output.serverDir
2553
+ );
2554
+ this.app = this.createApp();
2555
+ nitro.hooks.hook("close", () => this.close());
2556
+ nitro.hooks.hook("dev:start", () => {
2557
+ this.building = true;
2558
+ this.buildError = void 0;
2405
2559
  });
2406
- return reloadPromise;
2407
- });
2408
- nitro.hooks.hook("dev:reload", reloadWorkerDebounced);
2409
- const app = createApp();
2410
- for (const handler of nitro.options.devHandlers) {
2411
- app.use(handler.route || "/", handler.handler);
2560
+ nitro.hooks.hook("dev:reload", () => {
2561
+ this.buildError = void 0;
2562
+ this.building = false;
2563
+ this.reload();
2564
+ });
2565
+ nitro.hooks.hook("dev:error", (cause) => {
2566
+ this.buildError = cause;
2567
+ this.building = false;
2568
+ for (const worker of this.workers) {
2569
+ worker.close();
2570
+ }
2571
+ });
2572
+ if (nitro.options.devServer.watch.length > 0) {
2573
+ const debouncedReload = debounce(() => this.reload());
2574
+ this.watcher = watch(
2575
+ nitro.options.devServer.watch,
2576
+ nitro.options.watchOptions
2577
+ );
2578
+ this.watcher.on("add", debouncedReload).on("change", debouncedReload);
2579
+ }
2412
2580
  }
2413
- app.use("/_vfs", createVFSHandler(nitro));
2414
- for (const asset of nitro.options.publicAssets) {
2415
- const url = joinURL(
2416
- nitro.options.runtimeConfig.app.baseURL,
2417
- asset.baseURL || "/"
2581
+ async listen(port, opts) {
2582
+ const listener = await listen(toNodeListener(this.app), { port, ...opts });
2583
+ this.listeners.push(listener);
2584
+ listener.server.on(
2585
+ "upgrade",
2586
+ (req, sock, head) => this.handleUpgrade(req, sock, head)
2418
2587
  );
2419
- app.use(url, fromNodeMiddleware(serveStatic(asset.dir)));
2420
- if (!asset.fallthrough) {
2421
- app.use(url, fromNodeMiddleware(servePlaceholder()));
2422
- }
2588
+ return listener;
2423
2589
  }
2424
- for (const route of Object.keys(nitro.options.devProxy).sort().reverse()) {
2425
- let opts = nitro.options.devProxy[route];
2426
- if (typeof opts === "string") {
2427
- opts = { target: opts };
2428
- }
2429
- const proxy2 = createProxy(opts);
2430
- app.use(
2431
- route,
2432
- eventHandler(async (event) => {
2433
- await proxy2.handle(event);
2434
- })
2590
+ async close() {
2591
+ await Promise.all(
2592
+ [
2593
+ Promise.all(this.listeners.map((l) => l.close())).then(() => {
2594
+ this.listeners = [];
2595
+ }),
2596
+ Promise.all(this.workers.map((w) => w.close())).then(() => {
2597
+ this.workers = [];
2598
+ }),
2599
+ Promise.resolve(this.watcher?.close()).then(() => {
2600
+ this.watcher = void 0;
2601
+ })
2602
+ ].map(
2603
+ (p) => p.catch((error) => {
2604
+ consola.error(error);
2605
+ })
2606
+ )
2435
2607
  );
2436
2608
  }
2437
- const proxy = createProxy();
2438
- proxy.proxy.on("proxyReq", (proxyReq, req) => {
2439
- if (!proxyReq.hasHeader("x-forwarded-for")) {
2440
- const address = req.socket.remoteAddress;
2441
- if (address) {
2442
- proxyReq.appendHeader("x-forwarded-for", address);
2443
- }
2609
+ reload() {
2610
+ for (const worker2 of this.workers) {
2611
+ worker2.close();
2444
2612
  }
2445
- if (!proxyReq.hasHeader("x-forwarded-port")) {
2446
- const localPort = req?.socket?.localPort;
2447
- if (localPort) {
2448
- proxyReq.setHeader("x-forwarded-port", req.socket.localPort);
2613
+ const worker = new NodeDevWorker(++this.workerIdCtr, this.workerDir, {
2614
+ onClose: (worker2, cause) => {
2615
+ this.workerError = cause;
2616
+ const index = this.workers.indexOf(worker2);
2617
+ if (index !== -1) {
2618
+ this.workers.splice(index, 1);
2619
+ }
2620
+ },
2621
+ onReady: (worker2, addr) => {
2622
+ this.writeBuildInfo(worker2, addr);
2449
2623
  }
2624
+ });
2625
+ if (!worker.closed) {
2626
+ this.workers.unshift(worker);
2450
2627
  }
2451
- if (!proxyReq.hasHeader("x-forwarded-Proto")) {
2452
- const encrypted = req?.connection?.encrypted;
2453
- proxyReq.setHeader("x-forwarded-proto", encrypted ? "https" : "http");
2454
- }
2455
- });
2456
- const getWorkerAddress = () => {
2457
- const address = currentWorker?.address;
2458
- if (!address) {
2459
- return;
2460
- }
2461
- if (address.socketPath) {
2462
- try {
2463
- accessSync(address.socketPath);
2464
- } catch (error) {
2465
- if (!lastError) {
2466
- lastError = error;
2467
- }
2628
+ }
2629
+ async getWorker() {
2630
+ let retry = 0;
2631
+ while (this.building || ++retry < 10) {
2632
+ if ((this.workers.length === 0 || this.buildError) && !this.building) {
2468
2633
  return;
2469
2634
  }
2635
+ const activeWorker = this.workers.find((w) => w.ready);
2636
+ if (activeWorker) {
2637
+ return activeWorker;
2638
+ }
2639
+ await new Promise((resolve2) => setTimeout(resolve2, 500));
2470
2640
  }
2471
- return address;
2472
- };
2473
- app.use(
2474
- eventHandler(async (event) => {
2475
- await reloadPromise;
2476
- let address = getWorkerAddress();
2477
- for (let i = 0; i < 10 && !address && !lastError; i++) {
2478
- await (reloadPromise || new Promise((resolve2) => setTimeout(resolve2, 200)));
2479
- address = getWorkerAddress();
2641
+ }
2642
+ writeBuildInfo(_worker, addr) {
2643
+ const buildInfoPath = resolve(this.nitro.options.buildDir, "nitro.json");
2644
+ const buildInfo = {
2645
+ date: (/* @__PURE__ */ new Date()).toJSON(),
2646
+ preset: this.nitro.options.preset,
2647
+ framework: this.nitro.options.framework,
2648
+ versions: {
2649
+ nitro: version
2650
+ },
2651
+ dev: {
2652
+ pid: process.pid,
2653
+ workerAddress: addr
2480
2654
  }
2481
- if (!address || lastError) {
2482
- return errorHandler(
2483
- lastError || createError({
2484
- statusCode: 503,
2485
- message: "Worker not ready"
2486
- }),
2487
- event
2488
- );
2655
+ };
2656
+ writeFile$1(buildInfoPath, JSON.stringify(buildInfo, null, 2)).catch(
2657
+ (error) => {
2658
+ consola.error(error);
2489
2659
  }
2490
- await proxy.handle(event, { target: address }).catch((error) => {
2491
- lastError = error;
2492
- throw error;
2493
- });
2494
- })
2495
- );
2496
- const upgrade = (req, socket, head) => {
2497
- return proxy.proxy.ws(
2498
- req,
2499
- // @ts-expect-error
2500
- socket,
2501
- {
2502
- target: getWorkerAddress(),
2503
- xfwd: true
2504
- },
2505
- head
2506
2660
  );
2507
- };
2508
- let listeners = [];
2509
- const listen$1 = async (port, opts) => {
2510
- const listener = await listen(toNodeListener(app), { port, ...opts });
2511
- listener.server.on("upgrade", (req, sock, head) => {
2512
- upgrade(req, sock, head);
2513
- });
2514
- listeners.push(listener);
2515
- return listener;
2516
- };
2517
- let watcher;
2518
- if (nitro.options.devServer.watch.length > 0) {
2519
- watcher = watch(nitro.options.devServer.watch, nitro.options.watchOptions);
2520
- watcher.on("add", reloadWorkerDebounced).on("change", reloadWorkerDebounced);
2521
- }
2522
- const close = async () => {
2523
- if (watcher) {
2524
- await watcher.close();
2525
- }
2526
- await killWorker(currentWorker, nitro);
2527
- await Promise.all(listeners.map((l) => l.close()));
2528
- listeners = [];
2529
- };
2530
- nitro.hooks.hook("close", close);
2531
- return {
2532
- reload: reloadWorkerDebounced,
2533
- listen: listen$1,
2534
- app,
2535
- close,
2536
- watcher,
2537
- upgrade
2538
- };
2539
- }
2540
- function initWorker(workerDir) {
2541
- const workerEntry = join(workerDir, "index.mjs");
2542
- if (!existsSync(workerEntry)) {
2543
- return;
2544
2661
  }
2545
- return new Promise((resolve2, reject) => {
2546
- const worker = new Worker(workerEntry, {
2547
- env: {
2548
- ...process.env,
2549
- NITRO_DEV_WORKER_DIR: workerDir
2662
+ createApp() {
2663
+ const app = createApp({
2664
+ onError: async (error, event) => {
2665
+ const errorHandler = this.nitro.options.devErrorHandler || defaultErrorHandler;
2666
+ await loadStackTrace(error).catch(() => {
2667
+ });
2668
+ return errorHandler(error, event);
2550
2669
  }
2551
2670
  });
2552
- worker.once("exit", (code) => {
2553
- reject(
2554
- new Error(
2555
- code ? "[worker] exited with code: " + code : "[worker] exited"
2556
- )
2671
+ for (const handler of this.nitro.options.devHandlers) {
2672
+ app.use(handler.route || "/", handler.handler);
2673
+ }
2674
+ app.use("/_vfs", createVFSHandler(this.nitro));
2675
+ for (const asset of this.nitro.options.publicAssets) {
2676
+ const url = joinURL(
2677
+ this.nitro.options.runtimeConfig.app.baseURL,
2678
+ asset.baseURL || "/"
2557
2679
  );
2558
- });
2559
- worker.once("error", async (error) => {
2560
- reject(error);
2561
- });
2562
- const addressListener = (event) => {
2563
- if (!event || !event?.address) {
2564
- return;
2680
+ app.use(url, fromNodeMiddleware(serveStatic(asset.dir)));
2681
+ if (!asset.fallthrough) {
2682
+ app.use(url, fromNodeMiddleware(servePlaceholder()));
2565
2683
  }
2566
- worker.off("message", addressListener);
2567
- resolve2({
2568
- worker,
2569
- address: event.address
2570
- });
2571
- };
2572
- worker.on("message", addressListener);
2573
- });
2574
- }
2575
- async function killWorker(worker, nitro) {
2576
- if (!worker) {
2577
- return;
2578
- }
2579
- if (worker.worker) {
2580
- worker.worker.postMessage({ event: "shutdown" });
2581
- const gracefulShutdownTimeout = Number.parseInt(process.env.NITRO_SHUTDOWN_TIMEOUT || "", 10) || 3;
2582
- await new Promise((resolve2) => {
2583
- const timeout = setTimeout(() => {
2584
- nitro.logger.warn(
2585
- `[nitro] [dev] Force closing worker after ${gracefulShutdownTimeout} seconds...`
2586
- );
2587
- resolve2();
2588
- }, gracefulShutdownTimeout * 1e3);
2589
- worker.worker?.once("message", (message) => {
2590
- if (message.event === "exit") {
2591
- clearTimeout(timeout);
2592
- resolve2();
2684
+ }
2685
+ const routes = Object.keys(this.nitro.options.devProxy).sort().reverse();
2686
+ for (const route of routes) {
2687
+ let opts = this.nitro.options.devProxy[route];
2688
+ if (typeof opts === "string") {
2689
+ opts = { target: opts };
2690
+ }
2691
+ const proxy = createHTTPProxy(opts);
2692
+ app.use(
2693
+ route,
2694
+ eventHandler((event) => proxy.handleEvent(event))
2695
+ );
2696
+ }
2697
+ app.use(
2698
+ eventHandler(async (event) => {
2699
+ const worker = await this.getWorker();
2700
+ if (!worker) {
2701
+ return this.#generateError();
2593
2702
  }
2703
+ return worker.handleEvent(event);
2704
+ })
2705
+ );
2706
+ return app;
2707
+ }
2708
+ async handleUpgrade(req, socket, head) {
2709
+ const worker = await this.getWorker();
2710
+ if (!worker) {
2711
+ throw createError({
2712
+ statusCode: 503,
2713
+ message: "No worker available."
2594
2714
  });
2595
- });
2596
- worker.worker.removeAllListeners();
2597
- await worker.worker.terminate();
2598
- worker.worker = null;
2599
- }
2600
- if (worker.address.socketPath) {
2601
- await rm(worker.address.socketPath).catch(() => {
2602
- });
2715
+ }
2716
+ return worker.handleUpgrade(req, socket, head);
2603
2717
  }
2604
- }
2605
- function createProxy(defaults = {}) {
2606
- const proxy = createProxyServer(defaults);
2607
- const handle = async (event, opts = {}) => {
2608
- try {
2609
- await proxy.web(event.node.req, event.node.res, opts);
2610
- } catch (error) {
2611
- if (error?.code !== "ECONNRESET") {
2612
- throw error;
2718
+ #generateError() {
2719
+ const error = this.buildError || this.workerError;
2720
+ if (error) {
2721
+ try {
2722
+ error.unhandled = false;
2723
+ let id = error.id || error.path;
2724
+ if (id) {
2725
+ const cause = error.errors?.[0];
2726
+ const loc = error.location || error.loc || cause?.location || cause?.loc;
2727
+ if (loc) {
2728
+ id += `:${loc.line}:${loc.column}`;
2729
+ }
2730
+ error.stack = (error.stack || "").replace(
2731
+ /(^\s*at\s+.+)/m,
2732
+ ` at ${id}
2733
+ $1`
2734
+ );
2735
+ }
2736
+ } catch {
2613
2737
  }
2738
+ return createError(error);
2614
2739
  }
2615
- };
2616
- return {
2617
- proxy,
2618
- handle
2619
- };
2740
+ return new Response(
2741
+ JSON.stringify(
2742
+ {
2743
+ error: "The dev server is unavailable.",
2744
+ hint: "Please reload the page and check the console for errors if the issue persists."
2745
+ },
2746
+ null,
2747
+ 2
2748
+ ),
2749
+ {
2750
+ status: 503,
2751
+ headers: {
2752
+ "Content-Type": "application/json",
2753
+ "Cache-Control": "no-store",
2754
+ Refresh: "3"
2755
+ }
2756
+ }
2757
+ );
2758
+ }
2620
2759
  }
2621
2760
 
2622
2761
  const NEGATION_RE = /^(!?)(.*)$/;
@@ -1,5 +1,5 @@
1
1
  import { NitroPreset, NitroPresetMeta, Nitro } from 'nitropack/types';
2
- import { N as NitroModule } from '../shared/nitro.CkNr_mO9.mjs';
2
+ import { N as NitroModule } from '../shared/nitro.Bp0y4NyZ.mjs';
3
3
  import 'consola';
4
4
  import 'h3';
5
5
  import 'hookable';
@@ -1,5 +1,5 @@
1
1
  import { NitroPreset, NitroPresetMeta, Nitro } from 'nitropack/types';
2
- import { N as NitroModule } from '../shared/nitro.CkNr_mO9.js';
2
+ import { N as NitroModule } from '../shared/nitro.Bp0y4NyZ.js';
3
3
  import 'consola';
4
4
  import 'h3';
5
5
  import 'hookable';
@@ -1,3 +1,3 @@
1
- const version = "2.11.0-20250227-194814.c3d0e737";
1
+ const version = "2.11.0-20250228-190633.b15db0a7";
2
2
 
3
3
  export { version };
@@ -1,3 +1,3 @@
1
- const version = "2.11.0-20250227-194814.c3d0e737";
1
+ const version = "2.11.0-20250228-190633.b15db0a7";
2
2
 
3
3
  export { version };
@@ -1,3 +1,3 @@
1
- const version = "2.11.0-20250227-194814.c3d0e737";
1
+ const version = "2.11.0-20250228-190633.b15db0a7";
2
2
 
3
3
  export { version };
@@ -6,7 +6,7 @@ import { startScheduleRunner } from "nitropack/runtime/internal";
6
6
  import { scheduledTasks, tasks } from "#nitro-internal-virtual/tasks";
7
7
  import { Server } from "node:http";
8
8
  import { join } from "node:path";
9
- import { parentPort, threadId } from "node:worker_threads";
9
+ import { parentPort } from "node:worker_threads";
10
10
  import wsAdapter from "crossws/adapters/node";
11
11
  import {
12
12
  defineEventHandler,
@@ -15,7 +15,7 @@ import {
15
15
  readBody,
16
16
  toNodeListener
17
17
  } from "h3";
18
- import { isWindows, provider } from "std-env";
18
+ const { NITRO_NO_UNIX_SOCKET, NITRO_DEV_WORKER_DIR, NITRO_DEV_WORKER_ID } = process.env;
19
19
  const nitroApp = useNitroApp();
20
20
  const server = new Server(toNodeListener(nitroApp.h3App));
21
21
  if (import.meta._websocket) {
@@ -23,15 +23,21 @@ if (import.meta._websocket) {
23
23
  server.on("upgrade", handleUpgrade);
24
24
  }
25
25
  function getAddress() {
26
- if (provider === "stackblitz" || process.env.NITRO_NO_UNIX_SOCKET || process.versions.bun) {
26
+ if (NITRO_NO_UNIX_SOCKET || process.versions.webcontainer) {
27
27
  return 0;
28
28
  }
29
- const socketName = `worker-${process.pid}-${threadId}.sock`;
30
- if (isWindows) {
31
- return join(String.raw`\\.\pipe\nitro`, socketName);
29
+ const socketName = `worker-${process.pid}-${NITRO_DEV_WORKER_ID}.sock`;
30
+ switch (process.platform) {
31
+ case "win32": {
32
+ return join(String.raw`\\.\pipe\nitro`, socketName);
33
+ }
34
+ case "linux": {
35
+ return `\0${socketName}`;
36
+ }
37
+ default: {
38
+ return join(NITRO_DEV_WORKER_DIR || ".", socketName);
39
+ }
32
40
  }
33
- const workerDir = process.env.NITRO_DEV_WORKER_DIR || import.meta.dirname || process.cwd();
34
- return join(workerDir, socketName);
35
41
  }
36
42
  const listenAddress = getAddress();
37
43
  const listener = server.listen(listenAddress, () => {
@@ -174,6 +174,8 @@ interface NitroHooks {
174
174
  "rollup:before": (nitro: Nitro, config: RollupConfig) => HookResult;
175
175
  compiled: (nitro: Nitro) => HookResult;
176
176
  "dev:reload": () => HookResult;
177
+ "dev:start": () => HookResult;
178
+ "dev:error": (cause?: unknown) => HookResult;
177
179
  "rollup:reload": () => HookResult;
178
180
  restart: () => HookResult;
179
181
  close: () => HookResult;
@@ -629,7 +631,7 @@ interface NitroBuildInfo {
629
631
  };
630
632
  dev?: {
631
633
  pid: number;
632
- workerAddress: {
634
+ workerAddress?: {
633
635
  host: string;
634
636
  port: number;
635
637
  socketPath?: string;
@@ -174,6 +174,8 @@ interface NitroHooks {
174
174
  "rollup:before": (nitro: Nitro, config: RollupConfig) => HookResult;
175
175
  compiled: (nitro: Nitro) => HookResult;
176
176
  "dev:reload": () => HookResult;
177
+ "dev:start": () => HookResult;
178
+ "dev:error": (cause?: unknown) => HookResult;
177
179
  "rollup:reload": () => HookResult;
178
180
  restart: () => HookResult;
179
181
  close: () => HookResult;
@@ -629,7 +631,7 @@ interface NitroBuildInfo {
629
631
  };
630
632
  dev?: {
631
633
  pid: number;
632
- workerAddress: {
634
+ workerAddress?: {
633
635
  host: string;
634
636
  port: number;
635
637
  socketPath?: string;
@@ -1,7 +1,7 @@
1
1
  import { App, Router, H3Event, RouterMethod } from 'h3';
2
2
  import { FetchRequest, FetchOptions, FetchResponse } from 'ofetch';
3
- import { a as NitroOptions, b as NitroConfig, N as NitroModule } from '../shared/nitro.CkNr_mO9.mjs';
4
- export { A as AppConfig, C as CacheEntry, c as CacheOptions, d as CachedEventHandlerOptions, e as CompressOptions, g as DatabaseConnectionConfig, h as DatabaseConnectionConfigs, D as DatabaseConnectionName, l as DevServerOptions, I as EsbuildOptions, O as HTTPStatusCode, L as LoadConfigOptions, u as Nitro, y as NitroBuildInfo, q as NitroDevEventHandler, n as NitroDevServer, v as NitroDynamicConfig, r as NitroErrorHandler, p as NitroEventHandler, x as NitroFrameworkInfo, s as NitroHooks, t as NitroModuleInput, k as NitroOpenAPIConfig, E as NitroPreset, F as NitroPresetMeta, Q as NitroRouteConfig, o as NitroRouteMeta, T as NitroRouteRules, j as NitroRuntimeConfig, i as NitroRuntimeConfigApp, w as NitroTypes, m as NitroWorker, J as NodeExternalsOptions, B as PrerenderGenerateRoute, z as PrerenderRoute, P as PublicAssetDir, M as RawOptions, R as ResponseCacheEntry, G as RollupConfig, H as RollupVirtualOptions, S as ServerAssetDir, K as ServerAssetOptions, f as StorageMounts, V as VirtualModule } from '../shared/nitro.CkNr_mO9.mjs';
3
+ import { a as NitroOptions, b as NitroConfig, N as NitroModule } from '../shared/nitro.Bp0y4NyZ.mjs';
4
+ export { A as AppConfig, C as CacheEntry, c as CacheOptions, d as CachedEventHandlerOptions, e as CompressOptions, g as DatabaseConnectionConfig, h as DatabaseConnectionConfigs, D as DatabaseConnectionName, l as DevServerOptions, I as EsbuildOptions, O as HTTPStatusCode, L as LoadConfigOptions, u as Nitro, y as NitroBuildInfo, q as NitroDevEventHandler, n as NitroDevServer, v as NitroDynamicConfig, r as NitroErrorHandler, p as NitroEventHandler, x as NitroFrameworkInfo, s as NitroHooks, t as NitroModuleInput, k as NitroOpenAPIConfig, E as NitroPreset, F as NitroPresetMeta, Q as NitroRouteConfig, o as NitroRouteMeta, T as NitroRouteRules, j as NitroRuntimeConfig, i as NitroRuntimeConfigApp, w as NitroTypes, m as NitroWorker, J as NodeExternalsOptions, B as PrerenderGenerateRoute, z as PrerenderRoute, P as PublicAssetDir, M as RawOptions, R as ResponseCacheEntry, G as RollupConfig, H as RollupVirtualOptions, S as ServerAssetDir, K as ServerAssetOptions, f as StorageMounts, V as VirtualModule } from '../shared/nitro.Bp0y4NyZ.mjs';
5
5
  import { Hookable } from 'hookable';
6
6
  import { NitroRuntimeHooks as NitroRuntimeHooks$1 } from 'nitropack';
7
7
  import { AbstractRequest, AbstractResponse } from 'node-mock-http';
@@ -1,7 +1,7 @@
1
1
  import { App, Router, H3Event, RouterMethod } from 'h3';
2
2
  import { FetchRequest, FetchOptions, FetchResponse } from 'ofetch';
3
- import { a as NitroOptions, b as NitroConfig, N as NitroModule } from '../shared/nitro.CkNr_mO9.js';
4
- export { A as AppConfig, C as CacheEntry, c as CacheOptions, d as CachedEventHandlerOptions, e as CompressOptions, g as DatabaseConnectionConfig, h as DatabaseConnectionConfigs, D as DatabaseConnectionName, l as DevServerOptions, I as EsbuildOptions, O as HTTPStatusCode, L as LoadConfigOptions, u as Nitro, y as NitroBuildInfo, q as NitroDevEventHandler, n as NitroDevServer, v as NitroDynamicConfig, r as NitroErrorHandler, p as NitroEventHandler, x as NitroFrameworkInfo, s as NitroHooks, t as NitroModuleInput, k as NitroOpenAPIConfig, E as NitroPreset, F as NitroPresetMeta, Q as NitroRouteConfig, o as NitroRouteMeta, T as NitroRouteRules, j as NitroRuntimeConfig, i as NitroRuntimeConfigApp, w as NitroTypes, m as NitroWorker, J as NodeExternalsOptions, B as PrerenderGenerateRoute, z as PrerenderRoute, P as PublicAssetDir, M as RawOptions, R as ResponseCacheEntry, G as RollupConfig, H as RollupVirtualOptions, S as ServerAssetDir, K as ServerAssetOptions, f as StorageMounts, V as VirtualModule } from '../shared/nitro.CkNr_mO9.js';
3
+ import { a as NitroOptions, b as NitroConfig, N as NitroModule } from '../shared/nitro.Bp0y4NyZ.js';
4
+ export { A as AppConfig, C as CacheEntry, c as CacheOptions, d as CachedEventHandlerOptions, e as CompressOptions, g as DatabaseConnectionConfig, h as DatabaseConnectionConfigs, D as DatabaseConnectionName, l as DevServerOptions, I as EsbuildOptions, O as HTTPStatusCode, L as LoadConfigOptions, u as Nitro, y as NitroBuildInfo, q as NitroDevEventHandler, n as NitroDevServer, v as NitroDynamicConfig, r as NitroErrorHandler, p as NitroEventHandler, x as NitroFrameworkInfo, s as NitroHooks, t as NitroModuleInput, k as NitroOpenAPIConfig, E as NitroPreset, F as NitroPresetMeta, Q as NitroRouteConfig, o as NitroRouteMeta, T as NitroRouteRules, j as NitroRuntimeConfig, i as NitroRuntimeConfigApp, w as NitroTypes, m as NitroWorker, J as NodeExternalsOptions, B as PrerenderGenerateRoute, z as PrerenderRoute, P as PublicAssetDir, M as RawOptions, R as ResponseCacheEntry, G as RollupConfig, H as RollupVirtualOptions, S as ServerAssetDir, K as ServerAssetOptions, f as StorageMounts, V as VirtualModule } from '../shared/nitro.Bp0y4NyZ.js';
5
5
  import { Hookable } from 'hookable';
6
6
  import { NitroRuntimeHooks as NitroRuntimeHooks$1 } from 'nitropack';
7
7
  import { AbstractRequest, AbstractResponse } from 'node-mock-http';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitropack-nightly",
3
- "version": "2.11.0-20250227-194814.c3d0e737",
3
+ "version": "2.11.0-20250228-190633.b15db0a7",
4
4
  "description": "Build and Deploy Universal JavaScript Servers",
5
5
  "repository": "nitrojs/nitro",
6
6
  "license": "MIT",