vinext 0.0.23 → 0.0.25

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.
Files changed (105) hide show
  1. package/README.md +24 -0
  2. package/dist/check.d.ts.map +1 -1
  3. package/dist/check.js +3 -2
  4. package/dist/check.js.map +1 -1
  5. package/dist/client/entry.js +1 -1
  6. package/dist/client/entry.js.map +1 -1
  7. package/dist/config/config-matchers.d.ts +21 -0
  8. package/dist/config/config-matchers.d.ts.map +1 -1
  9. package/dist/config/config-matchers.js +59 -9
  10. package/dist/config/config-matchers.js.map +1 -1
  11. package/dist/config/next-config.d.ts +8 -2
  12. package/dist/config/next-config.d.ts.map +1 -1
  13. package/dist/config/next-config.js +90 -35
  14. package/dist/config/next-config.js.map +1 -1
  15. package/dist/deploy.d.ts +10 -0
  16. package/dist/deploy.d.ts.map +1 -1
  17. package/dist/deploy.js +140 -56
  18. package/dist/deploy.js.map +1 -1
  19. package/dist/index.d.ts +14 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +195 -25
  22. package/dist/index.js.map +1 -1
  23. package/dist/plugins/async-hooks-stub.d.ts +16 -0
  24. package/dist/plugins/async-hooks-stub.d.ts.map +1 -0
  25. package/dist/plugins/async-hooks-stub.js +45 -0
  26. package/dist/plugins/async-hooks-stub.js.map +1 -0
  27. package/dist/routing/app-router.d.ts +12 -6
  28. package/dist/routing/app-router.d.ts.map +1 -1
  29. package/dist/routing/app-router.js +19 -40
  30. package/dist/routing/app-router.js.map +1 -1
  31. package/dist/routing/pages-router.d.ts.map +1 -1
  32. package/dist/routing/pages-router.js +3 -9
  33. package/dist/routing/pages-router.js.map +1 -1
  34. package/dist/routing/utils.d.ts +9 -0
  35. package/dist/routing/utils.d.ts.map +1 -1
  36. package/dist/routing/utils.js +10 -0
  37. package/dist/routing/utils.js.map +1 -1
  38. package/dist/server/api-handler.d.ts.map +1 -1
  39. package/dist/server/api-handler.js +6 -0
  40. package/dist/server/api-handler.js.map +1 -1
  41. package/dist/server/app-dev-server.d.ts +2 -2
  42. package/dist/server/app-dev-server.d.ts.map +1 -1
  43. package/dist/server/app-dev-server.js +294 -133
  44. package/dist/server/app-dev-server.js.map +1 -1
  45. package/dist/server/dev-module-runner.d.ts +84 -0
  46. package/dist/server/dev-module-runner.d.ts.map +1 -0
  47. package/dist/server/dev-module-runner.js +105 -0
  48. package/dist/server/dev-module-runner.js.map +1 -0
  49. package/dist/server/dev-server.js.map +1 -1
  50. package/dist/server/instrumentation.d.ts +52 -9
  51. package/dist/server/instrumentation.d.ts.map +1 -1
  52. package/dist/server/instrumentation.js +52 -15
  53. package/dist/server/instrumentation.js.map +1 -1
  54. package/dist/server/middleware.d.ts +7 -3
  55. package/dist/server/middleware.d.ts.map +1 -1
  56. package/dist/server/middleware.js +16 -6
  57. package/dist/server/middleware.js.map +1 -1
  58. package/dist/server/prod-server.d.ts +8 -2
  59. package/dist/server/prod-server.d.ts.map +1 -1
  60. package/dist/server/prod-server.js +56 -35
  61. package/dist/server/prod-server.js.map +1 -1
  62. package/dist/server/worker-utils.d.ts +15 -0
  63. package/dist/server/worker-utils.d.ts.map +1 -0
  64. package/dist/server/worker-utils.js +41 -0
  65. package/dist/server/worker-utils.js.map +1 -0
  66. package/dist/shims/cache.d.ts.map +1 -1
  67. package/dist/shims/cache.js +14 -2
  68. package/dist/shims/cache.js.map +1 -1
  69. package/dist/shims/fetch-cache.d.ts.map +1 -1
  70. package/dist/shims/fetch-cache.js +139 -29
  71. package/dist/shims/fetch-cache.js.map +1 -1
  72. package/dist/shims/form.d.ts.map +1 -1
  73. package/dist/shims/form.js +2 -3
  74. package/dist/shims/form.js.map +1 -1
  75. package/dist/shims/headers.d.ts +6 -0
  76. package/dist/shims/headers.d.ts.map +1 -1
  77. package/dist/shims/headers.js +8 -0
  78. package/dist/shims/headers.js.map +1 -1
  79. package/dist/shims/layout-segment-context.d.ts +5 -4
  80. package/dist/shims/layout-segment-context.d.ts.map +1 -1
  81. package/dist/shims/layout-segment-context.js +6 -5
  82. package/dist/shims/layout-segment-context.js.map +1 -1
  83. package/dist/shims/link.d.ts.map +1 -1
  84. package/dist/shims/link.js +32 -17
  85. package/dist/shims/link.js.map +1 -1
  86. package/dist/shims/navigation.d.ts +14 -11
  87. package/dist/shims/navigation.d.ts.map +1 -1
  88. package/dist/shims/navigation.js +122 -102
  89. package/dist/shims/navigation.js.map +1 -1
  90. package/dist/shims/router.d.ts.map +1 -1
  91. package/dist/shims/router.js +37 -21
  92. package/dist/shims/router.js.map +1 -1
  93. package/dist/shims/server.d.ts +2 -0
  94. package/dist/shims/server.d.ts.map +1 -1
  95. package/dist/shims/server.js +4 -0
  96. package/dist/shims/server.js.map +1 -1
  97. package/dist/shims/url-utils.d.ts +13 -0
  98. package/dist/shims/url-utils.d.ts.map +1 -0
  99. package/dist/shims/url-utils.js +28 -0
  100. package/dist/shims/url-utils.js.map +1 -0
  101. package/dist/utils/project.d.ts +13 -1
  102. package/dist/utils/project.d.ts.map +1 -1
  103. package/dist/utils/project.js +63 -13
  104. package/dist/utils/project.js.map +1 -1
  105. package/package.json +6 -1
@@ -11,7 +11,38 @@
11
11
  *
12
12
  * References:
13
13
  * - https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation
14
+ *
15
+ * ## App Router
16
+ *
17
+ * For App Router, `register()` is baked directly into the generated RSC entry
18
+ * as a top-level `await` at module evaluation time (see `app-dev-server.ts`
19
+ * `generateRscEntry`). This means it runs inside the Worker process (or RSC
20
+ * Vite environment) — the same process that handles requests — before any
21
+ * request is served. `runInstrumentation()` is NOT called from `configureServer`
22
+ * for App Router.
23
+ *
24
+ * The `onRequestError` handler is stored on `globalThis` so it is visible across
25
+ * the RSC and SSR Vite environments (separate module graphs, same Node.js process).
26
+ * With `@cloudflare/vite-plugin` it runs entirely inside the Worker, so
27
+ * `globalThis` is the Worker's global — also correct.
28
+ *
29
+ * ## Pages Router
30
+ *
31
+ * Pages Router has no RSC entry, so `configureServer()` is the right place to
32
+ * call `register()`. `runInstrumentation()` accepts a `ModuleRunner` (created
33
+ * via `createDirectRunner()`) rather than `server.ssrLoadModule()` so it is
34
+ * safe when `@cloudflare/vite-plugin` is present — that plugin replaces the
35
+ * SSR environment's hot channel, causing `ssrLoadModule()` to crash with
36
+ * `TypeError: Cannot read properties of undefined (reading 'outsideEmitter')`.
14
37
  */
