vinext 0.0.34 → 0.0.36
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.
- package/README.md +1 -0
- package/dist/build/prerender.d.ts +9 -4
- package/dist/build/prerender.js +27 -9
- package/dist/build/prerender.js.map +1 -1
- package/dist/build/run-prerender.js +4 -1
- package/dist/build/run-prerender.js.map +1 -1
- package/dist/config/config-matchers.js +1 -1
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/config/next-config.d.ts +7 -1
- package/dist/config/next-config.js +39 -26
- package/dist/config/next-config.js.map +1 -1
- package/dist/entries/pages-server-entry.js +162 -354
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +40 -4
- package/dist/index.js.map +1 -1
- package/dist/plugins/optimize-imports.js +28 -2
- package/dist/plugins/optimize-imports.js.map +1 -1
- package/dist/server/app-browser-entry.js +2 -2
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-page-render.js +6 -4
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-stream.d.ts +9 -1
- package/dist/server/app-page-stream.js +37 -4
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-ssr-stream.js +5 -3
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/pages-page-data.d.ts +105 -0
- package/dist/server/pages-page-data.js +177 -0
- package/dist/server/pages-page-data.js.map +1 -0
- package/dist/server/pages-page-response.d.ts +55 -0
- package/dist/server/pages-page-response.js +140 -0
- package/dist/server/pages-page-response.js.map +1 -0
- package/dist/server/prod-server.js +3 -0
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/seed-cache.d.ts +44 -0
- package/dist/server/seed-cache.js +127 -0
- package/dist/server/seed-cache.js.map +1 -0
- package/dist/shims/image.js +4 -2
- package/dist/shims/image.js.map +1 -1
- package/dist/shims/navigation-state.js +5 -3
- package/dist/shims/navigation-state.js.map +1 -1
- package/dist/shims/navigation.d.ts +9 -7
- package/dist/shims/navigation.js +20 -5
- package/dist/shims/navigation.js.map +1 -1
- package/package.json +1 -1
|
@@ -9,15 +9,48 @@ function createAppPageFontData(options) {
|
|
|
9
9
|
async function renderAppPageHtmlStream(options) {
|
|
10
10
|
return options.ssrHandler.handleSsr(options.rscStream, options.navigationContext, options.fontData);
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Wraps a stream so that `onFlush` is called when the last byte has been read
|
|
14
|
+
* by the downstream consumer (i.e. when the HTTP layer finishes draining the
|
|
15
|
+
* response body). This is the correct place to clear per-request context,
|
|
16
|
+
* because the RSC/SSR pipeline is lazy — components execute while the stream
|
|
17
|
+
* is being consumed, not when the stream handle is first obtained.
|
|
18
|
+
*/
|
|
19
|
+
function deferUntilStreamConsumed(stream, onFlush) {
|
|
20
|
+
let called = false;
|
|
21
|
+
const once = () => {
|
|
22
|
+
if (!called) {
|
|
23
|
+
called = true;
|
|
24
|
+
onFlush();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const cleanup = new TransformStream({ flush() {
|
|
28
|
+
once();
|
|
29
|
+
} });
|
|
30
|
+
const reader = stream.pipeThrough(cleanup).getReader();
|
|
31
|
+
return new ReadableStream({
|
|
32
|
+
pull(controller) {
|
|
33
|
+
return reader.read().then(({ done, value }) => {
|
|
34
|
+
if (done) controller.close();
|
|
35
|
+
else controller.enqueue(value);
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
cancel(reason) {
|
|
39
|
+
once();
|
|
40
|
+
return reader.cancel(reason);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
12
44
|
async function renderAppPageHtmlResponse(options) {
|
|
13
|
-
const
|
|
14
|
-
|
|
45
|
+
const safeStream = deferUntilStreamConsumed(await renderAppPageHtmlStream(options), () => {
|
|
46
|
+
options.clearRequestContext();
|
|
47
|
+
});
|
|
15
48
|
const headers = {
|
|
16
49
|
"Content-Type": "text/html; charset=utf-8",
|
|
17
50
|
Vary: "RSC, Accept"
|
|
18
51
|
};
|
|
19
52
|
if (options.fontLinkHeader) headers.Link = options.fontLinkHeader;
|
|
20
|
-
return new Response(
|
|
53
|
+
return new Response(safeStream, {
|
|
21
54
|
status: options.status,
|
|
22
55
|
headers
|
|
23
56
|
});
|
|
@@ -60,6 +93,6 @@ function shouldRerenderAppPageWithGlobalError(options) {
|
|
|
60
93
|
return Boolean(options.capturedError) && !options.hasLocalBoundary;
|
|
61
94
|
}
|
|
62
95
|
//#endregion
|
|
63
|
-
export { createAppPageFontData, createAppPageRscErrorTracker, renderAppPageHtmlResponse, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery, shouldRerenderAppPageWithGlobalError };
|
|
96
|
+
export { createAppPageFontData, createAppPageRscErrorTracker, deferUntilStreamConsumed, renderAppPageHtmlResponse, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery, shouldRerenderAppPageWithGlobalError };
|
|
64
97
|
|
|
65
98
|
//# sourceMappingURL=app-page-stream.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-page-stream.js","names":[],"sources":["../../src/server/app-page-stream.ts"],"sourcesContent":["import type { AppPageFontPreload } from \"./app-page-execution.js\";\n\nexport interface AppPageFontData {\n links: string[];\n preloads: readonly AppPageFontPreload[];\n styles: string[];\n}\n\nexport interface CreateAppPageFontDataOptions {\n getLinks: () => string[];\n getPreloads: () => AppPageFontPreload[];\n getStyles: () => string[];\n}\n\nexport interface AppPageSsrHandler {\n handleSsr: (\n rscStream: ReadableStream<Uint8Array>,\n navigationContext: unknown,\n fontData: AppPageFontData,\n ) => Promise<ReadableStream<Uint8Array>>;\n}\n\nexport interface RenderAppPageHtmlStreamOptions {\n fontData: AppPageFontData;\n navigationContext: unknown;\n rscStream: ReadableStream<Uint8Array>;\n ssrHandler: AppPageSsrHandler;\n}\n\nexport interface RenderAppPageHtmlResponseOptions extends RenderAppPageHtmlStreamOptions {\n clearRequestContext: () => void;\n fontLinkHeader?: string;\n status: number;\n}\n\nexport interface AppPageHtmlStreamRecoveryResult {\n htmlStream: ReadableStream<Uint8Array> | null;\n response: Response | null;\n}\n\nexport interface RenderAppPageHtmlStreamWithRecoveryOptions<TSpecialError> {\n onShellRendered?: () => void;\n renderErrorBoundaryResponse: (error: unknown) => Promise<Response | null>;\n renderHtmlStream: () => Promise<ReadableStream<Uint8Array>>;\n renderSpecialErrorResponse: (specialError: TSpecialError) => Promise<Response>;\n resolveSpecialError: (error: unknown) => TSpecialError | null;\n}\n\nexport interface AppPageRscErrorTracker {\n getCapturedError: () => unknown;\n onRenderError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown;\n}\n\nexport interface ShouldRerenderAppPageWithGlobalErrorOptions {\n capturedError: unknown;\n hasLocalBoundary: boolean;\n}\n\nexport function createAppPageFontData(options: CreateAppPageFontDataOptions): AppPageFontData {\n return {\n links: options.getLinks(),\n preloads: options.getPreloads(),\n styles: options.getStyles(),\n };\n}\n\nexport async function renderAppPageHtmlStream(\n options: RenderAppPageHtmlStreamOptions,\n): Promise<ReadableStream<Uint8Array>> {\n return options.ssrHandler.handleSsr(\n options.rscStream,\n options.navigationContext,\n options.fontData,\n );\n}\n\nexport async function renderAppPageHtmlResponse(\n options: RenderAppPageHtmlResponseOptions,\n): Promise<Response> {\n const htmlStream = await renderAppPageHtmlStream(options);\n options.clearRequestContext();\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"text/html; charset=utf-8\",\n Vary: \"RSC, Accept\",\n };\n\n if (options.fontLinkHeader) {\n headers.Link = options.fontLinkHeader;\n }\n\n return new Response(
|
|
1
|
+
{"version":3,"file":"app-page-stream.js","names":[],"sources":["../../src/server/app-page-stream.ts"],"sourcesContent":["import type { AppPageFontPreload } from \"./app-page-execution.js\";\n\nexport interface AppPageFontData {\n links: string[];\n preloads: readonly AppPageFontPreload[];\n styles: string[];\n}\n\nexport interface CreateAppPageFontDataOptions {\n getLinks: () => string[];\n getPreloads: () => AppPageFontPreload[];\n getStyles: () => string[];\n}\n\nexport interface AppPageSsrHandler {\n handleSsr: (\n rscStream: ReadableStream<Uint8Array>,\n navigationContext: unknown,\n fontData: AppPageFontData,\n ) => Promise<ReadableStream<Uint8Array>>;\n}\n\nexport interface RenderAppPageHtmlStreamOptions {\n fontData: AppPageFontData;\n navigationContext: unknown;\n rscStream: ReadableStream<Uint8Array>;\n ssrHandler: AppPageSsrHandler;\n}\n\nexport interface RenderAppPageHtmlResponseOptions extends RenderAppPageHtmlStreamOptions {\n clearRequestContext: () => void;\n fontLinkHeader?: string;\n status: number;\n}\n\nexport interface AppPageHtmlStreamRecoveryResult {\n htmlStream: ReadableStream<Uint8Array> | null;\n response: Response | null;\n}\n\nexport interface RenderAppPageHtmlStreamWithRecoveryOptions<TSpecialError> {\n onShellRendered?: () => void;\n renderErrorBoundaryResponse: (error: unknown) => Promise<Response | null>;\n renderHtmlStream: () => Promise<ReadableStream<Uint8Array>>;\n renderSpecialErrorResponse: (specialError: TSpecialError) => Promise<Response>;\n resolveSpecialError: (error: unknown) => TSpecialError | null;\n}\n\nexport interface AppPageRscErrorTracker {\n getCapturedError: () => unknown;\n onRenderError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown;\n}\n\nexport interface ShouldRerenderAppPageWithGlobalErrorOptions {\n capturedError: unknown;\n hasLocalBoundary: boolean;\n}\n\nexport function createAppPageFontData(options: CreateAppPageFontDataOptions): AppPageFontData {\n return {\n links: options.getLinks(),\n preloads: options.getPreloads(),\n styles: options.getStyles(),\n };\n}\n\nexport async function renderAppPageHtmlStream(\n options: RenderAppPageHtmlStreamOptions,\n): Promise<ReadableStream<Uint8Array>> {\n return options.ssrHandler.handleSsr(\n options.rscStream,\n options.navigationContext,\n options.fontData,\n );\n}\n\n/**\n * Wraps a stream so that `onFlush` is called when the last byte has been read\n * by the downstream consumer (i.e. when the HTTP layer finishes draining the\n * response body). This is the correct place to clear per-request context,\n * because the RSC/SSR pipeline is lazy — components execute while the stream\n * is being consumed, not when the stream handle is first obtained.\n */\nexport function deferUntilStreamConsumed(\n stream: ReadableStream<Uint8Array>,\n onFlush: () => void,\n): ReadableStream<Uint8Array> {\n let called = false;\n const once = () => {\n if (!called) {\n called = true;\n onFlush();\n }\n };\n\n const cleanup = new TransformStream<Uint8Array, Uint8Array>({\n flush() {\n once();\n },\n });\n\n const piped = stream.pipeThrough(cleanup);\n\n // Wrap with a ReadableStream so we can intercept cancel() — the TransformStream\n // Transformer interface does not expose a cancel hook in the Web Streams spec.\n const reader = piped.getReader();\n return new ReadableStream<Uint8Array>({\n pull(controller) {\n return reader.read().then(({ done, value }) => {\n if (done) {\n controller.close();\n } else {\n controller.enqueue(value);\n }\n });\n },\n cancel(reason) {\n // Stream cancelled before fully consumed (e.g. client disconnected).\n // Still clear per-request context to avoid leaks.\n once();\n return reader.cancel(reason);\n },\n });\n}\n\nexport async function renderAppPageHtmlResponse(\n options: RenderAppPageHtmlResponseOptions,\n): Promise<Response> {\n const htmlStream = await renderAppPageHtmlStream(options);\n\n // Defer clearRequestContext() until the stream is fully consumed by the HTTP\n // layer. Calling it synchronously here would race the lazy RSC/SSR pipeline:\n // components execute while the stream is being pulled, not when the handle\n // is first returned. See: https://github.com/cloudflare/vinext/issues/660\n const safeStream = deferUntilStreamConsumed(htmlStream, () => {\n options.clearRequestContext();\n });\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"text/html; charset=utf-8\",\n Vary: \"RSC, Accept\",\n };\n\n if (options.fontLinkHeader) {\n headers.Link = options.fontLinkHeader;\n }\n\n return new Response(safeStream, {\n status: options.status,\n headers,\n });\n}\n\nexport async function renderAppPageHtmlStreamWithRecovery<TSpecialError>(\n options: RenderAppPageHtmlStreamWithRecoveryOptions<TSpecialError>,\n): Promise<AppPageHtmlStreamRecoveryResult> {\n try {\n const htmlStream = await options.renderHtmlStream();\n options.onShellRendered?.();\n return {\n htmlStream,\n response: null,\n };\n } catch (error) {\n const specialError = options.resolveSpecialError(error);\n if (specialError) {\n return {\n htmlStream: null,\n response: await options.renderSpecialErrorResponse(specialError),\n };\n }\n\n const boundaryResponse = await options.renderErrorBoundaryResponse(error);\n if (boundaryResponse) {\n return {\n htmlStream: null,\n response: boundaryResponse,\n };\n }\n\n throw error;\n }\n}\n\nexport function createAppPageRscErrorTracker(\n baseOnError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown,\n): AppPageRscErrorTracker {\n let capturedError: unknown = null;\n\n return {\n getCapturedError() {\n return capturedError;\n },\n onRenderError(error, requestInfo, errorContext) {\n if (!(error && typeof error === \"object\" && \"digest\" in error)) {\n capturedError = error;\n }\n return baseOnError(error, requestInfo, errorContext);\n },\n };\n}\n\nexport function shouldRerenderAppPageWithGlobalError(\n options: ShouldRerenderAppPageWithGlobalErrorOptions,\n): boolean {\n return Boolean(options.capturedError) && !options.hasLocalBoundary;\n}\n"],"mappings":";AA0DA,SAAgB,sBAAsB,SAAwD;AAC5F,QAAO;EACL,OAAO,QAAQ,UAAU;EACzB,UAAU,QAAQ,aAAa;EAC/B,QAAQ,QAAQ,WAAW;EAC5B;;AAGH,eAAsB,wBACpB,SACqC;AACrC,QAAO,QAAQ,WAAW,UACxB,QAAQ,WACR,QAAQ,mBACR,QAAQ,SACT;;;;;;;;;AAUH,SAAgB,yBACd,QACA,SAC4B;CAC5B,IAAI,SAAS;CACb,MAAM,aAAa;AACjB,MAAI,CAAC,QAAQ;AACX,YAAS;AACT,YAAS;;;CAIb,MAAM,UAAU,IAAI,gBAAwC,EAC1D,QAAQ;AACN,QAAM;IAET,CAAC;CAMF,MAAM,SAJQ,OAAO,YAAY,QAAQ,CAIpB,WAAW;AAChC,QAAO,IAAI,eAA2B;EACpC,KAAK,YAAY;AACf,UAAO,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM,YAAY;AAC7C,QAAI,KACF,YAAW,OAAO;QAElB,YAAW,QAAQ,MAAM;KAE3B;;EAEJ,OAAO,QAAQ;AAGb,SAAM;AACN,UAAO,OAAO,OAAO,OAAO;;EAE/B,CAAC;;AAGJ,eAAsB,0BACpB,SACmB;CAOnB,MAAM,aAAa,yBANA,MAAM,wBAAwB,QAAQ,QAMK;AAC5D,UAAQ,qBAAqB;GAC7B;CAEF,MAAM,UAAkC;EACtC,gBAAgB;EAChB,MAAM;EACP;AAED,KAAI,QAAQ,eACV,SAAQ,OAAO,QAAQ;AAGzB,QAAO,IAAI,SAAS,YAAY;EAC9B,QAAQ,QAAQ;EAChB;EACD,CAAC;;AAGJ,eAAsB,oCACpB,SAC0C;AAC1C,KAAI;EACF,MAAM,aAAa,MAAM,QAAQ,kBAAkB;AACnD,UAAQ,mBAAmB;AAC3B,SAAO;GACL;GACA,UAAU;GACX;UACM,OAAO;EACd,MAAM,eAAe,QAAQ,oBAAoB,MAAM;AACvD,MAAI,aACF,QAAO;GACL,YAAY;GACZ,UAAU,MAAM,QAAQ,2BAA2B,aAAa;GACjE;EAGH,MAAM,mBAAmB,MAAM,QAAQ,4BAA4B,MAAM;AACzE,MAAI,iBACF,QAAO;GACL,YAAY;GACZ,UAAU;GACX;AAGH,QAAM;;;AAIV,SAAgB,6BACd,aACwB;CACxB,IAAI,gBAAyB;AAE7B,QAAO;EACL,mBAAmB;AACjB,UAAO;;EAET,cAAc,OAAO,aAAa,cAAc;AAC9C,OAAI,EAAE,SAAS,OAAO,UAAU,YAAY,YAAY,OACtD,iBAAgB;AAElB,UAAO,YAAY,OAAO,aAAa,aAAa;;EAEvD;;AAGH,SAAgB,qCACd,SACS;AACT,QAAO,QAAQ,QAAQ,cAAc,IAAI,CAAC,QAAQ"}
|
|
@@ -92,9 +92,11 @@ function createTickBufferedTransform(rscEmbed, injectHTML = "") {
|
|
|
92
92
|
buffered.push(fixPreloadAs(decoder.decode(chunk, { stream: true })));
|
|
93
93
|
if (timeoutId !== null) return;
|
|
94
94
|
timeoutId = setTimeout(() => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
try {
|
|
96
|
+
flushBuffered(controller);
|
|
97
|
+
const rscScripts = rscEmbed.flush();
|
|
98
|
+
if (rscScripts) controller.enqueue(encoder.encode(rscScripts));
|
|
99
|
+
} catch {}
|
|
98
100
|
timeoutId = null;
|
|
99
101
|
}, 0);
|
|
100
102
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-ssr-stream.js","names":[],"sources":["../../src/server/app-ssr-stream.ts"],"sourcesContent":["import { safeJsonStringify } from \"./html.js\";\n\nexport interface RscEmbedTransform {\n flush(): string;\n finalize(): Promise<string>;\n}\n\n/**\n * Fix invalid preload \"as\" values in RSC Flight hint lines before they reach\n * the client. React Flight emits HL hints with as=\"stylesheet\" for CSS, but\n * the HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixFlightHints(text: string): string {\n return text.replace(/(\\d*:HL\\[.*?),\"stylesheet\"(\\]|,)/g, '$1,\"style\"$2');\n}\n\n/**\n * Create a helper that progressively embeds RSC chunks as inline <script> tags.\n * The browser entry turns the embedded text chunks back into Uint8Array data.\n */\nexport function createRscEmbedTransform(\n embedStream: ReadableStream<Uint8Array>,\n): RscEmbedTransform {\n const reader = embedStream.getReader();\n const decoder = new TextDecoder();\n let pendingChunks: string[] = [];\n let reading = false;\n\n async function pumpReader(): Promise<void> {\n if (reading) return;\n reading = true;\n try {\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n const text = decoder.decode(result.value, { stream: true });\n // The RSC entry already fixes HL hints at the source. Keep this second\n // pass as defense in depth for any embed stream that bypasses that\n // wrapper; the rewrite is idempotent, so double-application is safe.\n pendingChunks.push(fixFlightHints(text));\n }\n } catch (error) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] RSC embed stream read error:\", error);\n }\n } finally {\n reading = false;\n }\n }\n\n const pumpPromise = pumpReader();\n\n return {\n flush(): string {\n if (pendingChunks.length === 0) return \"\";\n\n const chunks = pendingChunks;\n pendingChunks = [];\n\n let scripts = \"\";\n for (const chunk of chunks) {\n scripts +=\n \"<script>self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];self.__VINEXT_RSC_CHUNKS__.push(\" +\n safeJsonStringify(chunk) +\n \")</script>\";\n }\n return scripts;\n },\n\n async finalize(): Promise<string> {\n await pumpPromise;\n let scripts = this.flush();\n scripts += \"<script>self.__VINEXT_RSC_DONE__=true</script>\";\n return scripts;\n },\n };\n}\n\n/**\n * Fix invalid preload \"as\" values in server-rendered HTML.\n * React Fizz emits <link rel=\"preload\" as=\"stylesheet\"> for CSS, but the\n * HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixPreloadAs(html: string): string {\n return html.replace(/<link(?=[^>]*\\srel=\"preload\")[^>]*>/g, (tag) => {\n return tag.replace(' as=\"stylesheet\"', ' as=\"style\"');\n });\n}\n\n/**\n * Create the tick-buffered HTML transform that injects RSC scripts between\n * React Fizz flush cycles without corrupting split HTML chunks.\n */\nexport function createTickBufferedTransform(\n rscEmbed: RscEmbedTransform,\n injectHTML = \"\",\n): TransformStream<Uint8Array, Uint8Array> {\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n let injected = false;\n let buffered: string[] = [];\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const flushBuffered = (controller: TransformStreamDefaultController<Uint8Array>): void => {\n for (const chunk of buffered) {\n if (!injected) {\n const headEnd = chunk.indexOf(\"</head>\");\n if (headEnd !== -1) {\n const before = chunk.slice(0, headEnd);\n const after = chunk.slice(headEnd);\n controller.enqueue(encoder.encode(before + injectHTML + after));\n injected = true;\n continue;\n }\n }\n controller.enqueue(encoder.encode(chunk));\n }\n buffered = [];\n };\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n buffered.push(fixPreloadAs(decoder.decode(chunk, { stream: true })));\n\n if (timeoutId !== null) return;\n\n timeoutId = setTimeout(() => {\n flushBuffered(controller);\n\n
|
|
1
|
+
{"version":3,"file":"app-ssr-stream.js","names":[],"sources":["../../src/server/app-ssr-stream.ts"],"sourcesContent":["import { safeJsonStringify } from \"./html.js\";\n\nexport interface RscEmbedTransform {\n flush(): string;\n finalize(): Promise<string>;\n}\n\n/**\n * Fix invalid preload \"as\" values in RSC Flight hint lines before they reach\n * the client. React Flight emits HL hints with as=\"stylesheet\" for CSS, but\n * the HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixFlightHints(text: string): string {\n return text.replace(/(\\d*:HL\\[.*?),\"stylesheet\"(\\]|,)/g, '$1,\"style\"$2');\n}\n\n/**\n * Create a helper that progressively embeds RSC chunks as inline <script> tags.\n * The browser entry turns the embedded text chunks back into Uint8Array data.\n */\nexport function createRscEmbedTransform(\n embedStream: ReadableStream<Uint8Array>,\n): RscEmbedTransform {\n const reader = embedStream.getReader();\n const decoder = new TextDecoder();\n let pendingChunks: string[] = [];\n let reading = false;\n\n async function pumpReader(): Promise<void> {\n if (reading) return;\n reading = true;\n try {\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n const text = decoder.decode(result.value, { stream: true });\n // The RSC entry already fixes HL hints at the source. Keep this second\n // pass as defense in depth for any embed stream that bypasses that\n // wrapper; the rewrite is idempotent, so double-application is safe.\n pendingChunks.push(fixFlightHints(text));\n }\n } catch (error) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] RSC embed stream read error:\", error);\n }\n } finally {\n reading = false;\n }\n }\n\n const pumpPromise = pumpReader();\n\n return {\n flush(): string {\n if (pendingChunks.length === 0) return \"\";\n\n const chunks = pendingChunks;\n pendingChunks = [];\n\n let scripts = \"\";\n for (const chunk of chunks) {\n scripts +=\n \"<script>self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];self.__VINEXT_RSC_CHUNKS__.push(\" +\n safeJsonStringify(chunk) +\n \")</script>\";\n }\n return scripts;\n },\n\n async finalize(): Promise<string> {\n await pumpPromise;\n let scripts = this.flush();\n scripts += \"<script>self.__VINEXT_RSC_DONE__=true</script>\";\n return scripts;\n },\n };\n}\n\n/**\n * Fix invalid preload \"as\" values in server-rendered HTML.\n * React Fizz emits <link rel=\"preload\" as=\"stylesheet\"> for CSS, but the\n * HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixPreloadAs(html: string): string {\n return html.replace(/<link(?=[^>]*\\srel=\"preload\")[^>]*>/g, (tag) => {\n return tag.replace(' as=\"stylesheet\"', ' as=\"style\"');\n });\n}\n\n/**\n * Create the tick-buffered HTML transform that injects RSC scripts between\n * React Fizz flush cycles without corrupting split HTML chunks.\n */\nexport function createTickBufferedTransform(\n rscEmbed: RscEmbedTransform,\n injectHTML = \"\",\n): TransformStream<Uint8Array, Uint8Array> {\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n let injected = false;\n let buffered: string[] = [];\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const flushBuffered = (controller: TransformStreamDefaultController<Uint8Array>): void => {\n for (const chunk of buffered) {\n if (!injected) {\n const headEnd = chunk.indexOf(\"</head>\");\n if (headEnd !== -1) {\n const before = chunk.slice(0, headEnd);\n const after = chunk.slice(headEnd);\n controller.enqueue(encoder.encode(before + injectHTML + after));\n injected = true;\n continue;\n }\n }\n controller.enqueue(encoder.encode(chunk));\n }\n buffered = [];\n };\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n buffered.push(fixPreloadAs(decoder.decode(chunk, { stream: true })));\n\n if (timeoutId !== null) return;\n\n timeoutId = setTimeout(() => {\n try {\n flushBuffered(controller);\n\n const rscScripts = rscEmbed.flush();\n if (rscScripts) {\n controller.enqueue(encoder.encode(rscScripts));\n }\n } catch {\n // Stream was cancelled between when the timeout was registered and\n // when it fired (e.g. client disconnected, health-check cancelled\n // the response body). Ignore — the stream is already closed.\n }\n\n timeoutId = null;\n }, 0);\n },\n\n async flush(controller) {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n flushBuffered(controller);\n\n if (!injected && injectHTML) {\n controller.enqueue(encoder.encode(injectHTML));\n }\n\n const finalScripts = await rscEmbed.finalize();\n if (finalScripts) {\n controller.enqueue(encoder.encode(finalScripts));\n }\n },\n });\n}\n"],"mappings":";;;;;;;AAYA,SAAgB,eAAe,MAAsB;AACnD,QAAO,KAAK,QAAQ,qCAAqC,iBAAe;;;;;;AAO1E,SAAgB,wBACd,aACmB;CACnB,MAAM,SAAS,YAAY,WAAW;CACtC,MAAM,UAAU,IAAI,aAAa;CACjC,IAAI,gBAA0B,EAAE;CAChC,IAAI,UAAU;CAEd,eAAe,aAA4B;AACzC,MAAI,QAAS;AACb,YAAU;AACV,MAAI;AACF,UAAO,MAAM;IACX,MAAM,SAAS,MAAM,OAAO,MAAM;AAClC,QAAI,OAAO,KAAM;IACjB,MAAM,OAAO,QAAQ,OAAO,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;AAI3D,kBAAc,KAAK,eAAe,KAAK,CAAC;;WAEnC,OAAO;AACd,OAAI,QAAQ,IAAI,aAAa,aAC3B,SAAQ,KAAK,yCAAyC,MAAM;YAEtD;AACR,aAAU;;;CAId,MAAM,cAAc,YAAY;AAEhC,QAAO;EACL,QAAgB;AACd,OAAI,cAAc,WAAW,EAAG,QAAO;GAEvC,MAAM,SAAS;AACf,mBAAgB,EAAE;GAElB,IAAI,UAAU;AACd,QAAK,MAAM,SAAS,OAClB,YACE,uGACA,kBAAkB,MAAM,GACxB;AAEJ,UAAO;;EAGT,MAAM,WAA4B;AAChC,SAAM;GACN,IAAI,UAAU,KAAK,OAAO;AAC1B,cAAW;AACX,UAAO;;EAEV;;;;;;;AAQH,SAAgB,aAAa,MAAsB;AACjD,QAAO,KAAK,QAAQ,yCAAyC,QAAQ;AACnE,SAAO,IAAI,QAAQ,sBAAoB,gBAAc;GACrD;;;;;;AAOJ,SAAgB,4BACd,UACA,aAAa,IAC4B;CACzC,MAAM,UAAU,IAAI,aAAa;CACjC,MAAM,UAAU,IAAI,aAAa;CACjC,IAAI,WAAW;CACf,IAAI,WAAqB,EAAE;CAC3B,IAAI,YAAkD;CAEtD,MAAM,iBAAiB,eAAmE;AACxF,OAAK,MAAM,SAAS,UAAU;AAC5B,OAAI,CAAC,UAAU;IACb,MAAM,UAAU,MAAM,QAAQ,UAAU;AACxC,QAAI,YAAY,IAAI;KAClB,MAAM,SAAS,MAAM,MAAM,GAAG,QAAQ;KACtC,MAAM,QAAQ,MAAM,MAAM,QAAQ;AAClC,gBAAW,QAAQ,QAAQ,OAAO,SAAS,aAAa,MAAM,CAAC;AAC/D,gBAAW;AACX;;;AAGJ,cAAW,QAAQ,QAAQ,OAAO,MAAM,CAAC;;AAE3C,aAAW,EAAE;;AAGf,QAAO,IAAI,gBAAwC;EACjD,UAAU,OAAO,YAAY;AAC3B,YAAS,KAAK,aAAa,QAAQ,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC;AAEpE,OAAI,cAAc,KAAM;AAExB,eAAY,iBAAiB;AAC3B,QAAI;AACF,mBAAc,WAAW;KAEzB,MAAM,aAAa,SAAS,OAAO;AACnC,SAAI,WACF,YAAW,QAAQ,QAAQ,OAAO,WAAW,CAAC;YAE1C;AAMR,gBAAY;MACX,EAAE;;EAGP,MAAM,MAAM,YAAY;AACtB,OAAI,cAAc,MAAM;AACtB,iBAAa,UAAU;AACvB,gBAAY;;AAGd,iBAAc,WAAW;AAEzB,OAAI,CAAC,YAAY,WACf,YAAW,QAAQ,QAAQ,OAAO,WAAW,CAAC;GAGhD,MAAM,eAAe,MAAM,SAAS,UAAU;AAC9C,OAAI,aACF,YAAW,QAAQ,QAAQ,OAAO,aAAa,CAAC;;EAGrD,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Route } from "../routing/pages-router.js";
|
|
2
|
+
import { CachedPagesValue } from "../shims/cache.js";
|
|
3
|
+
import { ISRCacheEntry } from "./isr-cache.js";
|
|
4
|
+
import { PagesGsspResponse, PagesI18nRenderContext } from "./pages-page-response.js";
|
|
5
|
+
import { ReactNode } from "react";
|
|
6
|
+
|
|
7
|
+
//#region src/server/pages-page-data.d.ts
|
|
8
|
+
interface PagesRedirectResult {
|
|
9
|
+
destination: string;
|
|
10
|
+
permanent?: boolean;
|
|
11
|
+
statusCode?: number;
|
|
12
|
+
}
|
|
13
|
+
interface PagesStaticPathsEntry {
|
|
14
|
+
params: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
interface PagesStaticPathsResult {
|
|
17
|
+
fallback?: boolean | "blocking";
|
|
18
|
+
paths?: PagesStaticPathsEntry[];
|
|
19
|
+
}
|
|
20
|
+
interface PagesPagePropsResult {
|
|
21
|
+
props?: Record<string, unknown>;
|
|
22
|
+
redirect?: PagesRedirectResult;
|
|
23
|
+
notFound?: boolean;
|
|
24
|
+
revalidate?: number;
|
|
25
|
+
}
|
|
26
|
+
interface PagesMutableGsspResponse extends PagesGsspResponse {
|
|
27
|
+
headersSent: boolean;
|
|
28
|
+
}
|
|
29
|
+
interface PagesGsspContextResponse {
|
|
30
|
+
req: unknown;
|
|
31
|
+
res: PagesMutableGsspResponse;
|
|
32
|
+
responsePromise: Promise<Response>;
|
|
33
|
+
}
|
|
34
|
+
interface PagesPageModule {
|
|
35
|
+
default?: unknown;
|
|
36
|
+
getStaticPaths?: (context: {
|
|
37
|
+
locales: string[];
|
|
38
|
+
defaultLocale: string;
|
|
39
|
+
}) => Promise<PagesStaticPathsResult> | PagesStaticPathsResult;
|
|
40
|
+
getServerSideProps?: (context: {
|
|
41
|
+
params: Record<string, unknown>;
|
|
42
|
+
req: unknown;
|
|
43
|
+
res: PagesMutableGsspResponse;
|
|
44
|
+
query: Record<string, unknown>;
|
|
45
|
+
resolvedUrl: string;
|
|
46
|
+
locale?: string;
|
|
47
|
+
locales?: string[];
|
|
48
|
+
defaultLocale?: string;
|
|
49
|
+
}) => Promise<PagesPagePropsResult> | PagesPagePropsResult;
|
|
50
|
+
getStaticProps?: (context: {
|
|
51
|
+
params: Record<string, unknown>;
|
|
52
|
+
locale?: string;
|
|
53
|
+
locales?: string[];
|
|
54
|
+
defaultLocale?: string;
|
|
55
|
+
}) => Promise<PagesPagePropsResult> | PagesPagePropsResult;
|
|
56
|
+
}
|
|
57
|
+
interface RenderPagesIsrHtmlOptions {
|
|
58
|
+
buildId: string | null;
|
|
59
|
+
cachedHtml: string;
|
|
60
|
+
createPageElement: (pageProps: Record<string, unknown>) => ReactNode;
|
|
61
|
+
i18n: PagesI18nRenderContext;
|
|
62
|
+
pageProps: Record<string, unknown>;
|
|
63
|
+
params: Record<string, unknown>;
|
|
64
|
+
renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;
|
|
65
|
+
routePattern: string;
|
|
66
|
+
safeJsonStringify: (value: unknown) => string;
|
|
67
|
+
}
|
|
68
|
+
interface ResolvePagesPageDataOptions {
|
|
69
|
+
applyRequestContexts: () => void;
|
|
70
|
+
buildId: string | null;
|
|
71
|
+
createGsspReqRes: () => PagesGsspContextResponse;
|
|
72
|
+
createPageElement: (pageProps: Record<string, unknown>) => ReactNode;
|
|
73
|
+
fontLinkHeader: string;
|
|
74
|
+
i18n: PagesI18nRenderContext;
|
|
75
|
+
isrCacheKey: (router: string, pathname: string) => string;
|
|
76
|
+
isrGet: (key: string) => Promise<ISRCacheEntry | null>;
|
|
77
|
+
isrSet: (key: string, data: CachedPagesValue, revalidateSeconds: number, tags?: string[]) => Promise<void>;
|
|
78
|
+
pageModule: PagesPageModule;
|
|
79
|
+
params: Record<string, unknown>;
|
|
80
|
+
query: Record<string, unknown>;
|
|
81
|
+
route: Pick<Route, "isDynamic">;
|
|
82
|
+
routePattern: string;
|
|
83
|
+
routeUrl: string;
|
|
84
|
+
runInFreshUnifiedContext: <T>(callback: () => Promise<T>) => Promise<T>;
|
|
85
|
+
safeJsonStringify: (value: unknown) => string;
|
|
86
|
+
sanitizeDestination: (destination: string) => string;
|
|
87
|
+
triggerBackgroundRegeneration: (key: string, renderFn: () => Promise<void>) => void;
|
|
88
|
+
renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;
|
|
89
|
+
}
|
|
90
|
+
interface ResolvePagesPageDataRenderResult {
|
|
91
|
+
kind: "render";
|
|
92
|
+
gsspRes: PagesGsspResponse | null;
|
|
93
|
+
isrRevalidateSeconds: number | null;
|
|
94
|
+
pageProps: Record<string, unknown>;
|
|
95
|
+
}
|
|
96
|
+
interface ResolvePagesPageDataResponseResult {
|
|
97
|
+
kind: "response";
|
|
98
|
+
response: Response;
|
|
99
|
+
}
|
|
100
|
+
type ResolvePagesPageDataResult = ResolvePagesPageDataRenderResult | ResolvePagesPageDataResponseResult;
|
|
101
|
+
declare function renderPagesIsrHtml(options: RenderPagesIsrHtmlOptions): Promise<string>;
|
|
102
|
+
declare function resolvePagesPageData(options: ResolvePagesPageDataOptions): Promise<ResolvePagesPageDataResult>;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { PagesGsspContextResponse, PagesMutableGsspResponse, PagesPageModule, RenderPagesIsrHtmlOptions, ResolvePagesPageDataOptions, ResolvePagesPageDataRenderResult, ResolvePagesPageDataResponseResult, ResolvePagesPageDataResult, renderPagesIsrHtml, resolvePagesPageData };
|
|
105
|
+
//# sourceMappingURL=pages-page-data.d.ts.map
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { buildPagesCacheValue } from "./isr-cache.js";
|
|
2
|
+
import { buildPagesNextDataScript } from "./pages-page-response.js";
|
|
3
|
+
//#region src/server/pages-page-data.ts
|
|
4
|
+
function buildPagesNotFoundResponse() {
|
|
5
|
+
return new Response("<!DOCTYPE html><html><body><h1>404 - Page not found</h1></body></html>", {
|
|
6
|
+
status: 404,
|
|
7
|
+
headers: { "Content-Type": "text/html" }
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function buildPagesDataNotFoundResponse() {
|
|
11
|
+
return new Response("404", { status: 404 });
|
|
12
|
+
}
|
|
13
|
+
function resolvePagesRedirectStatus(redirect) {
|
|
14
|
+
return redirect.statusCode != null ? redirect.statusCode : redirect.permanent ? 308 : 307;
|
|
15
|
+
}
|
|
16
|
+
function matchesPagesStaticPath(pathEntry, params) {
|
|
17
|
+
return Object.entries(pathEntry.params).every(([key, value]) => {
|
|
18
|
+
const actual = params[key];
|
|
19
|
+
if (Array.isArray(value)) return Array.isArray(actual) && value.join("/") === actual.join("/");
|
|
20
|
+
return String(value) === String(actual);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function buildPagesCacheResponse(html, cacheState, fontLinkHeader, revalidateSeconds) {
|
|
24
|
+
const headers = {
|
|
25
|
+
"Content-Type": "text/html",
|
|
26
|
+
"X-Vinext-Cache": cacheState,
|
|
27
|
+
"Cache-Control": cacheState === "HIT" ? `s-maxage=${revalidateSeconds ?? 60}, stale-while-revalidate` : "s-maxage=0, stale-while-revalidate"
|
|
28
|
+
};
|
|
29
|
+
if (fontLinkHeader) headers.Link = fontLinkHeader;
|
|
30
|
+
return new Response(html, {
|
|
31
|
+
status: 200,
|
|
32
|
+
headers
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function rewritePagesCachedHtml(cachedHtml, freshBody, nextDataScript) {
|
|
36
|
+
const bodyStart = cachedHtml.indexOf("<div id=\"__next\">");
|
|
37
|
+
const contentStart = bodyStart >= 0 ? bodyStart + 17 : -1;
|
|
38
|
+
const nextDataStart = cachedHtml.indexOf("<script>window.__NEXT_DATA__");
|
|
39
|
+
if (contentStart >= 0 && nextDataStart >= 0) {
|
|
40
|
+
const region = cachedHtml.slice(contentStart, nextDataStart);
|
|
41
|
+
const lastCloseDiv = region.lastIndexOf("</div>");
|
|
42
|
+
const gap = lastCloseDiv >= 0 ? region.slice(lastCloseDiv + 6) : "";
|
|
43
|
+
const nextDataEnd = cachedHtml.indexOf("<\/script>", nextDataStart) + 9;
|
|
44
|
+
const tail = cachedHtml.slice(nextDataEnd);
|
|
45
|
+
return cachedHtml.slice(0, contentStart) + freshBody + "</div>" + gap + nextDataScript + tail;
|
|
46
|
+
}
|
|
47
|
+
return "<!DOCTYPE html>\n<html>\n<head>\n</head>\n<body>\n <div id=\"__next\">" + freshBody + "</div>\n " + nextDataScript + "\n</body>\n</html>";
|
|
48
|
+
}
|
|
49
|
+
async function renderPagesIsrHtml(options) {
|
|
50
|
+
const freshBody = await options.renderIsrPassToStringAsync(options.createPageElement(options.pageProps));
|
|
51
|
+
const nextDataScript = buildPagesNextDataScript({
|
|
52
|
+
buildId: options.buildId,
|
|
53
|
+
i18n: options.i18n,
|
|
54
|
+
pageProps: options.pageProps,
|
|
55
|
+
params: options.params,
|
|
56
|
+
routePattern: options.routePattern,
|
|
57
|
+
safeJsonStringify: options.safeJsonStringify
|
|
58
|
+
});
|
|
59
|
+
return rewritePagesCachedHtml(options.cachedHtml, freshBody, nextDataScript);
|
|
60
|
+
}
|
|
61
|
+
async function resolvePagesPageData(options) {
|
|
62
|
+
if (typeof options.pageModule.getStaticPaths === "function" && options.route.isDynamic) {
|
|
63
|
+
const pathsResult = await options.pageModule.getStaticPaths({
|
|
64
|
+
locales: options.i18n.locales ?? [],
|
|
65
|
+
defaultLocale: options.i18n.defaultLocale ?? ""
|
|
66
|
+
});
|
|
67
|
+
if ((pathsResult?.fallback ?? false) === false) {
|
|
68
|
+
if (!(pathsResult?.paths ?? []).some((pathEntry) => matchesPagesStaticPath(pathEntry, options.params))) return {
|
|
69
|
+
kind: "response",
|
|
70
|
+
response: buildPagesNotFoundResponse()
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
let pageProps = {};
|
|
75
|
+
let gsspRes = null;
|
|
76
|
+
if (typeof options.pageModule.getServerSideProps === "function") {
|
|
77
|
+
const { req, res, responsePromise } = options.createGsspReqRes();
|
|
78
|
+
const result = await options.pageModule.getServerSideProps({
|
|
79
|
+
params: options.params,
|
|
80
|
+
req,
|
|
81
|
+
res,
|
|
82
|
+
query: options.query,
|
|
83
|
+
resolvedUrl: options.routeUrl,
|
|
84
|
+
locale: options.i18n.locale,
|
|
85
|
+
locales: options.i18n.locales,
|
|
86
|
+
defaultLocale: options.i18n.defaultLocale
|
|
87
|
+
});
|
|
88
|
+
if (res.headersSent) return {
|
|
89
|
+
kind: "response",
|
|
90
|
+
response: await responsePromise
|
|
91
|
+
};
|
|
92
|
+
if (result?.props) pageProps = result.props;
|
|
93
|
+
if (result?.redirect) return {
|
|
94
|
+
kind: "response",
|
|
95
|
+
response: new Response(null, {
|
|
96
|
+
status: resolvePagesRedirectStatus(result.redirect),
|
|
97
|
+
headers: { Location: options.sanitizeDestination(result.redirect.destination) }
|
|
98
|
+
})
|
|
99
|
+
};
|
|
100
|
+
if (result?.notFound) return {
|
|
101
|
+
kind: "response",
|
|
102
|
+
response: buildPagesDataNotFoundResponse()
|
|
103
|
+
};
|
|
104
|
+
gsspRes = res;
|
|
105
|
+
}
|
|
106
|
+
let isrRevalidateSeconds = null;
|
|
107
|
+
if (typeof options.pageModule.getStaticProps === "function") {
|
|
108
|
+
const pathname = options.routeUrl.split("?")[0];
|
|
109
|
+
const cacheKey = options.isrCacheKey("pages", pathname);
|
|
110
|
+
const cached = await options.isrGet(cacheKey);
|
|
111
|
+
const cachedValue = cached?.value.value;
|
|
112
|
+
if (cachedValue?.kind === "PAGES" && cached && !cached.isStale) return {
|
|
113
|
+
kind: "response",
|
|
114
|
+
response: buildPagesCacheResponse(cachedValue.html, "HIT", options.fontLinkHeader, cachedValue.revalidate)
|
|
115
|
+
};
|
|
116
|
+
if (cachedValue?.kind === "PAGES" && cached && cached.isStale) {
|
|
117
|
+
options.triggerBackgroundRegeneration(cacheKey, async function() {
|
|
118
|
+
return options.runInFreshUnifiedContext(async () => {
|
|
119
|
+
const freshResult = await options.pageModule.getStaticProps?.({
|
|
120
|
+
params: options.params,
|
|
121
|
+
locale: options.i18n.locale,
|
|
122
|
+
locales: options.i18n.locales,
|
|
123
|
+
defaultLocale: options.i18n.defaultLocale
|
|
124
|
+
});
|
|
125
|
+
if (freshResult?.props && typeof freshResult.revalidate === "number" && freshResult.revalidate > 0) {
|
|
126
|
+
options.applyRequestContexts();
|
|
127
|
+
const freshHtml = await renderPagesIsrHtml({
|
|
128
|
+
buildId: options.buildId,
|
|
129
|
+
cachedHtml: cachedValue.html,
|
|
130
|
+
createPageElement: options.createPageElement,
|
|
131
|
+
i18n: options.i18n,
|
|
132
|
+
pageProps: freshResult.props,
|
|
133
|
+
params: options.params,
|
|
134
|
+
renderIsrPassToStringAsync: options.renderIsrPassToStringAsync,
|
|
135
|
+
routePattern: options.routePattern,
|
|
136
|
+
safeJsonStringify: options.safeJsonStringify
|
|
137
|
+
});
|
|
138
|
+
await options.isrSet(cacheKey, buildPagesCacheValue(freshHtml, freshResult.props), freshResult.revalidate);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
kind: "response",
|
|
144
|
+
response: buildPagesCacheResponse(cachedValue.html, "STALE", options.fontLinkHeader)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const result = await options.pageModule.getStaticProps({
|
|
148
|
+
params: options.params,
|
|
149
|
+
locale: options.i18n.locale,
|
|
150
|
+
locales: options.i18n.locales,
|
|
151
|
+
defaultLocale: options.i18n.defaultLocale
|
|
152
|
+
});
|
|
153
|
+
if (result?.props) pageProps = result.props;
|
|
154
|
+
if (result?.redirect) return {
|
|
155
|
+
kind: "response",
|
|
156
|
+
response: new Response(null, {
|
|
157
|
+
status: resolvePagesRedirectStatus(result.redirect),
|
|
158
|
+
headers: { Location: options.sanitizeDestination(result.redirect.destination) }
|
|
159
|
+
})
|
|
160
|
+
};
|
|
161
|
+
if (result?.notFound) return {
|
|
162
|
+
kind: "response",
|
|
163
|
+
response: buildPagesDataNotFoundResponse()
|
|
164
|
+
};
|
|
165
|
+
if (typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
kind: "render",
|
|
169
|
+
gsspRes,
|
|
170
|
+
isrRevalidateSeconds,
|
|
171
|
+
pageProps
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
//#endregion
|
|
175
|
+
export { renderPagesIsrHtml, resolvePagesPageData };
|
|
176
|
+
|
|
177
|
+
//# sourceMappingURL=pages-page-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pages-page-data.js","names":[],"sources":["../../src/server/pages-page-data.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { Route } from \"../routing/pages-router.js\";\nimport type { CachedPagesValue } from \"../shims/cache.js\";\nimport { buildPagesCacheValue, type ISRCacheEntry } from \"./isr-cache.js\";\nimport {\n buildPagesNextDataScript,\n type PagesGsspResponse,\n type PagesI18nRenderContext,\n} from \"./pages-page-response.js\";\n\ninterface PagesRedirectResult {\n destination: string;\n permanent?: boolean;\n statusCode?: number;\n}\n\ninterface PagesStaticPathsEntry {\n params: Record<string, unknown>;\n}\n\ninterface PagesStaticPathsResult {\n fallback?: boolean | \"blocking\";\n paths?: PagesStaticPathsEntry[];\n}\n\ninterface PagesPagePropsResult {\n props?: Record<string, unknown>;\n redirect?: PagesRedirectResult;\n notFound?: boolean;\n revalidate?: number;\n}\n\nexport interface PagesMutableGsspResponse extends PagesGsspResponse {\n headersSent: boolean;\n}\n\nexport interface PagesGsspContextResponse {\n req: unknown;\n res: PagesMutableGsspResponse;\n responsePromise: Promise<Response>;\n}\n\nexport interface PagesPageModule {\n default?: unknown;\n getStaticPaths?: (context: {\n locales: string[];\n defaultLocale: string;\n }) => Promise<PagesStaticPathsResult> | PagesStaticPathsResult;\n getServerSideProps?: (context: {\n params: Record<string, unknown>;\n req: unknown;\n res: PagesMutableGsspResponse;\n query: Record<string, unknown>;\n resolvedUrl: string;\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n }) => Promise<PagesPagePropsResult> | PagesPagePropsResult;\n getStaticProps?: (context: {\n params: Record<string, unknown>;\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n }) => Promise<PagesPagePropsResult> | PagesPagePropsResult;\n}\n\nexport interface RenderPagesIsrHtmlOptions {\n buildId: string | null;\n cachedHtml: string;\n createPageElement: (pageProps: Record<string, unknown>) => ReactNode;\n i18n: PagesI18nRenderContext;\n pageProps: Record<string, unknown>;\n params: Record<string, unknown>;\n renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;\n routePattern: string;\n safeJsonStringify: (value: unknown) => string;\n}\n\nexport interface ResolvePagesPageDataOptions {\n applyRequestContexts: () => void;\n buildId: string | null;\n createGsspReqRes: () => PagesGsspContextResponse;\n createPageElement: (pageProps: Record<string, unknown>) => ReactNode;\n fontLinkHeader: string;\n i18n: PagesI18nRenderContext;\n isrCacheKey: (router: string, pathname: string) => string;\n isrGet: (key: string) => Promise<ISRCacheEntry | null>;\n isrSet: (\n key: string,\n data: CachedPagesValue,\n revalidateSeconds: number,\n tags?: string[],\n ) => Promise<void>;\n pageModule: PagesPageModule;\n params: Record<string, unknown>;\n query: Record<string, unknown>;\n route: Pick<Route, \"isDynamic\">;\n routePattern: string;\n routeUrl: string;\n runInFreshUnifiedContext: <T>(callback: () => Promise<T>) => Promise<T>;\n safeJsonStringify: (value: unknown) => string;\n sanitizeDestination: (destination: string) => string;\n triggerBackgroundRegeneration: (key: string, renderFn: () => Promise<void>) => void;\n renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;\n}\n\nexport interface ResolvePagesPageDataRenderResult {\n kind: \"render\";\n gsspRes: PagesGsspResponse | null;\n isrRevalidateSeconds: number | null;\n pageProps: Record<string, unknown>;\n}\n\nexport interface ResolvePagesPageDataResponseResult {\n kind: \"response\";\n response: Response;\n}\n\nexport type ResolvePagesPageDataResult =\n | ResolvePagesPageDataRenderResult\n | ResolvePagesPageDataResponseResult;\n\nfunction buildPagesNotFoundResponse(): Response {\n return new Response(\"<!DOCTYPE html><html><body><h1>404 - Page not found</h1></body></html>\", {\n status: 404,\n headers: { \"Content-Type\": \"text/html\" },\n });\n}\n\nfunction buildPagesDataNotFoundResponse(): Response {\n return new Response(\"404\", { status: 404 });\n}\n\nfunction resolvePagesRedirectStatus(redirect: PagesRedirectResult): number {\n return redirect.statusCode != null ? redirect.statusCode : redirect.permanent ? 308 : 307;\n}\n\nfunction matchesPagesStaticPath(\n pathEntry: PagesStaticPathsEntry,\n params: Record<string, unknown>,\n): boolean {\n return Object.entries(pathEntry.params).every(([key, value]) => {\n const actual = params[key];\n if (Array.isArray(value)) {\n return Array.isArray(actual) && value.join(\"/\") === actual.join(\"/\");\n }\n return String(value) === String(actual);\n });\n}\n\nfunction buildPagesCacheResponse(\n html: string,\n cacheState: \"HIT\" | \"STALE\",\n fontLinkHeader: string,\n revalidateSeconds?: number,\n): Response {\n const headers: Record<string, string> = {\n \"Content-Type\": \"text/html\",\n \"X-Vinext-Cache\": cacheState,\n \"Cache-Control\":\n cacheState === \"HIT\"\n ? `s-maxage=${revalidateSeconds ?? 60}, stale-while-revalidate`\n : \"s-maxage=0, stale-while-revalidate\",\n };\n\n if (fontLinkHeader) {\n headers.Link = fontLinkHeader;\n }\n\n return new Response(html, {\n status: 200,\n headers,\n });\n}\n\nfunction rewritePagesCachedHtml(\n cachedHtml: string,\n freshBody: string,\n nextDataScript: string,\n): string {\n const bodyMarker = '<div id=\"__next\">';\n const bodyStart = cachedHtml.indexOf(bodyMarker);\n const contentStart = bodyStart >= 0 ? bodyStart + bodyMarker.length : -1;\n const nextDataMarker = \"<script>window.__NEXT_DATA__\";\n const nextDataStart = cachedHtml.indexOf(nextDataMarker);\n\n if (contentStart >= 0 && nextDataStart >= 0) {\n const region = cachedHtml.slice(contentStart, nextDataStart);\n const lastCloseDiv = region.lastIndexOf(\"</div>\");\n const gap = lastCloseDiv >= 0 ? region.slice(lastCloseDiv + 6) : \"\";\n const nextDataEnd = cachedHtml.indexOf(\"</script>\", nextDataStart) + 9;\n const tail = cachedHtml.slice(nextDataEnd);\n\n return cachedHtml.slice(0, contentStart) + freshBody + \"</div>\" + gap + nextDataScript + tail;\n }\n\n return (\n '<!DOCTYPE html>\\n<html>\\n<head>\\n</head>\\n<body>\\n <div id=\"__next\">' +\n freshBody +\n \"</div>\\n \" +\n nextDataScript +\n \"\\n</body>\\n</html>\"\n );\n}\n\nexport async function renderPagesIsrHtml(options: RenderPagesIsrHtmlOptions): Promise<string> {\n const freshBody = await options.renderIsrPassToStringAsync(\n options.createPageElement(options.pageProps),\n );\n const nextDataScript = buildPagesNextDataScript({\n buildId: options.buildId,\n i18n: options.i18n,\n pageProps: options.pageProps,\n params: options.params,\n routePattern: options.routePattern,\n safeJsonStringify: options.safeJsonStringify,\n });\n\n return rewritePagesCachedHtml(options.cachedHtml, freshBody, nextDataScript);\n}\n\nexport async function resolvePagesPageData(\n options: ResolvePagesPageDataOptions,\n): Promise<ResolvePagesPageDataResult> {\n if (typeof options.pageModule.getStaticPaths === \"function\" && options.route.isDynamic) {\n const pathsResult = await options.pageModule.getStaticPaths({\n locales: options.i18n.locales ?? [],\n defaultLocale: options.i18n.defaultLocale ?? \"\",\n });\n const fallback = pathsResult?.fallback ?? false;\n\n if (fallback === false) {\n const paths = pathsResult?.paths ?? [];\n const isValidPath = paths.some((pathEntry) =>\n matchesPagesStaticPath(pathEntry, options.params),\n );\n\n if (!isValidPath) {\n return {\n kind: \"response\",\n response: buildPagesNotFoundResponse(),\n };\n }\n }\n }\n\n let pageProps: Record<string, unknown> = {};\n let gsspRes: PagesMutableGsspResponse | null = null;\n\n if (typeof options.pageModule.getServerSideProps === \"function\") {\n const { req, res, responsePromise } = options.createGsspReqRes();\n const result = await options.pageModule.getServerSideProps({\n params: options.params,\n req,\n res,\n query: options.query,\n resolvedUrl: options.routeUrl,\n locale: options.i18n.locale,\n locales: options.i18n.locales,\n defaultLocale: options.i18n.defaultLocale,\n });\n\n if (res.headersSent) {\n return {\n kind: \"response\",\n response: await responsePromise,\n };\n }\n\n if (result?.props) {\n pageProps = result.props;\n }\n\n if (result?.redirect) {\n return {\n kind: \"response\",\n response: new Response(null, {\n status: resolvePagesRedirectStatus(result.redirect),\n headers: { Location: options.sanitizeDestination(result.redirect.destination) },\n }),\n };\n }\n\n if (result?.notFound) {\n return {\n kind: \"response\",\n response: buildPagesDataNotFoundResponse(),\n };\n }\n\n gsspRes = res;\n }\n\n let isrRevalidateSeconds: number | null = null;\n\n if (typeof options.pageModule.getStaticProps === \"function\") {\n const pathname = options.routeUrl.split(\"?\")[0];\n const cacheKey = options.isrCacheKey(\"pages\", pathname);\n const cached = await options.isrGet(cacheKey);\n const cachedValue = cached?.value.value;\n\n if (cachedValue?.kind === \"PAGES\" && cached && !cached.isStale) {\n return {\n kind: \"response\",\n response: buildPagesCacheResponse(\n cachedValue.html,\n \"HIT\",\n options.fontLinkHeader,\n (cachedValue as CachedPagesValue & { revalidate?: number }).revalidate,\n ),\n };\n }\n\n if (cachedValue?.kind === \"PAGES\" && cached && cached.isStale) {\n options.triggerBackgroundRegeneration(cacheKey, async function () {\n return options.runInFreshUnifiedContext(async () => {\n const freshResult = await options.pageModule.getStaticProps?.({\n params: options.params,\n locale: options.i18n.locale,\n locales: options.i18n.locales,\n defaultLocale: options.i18n.defaultLocale,\n });\n\n if (\n freshResult?.props &&\n typeof freshResult.revalidate === \"number\" &&\n freshResult.revalidate > 0\n ) {\n options.applyRequestContexts();\n const freshHtml = await renderPagesIsrHtml({\n buildId: options.buildId,\n cachedHtml: cachedValue.html,\n createPageElement: options.createPageElement,\n i18n: options.i18n,\n pageProps: freshResult.props,\n params: options.params,\n renderIsrPassToStringAsync: options.renderIsrPassToStringAsync,\n routePattern: options.routePattern,\n safeJsonStringify: options.safeJsonStringify,\n });\n\n await options.isrSet(\n cacheKey,\n buildPagesCacheValue(freshHtml, freshResult.props),\n freshResult.revalidate,\n );\n }\n });\n });\n\n return {\n kind: \"response\",\n response: buildPagesCacheResponse(cachedValue.html, \"STALE\", options.fontLinkHeader),\n };\n }\n\n const result = await options.pageModule.getStaticProps({\n params: options.params,\n locale: options.i18n.locale,\n locales: options.i18n.locales,\n defaultLocale: options.i18n.defaultLocale,\n });\n\n if (result?.props) {\n pageProps = result.props;\n }\n\n if (result?.redirect) {\n return {\n kind: \"response\",\n response: new Response(null, {\n status: resolvePagesRedirectStatus(result.redirect),\n headers: { Location: options.sanitizeDestination(result.redirect.destination) },\n }),\n };\n }\n\n if (result?.notFound) {\n return {\n kind: \"response\",\n response: buildPagesDataNotFoundResponse(),\n };\n }\n\n if (typeof result?.revalidate === \"number\" && result.revalidate > 0) {\n isrRevalidateSeconds = result.revalidate;\n }\n }\n\n return {\n kind: \"render\",\n gsspRes,\n isrRevalidateSeconds,\n pageProps,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ComponentType, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/server/pages-page-response.d.ts
|
|
4
|
+
interface PagesFontPreload {
|
|
5
|
+
href: string;
|
|
6
|
+
type: string;
|
|
7
|
+
}
|
|
8
|
+
interface PagesI18nRenderContext {
|
|
9
|
+
locale?: string;
|
|
10
|
+
locales?: string[];
|
|
11
|
+
defaultLocale?: string;
|
|
12
|
+
domainLocales?: unknown;
|
|
13
|
+
}
|
|
14
|
+
interface PagesGsspResponse {
|
|
15
|
+
statusCode: number;
|
|
16
|
+
getHeaders(): Record<string, string | number | boolean | string[]>;
|
|
17
|
+
}
|
|
18
|
+
interface RenderPagesPageResponseOptions {
|
|
19
|
+
assetTags: string;
|
|
20
|
+
buildId: string | null;
|
|
21
|
+
clearSsrContext: () => void;
|
|
22
|
+
createPageElement: (pageProps: Record<string, unknown>) => ReactNode;
|
|
23
|
+
DocumentComponent: ComponentType | null;
|
|
24
|
+
flushPreloads?: (() => Promise<void> | void) | undefined;
|
|
25
|
+
fontLinkHeader: string;
|
|
26
|
+
fontPreloads: PagesFontPreload[];
|
|
27
|
+
getFontLinks: () => string[];
|
|
28
|
+
getFontStyles: () => string[];
|
|
29
|
+
getSSRHeadHTML?: (() => string) | undefined;
|
|
30
|
+
gsspRes: PagesGsspResponse | null;
|
|
31
|
+
isrCacheKey: (router: string, pathname: string) => string;
|
|
32
|
+
isrRevalidateSeconds: number | null;
|
|
33
|
+
isrSet: (key: string, data: {
|
|
34
|
+
kind: "PAGES";
|
|
35
|
+
html: string;
|
|
36
|
+
pageData: Record<string, unknown>;
|
|
37
|
+
headers: undefined;
|
|
38
|
+
status: undefined;
|
|
39
|
+
}, revalidateSeconds: number) => Promise<void>;
|
|
40
|
+
i18n: PagesI18nRenderContext;
|
|
41
|
+
pageProps: Record<string, unknown>;
|
|
42
|
+
params: Record<string, unknown>;
|
|
43
|
+
renderDocumentToString: (element: ReactNode) => Promise<string>;
|
|
44
|
+
renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;
|
|
45
|
+
renderToReadableStream: (element: ReactNode) => Promise<ReadableStream<Uint8Array>>;
|
|
46
|
+
resetSSRHead?: (() => void) | undefined;
|
|
47
|
+
routePattern: string;
|
|
48
|
+
routeUrl: string;
|
|
49
|
+
safeJsonStringify: (value: unknown) => string;
|
|
50
|
+
}
|
|
51
|
+
declare function buildPagesNextDataScript(options: Pick<RenderPagesPageResponseOptions, "buildId" | "i18n" | "pageProps" | "params" | "routePattern" | "safeJsonStringify">): string;
|
|
52
|
+
declare function renderPagesPageResponse(options: RenderPagesPageResponseOptions): Promise<Response>;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { PagesFontPreload, PagesGsspResponse, PagesI18nRenderContext, RenderPagesPageResponseOptions, buildPagesNextDataScript, renderPagesPageResponse };
|
|
55
|
+
//# sourceMappingURL=pages-page-response.d.ts.map
|