@thomas.f/prnext 0.1.0-alpha.1

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 (211) hide show
  1. package/README.md +54 -0
  2. package/build/action-exports.mjs +107 -0
  3. package/build/actions.mjs +214 -0
  4. package/build/app-config.mjs +138 -0
  5. package/build/app-routing.mjs +119 -0
  6. package/build/app-static.mjs +158 -0
  7. package/build/app.mjs +536 -0
  8. package/build/cache-components.mjs +91 -0
  9. package/build/cache-handlers.mjs +54 -0
  10. package/build/check.mjs +40 -0
  11. package/build/compiler.mjs +222 -0
  12. package/build/config.mjs +193 -0
  13. package/build/css-modules.mjs +114 -0
  14. package/build/custom-routes.mjs +363 -0
  15. package/build/dev-worker.mjs +18 -0
  16. package/build/dev.mjs +76 -0
  17. package/build/directory-cache.mjs +29 -0
  18. package/build/dynamic.mjs +125 -0
  19. package/build/edge.mjs +141 -0
  20. package/build/font-data.LICENSE.txt +25 -0
  21. package/build/font-data.json +1 -0
  22. package/build/font-files.mjs +221 -0
  23. package/build/fonts.mjs +131 -0
  24. package/build/framework-imports.mjs +17 -0
  25. package/build/i18n.mjs +39 -0
  26. package/build/images-config.mjs +62 -0
  27. package/build/images.mjs +86 -0
  28. package/build/incremental.mjs +76 -0
  29. package/build/index.mjs +356 -0
  30. package/build/loader-cache.mjs +69 -0
  31. package/build/metadata-images.mjs +72 -0
  32. package/build/metadata-routes.mjs +77 -0
  33. package/build/middleware.mjs +231 -0
  34. package/build/migrate.mjs +289 -0
  35. package/build/module-resolution.mjs +78 -0
  36. package/build/native-discovery.mjs +134 -0
  37. package/build/native-packages.mjs +105 -0
  38. package/build/npm-packages.mjs +110 -0
  39. package/build/og.mjs +14 -0
  40. package/build/page-extensions.mjs +12 -0
  41. package/build/passthrough-loader.cjs +4 -0
  42. package/build/precompress.mjs +122 -0
  43. package/build/scan.mjs +178 -0
  44. package/build/script-workers.mjs +25 -0
  45. package/build/server-actions-config.mjs +27 -0
  46. package/build/source-maps.mjs +34 -0
  47. package/build/standalone-config.mjs +19 -0
  48. package/build/standalone-runner.mjs +21 -0
  49. package/build/standalone-worker.mjs +5 -0
  50. package/build/standalone.mjs +240 -0
  51. package/build/static-export.mjs +93 -0
  52. package/build/styles.mjs +174 -0
  53. package/build/transform-cache.mjs +50 -0
  54. package/build/transform.mjs +155 -0
  55. package/build/turbopack-conditions.mjs +59 -0
  56. package/build/webpack-bridge.cjs +3 -0
  57. package/build/webpack-graph.mjs +256 -0
  58. package/build/webpack-loader.cjs +9 -0
  59. package/cli.mjs +210 -0
  60. package/compat/app-client-page.cjs +32 -0
  61. package/compat/app-context.cjs +40 -0
  62. package/compat/app-context.d.cts +20 -0
  63. package/compat/app-dynamic.cjs +34 -0
  64. package/compat/app-error-boundary.cjs +53 -0
  65. package/compat/app-global-error-boundary.cjs +34 -0
  66. package/compat/app-layout-context.cjs +27 -0
  67. package/compat/app-navigation-boundary.cjs +47 -0
  68. package/compat/app.cjs +33 -0
  69. package/compat/app.d.cts +9 -0
  70. package/compat/cache-handlers.cjs +223 -0
  71. package/compat/cache-life.cjs +34 -0
  72. package/compat/cache.cjs +99 -0
  73. package/compat/cache.d.cts +36 -0
  74. package/compat/compat/router.d.cts +2 -0
  75. package/compat/compat-router.cjs +7 -0
  76. package/compat/constants.cjs +8 -0
  77. package/compat/constants.d.cts +7 -0
  78. package/compat/cookies.cjs +129 -0
  79. package/compat/data-cache.cjs +389 -0
  80. package/compat/dist/shared/lib/app-router-context.shared-runtime.d.cts +3 -0
  81. package/compat/dist/shared/lib/router-context.d.cts +1 -0
  82. package/compat/dist/shared/lib/router-context.shared-runtime.d.cts +3 -0
  83. package/compat/document.cjs +73 -0
  84. package/compat/document.d.cts +41 -0
  85. package/compat/draft.cjs +28 -0
  86. package/compat/dynamic-bailout.cjs +13 -0
  87. package/compat/dynamic.cjs +119 -0
  88. package/compat/dynamic.d.cts +32 -0
  89. package/compat/error.cjs +36 -0
  90. package/compat/error.d.cts +15 -0
  91. package/compat/fetch.d.cts +10 -0
  92. package/compat/font/google.d.cts +1 -0
  93. package/compat/font/local.d.cts +2 -0
  94. package/compat/font-google.cjs +2 -0
  95. package/compat/font-google.d.cts +1944 -0
  96. package/compat/font-local.cjs +5 -0
  97. package/compat/font-local.d.cts +3 -0
  98. package/compat/font-types.d.cts +25 -0
  99. package/compat/head.cjs +180 -0
  100. package/compat/head.d.cts +3 -0
  101. package/compat/headers.cjs +89 -0
  102. package/compat/headers.d.cts +26 -0
  103. package/compat/image-config.cjs +3 -0
  104. package/compat/image-imports.d.ts +12 -0
  105. package/compat/image-shared.cjs +62 -0
  106. package/compat/image.cjs +64 -0
  107. package/compat/image.d.cts +35 -0
  108. package/compat/incremental-cache.cjs +186 -0
  109. package/compat/index.d.ts +159 -0
  110. package/compat/instant-samples.cjs +39 -0
  111. package/compat/instant.cjs +13 -0
  112. package/compat/link.cjs +68 -0
  113. package/compat/link.d.cts +17 -0
  114. package/compat/locale.cjs +30 -0
  115. package/compat/metadata.d.cts +77 -0
  116. package/compat/navigation-controls.cjs +27 -0
  117. package/compat/navigation-server.cjs +25 -0
  118. package/compat/navigation.cjs +52 -0
  119. package/compat/navigation.d.cts +28 -0
  120. package/compat/next-app-router-context.cjs +6 -0
  121. package/compat/next-router-context.cjs +3 -0
  122. package/compat/og.cjs +32 -0
  123. package/compat/og.d.cts +1 -0
  124. package/compat/paths.cjs +33 -0
  125. package/compat/preview.cjs +41 -0
  126. package/compat/rewrite.cjs +41 -0
  127. package/compat/route-request.cjs +64 -0
  128. package/compat/router.cjs +137 -0
  129. package/compat/router.d.cts +58 -0
  130. package/compat/script-context.cjs +5 -0
  131. package/compat/script-loader.cjs +135 -0
  132. package/compat/script.cjs +69 -0
  133. package/compat/script.d.cts +17 -0
  134. package/compat/server-primitives.cjs +109 -0
  135. package/compat/server.cjs +13 -0
  136. package/compat/server.d.cts +37 -0
  137. package/compat/static-generation.cjs +108 -0
  138. package/compat/use-cache.cjs +129 -0
  139. package/index.mjs +1 -0
  140. package/native/platforms.json +6 -0
  141. package/native/resolve.mjs +81 -0
  142. package/package.json +127 -0
  143. package/release/verify.mjs +59 -0
  144. package/runtime/action-client.mjs +19 -0
  145. package/runtime/action-crypto.mjs +138 -0
  146. package/runtime/action-reference.mjs +2 -0
  147. package/runtime/action-server.mjs +161 -0
  148. package/runtime/action-ssr.mjs +5 -0
  149. package/runtime/api.mjs +326 -0
  150. package/runtime/app-client.mjs +539 -0
  151. package/runtime/app-content.mjs +8 -0
  152. package/runtime/app-errors.mjs +12 -0
  153. package/runtime/app-instant.mjs +30 -0
  154. package/runtime/app-metadata.mjs +410 -0
  155. package/runtime/app-partial-model.mjs +228 -0
  156. package/runtime/app-partial.mjs +130 -0
  157. package/runtime/app-prefetch.mjs +34 -0
  158. package/runtime/app-recovery.mjs +87 -0
  159. package/runtime/app-render.mjs +544 -0
  160. package/runtime/app-routing-access.mjs +143 -0
  161. package/runtime/app-routing-context.mjs +35 -0
  162. package/runtime/app-routing-state.mjs +61 -0
  163. package/runtime/app-routing.mjs +323 -0
  164. package/runtime/app-segments.mjs +22 -0
  165. package/runtime/app-static-params.mjs +120 -0
  166. package/runtime/app-static.mjs +86 -0
  167. package/runtime/app-stream-channel.mjs +92 -0
  168. package/runtime/app-stream-html.mjs +279 -0
  169. package/runtime/build-directory.mjs +48 -0
  170. package/runtime/cache-arguments.mjs +33 -0
  171. package/runtime/cache-components-rsc.mjs +64 -0
  172. package/runtime/cache-request.mjs +23 -0
  173. package/runtime/client-navigation.mjs +40 -0
  174. package/runtime/dev-client.mjs +129 -0
  175. package/runtime/dev-file-changes.mjs +110 -0
  176. package/runtime/document.mjs +96 -0
  177. package/runtime/edge.mjs +105 -0
  178. package/runtime/env.mjs +68 -0
  179. package/runtime/fetch-cache.mjs +379 -0
  180. package/runtime/font-preload.mjs +4 -0
  181. package/runtime/http.mjs +190 -0
  182. package/runtime/incremental-cache.mjs +83 -0
  183. package/runtime/metadata-route.mjs +83 -0
  184. package/runtime/middleware.mjs +186 -0
  185. package/runtime/module-loader.mjs +21 -0
  186. package/runtime/navigation.mjs +16 -0
  187. package/runtime/og-loader.mjs +17 -0
  188. package/runtime/pages-client.mjs +197 -0
  189. package/runtime/pages-error-props.mjs +6 -0
  190. package/runtime/pages-errors.mjs +85 -0
  191. package/runtime/pages-initial-props.mjs +43 -0
  192. package/runtime/pages-navigation.mjs +565 -0
  193. package/runtime/pages-paths.mjs +80 -0
  194. package/runtime/pages-revalidate.mjs +33 -0
  195. package/runtime/partial-artifact-cache.mjs +37 -0
  196. package/runtime/partial-template.mjs +174 -0
  197. package/runtime/profile.mjs +9 -0
  198. package/runtime/profiles.json +47 -0
  199. package/runtime/render.mjs +304 -0
  200. package/runtime/request-transport.mjs +41 -0
  201. package/runtime/request-work.mjs +7 -0
  202. package/runtime/route-static.mjs +117 -0
  203. package/runtime/rsc-worker.mjs +426 -0
  204. package/runtime/script-html.mjs +61 -0
  205. package/runtime/stream-select.mjs +25 -0
  206. package/runtime/stream-utils.mjs +127 -0
  207. package/runtime/transport.mjs +108 -0
  208. package/runtime/watch-project.mjs +130 -0
  209. package/runtime/worker-artifacts.mjs +22 -0
  210. package/runtime/worker-metadata.mjs +31 -0
  211. package/runtime/worker.mjs +197 -0
