nitro-nightly 3.1.0-20251029-083418-84ceb86e → 3.1.0-20251029-124449-782162a4

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.
@@ -291,7 +291,6 @@ import * as __routeRules__ from "nitro/runtime/internal/route-rules";
291
291
  ${nitro.options.serverEntry ? `import __serverEntry__ from ${JSON.stringify(nitro.options.serverEntry)};` : ""}
292
292
  import {${h3Imports.join(", ")}} from "nitro/deps/h3";
293
293
 
294
- export const hasRouteRules = ${nitro.routing.routeRules.hasRoutes() ? "true" : "false"};
295
294
  export const findRouteRules = ${nitro.routing.routeRules.compileToString({ serialize: serializeRouteRule, matchAll: true })}
296
295
 
297
296
  ${allHandlers.filter((h) => !h.lazy).map((h) => (
@@ -306,13 +305,10 @@ ${allHandlers.filter((h) => h.lazy).map(
306
305
  )
307
306
  ).join("\n")}
308
307
 
309
- export const hasRoutes = ${nitro.routing.routes.hasRoutes() ? "true" : "false"};
310
308
  export const findRoute = ${nitro.routing.routes.compileToString({ serialize: serializeHandler })}
311
309
 
312
- export const hasRoutedMiddleware = ${nitro.routing.routedMiddleware.hasRoutes() ? "true" : "false"};
313
310
  export const findRoutedMiddleware = ${nitro.routing.routedMiddleware.compileToString({ serialize: serializeHandler, matchAll: true })};
314
311
 
315
- export const hasGlobalMiddleware = ${nitro.routing.globalMiddleware.length > 0 || nitro.options.serverEntry ? "true" : "false"};
316
312
  export const globalMiddleware = [
317
313
  ${nitro.routing.globalMiddleware.map((h) => h.lazy ? h._importHash : `toEventHandler(${h._importHash})`).join(",")}
318
314
  ${nitro.options.serverEntry ? `,toMiddleware(__serverEntry__)` : ""}
@@ -827,6 +823,35 @@ function rendererTemplate(nitro) {
827
823
  );
828
824
  }
829
825
 
826
+ function featureFlags(nitro) {
827
+ return virtual(
828
+ {
829
+ "#nitro-internal-virtual/feature-flags": () => {
830
+ const featureFlags2 = {
831
+ // Routing
832
+ hasRoutes: nitro.routing.routes.hasRoutes(),
833
+ hasRouteRules: nitro.routing.routeRules.hasRoutes(),
834
+ hasRoutedMiddleware: nitro.routing.routedMiddleware.hasRoutes(),
835
+ hasGlobalMiddleware: nitro.routing.globalMiddleware.length > 0 || !!nitro.options.serverEntry,
836
+ // Plugins
837
+ hasPlugins: nitro.options.plugins.length > 0,
838
+ hasHooks: nitro.options.features?.runtimeHooks ?? nitro.options.plugins.length > 0
839
+ };
840
+ return (
841
+ /* js */
842
+ Object.entries(featureFlags2).map(
843
+ ([key, value]) => (
844
+ /* js */
845
+ `export const ${key} = ${Boolean(value)};`
846
+ )
847
+ ).join("\n")
848
+ );
849
+ }
850
+ },
851
+ nitro.vfs
852
+ );
853
+ }
854
+
830
855
  function baseBuildPlugins(nitro, base) {
831
856
  const plugins = [];
832
857
  if (nitro.options.imports) {
@@ -856,6 +881,7 @@ function baseBuildPlugins(nitro, base) {
856
881
  nitro.vfs
857
882
  )
858
883
  );
884
+ plugins.push(featureFlags(nitro));
859
885
  plugins.push(serverAssets(nitro));
860
886
  plugins.push(publicAssets(nitro));
861
887
  plugins.push(storage(nitro));
@@ -738,7 +738,9 @@ function nitroMain(ctx) {
738
738
  sharedConfigBuild: true
739
739
  },
740
740
  server: {
741
- port: Number.parseInt(process.env.PORT || "") || userConfig.server?.port || useNitro(ctx).options.devServer?.port || 3e3
741
+ port: Number.parseInt(process.env.PORT || "") || userConfig.server?.port || useNitro(ctx).options.devServer?.port || 3e3,
742
+ // #3673, disable Vite's `cors` by default as Nitro handles all requests
743
+ cors: false
742
744
  }
743
745
  };
744
746
  },
@@ -1,5 +1,5 @@
1
1
  import "#nitro-internal-pollyfills";
2
- import { useNitroApp } from "nitro/runtime";
2
+ import { useNitroApp, useNitroHooks } from "nitro/runtime";
3
3
  import { trapUnhandledNodeErrors } from "nitro/runtime/internal";
4
4
  import { startScheduleRunner } from "nitro/runtime/internal";
5
5
  import { Server } from "node:http";
@@ -18,6 +18,7 @@ parentPort?.on("message", (msg) => {
18
18
  }
19
19
  });