38
+ /**
39
+ * Minimal duck-typed interface for the module runner passed to
40
+ * `runInstrumentation`. Only `.import()` is used — this avoids requiring
41
+ * callers (including tests) to provide a full `ModuleRunner` instance.
42
+ */
43
+ export interface ModuleImporter {
44
+ import(id: string): Promise<unknown>;
45
+ }
15
46
  /**
16
47
  * Find the instrumentation file in the project root.
17
48
  */
@@ -39,26 +70,38 @@ export type OnRequestErrorHandler = (error: Error, request: {
39
70
  }, context: OnRequestErrorContext) => void | Promise<void>;
40
71
  /**
41
72
  * Get the registered onRequestError handler (if any).
73
+ *
74
+ * Reads from globalThis so it works across Vite environment boundaries.
42
75
  */
43
76
  export declare function getOnRequestErrorHandler(): OnRequestErrorHandler | null;
44
77
  /**
45
- * Load and execute the instrumentation file.
78
+ * Load and execute the instrumentation file via a ModuleRunner.
46
79
  *
47
- * This should be called once during server startup. It:
48
- * 1. Loads the instrumentation module via Vite's SSR module loader
49
- * 2. Calls the `register()` function if exported
50
- * 3. Stores the `onRequestError()` handler if exported
80
+ * Called once during Pages Router server startup (`configureServer`). It:
81
+ * 1. Loads the instrumentation module via `runner.import()`.
82
+ * 2. Calls the `register()` function if exported.
83
+ * 3. Stores the `onRequestError()` handler on `globalThis` so it is visible
84
+ * to all Vite environment module graphs (SSR and the host process share
85
+ * the same Node.js `globalThis`).
51
86
  *
52
- * @param server - Vite dev server (for SSR module loading)
87
+ * **App Router** does not use this function. For App Router, `register()` is
88
+ * emitted as a top-level `await` inside the generated RSC entry module so it
89
+ * runs in the same Worker/environment as request handling.
90
+ *
91
+ * @param runner - A ModuleRunner created via `createDirectRunner()`. Must be
92
+ * the same long-lived runner used for middleware and SSR so the module graph
93
+ * is shared. Safe with all Vite plugin combinations, including
94
+ * `@cloudflare/vite-plugin`, because it never touches the hot channel.
53
95
  * @param instrumentationPath - Absolute path to the instrumentation file
54
96
  */
55
- export declare function runInstrumentation(server: {
56
- ssrLoadModule: (id: string) => Promise<Record<string, unknown>>;
57
- }, instrumentationPath: string): Promise<void>;
97
+ export declare function runInstrumentation(runner: ModuleImporter, instrumentationPath: string): Promise<void>;
58
98
  /**
59
99
  * Report a request error via the instrumentation handler.
60
100
  *
61
101
  * No-op if no onRequestError handler is registered.
102
+ *
103
+ * Reads the handler from globalThis so this function works correctly regardless
104
+ * of which environment it is called from.
62
105
  */