@@ -0,0 +1,326 @@
1
+ import { PassThrough } from 'node:stream';
2
+ import { trackRequestWork } from './request-work.mjs';
3
+ import { AsyncLocalStorage } from 'node:async_hooks';
4
+ import path from 'node:path';
5
+ import { removeBasePath } from '../compat/paths.cjs';
6
+ import { runRequestContext, currentRequest } from '../compat/headers.cjs';
7
+ import { flushCacheInvalidations } from '../compat/data-cache.cjs';
8
+ import { navigationResponse } from './navigation.mjs';
9
+ import { abortError, apiTimeoutError, STREAM_CHUNK_BYTES, streamingBody, withSignal } from './stream-utils.mjs';
10
+ import { withCacheRequest } from './cache-request.mjs';
11
+ import { installFetchCache } from './fetch-cache.mjs';
12
+ import { staticPath } from './pages-paths.mjs';
13
+ import { collectStaticParams } from './app-static-params.mjs';
14
+ import { loadModule, loadedPagesModule } from './module-loader.mjs';
15
+ import { CapturedResponse, createRequest, queryFromUrl, MAX_RESPONSE_BYTES, forbiddenHeaders } from './http.mjs';
16
+
17
+ installFetchCache();
18
+ let routePrimitives;
19
+ async function appRequestPrimitives() {
20
+ if (!routePrimitives) {
21
+ const [{NextRequest}, {proxyRouteRequest}] = await Promise.all([import('../compat/server.cjs'), import('../compat/route-request.cjs')]);
22
+ // Retain functions only: a cached import promise can keep the first
23
+ // request's AsyncLocalStorage frame alive after that request completes.
24
+ routePrimitives = {NextRequest, proxyRouteRequest};
25
+ }
26
+ return routePrimitives;
27
+ }
28
+
29
+ class StreamingResponse extends CapturedResponse {
30
+ constructor() {
31
+ super({ highWaterMark: STREAM_CHUNK_BYTES }, true);
32
+ this.head = new Promise((resolve, reject) => { this._resolveHead = resolve; this._rejectHead = reject; });
33
+ // Headers can fail synchronously in a handler before runApi awaits them.
34
+ this.head.catch(() => {});
35
+ }
36
+ get output() {
37
+ if (!this._output) {
38
+ this._output = new PassThrough({ highWaterMark: STREAM_CHUNK_BYTES });
39
+ this._output.on('error', () => {});
40
+ }
41
+ return this._output;
42
+ }
43
+ _commit() {
44
+ if (this._committed) return;
45
+ if (!Number.isInteger(this.statusCode) || this.statusCode < 100 || this.statusCode > 599) throw new Error('Invalid response status');
46
+ this._committed = true;
47
+ this.headersSent = true;
48
+ const headers = Object.fromEntries(Object.entries(this._headers).filter(([name]) => !forbiddenHeaders.has(name) && name !== 'content-length').map(([name, value]) => [name, Array.isArray(value) ? [...value] : value]));
49
+ this._resolveHead({ status: this.statusCode, headers });
50
+ }
51
+ _write(chunk, _encoding, callback) {
52
+ try { this._commit(); } catch (error) { callback(error); return; }
53
+ if (this._compact) {
54
+ // Own the bytes before acknowledging the producer's reusable buffer.
55
+ this._compactBody = Buffer.from(chunk);
56
+ callback();
57
+ return;
58
+ }
59
+ let offset = 0;
60
+ const writeNext = error => {
61
+ if (error) { callback(error); return; }
62
+ if (offset >= chunk.byteLength) { callback(); return; }
63
+ // Node permits reusing a write buffer after its callback. PassThrough can
64
+ // acknowledge before its reader consumes these bytes, so retain an owned
65
+ // copy of each bounded part rather than an alias to the user's buffer.
66
+ const part = Buffer.from(chunk.subarray(offset, Math.min(offset + STREAM_CHUNK_BYTES, chunk.byteLength)));
67
+ offset += part.byteLength;
68
+ this.output.write(part, writeNext);
69
+ };
70
+ writeNext();
71
+ }
72
+ _final(callback) {
73
+ try { this._commit(); } catch (error) { callback(error); return; }
74
+ if (this._compact) { this._compactComplete = true; callback(); return; }
75
+ this.output.end(callback);
76
+ }
77
+ _destroy(error, callback) {
78
+ if (!this._committed) this._rejectHead(error || new Error('API response closed before sending headers'));
79
+ // Writable's normal autoDestroy happens at finish while unread response
80
+ // bytes may still be in the readable side. Keep those bytes available.
81
+ if (error || !this.writableFinished) this.output.destroy(error);
82
+ callback(error);
83
+ }
84
+ writeHead(...args) { super.writeHead(...args); this._commit(); return this; }
85
+ flushHeaders() { this._commit(); }
86
+ end(chunk, encoding, callback) {
87
+ // Only a single terminal write qualifies. Explicit flush/writeHead/write
88
+ // always keeps progressive delivery and backpressure, regardless of size.
89
+ if (!this.headersSent && !this._output && !this.writableEnded &&
90
+ (chunk == null || typeof chunk === 'function' || typeof chunk === 'string' || chunk instanceof Uint8Array)) {
91
+ const size = typeof chunk === 'string' ? Buffer.byteLength(chunk, typeof encoding === 'string' ? encoding : 'utf8') : chunk?.byteLength || 0;
92
+ this._compact = size <= 16 * 1024;
93
+ }
94
+ this._commit();
95
+ return super.end(chunk, encoding, callback);
96
+ }
97
+ }
98
+
99
+ function abortIncomingRequest(request, error) {
100
+ request.on('error', () => {});
101
+ request.socket?.on('error', () => {});
102
+ if (request.destroyed || (request.readableEnded && request.complete)) {
103
+ if (!request.aborted) { request.aborted = true; request.emit('aborted'); }
104
+ }
105
+ request.destroy(error);
106
+ }
107
+
108
+ function requestAbortController(signal) {
109
+ const controller = new AbortController();
110
+ const aborted = () => controller.abort(abortError(signal.reason));
111
+ if (signal?.aborted) aborted();
112
+ else signal?.addEventListener('abort', aborted, { once: true });
113
+ return { controller, cleanup: () => signal?.removeEventListener('abort', aborted) };
114
+ }
115
+
116
+ async function runStreamingPagesApi(handler, request, { method, signal, timeoutMs }) {
117
+ const response = new StreamingResponse();
118
+ const { controller, cleanup } = requestAbortController(signal);
119
+ response._revalidateSignal = controller.signal;
120
+ let canceled = false;
121
+ const cancelRequest = error => {
122
+ if (canceled) return;
123
+ canceled = true;
124
+ controller.abort(error);
125
+ response.destroy(error);
126
+ abortIncomingRequest(request, error);
127
+ };
128
+ const aborted = () => cancelRequest(abortError(controller.signal.reason));
129
+ controller.signal.addEventListener('abort', aborted, { once: true });
130
+ const finish = () => { clearTimeout(timer); controller.signal.removeEventListener('abort', aborted); cleanup(); };
131
+ const timer = setTimeout(() => cancelRequest(apiTimeoutError('response headers', timeoutMs)), timeoutMs);
132
+ timer.unref();
133
+ if (controller.signal.aborted) cancelRequest(abortError(controller.signal.reason));
134
+ else trackRequestWork(Promise.resolve().then(() => handler(request, response)), signal).then(result => {
135
+ if (result !== undefined && result !== response && !response.writableEnded) {
136
+ throw new Error('Pages API handlers must send their response with res.send(), res.json(), or res.end()');
137
+ }
138
+ }).catch(error => response.destroy(error));
139
+ try {
140
+ const head = await response.head;
141
+ clearTimeout(timer);
142
+ if (response._compactComplete) {
143
+ if (response.errored) throw response.errored;
144
+ // The body is already complete. The transport can send head/body/end in
145
+ // one writev without consuming an async iterator or a PassThrough.
146
+ finish();
147
+ const body = method === 'HEAD' || [204, 205, 304].includes(head.status)
148
+ ? Buffer.alloc(0) : response._compactBody || Buffer.alloc(0);
149
+ response._compactBody = undefined;
150
+ return { ...head, bufferedBody: body,
151
+ body: method === 'HEAD' || [204, 205, 304].includes(head.status)
152
+ ? body : (async function* () { if (body.length) yield body; })(), cancel: () => {} };
153
+ }
154
+ const body = streamingBody(response.output, { signal: controller.signal, timeoutMs, onCancel: cancelRequest, onComplete: finish });
155
+ const cancel = reason => body.cancel(reason);
156
+ if (method === 'HEAD' || [204, 205, 304].includes(head.status)) {
157
+ await cancel(abortError('HTTP response does not carry a body'));
158
+ return { ...head, body: Buffer.alloc(0), cancel };
159
+ }
160
+ return { ...head, body, cancel };
161
+ } catch (error) {
162
+ cancelRequest(error);
163
+ finish();
164
+ throw error;
165
+ }
166
+ }
167
+
168
+ function parseBody(bytes, contentType = '') {
169
+ if (!bytes.length) return undefined;
170
+ const type = String(contentType).split(';')[0].trim().toLowerCase();
171
+ if (type === 'application/json' || type.endsWith('+json')) {
172
+ try { return JSON.parse(bytes.toString('utf8')); } catch { const error = new Error('Invalid JSON request body'); error.statusCode = 400; throw error; }
173
+ }
174
+ if (type === 'application/x-www-form-urlencoded') {
175
+ return queryFromUrl(new URL(`http://localhost/?${bytes.toString('utf8')}`));
176
+ }
177
+ return bytes.toString('utf8');
178
+ }
179
+
180
+ export function runApi(options) {
181
+ return withCacheRequest(options, options.route?.router === 'app' ? 'route' : 'pages', context => runApiInner(options, context));
182
+ }
183
+
184
+ async function runApiInner({ modulePath, route = {}, manifest = {}, production = process.env.NODE_ENV === 'production', url, originalUrl, params = {}, method = 'GET', headers = {}, body = '', stream = false, signal, timeoutMs = 25_000, preserveHeadBody = false }, cacheContext) {
185
+ if (stream && signal?.aborted) throw abortError(signal.reason);
186
+ const pages = route.router !== 'app';
187
+ // A resolved immutable ESM namespace needs neither another import job nor an
188
+ // abort listener/promise chain before every small Pages API response.
189
+ const mod = (pages && loadedPagesModule(modulePath)) || await withSignal(loadModule(modulePath, pages), stream ? signal : undefined);
190
+ const requestOriginal = originalUrl ? new URL(originalUrl, url) : undefined;
191
+ if (requestOriginal) requestOriginal.pathname = removeBasePath(requestOriginal.pathname, manifest.config?.basePath || '');
192
+ const { request, bytes } = createRequest({ url, originalUrl: requestOriginal?.href, params, method, headers, body });
193
+ request.draftMode = cacheContext.draftMode;
194
+ request.previewData = cacheContext.previewData;
195
+ request.preview = cacheContext.previewData !== false;
196
+ if (typeof mod.default !== 'function') {
197
+ const {NextRequest, proxyRouteRequest} = await appRequestPrimitives();
198
+ if (route.router === 'app' && manifest.dev) {
199
+ const listing = await collectStaticParams({ page: mod, pageConfig: route.handlerConfig, segments: [] }, route.pattern);
200
+ if (route.pattern?.includes('[') && route.cacheConfig?.dynamicParams === false) {
201
+ const requested = staticPath(route.pattern, new URL(url).pathname).path;
202
+ if (!listing.generated || !listing.params.some(params => staticPath(route.pattern, { params }).path === requested)) {
203
+ return { status: 404, headers: { 'content-type': 'text/plain; charset=utf-8' }, body: Buffer.from('Not Found') };
204
+ }
205
+ }
206
+ }
207
+ const handler = mod[method] || (method === 'HEAD' ? mod.GET : undefined);
208
+ if (!handler) {
209
+ const allowed = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'].filter(verb => typeof mod[verb] === 'function');
210
+ if (allowed.includes('GET') && !allowed.includes('HEAD')) allowed.push('HEAD');
211
+ if (!allowed.includes('OPTIONS')) allowed.push('OPTIONS');
212
+ return { status: method === 'OPTIONS' ? 204 : 405, headers: { allow: allowed.sort().join(', ') }, body: Buffer.alloc(0) };
213
+ }
214
+ const lifecycle = stream ? requestAbortController(signal) : null;
215
+ if (lifecycle?.controller.signal.aborted) {
216
+ lifecycle.cleanup();
217
+ throw abortError(lifecycle.controller.signal.reason);
218
+ }
219
+ const init = { method, headers, ...(lifecycle ? { signal: lifecycle.controller.signal } : {}) };
220
+ if (!['GET', 'HEAD'].includes(method) && bytes.length) init.body = bytes;
221
+ const timeout = lifecycle && setTimeout(() => lifecycle.controller.abort(apiTimeoutError('response headers', timeoutMs)), timeoutMs);
222
+ timeout?.unref();
223
+ const pendingResponse = runRequestContext({ url, method, headers, params, production, cacheComponents: manifest.config?.cacheComponents, cacheLife: manifest.config?.cacheLife, cacheHandlers: manifest.config?.cacheHandlers, cacheHandler: manifest.config?.cacheHandler, cacheMaxMemorySize: manifest.config?.cacheMaxMemorySize, distDir: path.dirname(path.dirname(modulePath)), signal: lifecycle?.controller.signal || signal, previewModeId: manifest.previewModeId, basePath: manifest.config?.basePath || '', routePattern: route.pattern, cacheConfig: route.cacheConfig, phase: 'route', cacheState: cacheContext.cacheState, staticState: cacheContext.staticState, mutableCookies: true }, async () => {
224
+ let result;
225
+ try { result = await handler(proxyRouteRequest(new NextRequest(requestOriginal || url, init), currentRequest()), { params: Promise.resolve(params) }); }
226
+ catch (error) {
227
+ const navigation = navigationResponse(error);
228
+ if (!navigation) throw error;
229
+ result = new Response(navigation.body, { status: navigation.status, headers: navigation.headers });
230
+ }
231
+ const context = currentRequest();
232
+ if (context.draftMode || context.draftChanged) cacheContext.draftChanged = true;
233
+ try { await flushCacheInvalidations(context); }
234
+ catch (error) {
235
+ // The handler may already have opened a live origin stream. A failed
236
+ // invalidation prevents returning its response, so cancel that body too.
237
+ // Application cancellation callbacks must not hold the error response.
238
+ if (result instanceof Response) void result.body?.cancel(error).catch(() => {});
239
+ throw error;
240
+ }
241
+ const changedCookies = [...context.outgoingCookies.values()];
242
+ if (stream) context.cookies._mutable = false;
243
+ return { result, changedCookies, runInContext: stream ? AsyncLocalStorage.snapshot() : undefined };
244
+ });
245
+ if (lifecycle) pendingResponse.then(({ result }) => {
246
+ if (lifecycle.controller.signal.aborted) result?.body?.cancel(lifecycle.controller.signal.reason).catch(() => {});
247
+ }, () => {});
248
+ trackRequestWork(pendingResponse, signal);
249
+ let result, changedCookies, runInContext;
250
+ try {
251
+ ({ result, changedCookies, runInContext } = await withSignal(pendingResponse, lifecycle?.controller.signal));
252
+ if (!(result instanceof Response)) throw new Error('Route handler must return a Response');
253
+ if (result.headers.has('x-middleware-rewrite') || result.headers.get('x-middleware-next') === '1') {
254
+ void result.body?.cancel().catch(() => {});
255
+ throw new Error('NextResponse.next() and NextResponse.rewrite() are only supported in middleware or proxy, not Route Handlers');
256
+ }
257
+ } catch (error) {
258
+ lifecycle?.controller.abort(error);
259
+ lifecycle?.cleanup();
260
+ throw error;
261
+ } finally { clearTimeout(timeout); }
262
+ const responseHeaders = Object.fromEntries(result.headers);
263
+ delete responseHeaders['x-middleware-set-cookie'];
264
+ const cookies = [...(result.headers.getSetCookie?.() || []), ...changedCookies];
265
+ if (cookies?.length) responseHeaders['set-cookie'] = cookies;
266
+ if (stream) {
267
+ for (const name of Object.keys(responseHeaders)) if (forbiddenHeaders.has(name) || name === 'content-length') delete responseHeaders[name];
268
+ const cancelRequest = reason => lifecycle.controller.abort(reason);
269
+ if (!result.body) {
270
+ lifecycle.cleanup();
271
+ return { status: result.status, headers: responseHeaders, body: Buffer.alloc(0), cancel: () => {} };
272
+ }
273
+ const streamed = streamingBody(result.body, { signal: lifecycle.controller.signal, timeoutMs,
274
+ onCancel: cancelRequest, onComplete: lifecycle.cleanup, runInContext });
275
+ const cancel = reason => streamed.cancel(reason);
276
+ if ((method === 'HEAD' && !preserveHeadBody) || [204, 205, 304].includes(result.status)) {
277
+ await cancel(abortError('HTTP response does not carry a body'));
278
+ return { status: result.status, headers: responseHeaders, body: Buffer.alloc(0), cancel };
279
+ }
280
+ return { status: result.status, headers: responseHeaders, body: streamed, cancel };
281
+ }
282
+ if ((method === 'HEAD' && !preserveHeadBody) || [204, 205, 304].includes(result.status)) {
283
+ await result.body?.cancel();
284
+ return { status: result.status, headers: responseHeaders, body: Buffer.alloc(0) };
285
+ }
286
+ const reader = result.body?.getReader();
287
+ const chunks = [];
288
+ let size = 0;
289
+ if (reader) {
290
+ try {
291
+ while (true) {
292
+ const { value, done } = await reader.read();
293
+ if (done) break;
294
+ size += value.byteLength;
295
+ if (size > MAX_RESPONSE_BYTES) { await reader.cancel(); throw new Error('Response exceeds the 16 MiB PRNext limit'); }
296
+ chunks.push(Buffer.from(value));
297
+ }
298
+ } finally { reader.releaseLock(); }
299
+ }
300
+ return { status: result.status, headers: responseHeaders, body: Buffer.concat(chunks, size) };
301
+ }
302
+ if (mod.config?.api?.bodyParser !== false) {
303
+ request.body = parseBody(bytes, request.headers['content-type']);
304
+ // Next's body parser consumes the incoming stream. Release the raw upload
305
+ // queue once parsed, instead of retaining both JSON and bytes in req.
306
+ while (request.read() !== null) { /* Already buffered by native ingress. */ }
307
+ }
308
+ if (stream) return runStreamingPagesApi(mod.default, request, { method, signal, timeoutMs });
309
+ const response = new CapturedResponse(undefined, true);
310
+ let timeout;
311
+ const completed = new Promise((resolve, reject) => {
312
+ response.once('finish', resolve);
313
+ response.once('error', reject);
314
+ timeout = setTimeout(() => reject(new Error('API route did not end its response within 25 seconds')), 25_000);
315
+ timeout.unref();
316
+ });
317
+ // Install a rejection handler before awaiting user code to avoid an unhandled timeout.
318
+ completed.catch(() => {});
319
+ try {
320
+ const result = await Promise.race([Promise.resolve(mod.default(request, response)), completed.then(() => undefined)]);
321
+ if (result !== undefined && result !== response && !response.writableEnded) throw new Error('Pages API handlers must send their response with res.send(), res.json(), or res.end()');
322
+ await completed;
323
+ const captured = response.result();
324
+ return method === 'HEAD' ? { ...captured, body: Buffer.alloc(0) } : captured;
325
+ } finally { clearTimeout(timeout); }
326
+ }