20
20
  const nitroApp = useNitroApp();
21
+ const nitroHooks = useNitroHooks();
21
22
  const server = new Server(toNodeHandler(nitroApp.fetch));
22
23
  let listener;
23
24
  listen().catch((error) => {
@@ -56,7 +57,7 @@ async function shutdown() {
56
57
  server.closeAllConnections?.();
57
58
  await Promise.all([
58
59
  new Promise((resolve) => listener?.close(resolve)),
59
- nitroApp.hooks.callHook("close").catch(console.error)
60
+ nitroHooks.callHook("close").catch(console.error)
60
61
  ]);
61
62
  parentPort?.postMessage({ event: "exit" });
62
63
  }
@@ -1,11 +1,12 @@
1
1
  import "#nitro-internal-pollyfills";
2
2
  import consola from "consola";
3
- import { useNitroApp } from "nitro/runtime";
3
+ import { useNitroApp, useNitroHooks } from "nitro/runtime";
4
4
  import { trapUnhandledNodeErrors } from "nitro/runtime/internal";
5
5
  const nitroApp = useNitroApp();
6
+ const nitroHooks = useNitroHooks();
6
7
  export const appFetch = nitroApp.fetch;
7
- export const closePrerenderer = () => nitroApp.hooks.callHook("close");
8
- nitroApp.hooks.hook("error", (error, context) => {
8
+ export const closePrerenderer = () => nitroHooks.callHook("close");
9
+ nitroHooks.hook("error", (error, context) => {
9
10
  if (!error.unhandled && error.status >= 500 && context.event?.req?.headers instanceof Headers && context.event.req.headers.get("x-nitro-prerender")) {
10
11
  consola.error(
11
12
  `[prerender error]`,
@@ -1,8 +1,9 @@
1
1
  import "#nitro-internal-pollyfills";
2
- import { useNitroApp } from "nitro/runtime";
2
+ import { useNitroApp, useNitroHooks } from "nitro/runtime";
3
3
  import { runCronTasks } from "nitro/runtime/internal";
4
4
  export function createHandler(hooks) {
5
5
  const nitroApp = useNitroApp();
6
+ const nitroHooks = useNitroHooks();
6
7
  return {
7
8
  async fetch(request, env, context) {
8
9
  const ctxExt = {};
@@ -18,7 +19,7 @@ export function createHandler(hooks) {
18
19
  scheduled(controller, env, context) {
19
20
  globalThis.__env__ = env;
20
21
  context.waitUntil(
21
- nitroApp.hooks.callHook("cloudflare:scheduled", {
22
+ nitroHooks.callHook("cloudflare:scheduled", {
22
23
  controller,
23
24
  env,
24
25
  context
@@ -41,7 +42,7 @@ export function createHandler(hooks) {
41
42
  email(message, env, context) {
42
43
  globalThis.__env__ = env;
43
44
  context.waitUntil(
44
- nitroApp.hooks.callHook("cloudflare:email", {
45
+ nitroHooks.callHook("cloudflare:email", {
45
46
  message,
46
47
  event: message,
47
48
  // backward compat
@@ -53,7 +54,7 @@ export function createHandler(hooks) {
53
54
  queue(batch, env, context) {
54
55
  globalThis.__env__ = env;
55
56
  context.waitUntil(
56
- nitroApp.hooks.callHook("cloudflare:queue", {
57
+ nitroHooks.callHook("cloudflare:queue", {
57
58
  batch,
58
59
  event: batch,
59
60
  env,
@@ -64,7 +65,7 @@ export function createHandler(hooks) {
64
65
  tail(traces, env, context) {
65
66
  globalThis.__env__ = env;
66
67
  context.waitUntil(
67
- nitroApp.hooks.callHook("cloudflare:tail", {
68
+ nitroHooks.callHook("cloudflare:tail", {
68
69
  traces,
69
70
  env,
70
71
  context
@@ -74,7 +75,7 @@ export function createHandler(hooks) {
74
75
  trace(traces, env, context) {
75
76
  globalThis.__env__ = env;
76
77
  context.waitUntil(
77
- nitroApp.hooks.callHook("cloudflare:trace", {
78
+ nitroHooks.callHook("cloudflare:trace", {
78
79
  traces,
79
80
  env,
80
81
  context
@@ -1,12 +1,13 @@
1
1
  import "#nitro-internal-pollyfills";
2
2
  import { DurableObject } from "cloudflare:workers";
3
3
  import wsAdapter from "crossws/adapters/cloudflare";
4
- import { useNitroApp } from "nitro/runtime";
4
+ import { useNitroApp, useNitroHooks } from "nitro/runtime";
5
5
  import { isPublicAssetURL } from "#nitro-internal-virtual/public-assets";
6
6
  import { createHandler, fetchHandler } from "./_module-handler.mjs";
7
7
  const DURABLE_BINDING = "$DurableObject";
8
8
  const DURABLE_INSTANCE = "server";
9
9
  const nitroApp = useNitroApp();
10
+ const nitroHooks = useNitroHooks();
10
11
  const getDurableStub = (env) => {
11
12
  const binding = env[DURABLE_BINDING];
12
13
  if (!binding) {
@@ -38,7 +39,7 @@ export class $DurableObject extends DurableObject {
38
39
  constructor(state, env) {
39
40
  super(state, env);
40
41
  state.waitUntil(
41
- nitroApp.hooks.callHook("cloudflare:durable:init", this, {
42
+ nitroHooks.callHook("cloudflare:durable:init", this, {
42
43
  state,
43
44
  env
44
45
  })
@@ -57,9 +58,7 @@ export class $DurableObject extends DurableObject {
57
58
  });
58
59
  }
59
60
  alarm() {
60
- this.ctx.waitUntil(
61
- nitroApp.hooks.callHook("cloudflare:durable:alarm", this)
62
- );
61
+ this.ctx.waitUntil(nitroHooks.callHook("cloudflare:durable:alarm", this));
63
62
  }
64
63
  async webSocketMessage(client, message) {
65
64
  if (import.meta._websocket) {
@@ -33,7 +33,7 @@ const listener = server.listen(path ? { path } : { port, host }, (err) => {
33
33
  console.log(`Listening on ${url}`);
34
34
  });
35
35
  trapUnhandledNodeErrors();
36
- setupGracefulShutdown(listener, nitroApp);
36
+ setupGracefulShutdown(listener);
37
37
  if (import.meta._websocket) {
38
38
  const { handleUpgrade } = wsAdapter(nitroApp.h3App.websocket);
39
39
  server.on("upgrade", handleUpgrade);
@@ -5,7 +5,7 @@ export { useRuntimeConfig } from "./internal/runtime-config.mjs";
5
5
  export { useRequest } from "./internal/context.mjs";
6
6
  export { defineRenderHandler } from "./internal/renderer.mjs";
7
7
  export { defineCachedFunction, defineCachedEventHandler, defineCachedHandler, cachedFunction, cachedEventHandler, } from "./internal/cache.mjs";
8
- export { useNitroApp } from "./internal/app.mjs";
8
+ export { useNitroApp, useNitroHooks } from "./internal/app.mjs";
9
9
  export { useStorage } from "./internal/storage.mjs";
10
10
  export { useDatabase } from "./internal/database.mjs";
11
11
  export { defineTask, runTask } from "./internal/task.mjs";
@@ -11,7 +11,7 @@ export {
11
11
  cachedFunction,
12
12
  cachedEventHandler
13
13
  } from "./internal/cache.mjs";
14
- export { useNitroApp } from "./internal/app.mjs";
14
+ export { useNitroApp, useNitroHooks } from "./internal/app.mjs";
15
15
  export { useStorage } from "./internal/storage.mjs";
16
16
  export { useDatabase } from "./internal/database.mjs";
17
17
  export { defineTask, runTask } from "./internal/task.mjs";
@@ -1,2 +1,4 @@
1
- import type { NitroApp } from "nitro/types";
1
+ import type { NitroApp, NitroRuntimeHooks } from "nitro/types";
2
+ import { HookableCore } from "hookable";
2
3
  export declare function useNitroApp(): NitroApp;
4
+ export declare function useNitroHooks(): HookableCore<NitroRuntimeHooks>;
@@ -4,34 +4,48 @@ import { nitroAsyncContext } from "./context.mjs";
4
4
  import errorHandler from "#nitro-internal-virtual/error-handler";
5
5
  import { plugins } from "#nitro-internal-virtual/plugins";
6
6
  import {
7
- hasRouteRules,
8
7
  findRoute,
9
8
  findRouteRules,
10
9
  globalMiddleware,
11
- findRoutedMiddleware,
10
+ findRoutedMiddleware
11
+ } from "#nitro-internal-virtual/routing";
12
+ import {
13
+ hasRouteRules,
12
14
  hasRoutedMiddleware,
13
15
  hasGlobalMiddleware,
14
- hasRoutes
15
- } from "#nitro-internal-virtual/routing";
16
+ hasRoutes,
17
+ hasHooks,
18
+ hasPlugins
19
+ } from "#nitro-internal-virtual/feature-flags";
16
20
  export function useNitroApp() {
17
21
  return useNitroApp.__instance__ ??= initNitroApp();
18
22
  }
23
+ export function useNitroHooks() {
24
+ const nitroApp = useNitroApp();
25
+ const hooks = nitroApp.hooks;
26
+ if (hooks) {
27
+ return hooks;
28
+ }
29
+ return nitroApp.hooks = new HookableCore();
30
+ }
19
31
  function initNitroApp() {
20
32
  const nitroApp = createNitroApp();
21
- for (const plugin of plugins) {
22
- try {
23
- plugin(nitroApp);
24
- } catch (error) {
25
- nitroApp.captureError(error, { tags: ["plugin"] });
26
- throw error;
33
+ if (hasPlugins) {
34
+ for (const plugin of plugins) {
35
+ try {
36
+ plugin(nitroApp);
37
+ } catch (error) {
38
+ nitroApp.captureError(error, { tags: ["plugin"] });
39
+ throw error;
40
+ }
27
41
  }
28
42
  }
29
43
  return nitroApp;
30
44
  }
31
45
  function createNitroApp() {
32
- const hooks = new HookableCore();
46
+ const hooks = hasHooks ? new HookableCore() : void 0;
33
47
  const captureError = (error, errorCtx) => {
34
- const promise = hooks.callHook("error", error, errorCtx)?.catch?.((hookError) => {
48
+ const promise = hasHooks && hooks.callHook("error", error, errorCtx)?.catch?.((hookError) => {
35
49
  console.error("Error while capturing another error", hookError);
36
50
  });
37
51
  if (errorCtx?.event) {
@@ -39,62 +53,60 @@ function createNitroApp() {
39
53
  if (errors) {
40
54
  errors.push({ error, context: errorCtx });
41
55
  }
42
- if (typeof errorCtx.event.req.waitUntil === "function") {
56
+ if (hasHooks && typeof errorCtx.event.req.waitUntil === "function") {
43
57
  errorCtx.event.req.waitUntil(promise);
44
58
  }
45
59
  }
46
60
  };
47
61
  const h3App = createH3App({
48
62
  onError(error, event) {
49
- captureError(error, { event });
63
+ hasHooks && captureError(error, { event });
50
64
  return errorHandler(error, event);
51
- },
52
- onRequest(event) {
65
+ }
66
+ });
67
+ if (hasHooks) {
68
+ h3App.config.onRequest = (event) => {
53
69
  return hooks.callHook("request", event)?.catch?.((error) => {
54
70
  captureError(error, { event, tags: ["request"] });
55
71
  });
56
- },
57
- onResponse(res, event) {
72
+ };
73
+ h3App.config.onResponse = (res, event) => {
58
74
  return hooks.callHook("response", res, event)?.catch?.((error) => {
59
75
  captureError(error, { event, tags: ["response"] });
60
76
  });
61
- }
62
- });
63
- let fetchHandler = (req) => {
64
- req.context ??= {};
77
+ };
78
+ }
79
+ let appReqHandler = (req) => {
80
+ req.context ||= {};
65
81
  req.context.nitro = req.context.nitro || { errors: [] };
66
82
  return h3App.fetch(req);
67
83
  };
68
84
  if (import.meta._asyncContext) {
69
- const originalFetchHandler = fetchHandler;
70
- fetchHandler = (req) => {
85
+ const originalHandler = appReqHandler;
86
+ appReqHandler = (req) => {
71
87
  const asyncCtx = { request: req };
72
- return nitroAsyncContext.callAsync(
73
- asyncCtx,
74
- () => originalFetchHandler(req)
75
- );
88
+ return nitroAsyncContext.callAsync(asyncCtx, () => originalHandler(req));
76
89
  };
77
90
  }
78
- const requestHandler = (input, init, context) => {
91
+ const appFetchHandler = (input, init, context) => {
79
92
  const req = toRequest(input, init);
80
93
  req.context = { ...req.context, ...context };
81
- return Promise.resolve(fetchHandler(req));
94
+ return Promise.resolve(appReqHandler(req));
82
95
  };
83
- const originalFetch = globalThis.fetch;
84
- const nitroFetch = (input, init) => {
85
- if (typeof input === "string" && input.startsWith("/")) {
86
- return requestHandler(input, init);
96
+ const nativeFetch = globalThis.fetch;
97
+ globalThis.fetch = (input, init) => {
98
+ if (typeof input === "string" && input.charCodeAt(0) === 47) {
99
+ return appFetchHandler(input, init);
87
100
  }
88
- if (input instanceof Request && "_request" in input) {
101
+ if ("_request" in input) {
89
102
  input = input._request;
90
103
  }
91
- return originalFetch(input, init);
104
+ return nativeFetch(input, init);
92
105
  };
93
- globalThis.fetch = nitroFetch;
94
106
  const app = {
107
+ fetch: appFetchHandler,
95
108
  _h3: h3App,
96
109
  hooks,
97
- fetch: requestHandler,
98
110
  captureError
99
111
  };
100
112
  return app;
@@ -1,12 +1,12 @@
1
1
  import { defineHandler } from "h3";
2
- import { useNitroApp } from "./app.mjs";
2
+ import { useNitroHooks } from "./app.mjs";
3
3
  import { useRuntimeConfig } from "./runtime-config.mjs";
4
4
  export function defineRenderHandler(render) {
5
5
  const runtimeConfig = useRuntimeConfig();
6
6
  return defineHandler(async (event) => {
7
- const nitroApp = useNitroApp();
7
+ const nitroHooks = useNitroHooks();
8
8
  const ctx = { event, render, response: void 0 };
9
- await nitroApp.hooks.callHook("render:before", ctx);
9
+ await nitroHooks.callHook("render:before", ctx);
10
10
  if (!ctx.response) {
11
11
  if (event.url.pathname === `${runtimeConfig.app.baseURL}favicon.ico`) {
12
12
  event.res.headers.set("Content-Type", "image/x-icon");
@@ -21,7 +21,7 @@ export function defineRenderHandler(render) {
21
21
  return "No response returned from render handler: " + event.url.pathname;
22
22
  }
23
23
  }
24
- await nitroApp.hooks.callHook("render:response", ctx.response, ctx);
24
+ await nitroHooks.callHook("render:response", ctx.response, ctx);
25
25
  if (ctx.response.headers) {
26
26
  for (const [key, value] of Object.entries(ctx.response.headers)) {
27
27
  event.res.headers.set(key, value);
@@ -1,9 +1,8 @@
1
1
  import type { Server as HttpServer } from "node:http";
2
- import type { NitroApp } from "nitro/types";
3
2
  export declare function getGracefulShutdownConfig(): {
4
3
  disabled: boolean;
5
4
  signals: string[];
6
5
  timeout: number;
7
6
  forceExit: boolean;
8
7
  };
9
- export declare function setupGracefulShutdown(listener: HttpServer, nitroApp: NitroApp): void;
8
+ export declare function setupGracefulShutdown(listener: HttpServer): void;
@@ -1,4 +1,5 @@
1
1
  import gracefulShutdown from "./lib/http-graceful-shutdown.mjs";
2
+ import { useNitroHooks } from "./app.mjs";
2
3
  export function getGracefulShutdownConfig() {
3
4
  return {
4
5
  disabled: !!process.env.NITRO_SHUTDOWN_DISABLED,
@@ -7,7 +8,7 @@ export function getGracefulShutdownConfig() {
7
8
  forceExit: !process.env.NITRO_SHUTDOWN_NO_FORCE_EXIT
8
9
  };
9
10
  }
10
- export function setupGracefulShutdown(listener, nitroApp) {
11
+ export function setupGracefulShutdown(listener) {
11
12
  const shutdownConfig = getGracefulShutdownConfig();
12
13
  if (shutdownConfig.disabled) {
13
14
  return;
@@ -22,7 +23,7 @@ export function setupGracefulShutdown(listener, nitroApp) {
22
23
  console.warn("Graceful shutdown timeout, force exiting...");
23
24
  resolve();
24
25
  }, shutdownConfig.timeout);
25
- Promise.resolve(nitroApp.hooks.callHook("close")).catch((error) => {
26
+ Promise.resolve(useNitroHooks().callHook("close")).catch((error) => {
26
27
  console.error(error);
27
28
  }).finally(() => {
28
29
  clearTimeout(timeout);
@@ -146,12 +146,14 @@ interface CachedEventHandlerOptions extends Omit<CacheOptions<ResponseCacheEntry
146
146
 
147
147
  interface NitroApp {
148
148
  _h3?: H3Core;
149
- hooks: HookableCore<NitroRuntimeHooks>;
150
- fetch: (req: string | URL | Request, init?: RequestInit, context?: ServerRequestContext | H3EventContext) => Promise<Response>;
149
+ hooks?: HookableCore<NitroRuntimeHooks>;
151
150
  captureError: CaptureError;
151
+ fetch: (req: string | URL | Request, init?: RequestInit, context?: ServerRequestContext | H3EventContext) => Promise<Response>;
152
152
  }
153
153
  interface NitroAppPlugin {
154
- (nitro: NitroApp): void;
154
+ (nitro: NitroApp & {
155
+ hooks: NonNullable<NitroApp["hooks"]>;
156
+ }): void;
155
157
  }
156
158
  interface NitroAsyncContext {
157
159
  request: ServerRequest;
@@ -4118,6 +4120,14 @@ interface NitroOptions extends PresetOptions {
4118
4120
  ssrRoutes: string[];
4119
4121
  serveStatic: boolean | "node" | "deno" | "inline";
4120
4122
  noPublicDir: boolean;
4123
+ features: {
4124
+ /**
4125
+ * Enable runtime hooks for request and response.
4126
+ *
4127
+ * By default this feature will be enabled if there is at least one nitro plugin.
4128
+ */
4129
+ runtimeHooks: boolean;
4130
+ };
4121
4131
  /**
4122
4132
  * @experimental Requires `experimental.wasm` to work
4123
4133
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitro-nightly",
3
- "version": "3.1.0-20251029-083418-84ceb86e",
3
+ "version": "3.1.0-20251029-124449-782162a4",
4
4
  "description": "Build and Deploy Universal JavaScript Servers",
5
5
  "homepage": "https://nitro.build",
6
6
  "repository": "nitrojs/nitro",