63
106
  export declare function reportRequestError(error: Error, request: {
64
107
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/server/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAiBH;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQnE;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,UAAU,EAAE,cAAc,GAAG,YAAY,CAAC;IAC1C,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,SAAS,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,CAAC;IACxD,yCAAyC;IACzC,gBAAgB,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;CACtD;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAC1E,OAAO,EAAE,qBAAqB,KAC3B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAK1B;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,qBAAqB,GAAG,IAAI,CAEvE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE;IAAE,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,EAC3E,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAC1E,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAUf"}
1
+ {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/server/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAIH;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtC;AAcD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQnE;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,UAAU,EAAE,cAAc,GAAG,YAAY,CAAC;IAC1C,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,SAAS,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,CAAC;IACxD,yCAAyC;IACzC,gBAAgB,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;CACtD;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAC1E,OAAO,EAAE,qBAAqB,KAC3B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,qBAAqB,GAAG,IAAI,CAEvE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAC1E,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAYf"}
@@ -11,6 +11,29 @@
11
11
  *
12
12
  * References:
13
13
  * - https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation
14
+ *
15
+ * ## App Router
16
+ *
17
+ * For App Router, `register()` is baked directly into the generated RSC entry
18
+ * as a top-level `await` at module evaluation time (see `app-dev-server.ts`
19
+ * `generateRscEntry`). This means it runs inside the Worker process (or RSC
20
+ * Vite environment) — the same process that handles requests — before any
21
+ * request is served. `runInstrumentation()` is NOT called from `configureServer`
22
+ * for App Router.
23
+ *
24
+ * The `onRequestError` handler is stored on `globalThis` so it is visible across
25
+ * the RSC and SSR Vite environments (separate module graphs, same Node.js process).
26
+ * With `@cloudflare/vite-plugin` it runs entirely inside the Worker, so
27
+ * `globalThis` is the Worker's global — also correct.
28
+ *
29
+ * ## Pages Router
30
+ *
31
+ * Pages Router has no RSC entry, so `configureServer()` is the right place to
32
+ * call `register()`. `runInstrumentation()` accepts a `ModuleRunner` (created
33
+ * via `createDirectRunner()`) rather than `server.ssrLoadModule()` so it is
34
+ * safe when `@cloudflare/vite-plugin` is present — that plugin replaces the
35
+ * SSR environment's hot channel, causing `ssrLoadModule()` to crash with
36
+ * `TypeError: Cannot read properties of undefined (reading 'outsideEmitter')`.
14
37
  */
15
38
  import fs from "node:fs";
16
39
  import path from "node:path";
@@ -37,35 +60,45 @@ export function findInstrumentationFile(root) {
37
60
  }
38
61
  return null;
39
62
  }
40
- /** Module-level reference to the onRequestError handler. */
41
- let _onRequestError = null;
42
63
  /**
43
64
  * Get the registered onRequestError handler (if any).
65
+ *
66
+ * Reads from globalThis so it works across Vite environment boundaries.
44
67
  */
45
68
  export function getOnRequestErrorHandler() {
46
- return _onRequestError;
69
+ return globalThis.__VINEXT_onRequestErrorHandler__ ?? null;
47
70
  }
48
71
  /**
49
- * Load and execute the instrumentation file.
72
+ * Load and execute the instrumentation file via a ModuleRunner.
50
73
  *
51
- * This should be called once during server startup. It:
52
- * 1. Loads the instrumentation module via Vite's SSR module loader
53
- * 2. Calls the `register()` function if exported
54
- * 3. Stores the `onRequestError()` handler if exported
74
+ * Called once during Pages Router server startup (`configureServer`). It:
75
+ * 1. Loads the instrumentation module via `runner.import()`.
76
+ * 2. Calls the `register()` function if exported.
77
+ * 3. Stores the `onRequestError()` handler on `globalThis` so it is visible
78
+ * to all Vite environment module graphs (SSR and the host process share
79
+ * the same Node.js `globalThis`).
55
80
  *
56
- * @param server - Vite dev server (for SSR module loading)
81
+ * **App Router** does not use this function. For App Router, `register()` is
82
+ * emitted as a top-level `await` inside the generated RSC entry module so it
83
+ * runs in the same Worker/environment as request handling.
84
+ *
85
+ * @param runner - A ModuleRunner created via `createDirectRunner()`. Must be
86
+ * the same long-lived runner used for middleware and SSR so the module graph
87
+ * is shared. Safe with all Vite plugin combinations, including
88
+ * `@cloudflare/vite-plugin`, because it never touches the hot channel.
57
89
  * @param instrumentationPath - Absolute path to the instrumentation file
58
90
  */
59
- export async function runInstrumentation(server, instrumentationPath) {
91
+ export async function runInstrumentation(runner, instrumentationPath) {
60
92
  try {
61
- const mod = await server.ssrLoadModule(instrumentationPath);
93
+ const mod = await runner.import(instrumentationPath);
62
94
  // Call register() if exported
63
95
  if (typeof mod.register === "function") {
64
96
  await mod.register();
65
97
  }
66
- // Store onRequestError handler if exported
98
+ // Store onRequestError handler on globalThis so environments can reach the
99
+ // same handler.
67
100
  if (typeof mod.onRequestError === "function") {
68
- _onRequestError = mod.onRequestError;
101
+ globalThis.__VINEXT_onRequestErrorHandler__ = mod.onRequestError;
69
102
  }
70
103
  }
71
104
  catch (err) {
@@ -76,12 +109,16 @@ export async function runInstrumentation(server, instrumentationPath) {
76
109
  * Report a request error via the instrumentation handler.
77
110
  *
78
111
  * No-op if no onRequestError handler is registered.
112
+ *
113
+ * Reads the handler from globalThis so this function works correctly regardless
114
+ * of which environment it is called from.
79
115
  */
80
116
  export async function reportRequestError(error, request, context) {
81
- if (!_onRequestError)
117
+ const handler = getOnRequestErrorHandler();
118
+ if (!handler)
82
119
  return;
83
120
  try {
84
- await _onRequestError(error, request, context);
121
+ await handler(error, request, context);
85
122
  }
86
123
  catch (reportErr) {
87
124
  console.error("[vinext] onRequestError handler threw:", reportErr instanceof Error ? reportErr.message : String(reportErr));
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../src/server/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,2CAA2C;AAC3C,MAAM,qBAAqB,GAAG;IAC5B,oBAAoB;IACpB,qBAAqB;IACrB,oBAAoB;IACpB,qBAAqB;IACrB,wBAAwB;IACxB,yBAAyB;IACzB,wBAAwB;IACxB,yBAAyB;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,KAAK,MAAM,IAAI,IAAI,qBAAqB,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAyBD,4DAA4D;AAC5D,IAAI,eAAe,GAAiC,IAAI,CAAC;AAEzD;;GAEG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAA2E,EAC3E,mBAA2B;IAE3B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC;QAE5D,8BAA8B;QAC9B,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;YACvC,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC;QACvB,CAAC;QAED,2CAA2C;QAC3C,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU,EAAE,CAAC;YAC7C,eAAe,GAAG,GAAG,CAAC,cAAuC,CAAC;QAChE,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CACX,0CAA0C,EAC1C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAY,EACZ,OAA0E,EAC1E,OAA8B;IAE9B,IAAI,CAAC,eAAe;QAAE,OAAO;IAC7B,IAAI,CAAC;QACH,MAAM,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,SAAS,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CACX,wCAAwC,EACxC,SAAS,YAAY,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CACnE,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["/**\n * instrumentation.ts support\n *\n * Next.js supports an `instrumentation.ts` file at the project root that\n * exports a `register()` function. This function is called once when the\n * server starts, before any request handling. It's the recommended way to\n * set up observability tools (Sentry, Datadog, OpenTelemetry, etc.).\n *\n * Optionally, it can also export `onRequestError()` which is called when\n * an unhandled error occurs during request handling.\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n/** Possible instrumentation file names. */\nconst INSTRUMENTATION_FILES = [\n \"instrumentation.ts\",\n \"instrumentation.tsx\",\n \"instrumentation.js\",\n \"instrumentation.mjs\",\n \"src/instrumentation.ts\",\n \"src/instrumentation.tsx\",\n \"src/instrumentation.js\",\n \"src/instrumentation.mjs\",\n];\n\n/**\n * Find the instrumentation file in the project root.\n */\nexport function findInstrumentationFile(root: string): string | null {\n for (const file of INSTRUMENTATION_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n return null;\n}\n\n/**\n * The onRequestError handler type from Next.js instrumentation.\n *\n * Called when an unhandled error occurs during request handling.\n * Provides the error, the request info, and an error context.\n */\nexport interface OnRequestErrorContext {\n /** The route path (e.g., '/blog/[slug]') */\n routerKind: \"Pages Router\" | \"App Router\";\n /** The matched route pattern */\n routePath: string;\n /** The route type */\n routeType: \"render\" | \"route\" | \"action\" | \"middleware\";\n /** HTTP status code that will be sent */\n revalidateReason?: \"on-demand\" | \"stale\" | undefined;\n}\n\nexport type OnRequestErrorHandler = (\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n context: OnRequestErrorContext,\n) => void | Promise<void>;\n\n/** Module-level reference to the onRequestError handler. */\nlet _onRequestError: OnRequestErrorHandler | null = null;\n\n/**\n * Get the registered onRequestError handler (if any).\n */\nexport function getOnRequestErrorHandler(): OnRequestErrorHandler | null {\n return _onRequestError;\n}\n\n/**\n * Load and execute the instrumentation file.\n *\n * This should be called once during server startup. It:\n * 1. Loads the instrumentation module via Vite's SSR module loader\n * 2. Calls the `register()` function if exported\n * 3. Stores the `onRequestError()` handler if exported\n *\n * @param server - Vite dev server (for SSR module loading)\n * @param instrumentationPath - Absolute path to the instrumentation file\n */\nexport async function runInstrumentation(\n server: { ssrLoadModule: (id: string) => Promise<Record<string, unknown>> },\n instrumentationPath: string,\n): Promise<void> {\n try {\n const mod = await server.ssrLoadModule(instrumentationPath);\n\n // Call register() if exported\n if (typeof mod.register === \"function\") {\n await mod.register();\n }\n\n // Store onRequestError handler if exported\n if (typeof mod.onRequestError === \"function\") {\n _onRequestError = mod.onRequestError as OnRequestErrorHandler;\n }\n } catch (err) {\n console.error(\n \"[vinext] Failed to load instrumentation:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n}\n\n/**\n * Report a request error via the instrumentation handler.\n *\n * No-op if no onRequestError handler is registered.\n */\nexport async function reportRequestError(\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n context: OnRequestErrorContext,\n): Promise<void> {\n if (!_onRequestError) return;\n try {\n await _onRequestError(error, request, context);\n } catch (reportErr) {\n console.error(\n \"[vinext] onRequestError handler threw:\",\n reportErr instanceof Error ? reportErr.message : String(reportErr),\n );\n }\n}\n"]}
1
+ {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../src/server/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAU7B,2CAA2C;AAC3C,MAAM,qBAAqB,GAAG;IAC5B,oBAAoB;IACpB,qBAAqB;IACrB,oBAAoB;IACpB,qBAAqB;IACrB,wBAAwB;IACxB,yBAAyB;IACzB,wBAAwB;IACxB,yBAAyB;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,KAAK,MAAM,IAAI,IAAI,qBAAqB,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAyBD;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,UAAU,CAAC,gCAAgC,IAAI,IAAI,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAsB,EACtB,mBAA2B;IAE3B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAA4B,CAAC;QAEhF,8BAA8B;QAC9B,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;YACvC,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC;QACvB,CAAC;QAED,2EAA2E;QAC3E,gBAAgB;QAChB,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU,EAAE,CAAC;YAChD,UAAU,CAAC,gCAAgC,GAAG,GAAG,CAAC,cAAuC,CAAC;QACzF,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CACX,0CAA0C,EAC1C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAY,EACZ,OAA0E,EAC1E,OAA8B;IAE9B,MAAM,OAAO,GAAG,wBAAwB,EAAE,CAAC;IAC5C,IAAI,CAAC,OAAO;QAAE,OAAO;IAEpB,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,SAAS,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CACX,wCAAwC,EACxC,SAAS,YAAY,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CACnE,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["/**\n * instrumentation.ts support\n *\n * Next.js supports an `instrumentation.ts` file at the project root that\n * exports a `register()` function. This function is called once when the\n * server starts, before any request handling. It's the recommended way to\n * set up observability tools (Sentry, Datadog, OpenTelemetry, etc.).\n *\n * Optionally, it can also export `onRequestError()` which is called when\n * an unhandled error occurs during request handling.\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation\n *\n * ## App Router\n *\n * For App Router, `register()` is baked directly into the generated RSC entry\n * as a top-level `await` at module evaluation time (see `app-dev-server.ts`\n * `generateRscEntry`). This means it runs inside the Worker process (or RSC\n * Vite environment) — the same process that handles requests — before any\n * request is served. `runInstrumentation()` is NOT called from `configureServer`\n * for App Router.\n *\n * The `onRequestError` handler is stored on `globalThis` so it is visible across\n * the RSC and SSR Vite environments (separate module graphs, same Node.js process).\n * With `@cloudflare/vite-plugin` it runs entirely inside the Worker, so\n * `globalThis` is the Worker's global — also correct.\n *\n * ## Pages Router\n *\n * Pages Router has no RSC entry, so `configureServer()` is the right place to\n * call `register()`. `runInstrumentation()` accepts a `ModuleRunner` (created\n * via `createDirectRunner()`) rather than `server.ssrLoadModule()` so it is\n * safe when `@cloudflare/vite-plugin` is present — that plugin replaces the\n * SSR environment's hot channel, causing `ssrLoadModule()` to crash with\n * `TypeError: Cannot read properties of undefined (reading 'outsideEmitter')`.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n/**\n * Minimal duck-typed interface for the module runner passed to\n * `runInstrumentation`. Only `.import()` is used — this avoids requiring\n * callers (including tests) to provide a full `ModuleRunner` instance.\n */\nexport interface ModuleImporter {\n import(id: string): Promise<unknown>;\n}\n\n/** Possible instrumentation file names. */\nconst INSTRUMENTATION_FILES = [\n \"instrumentation.ts\",\n \"instrumentation.tsx\",\n \"instrumentation.js\",\n \"instrumentation.mjs\",\n \"src/instrumentation.ts\",\n \"src/instrumentation.tsx\",\n \"src/instrumentation.js\",\n \"src/instrumentation.mjs\",\n];\n\n/**\n * Find the instrumentation file in the project root.\n */\nexport function findInstrumentationFile(root: string): string | null {\n for (const file of INSTRUMENTATION_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n return null;\n}\n\n/**\n * The onRequestError handler type from Next.js instrumentation.\n *\n * Called when an unhandled error occurs during request handling.\n * Provides the error, the request info, and an error context.\n */\nexport interface OnRequestErrorContext {\n /** The route path (e.g., '/blog/[slug]') */\n routerKind: \"Pages Router\" | \"App Router\";\n /** The matched route pattern */\n routePath: string;\n /** The route type */\n routeType: \"render\" | \"route\" | \"action\" | \"middleware\";\n /** HTTP status code that will be sent */\n revalidateReason?: \"on-demand\" | \"stale\" | undefined;\n}\n\nexport type OnRequestErrorHandler = (\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n context: OnRequestErrorContext,\n) => void | Promise<void>;\n\n/**\n * Get the registered onRequestError handler (if any).\n *\n * Reads from globalThis so it works across Vite environment boundaries.\n */\nexport function getOnRequestErrorHandler(): OnRequestErrorHandler | null {\n return globalThis.__VINEXT_onRequestErrorHandler__ ?? null;\n}\n\n/**\n * Load and execute the instrumentation file via a ModuleRunner.\n *\n * Called once during Pages Router server startup (`configureServer`). It:\n * 1. Loads the instrumentation module via `runner.import()`.\n * 2. Calls the `register()` function if exported.\n * 3. Stores the `onRequestError()` handler on `globalThis` so it is visible\n * to all Vite environment module graphs (SSR and the host process share\n * the same Node.js `globalThis`).\n *\n * **App Router** does not use this function. For App Router, `register()` is\n * emitted as a top-level `await` inside the generated RSC entry module so it\n * runs in the same Worker/environment as request handling.\n *\n * @param runner - A ModuleRunner created via `createDirectRunner()`. Must be\n * the same long-lived runner used for middleware and SSR so the module graph\n * is shared. Safe with all Vite plugin combinations, including\n * `@cloudflare/vite-plugin`, because it never touches the hot channel.\n * @param instrumentationPath - Absolute path to the instrumentation file\n */\nexport async function runInstrumentation(\n runner: ModuleImporter,\n instrumentationPath: string,\n): Promise<void> {\n try {\n const mod = await runner.import(instrumentationPath) as Record<string, unknown>;\n\n // Call register() if exported\n if (typeof mod.register === \"function\") {\n await mod.register();\n }\n\n // Store onRequestError handler on globalThis so environments can reach the\n // same handler.\n if (typeof mod.onRequestError === \"function\") {\n\t\t\tglobalThis.__VINEXT_onRequestErrorHandler__ = mod.onRequestError as OnRequestErrorHandler;\n }\n } catch (err) {\n console.error(\n \"[vinext] Failed to load instrumentation:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n}\n\n/**\n * Report a request error via the instrumentation handler.\n *\n * No-op if no onRequestError handler is registered.\n *\n * Reads the handler from globalThis so this function works correctly regardless\n * of which environment it is called from.\n */\nexport async function reportRequestError(\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n context: OnRequestErrorContext,\n): Promise<void> {\n const handler = getOnRequestErrorHandler();\n\tif (!handler) return;\n\n try {\n await handler(error, request, context);\n } catch (reportErr) {\n console.error(\n \"[vinext] onRequestError handler threw:\",\n reportErr instanceof Error ? reportErr.message : String(reportErr),\n );\n }\n}\n"]}
@@ -17,7 +17,7 @@
17
17
  *
18
18
  * Supports the `config.matcher` export for path filtering.
19
19
  */
20
- import type { ViteDevServer } from "vite";
20
+ import type { ModuleRunner } from "vite/module-runner";
21
21
  /**
22
22
  * Determine whether a middleware/proxy file path refers to a proxy file.
23
23
  * proxy.ts files accept `proxy` or `default` exports.
@@ -89,11 +89,15 @@ export interface MiddlewareResult {
89
89
  /**
90
90
  * Load and execute middleware for a given request.
91
91
  *
92
- * @param server - Vite dev server (for SSR module loading)
92
+ * @param runner - A ModuleRunner used to load the middleware module.
93
+ * Must be a long-lived instance created once (e.g. in configureServer) via
94
+ * createDirectRunner() — NOT recreated per request. Using server.ssrLoadModule
95
+ * directly crashes with `outsideEmitter` when @cloudflare/vite-plugin is
96
+ * present because SSRCompatModuleRunner reads environment.hot.api synchronously.
93
97
  * @param middlewarePath - Absolute path to the middleware file
94
98
  * @param request - The incoming Request object
95
99
  * @returns Middleware result describing what action to take
96
100
  */
97
- export declare function runMiddleware(server: ViteDevServer, middlewarePath: string, request: Request): Promise<MiddlewareResult>;
101
+ export declare function runMiddleware(runner: ModuleRunner, middlewarePath: string, request: Request): Promise<MiddlewareResult>;
98
102
  export {};
99
103
  //# sourceMappingURL=middleware.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAO1C;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGrD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,QAAQ,EAAE,MAAM,GACf,QAAQ,CAeV;AA0BD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqB9D;AAED,qDAAqD;AACrD,KAAK,aAAa,GACd,MAAM,GACN,MAAM,EAAE,GACR;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,EAAE,CAAC;AAE1F;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,GAAG,SAAS,GACjC,OAAO,CAqBT;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAkCvE;AAED,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,QAAQ,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,gBAAgB,CAAC,CA8H3B"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOvD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGrD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,QAAQ,EAAE,MAAM,GACf,QAAQ,CAeV;AA0BD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqB9D;AAED,qDAAqD;AACrD,KAAK,aAAa,GACd,MAAM,GACN,MAAM,EAAE,GACR;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,EAAE,CAAC;AAE1F;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,GAAG,SAAS,GACjC,OAAO,CAqBT;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAkCvE;AAED,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,QAAQ,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,gBAAgB,CAAC,CAqI3B"}
@@ -19,7 +19,7 @@
19
19
  */
20
20
  import fs from "node:fs";
21
21
  import path from "node:path";
22
- import { NextRequest } from "../shims/server.js";
22
+ import { NextRequest, NextFetchEvent } from "../shims/server.js";
23
23
  import { safeRegExp } from "../config/config-matchers.js";
24
24
  import { normalizePath } from "./normalize-path.js";
25
25
  /**
@@ -181,14 +181,20 @@ export function matchPattern(pathname, pattern) {
181
181
  /**
182
182
  * Load and execute middleware for a given request.
183
183
  *
184
- * @param server - Vite dev server (for SSR module loading)
184
+ * @param runner - A ModuleRunner used to load the middleware module.
185
+ * Must be a long-lived instance created once (e.g. in configureServer) via
186
+ * createDirectRunner() — NOT recreated per request. Using server.ssrLoadModule
187
+ * directly crashes with `outsideEmitter` when @cloudflare/vite-plugin is
188
+ * present because SSRCompatModuleRunner reads environment.hot.api synchronously.
185
189
  * @param middlewarePath - Absolute path to the middleware file
186
190
  * @param request - The incoming Request object
187
191
  * @returns Middleware result describing what action to take
188
192
  */
189
- export async function runMiddleware(server, middlewarePath, request) {
190
- // Load the middleware module via Vite's SSR module loader
191
- const mod = await server.ssrLoadModule(middlewarePath);
193
+ export async function runMiddleware(runner, middlewarePath, request) {
194
+ // Load the middleware module via the direct-call ModuleRunner.
195
+ // This bypasses the hot channel entirely and is safe with all Vite plugin
196
+ // combinations, including @cloudflare/vite-plugin.
197
+ const mod = await runner.import(middlewarePath);
192
198
  // Resolve the handler based on file type (proxy.ts vs middleware.ts).
193
199
  // Throws if the file doesn't export a valid function, matching Next.js behavior.
194
200
  // https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts
@@ -221,10 +227,11 @@ export async function runMiddleware(server, middlewarePath, request) {
221
227
  }
222
228
  // Wrap in NextRequest so middleware gets .nextUrl, .cookies, .geo, .ip, etc.
223
229
  const nextRequest = mwRequest instanceof NextRequest ? mwRequest : new NextRequest(mwRequest);
230
+ const fetchEvent = new NextFetchEvent({ page: normalizedPathname });
224
231
  // Execute the middleware
225
232
  let response;
226
233
  try {
227
- response = await middlewareFn(nextRequest);
234
+ response = await middlewareFn(nextRequest, fetchEvent);
228
235
  }
229
236
  catch (e) {
230
237
  console.error("[vinext] Middleware error:", e);
@@ -238,6 +245,9 @@ export async function runMiddleware(server, middlewarePath, request) {
238
245
  }),
239
246
  };
240
247
  }
248
+ // Drain waitUntil promises (fire-and-forget: we don't block the response
249
+ // on these — matches platform semantics where waitUntil runs after response).
250
+ fetchEvent.drainWaitUntil();
241
251
  // No response = continue
242
252
  if (!response) {
243
253
  return { continue: true };
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * proxy.ts / middleware.ts runner\n *\n * Loads and executes the user's proxy.ts (Next.js 16) or middleware.ts file\n * before routing. Runs in Node (not Edge Runtime), per the vinext design.\n *\n * In Next.js 16, proxy.ts replaces middleware.ts:\n * - proxy.ts: default export OR named `proxy` function, runs on Node.js runtime\n * - middleware.ts: deprecated but still supported for Edge runtime use cases\n *\n * The proxy/middleware receives a NextRequest and can:\n * - Return NextResponse.next() to continue to the route\n * - Return NextResponse.redirect() to redirect\n * - Return NextResponse.rewrite() to rewrite the URL\n * - Set/modify headers and cookies\n * - Return a Response directly (e.g., for auth guards)\n *\n * Supports the `config.matcher` export for path filtering.\n */\n\nimport type { ViteDevServer } from \"vite\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { NextRequest } from \"../shims/server.js\";\nimport { safeRegExp } from \"../config/config-matchers.js\";\nimport { normalizePath } from \"./normalize-path.js\";\n\n/**\n * Determine whether a middleware/proxy file path refers to a proxy file.\n * proxy.ts files accept `proxy` or `default` exports.\n * middleware.ts files accept `middleware` or `default` exports.\n *\n * Matches Next.js behavior where each file type only accepts its own\n * named export or a default export:\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/middleware.ts\n */\nexport function isProxyFile(filePath: string): boolean {\n const base = path.basename(filePath).replace(/\\.\\w+$/, \"\");\n return base === \"proxy\";\n}\n\n/**\n * Resolve the middleware/proxy handler function from a module's exports.\n * Matches Next.js behavior: for proxy files, check `proxy` then `default`;\n * for middleware files, check `middleware` then `default`.\n *\n * Throws if the file exists but doesn't export a valid function, matching\n * Next.js's ProxyMissingExportError behavior.\n *\n * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/middleware.ts\n * @see https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts\n */\nexport function resolveMiddlewareHandler(\n mod: Record<string, unknown>,\n filePath: string,\n): Function {\n const isProxy = isProxyFile(filePath);\n const handler = isProxy\n ? (mod.proxy ?? mod.default)\n : (mod.middleware ?? mod.default);\n\n if (typeof handler !== \"function\") {\n const fileType = isProxy ? \"Proxy\" : \"Middleware\";\n const expectedExport = isProxy ? \"proxy\" : \"middleware\";\n throw new Error(\n `The ${fileType} file \"${filePath}\" must export a function named \\`${expectedExport}\\` or a \\`default\\` function.`,\n );\n }\n\n return handler as Function;\n}\n\n/**\n * Possible proxy/middleware file names.\n * proxy.ts (Next.js 16) is checked first, then middleware.ts (deprecated).\n */\nconst PROXY_FILES = [\n \"proxy.ts\",\n \"proxy.js\",\n \"proxy.mjs\",\n \"src/proxy.ts\",\n \"src/proxy.js\",\n \"src/proxy.mjs\",\n];\n\nconst MIDDLEWARE_FILES = [\n \"middleware.ts\",\n \"middleware.tsx\",\n \"middleware.js\",\n \"middleware.mjs\",\n \"src/middleware.ts\",\n \"src/middleware.tsx\",\n \"src/middleware.js\",\n \"src/middleware.mjs\",\n];\n\n/**\n * Find the proxy or middleware file in the project root.\n * Checks for proxy.ts (Next.js 16) first, then falls back to middleware.ts.\n * If middleware.ts is found, logs a deprecation warning.\n */\nexport function findMiddlewareFile(root: string): string | null {\n // Check proxy.ts first (Next.js 16 replacement for middleware.ts)\n for (const file of PROXY_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n\n // Fall back to middleware.ts (deprecated in Next.js 16)\n for (const file of MIDDLEWARE_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n console.warn(\n \"[vinext] middleware.ts is deprecated in Next.js 16. \" +\n \"Rename to proxy.ts and export a default or named proxy function.\",\n );\n return fullPath;\n }\n }\n return null;\n}\n\n/** Matcher pattern from middleware config export. */\ntype MatcherConfig =\n | string\n | string[]\n | { source: string; regexp?: string; locale?: boolean; has?: any[]; missing?: any[] }[];\n\n/**\n * Check if a pathname matches the middleware matcher config.\n * If no matcher is configured, middleware runs on all paths\n * except static files and internal Next.js paths.\n */\nexport function matchesMiddleware(\n pathname: string,\n matcher: MatcherConfig | undefined,\n): boolean {\n if (!matcher) {\n // Next.js default: middleware runs on ALL paths when no matcher is configured.\n // Users opt out of specific paths by configuring a matcher pattern.\n return true;\n }\n\n const patterns: string[] = [];\n if (typeof matcher === \"string\") {\n patterns.push(matcher);\n } else if (Array.isArray(matcher)) {\n for (const m of matcher) {\n if (typeof m === \"string\") {\n patterns.push(m);\n } else if (m && typeof m === \"object\" && \"source\" in m) {\n patterns.push(m.source);\n }\n }\n }\n\n return patterns.some((pattern) => matchPattern(pathname, pattern));\n}\n\n/**\n * Match a single pattern against a pathname.\n * Supports Next.js matcher patterns:\n * /about -> exact match\n * /dashboard/:path* -> prefix match with params\n * /api/:path+ -> one or more segments\n * /((?!api|_next).*) -> regex patterns\n */\nexport function matchPattern(pathname: string, pattern: string): boolean {\n // Handle regex patterns (starts with /)\n if (pattern.includes(\"(\") || pattern.includes(\"\\\\\")) {\n const re = safeRegExp(\"^\" + pattern + \"$\");\n if (re) return re.test(pathname);\n // Fall through to simple matching\n }\n\n // Convert Next.js path patterns to regex in a single pass.\n // Matches /:param*, /:param+, :param, dots, and literal text.\n // Param names may contain hyphens (e.g. [[...sign-in]]).\n let regexStr = \"\";\n const tokenRe = /\\/:([\\w-]+)\\*|\\/:([\\w-]+)\\+|:([\\w-]+)|[.]|[^/:.]+|./g;\n let tok: RegExpExecArray | null;\n while ((tok = tokenRe.exec(pattern)) !== null) {\n if (tok[1] !== undefined) {\n // /:param* → optionally match slash + zero or more segments\n regexStr += \"(?:/.*)?\";\n } else if (tok[2] !== undefined) {\n // /:param+ → match slash + one or more segments\n regexStr += \"(?:/.+)\";\n } else if (tok[3] !== undefined) {\n // :param → match one segment\n regexStr += \"([^/]+)\";\n } else if (tok[0] === \".\") {\n regexStr += \"\\\\.\";\n } else {\n regexStr += tok[0];\n }\n }\n\n const re = safeRegExp(\"^\" + regexStr + \"$\");\n if (re) return re.test(pathname);\n return pathname === pattern;\n}\n\n/** Result of running middleware. */\nexport interface MiddlewareResult {\n /** Whether to continue to the route handler. */\n continue: boolean;\n /** If set, redirect to this URL. */\n redirectUrl?: string;\n /** HTTP status for redirect (default 307). */\n redirectStatus?: number;\n /** If set, rewrite to this URL (internal). */\n rewriteUrl?: string;\n /** HTTP status for rewrite (e.g. 403 from NextResponse.rewrite(url, { status: 403 })). */\n rewriteStatus?: number;\n /** Headers to set on the response. */\n responseHeaders?: Headers;\n /** If the middleware returned a full Response, use it directly. */\n response?: Response;\n}\n\n/**\n * Load and execute middleware for a given request.\n *\n * @param server - Vite dev server (for SSR module loading)\n * @param middlewarePath - Absolute path to the middleware file\n * @param request - The incoming Request object\n * @returns Middleware result describing what action to take\n */\nexport async function runMiddleware(\n server: ViteDevServer,\n middlewarePath: string,\n request: Request,\n): Promise<MiddlewareResult> {\n // Load the middleware module via Vite's SSR module loader\n const mod = await server.ssrLoadModule(middlewarePath);\n\n // Resolve the handler based on file type (proxy.ts vs middleware.ts).\n // Throws if the file doesn't export a valid function, matching Next.js behavior.\n // https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts\n const middlewareFn = resolveMiddlewareHandler(mod, middlewarePath);\n\n // Check matcher config\n const config = mod.config;\n const matcher = config?.matcher;\n const url = new URL(request.url);\n\n // Normalize the pathname before middleware matching to prevent bypasses\n // via percent-encoding (/%61dmin → /admin) or double slashes (/dashboard//settings).\n let decodedPathname: string;\n try {\n decodedPathname = decodeURIComponent(url.pathname);\n } catch {\n // Malformed percent-encoding (e.g. /%E0%A4%A) — return 400 instead of throwing.\n return { continue: false, response: new Response(\"Bad Request\", { status: 400 }) };\n }\n const normalizedPathname = normalizePath(decodedPathname);\n\n if (!matchesMiddleware(normalizedPathname, matcher)) {\n return { continue: true };\n }\n\n // Construct a new Request with the fully decoded + normalized pathname so\n // middleware always sees the same canonical path that the router uses.\n let mwRequest = request;\n if (normalizedPathname !== url.pathname) {\n const mwUrl = new URL(url);\n mwUrl.pathname = normalizedPathname;\n mwRequest = new Request(mwUrl, request);\n }\n\n // Wrap in NextRequest so middleware gets .nextUrl, .cookies, .geo, .ip, etc.\n const nextRequest = mwRequest instanceof NextRequest ? mwRequest : new NextRequest(mwRequest);\n\n // Execute the middleware\n let response: Response | undefined;\n try {\n response = await middlewareFn(nextRequest);\n } catch (e: any) {\n console.error(\"[vinext] Middleware error:\", e);\n const message =\n process.env.NODE_ENV === \"production\"\n ? \"Internal Server Error\"\n : \"Middleware Error: \" + (e?.message ?? String(e));\n return {\n continue: false,\n response: new Response(message, {\n status: 500,\n }),\n };\n }\n\n // No response = continue\n if (!response) {\n return { continue: true };\n }\n\n // Check for x-middleware-next header (NextResponse.next())\n if (response.headers.get(\"x-middleware-next\") === \"1\") {\n // Continue to the route, but apply any headers the middleware set.\n // Strip ALL x-middleware-* headers (including x-middleware-request-*)\n // so they never leak to the client — they are internal routing signals.\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (!key.startsWith(\"x-middleware-\")) {\n responseHeaders.append(key, value);\n }\n }\n return { continue: true, responseHeaders };\n }\n\n // Check for redirect (3xx status)\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"Location\") ?? response.headers.get(\"location\");\n if (location) {\n // Collect non-internal headers (e.g. Set-Cookie) to forward with the redirect.\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (!key.startsWith(\"x-middleware-\") && key.toLowerCase() !== \"location\") {\n responseHeaders.append(key, value);\n }\n }\n return {\n continue: false,\n redirectUrl: location,\n redirectStatus: response.status,\n responseHeaders,\n };\n }\n }\n\n // Check for rewrite (x-middleware-rewrite header)\n const rewriteUrl = response.headers.get(\"x-middleware-rewrite\");\n if (rewriteUrl) {\n // Continue to the route but with a rewritten URL\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (!key.startsWith(\"x-middleware-\")) {\n responseHeaders.append(key, value);\n }\n }\n // Parse the rewrite URL — may be absolute or relative\n let rewritePath: string;\n try {\n const rewriteParsed = new URL(rewriteUrl, request.url);\n rewritePath = rewriteParsed.pathname + rewriteParsed.search;\n } catch {\n rewritePath = rewriteUrl;\n }\n return {\n continue: true,\n rewriteUrl: rewritePath,\n rewriteStatus: response.status !== 200 ? response.status : undefined,\n responseHeaders,\n };\n }\n\n // Middleware returned a full Response (e.g., blocking, custom body)\n return { continue: false, response };\n}\n"]}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * proxy.ts / middleware.ts runner\n *\n * Loads and executes the user's proxy.ts (Next.js 16) or middleware.ts file\n * before routing. Runs in Node (not Edge Runtime), per the vinext design.\n *\n * In Next.js 16, proxy.ts replaces middleware.ts:\n * - proxy.ts: default export OR named `proxy` function, runs on Node.js runtime\n * - middleware.ts: deprecated but still supported for Edge runtime use cases\n *\n * The proxy/middleware receives a NextRequest and can:\n * - Return NextResponse.next() to continue to the route\n * - Return NextResponse.redirect() to redirect\n * - Return NextResponse.rewrite() to rewrite the URL\n * - Set/modify headers and cookies\n * - Return a Response directly (e.g., for auth guards)\n *\n * Supports the `config.matcher` export for path filtering.\n */\n\nimport type { ModuleRunner } from \"vite/module-runner\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { NextRequest, NextFetchEvent } from \"../shims/server.js\";\nimport { safeRegExp } from \"../config/config-matchers.js\";\nimport { normalizePath } from \"./normalize-path.js\";\n\n/**\n * Determine whether a middleware/proxy file path refers to a proxy file.\n * proxy.ts files accept `proxy` or `default` exports.\n * middleware.ts files accept `middleware` or `default` exports.\n *\n * Matches Next.js behavior where each file type only accepts its own\n * named export or a default export:\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/middleware.ts\n */\nexport function isProxyFile(filePath: string): boolean {\n const base = path.basename(filePath).replace(/\\.\\w+$/, \"\");\n return base === \"proxy\";\n}\n\n/**\n * Resolve the middleware/proxy handler function from a module's exports.\n * Matches Next.js behavior: for proxy files, check `proxy` then `default`;\n * for middleware files, check `middleware` then `default`.\n *\n * Throws if the file exists but doesn't export a valid function, matching\n * Next.js's ProxyMissingExportError behavior.\n *\n * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/middleware.ts\n * @see https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts\n */\nexport function resolveMiddlewareHandler(\n mod: Record<string, unknown>,\n filePath: string,\n): Function {\n const isProxy = isProxyFile(filePath);\n const handler = isProxy\n ? (mod.proxy ?? mod.default)\n : (mod.middleware ?? mod.default);\n\n if (typeof handler !== \"function\") {\n const fileType = isProxy ? \"Proxy\" : \"Middleware\";\n const expectedExport = isProxy ? \"proxy\" : \"middleware\";\n throw new Error(\n `The ${fileType} file \"${filePath}\" must export a function named \\`${expectedExport}\\` or a \\`default\\` function.`,\n );\n }\n\n return handler as Function;\n}\n\n/**\n * Possible proxy/middleware file names.\n * proxy.ts (Next.js 16) is checked first, then middleware.ts (deprecated).\n */\nconst PROXY_FILES = [\n \"proxy.ts\",\n \"proxy.js\",\n \"proxy.mjs\",\n \"src/proxy.ts\",\n \"src/proxy.js\",\n \"src/proxy.mjs\",\n];\n\nconst MIDDLEWARE_FILES = [\n \"middleware.ts\",\n \"middleware.tsx\",\n \"middleware.js\",\n \"middleware.mjs\",\n \"src/middleware.ts\",\n \"src/middleware.tsx\",\n \"src/middleware.js\",\n \"src/middleware.mjs\",\n];\n\n/**\n * Find the proxy or middleware file in the project root.\n * Checks for proxy.ts (Next.js 16) first, then falls back to middleware.ts.\n * If middleware.ts is found, logs a deprecation warning.\n */\nexport function findMiddlewareFile(root: string): string | null {\n // Check proxy.ts first (Next.js 16 replacement for middleware.ts)\n for (const file of PROXY_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n\n // Fall back to middleware.ts (deprecated in Next.js 16)\n for (const file of MIDDLEWARE_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n console.warn(\n \"[vinext] middleware.ts is deprecated in Next.js 16. \" +\n \"Rename to proxy.ts and export a default or named proxy function.\",\n );\n return fullPath;\n }\n }\n return null;\n}\n\n/** Matcher pattern from middleware config export. */\ntype MatcherConfig =\n | string\n | string[]\n | { source: string; regexp?: string; locale?: boolean; has?: any[]; missing?: any[] }[];\n\n/**\n * Check if a pathname matches the middleware matcher config.\n * If no matcher is configured, middleware runs on all paths\n * except static files and internal Next.js paths.\n */\nexport function matchesMiddleware(\n pathname: string,\n matcher: MatcherConfig | undefined,\n): boolean {\n if (!matcher) {\n // Next.js default: middleware runs on ALL paths when no matcher is configured.\n // Users opt out of specific paths by configuring a matcher pattern.\n return true;\n }\n\n const patterns: string[] = [];\n if (typeof matcher === \"string\") {\n patterns.push(matcher);\n } else if (Array.isArray(matcher)) {\n for (const m of matcher) {\n if (typeof m === \"string\") {\n patterns.push(m);\n } else if (m && typeof m === \"object\" && \"source\" in m) {\n patterns.push(m.source);\n }\n }\n }\n\n return patterns.some((pattern) => matchPattern(pathname, pattern));\n}\n\n/**\n * Match a single pattern against a pathname.\n * Supports Next.js matcher patterns:\n * /about -> exact match\n * /dashboard/:path* -> prefix match with params\n * /api/:path+ -> one or more segments\n * /((?!api|_next).*) -> regex patterns\n */\nexport function matchPattern(pathname: string, pattern: string): boolean {\n // Handle regex patterns (starts with /)\n if (pattern.includes(\"(\") || pattern.includes(\"\\\\\")) {\n const re = safeRegExp(\"^\" + pattern + \"$\");\n if (re) return re.test(pathname);\n // Fall through to simple matching\n }\n\n // Convert Next.js path patterns to regex in a single pass.\n // Matches /:param*, /:param+, :param, dots, and literal text.\n // Param names may contain hyphens (e.g. [[...sign-in]]).\n let regexStr = \"\";\n const tokenRe = /\\/:([\\w-]+)\\*|\\/:([\\w-]+)\\+|:([\\w-]+)|[.]|[^/:.]+|./g;\n let tok: RegExpExecArray | null;\n while ((tok = tokenRe.exec(pattern)) !== null) {\n if (tok[1] !== undefined) {\n // /:param* → optionally match slash + zero or more segments\n regexStr += \"(?:/.*)?\";\n } else if (tok[2] !== undefined) {\n // /:param+ → match slash + one or more segments\n regexStr += \"(?:/.+)\";\n } else if (tok[3] !== undefined) {\n // :param → match one segment\n regexStr += \"([^/]+)\";\n } else if (tok[0] === \".\") {\n regexStr += \"\\\\.\";\n } else {\n regexStr += tok[0];\n }\n }\n\n const re = safeRegExp(\"^\" + regexStr + \"$\");\n if (re) return re.test(pathname);\n return pathname === pattern;\n}\n\n/** Result of running middleware. */\nexport interface MiddlewareResult {\n /** Whether to continue to the route handler. */\n continue: boolean;\n /** If set, redirect to this URL. */\n redirectUrl?: string;\n /** HTTP status for redirect (default 307). */\n redirectStatus?: number;\n /** If set, rewrite to this URL (internal). */\n rewriteUrl?: string;\n /** HTTP status for rewrite (e.g. 403 from NextResponse.rewrite(url, { status: 403 })). */\n rewriteStatus?: number;\n /** Headers to set on the response. */\n responseHeaders?: Headers;\n /** If the middleware returned a full Response, use it directly. */\n response?: Response;\n}\n\n/**\n * Load and execute middleware for a given request.\n *\n * @param runner - A ModuleRunner used to load the middleware module.\n * Must be a long-lived instance created once (e.g. in configureServer) via\n * createDirectRunner() — NOT recreated per request. Using server.ssrLoadModule\n * directly crashes with `outsideEmitter` when @cloudflare/vite-plugin is\n * present because SSRCompatModuleRunner reads environment.hot.api synchronously.\n * @param middlewarePath - Absolute path to the middleware file\n * @param request - The incoming Request object\n * @returns Middleware result describing what action to take\n */\nexport async function runMiddleware(\n runner: ModuleRunner,\n middlewarePath: string,\n request: Request,\n): Promise<MiddlewareResult> {\n // Load the middleware module via the direct-call ModuleRunner.\n // This bypasses the hot channel entirely and is safe with all Vite plugin\n // combinations, including @cloudflare/vite-plugin.\n const mod = await runner.import(middlewarePath) as Record<string, unknown>;\n\n // Resolve the handler based on file type (proxy.ts vs middleware.ts).\n // Throws if the file doesn't export a valid function, matching Next.js behavior.\n // https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts\n const middlewareFn = resolveMiddlewareHandler(mod, middlewarePath);\n\n // Check matcher config\n const config = mod.config as { matcher?: MatcherConfig } | undefined;\n const matcher = config?.matcher;\n const url = new URL(request.url);\n\n // Normalize the pathname before middleware matching to prevent bypasses\n // via percent-encoding (/%61dmin → /admin) or double slashes (/dashboard//settings).\n let decodedPathname: string;\n try {\n decodedPathname = decodeURIComponent(url.pathname);\n } catch {\n // Malformed percent-encoding (e.g. /%E0%A4%A) — return 400 instead of throwing.\n return { continue: false, response: new Response(\"Bad Request\", { status: 400 }) };\n }\n const normalizedPathname = normalizePath(decodedPathname);\n\n if (!matchesMiddleware(normalizedPathname, matcher)) {\n return { continue: true };\n }\n\n // Construct a new Request with the fully decoded + normalized pathname so\n // middleware always sees the same canonical path that the router uses.\n let mwRequest = request;\n if (normalizedPathname !== url.pathname) {\n const mwUrl = new URL(url);\n mwUrl.pathname = normalizedPathname;\n mwRequest = new Request(mwUrl, request);\n }\n\n // Wrap in NextRequest so middleware gets .nextUrl, .cookies, .geo, .ip, etc.\n const nextRequest = mwRequest instanceof NextRequest ? mwRequest : new NextRequest(mwRequest);\n const fetchEvent = new NextFetchEvent({ page: normalizedPathname });\n\n // Execute the middleware\n let response: Response | undefined;\n try {\n response = await middlewareFn(nextRequest, fetchEvent);\n } catch (e: any) {\n console.error(\"[vinext] Middleware error:\", e);\n const message =\n process.env.NODE_ENV === \"production\"\n ? \"Internal Server Error\"\n : \"Middleware Error: \" + (e?.message ?? String(e));\n return {\n continue: false,\n response: new Response(message, {\n status: 500,\n }),\n };\n }\n\n // Drain waitUntil promises (fire-and-forget: we don't block the response\n // on these — matches platform semantics where waitUntil runs after response).\n fetchEvent.drainWaitUntil();\n\n // No response = continue\n if (!response) {\n return { continue: true };\n }\n\n // Check for x-middleware-next header (NextResponse.next())\n if (response.headers.get(\"x-middleware-next\") === \"1\") {\n // Continue to the route, but apply any headers the middleware set.\n // Strip ALL x-middleware-* headers (including x-middleware-request-*)\n // so they never leak to the client — they are internal routing signals.\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (!key.startsWith(\"x-middleware-\")) {\n responseHeaders.append(key, value);\n }\n }\n return { continue: true, responseHeaders };\n }\n\n // Check for redirect (3xx status)\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"Location\") ?? response.headers.get(\"location\");\n if (location) {\n // Collect non-internal headers (e.g. Set-Cookie) to forward with the redirect.\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (!key.startsWith(\"x-middleware-\") && key.toLowerCase() !== \"location\") {\n responseHeaders.append(key, value);\n }\n }\n return {\n continue: false,\n redirectUrl: location,\n redirectStatus: response.status,\n responseHeaders,\n };\n }\n }\n\n // Check for rewrite (x-middleware-rewrite header)\n const rewriteUrl = response.headers.get(\"x-middleware-rewrite\");\n if (rewriteUrl) {\n // Continue to the route but with a rewritten URL\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (!key.startsWith(\"x-middleware-\")) {\n responseHeaders.append(key, value);\n }\n }\n // Parse the rewrite URL — may be absolute or relative\n let rewritePath: string;\n try {\n const rewriteParsed = new URL(rewriteUrl, request.url);\n rewritePath = rewriteParsed.pathname + rewriteParsed.search;\n } catch {\n rewritePath = rewriteUrl;\n }\n return {\n continue: true,\n rewriteUrl: rewritePath,\n rewriteStatus: response.status !== 200 ? response.status : undefined,\n responseHeaders,\n };\n }\n\n // Middleware returned a full Response (e.g., blocking, custom body)\n return { continue: false, response };\n}\n"]}
@@ -37,11 +37,17 @@ declare const COMPRESS_THRESHOLD = 1024;
37
37
  * Preference order: br > gzip > deflate > identity.
38
38
  */
39
39
  declare function negotiateEncoding(req: IncomingMessage): "br" | "gzip" | "deflate" | null;
40
+ /**
41
+ * Merge middleware headers and a Web Response's headers into a single
42
+ * record suitable for Node.js `res.writeHead()`. Uses `getSetCookie()`
43
+ * to preserve multiple Set-Cookie values instead of flattening them.
44
+ */
45
+ declare function mergeResponseHeaders(middlewareHeaders: Record<string, string | string[]>, response: Response): Record<string, string | string[]>;
40
46
  /**
41
47
  * Send a compressed response if the content type is compressible and the
42
48
  * client supports compression. Otherwise send uncompressed.
43
49
  */
44
- declare function sendCompressed(req: IncomingMessage, res: ServerResponse, body: string | Buffer, contentType: string, statusCode: number, extraHeaders?: Record<string, string>, compress?: boolean): void;
50
+ declare function sendCompressed(req: IncomingMessage, res: ServerResponse, body: string | Buffer, contentType: string, statusCode: number, extraHeaders?: Record<string, string | string[]>, compress?: boolean): void;
45
51
  /**
46
52
  * Resolve the host for a request, ignoring X-Forwarded-Host to prevent
47
53
  * host header poisoning attacks (open redirects, cache poisoning).
@@ -74,5 +80,5 @@ declare function nodeToWebRequest(req: IncomingMessage): Request;
74
80
  * Pages Router (dist/server/entry.js) and configures the appropriate handler.
75
81
  */
76
82
  export declare function startProdServer(options?: ProdServerOptions): Promise<import("node:http").Server<typeof IncomingMessage, typeof ServerResponse>>;
77
- export { sendCompressed, negotiateEncoding, COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, resolveHost, trustedHosts, trustProxy, nodeToWebRequest };
83
+ export { sendCompressed, negotiateEncoding, COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, resolveHost, trustedHosts, trustProxy, nodeToWebRequest, mergeResponseHeaders };
78
84
  //# sourceMappingURL=prod-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prod-server.d.ts","sourceRoot":"","sources":["../../src/server/prod-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAuBpF,MAAM,WAAW,iBAAiB;IAChC,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,mDAAmD;AACnD,QAAA,MAAM,kBAAkB,aAetB,CAAC;AAEH,0GAA0G;AAC1G,QAAA,MAAM,kBAAkB,OAAO,CAAC;AAEhC;;;GAGG;AACH,iBAAS,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAQjF;AAoBD;;;GAGG;AACH,iBAAS,cAAc,CACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACzC,QAAQ,GAAE,OAAc,GACvB,IAAI,CAkCN;AAkGD;;;;;;;;;;;GAWG;AACH,iBAAS,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAcnE;AAED,4EAA4E;AAC5E,QAAA,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,CAK7B,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,UAAU,SAAkE,CAAC;AAEnF;;GAEG;AACH,iBAAS,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAmCvD;AA+ED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,iBAAsB,sFA6BpE;AAsiBD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"prod-server.d.ts","sourceRoot":"","sources":["../../src/server/prod-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAuBpF,MAAM,WAAW,iBAAiB;IAChC,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,mDAAmD;AACnD,QAAA,MAAM,kBAAkB,aAetB,CAAC;AAEH,0GAA0G;AAC1G,QAAA,MAAM,kBAAkB,OAAO,CAAC;AAEhC;;;GAGG;AACH,iBAAS,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAQjF;AAoBD;;;;GAIG;AACH,iBAAS,oBAAoB,CAC3B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EACpD,QAAQ,EAAE,QAAQ,GACjB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAqBnC;AAED;;;GAGG;AACH,iBAAS,cAAc,CACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAM,EACpD,QAAQ,GAAE,OAAc,GACvB,IAAI,CAmCN;AAkGD;;;;;;;;;;;GAWG;AACH,iBAAS,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAcnE;AAED,4EAA4E;AAC5E,QAAA,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,CAK7B,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,UAAU,SAAkE,CAAC;AAEnF;;GAEG;AACH,iBAAS,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAmCvD;AA+ED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,iBAAsB,sFA6BpE;AAkiBD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC"}