vinext 0.0.33 → 0.0.34
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/dist/deploy.js +52 -4
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-rsc-entry.js +278 -740
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +8 -1
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.js +299 -22
- package/dist/index.js.map +1 -1
- package/dist/server/app-browser-entry.js +2 -3
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-page-boundary-render.d.ts +63 -0
- package/dist/server/app-page-boundary-render.js +182 -0
- package/dist/server/app-page-boundary-render.js.map +1 -0
- package/dist/server/app-page-boundary.d.ts +57 -0
- package/dist/server/app-page-boundary.js +60 -0
- package/dist/server/app-page-boundary.js.map +1 -0
- package/dist/server/app-page-execution.d.ts +46 -0
- package/dist/server/app-page-execution.js +109 -0
- package/dist/server/app-page-execution.js.map +1 -0
- package/dist/server/app-page-probe.d.ts +17 -0
- package/dist/server/app-page-probe.js +35 -0
- package/dist/server/app-page-probe.js.map +1 -0
- package/dist/server/app-page-render.d.ts +59 -0
- package/dist/server/app-page-render.js +174 -0
- package/dist/server/app-page-render.js.map +1 -0
- package/dist/server/app-page-request.d.ts +58 -0
- package/dist/server/app-page-request.js +79 -0
- package/dist/server/app-page-request.js.map +1 -0
- package/dist/server/app-page-stream.d.ts +55 -0
- package/dist/server/app-page-stream.js +65 -0
- package/dist/server/app-page-stream.js.map +1 -0
- package/dist/server/app-ssr-stream.js +1 -1
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/prod-server.d.ts +13 -1
- package/dist/server/prod-server.js +113 -19
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/worker-utils.d.ts +0 -6
- package/dist/server/worker-utils.js +41 -5
- package/dist/server/worker-utils.js.map +1 -1
- package/dist/shims/error-boundary.js +1 -1
- package/dist/shims/font-google-base.js +1 -1
- package/dist/shims/font-google-base.js.map +1 -1
- package/dist/shims/font-google.d.ts +2 -3
- package/dist/shims/font-google.js +2 -3
- package/package.json +1 -1
- package/dist/shims/font-google.generated.d.ts +0 -1929
- package/dist/shims/font-google.generated.js +0 -1929
- package/dist/shims/font-google.generated.js.map +0 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AppPageFontPreload } from "./app-page-execution.js";
|
|
2
|
+
|
|
3
|
+
//#region src/server/app-page-stream.d.ts
|
|
4
|
+
interface AppPageFontData {
|
|
5
|
+
links: string[];
|
|
6
|
+
preloads: readonly AppPageFontPreload[];
|
|
7
|
+
styles: string[];
|
|
8
|
+
}
|
|
9
|
+
interface CreateAppPageFontDataOptions {
|
|
10
|
+
getLinks: () => string[];
|
|
11
|
+
getPreloads: () => AppPageFontPreload[];
|
|
12
|
+
getStyles: () => string[];
|
|
13
|
+
}
|
|
14
|
+
interface AppPageSsrHandler {
|
|
15
|
+
handleSsr: (rscStream: ReadableStream<Uint8Array>, navigationContext: unknown, fontData: AppPageFontData) => Promise<ReadableStream<Uint8Array>>;
|
|
16
|
+
}
|
|
17
|
+
interface RenderAppPageHtmlStreamOptions {
|
|
18
|
+
fontData: AppPageFontData;
|
|
19
|
+
navigationContext: unknown;
|
|
20
|
+
rscStream: ReadableStream<Uint8Array>;
|
|
21
|
+
ssrHandler: AppPageSsrHandler;
|
|
22
|
+
}
|
|
23
|
+
interface RenderAppPageHtmlResponseOptions extends RenderAppPageHtmlStreamOptions {
|
|
24
|
+
clearRequestContext: () => void;
|
|
25
|
+
fontLinkHeader?: string;
|
|
26
|
+
status: number;
|
|
27
|
+
}
|
|
28
|
+
interface AppPageHtmlStreamRecoveryResult {
|
|
29
|
+
htmlStream: ReadableStream<Uint8Array> | null;
|
|
30
|
+
response: Response | null;
|
|
31
|
+
}
|
|
32
|
+
interface RenderAppPageHtmlStreamWithRecoveryOptions<TSpecialError> {
|
|
33
|
+
onShellRendered?: () => void;
|
|
34
|
+
renderErrorBoundaryResponse: (error: unknown) => Promise<Response | null>;
|
|
35
|
+
renderHtmlStream: () => Promise<ReadableStream<Uint8Array>>;
|
|
36
|
+
renderSpecialErrorResponse: (specialError: TSpecialError) => Promise<Response>;
|
|
37
|
+
resolveSpecialError: (error: unknown) => TSpecialError | null;
|
|
38
|
+
}
|
|
39
|
+
interface AppPageRscErrorTracker {
|
|
40
|
+
getCapturedError: () => unknown;
|
|
41
|
+
onRenderError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown;
|
|
42
|
+
}
|
|
43
|
+
interface ShouldRerenderAppPageWithGlobalErrorOptions {
|
|
44
|
+
capturedError: unknown;
|
|
45
|
+
hasLocalBoundary: boolean;
|
|
46
|
+
}
|
|
47
|
+
declare function createAppPageFontData(options: CreateAppPageFontDataOptions): AppPageFontData;
|
|
48
|
+
declare function renderAppPageHtmlStream(options: RenderAppPageHtmlStreamOptions): Promise<ReadableStream<Uint8Array>>;
|
|
49
|
+
declare function renderAppPageHtmlResponse(options: RenderAppPageHtmlResponseOptions): Promise<Response>;
|
|
50
|
+
declare function renderAppPageHtmlStreamWithRecovery<TSpecialError>(options: RenderAppPageHtmlStreamWithRecoveryOptions<TSpecialError>): Promise<AppPageHtmlStreamRecoveryResult>;
|
|
51
|
+
declare function createAppPageRscErrorTracker(baseOnError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown): AppPageRscErrorTracker;
|
|
52
|
+
declare function shouldRerenderAppPageWithGlobalError(options: ShouldRerenderAppPageWithGlobalErrorOptions): boolean;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { AppPageFontData, AppPageHtmlStreamRecoveryResult, AppPageRscErrorTracker, AppPageSsrHandler, CreateAppPageFontDataOptions, RenderAppPageHtmlResponseOptions, RenderAppPageHtmlStreamOptions, RenderAppPageHtmlStreamWithRecoveryOptions, ShouldRerenderAppPageWithGlobalErrorOptions, createAppPageFontData, createAppPageRscErrorTracker, renderAppPageHtmlResponse, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery, shouldRerenderAppPageWithGlobalError };
|
|
55
|
+
//# sourceMappingURL=app-page-stream.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//#region src/server/app-page-stream.ts
|
|
2
|
+
function createAppPageFontData(options) {
|
|
3
|
+
return {
|
|
4
|
+
links: options.getLinks(),
|
|
5
|
+
preloads: options.getPreloads(),
|
|
6
|
+
styles: options.getStyles()
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
async function renderAppPageHtmlStream(options) {
|
|
10
|
+
return options.ssrHandler.handleSsr(options.rscStream, options.navigationContext, options.fontData);
|
|
11
|
+
}
|
|
12
|
+
async function renderAppPageHtmlResponse(options) {
|
|
13
|
+
const htmlStream = await renderAppPageHtmlStream(options);
|
|
14
|
+
options.clearRequestContext();
|
|
15
|
+
const headers = {
|
|
16
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
17
|
+
Vary: "RSC, Accept"
|
|
18
|
+
};
|
|
19
|
+
if (options.fontLinkHeader) headers.Link = options.fontLinkHeader;
|
|
20
|
+
return new Response(htmlStream, {
|
|
21
|
+
status: options.status,
|
|
22
|
+
headers
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function renderAppPageHtmlStreamWithRecovery(options) {
|
|
26
|
+
try {
|
|
27
|
+
const htmlStream = await options.renderHtmlStream();
|
|
28
|
+
options.onShellRendered?.();
|
|
29
|
+
return {
|
|
30
|
+
htmlStream,
|
|
31
|
+
response: null
|
|
32
|
+
};
|
|
33
|
+
} catch (error) {
|
|
34
|
+
const specialError = options.resolveSpecialError(error);
|
|
35
|
+
if (specialError) return {
|
|
36
|
+
htmlStream: null,
|
|
37
|
+
response: await options.renderSpecialErrorResponse(specialError)
|
|
38
|
+
};
|
|
39
|
+
const boundaryResponse = await options.renderErrorBoundaryResponse(error);
|
|
40
|
+
if (boundaryResponse) return {
|
|
41
|
+
htmlStream: null,
|
|
42
|
+
response: boundaryResponse
|
|
43
|
+
};
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function createAppPageRscErrorTracker(baseOnError) {
|
|
48
|
+
let capturedError = null;
|
|
49
|
+
return {
|
|
50
|
+
getCapturedError() {
|
|
51
|
+
return capturedError;
|
|
52
|
+
},
|
|
53
|
+
onRenderError(error, requestInfo, errorContext) {
|
|
54
|
+
if (!(error && typeof error === "object" && "digest" in error)) capturedError = error;
|
|
55
|
+
return baseOnError(error, requestInfo, errorContext);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function shouldRerenderAppPageWithGlobalError(options) {
|
|
60
|
+
return Boolean(options.capturedError) && !options.hasLocalBoundary;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { createAppPageFontData, createAppPageRscErrorTracker, renderAppPageHtmlResponse, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery, shouldRerenderAppPageWithGlobalError };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=app-page-stream.js.map
|
|
@@ -0,0 +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(htmlStream, {\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;;AAGH,eAAsB,0BACpB,SACmB;CACnB,MAAM,aAAa,MAAM,wBAAwB,QAAQ;AACzD,SAAQ,qBAAqB;CAE7B,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"}
|
|
@@ -6,7 +6,7 @@ import { safeJsonStringify } from "./html.js";
|
|
|
6
6
|
* the HTML spec requires as="style" for <link rel="preload">.
|
|
7
7
|
*/
|
|
8
8
|
function fixFlightHints(text) {
|
|
9
|
-
return text.replace(/(\d
|
|
9
|
+
return text.replace(/(\d*:HL\[.*?),"stylesheet"(\]|,)/g, "$1,\"style\"$2");
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* Create a helper that progressively embeds RSC chunks as inline <script> tags.
|
|
@@ -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
|
|
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 const rscScripts = rscEmbed.flush();\n if (rscScripts) {\n controller.enqueue(encoder.encode(rscScripts));\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,kBAAc,WAAW;IAEzB,MAAM,aAAa,SAAS,OAAO;AACnC,QAAI,WACF,YAAW,QAAQ,QAAQ,OAAO,WAAW,CAAC;AAGhD,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"}
|
|
@@ -27,6 +27,13 @@ declare function negotiateEncoding(req: IncomingMessage): "br" | "gzip" | "defla
|
|
|
27
27
|
* to preserve multiple Set-Cookie values instead of flattening them.
|
|
28
28
|
*/
|
|
29
29
|
declare function mergeResponseHeaders(middlewareHeaders: Record<string, string | string[]>, response: Response): Record<string, string | string[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Merge middleware/config headers and an optional status override into a new
|
|
32
|
+
* Web Response while preserving the original body stream when allowed.
|
|
33
|
+
* Keep this in sync with server/worker-utils.ts and the generated copy in
|
|
34
|
+
* deploy.ts.
|
|
35
|
+
*/
|
|
36
|
+
declare function mergeWebResponse(middlewareHeaders: Record<string, string | string[]>, response: Response, statusOverride?: number): Response;
|
|
30
37
|
/**
|
|
31
38
|
* Send a compressed response if the content type is compressible and the
|
|
32
39
|
* client supports compression. Otherwise send uncompressed.
|
|
@@ -63,6 +70,11 @@ declare const trustProxy: boolean;
|
|
|
63
70
|
* the result here so the downstream RSC handler doesn't re-normalize).
|
|
64
71
|
*/
|
|
65
72
|
declare function nodeToWebRequest(req: IncomingMessage, urlOverride?: string): Request;
|
|
73
|
+
/**
|
|
74
|
+
* Stream a Web Response back to a Node.js ServerResponse.
|
|
75
|
+
* Supports streaming compression for SSR responses.
|
|
76
|
+
*/
|
|
77
|
+
declare function sendWebResponse(webResponse: Response, req: IncomingMessage, res: ServerResponse, compress: boolean): Promise<void>;
|
|
66
78
|
/**
|
|
67
79
|
* Start the production server.
|
|
68
80
|
*
|
|
@@ -74,5 +86,5 @@ declare function startProdServer(options?: ProdServerOptions): Promise<{
|
|
|
74
86
|
port: number;
|
|
75
87
|
}>;
|
|
76
88
|
//#endregion
|
|
77
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, mergeResponseHeaders, negotiateEncoding, nodeToWebRequest, resolveHost, sendCompressed, startProdServer, trustProxy, trustedHosts };
|
|
89
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveHost, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts };
|
|
78
90
|
//# sourceMappingURL=prod-server.d.ts.map
|
|
@@ -75,11 +75,20 @@ function negotiateEncoding(req) {
|
|
|
75
75
|
/**
|
|
76
76
|
* Create a compression stream for the given encoding.
|
|
77
77
|
*/
|
|
78
|
-
function createCompressor(encoding) {
|
|
78
|
+
function createCompressor(encoding, mode = "default") {
|
|
79
79
|
switch (encoding) {
|
|
80
|
-
case "br": return zlib.createBrotliCompress({
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
case "br": return zlib.createBrotliCompress({
|
|
81
|
+
...mode === "streaming" ? { flush: zlib.constants.BROTLI_OPERATION_FLUSH } : {},
|
|
82
|
+
params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 4 }
|
|
83
|
+
});
|
|
84
|
+
case "gzip": return zlib.createGzip({
|
|
85
|
+
level: 6,
|
|
86
|
+
...mode === "streaming" ? { flush: zlib.constants.Z_SYNC_FLUSH } : {}
|
|
87
|
+
});
|
|
88
|
+
case "deflate": return zlib.createDeflate({
|
|
89
|
+
level: 6,
|
|
90
|
+
...mode === "streaming" ? { flush: zlib.constants.Z_SYNC_FLUSH } : {}
|
|
91
|
+
});
|
|
83
92
|
}
|
|
84
93
|
}
|
|
85
94
|
/**
|
|
@@ -100,6 +109,79 @@ function mergeResponseHeaders(middlewareHeaders, response) {
|
|
|
100
109
|
}
|
|
101
110
|
return merged;
|
|
102
111
|
}
|
|
112
|
+
function toWebHeaders(headersRecord) {
|
|
113
|
+
const headers = new Headers();
|
|
114
|
+
for (const [key, value] of Object.entries(headersRecord)) if (Array.isArray(value)) for (const item of value) headers.append(key, item);
|
|
115
|
+
else headers.set(key, value);
|
|
116
|
+
return headers;
|
|
117
|
+
}
|
|
118
|
+
const NO_BODY_RESPONSE_STATUSES = new Set([
|
|
119
|
+
204,
|
|
120
|
+
205,
|
|
121
|
+
304
|
|
122
|
+
]);
|
|
123
|
+
function hasHeader(headersRecord, name) {
|
|
124
|
+
const target = name.toLowerCase();
|
|
125
|
+
return Object.keys(headersRecord).some((key) => key.toLowerCase() === target);
|
|
126
|
+
}
|
|
127
|
+
function omitHeadersCaseInsensitive(headersRecord, names) {
|
|
128
|
+
const targets = new Set(names.map((name) => name.toLowerCase()));
|
|
129
|
+
const filtered = {};
|
|
130
|
+
for (const [key, value] of Object.entries(headersRecord)) {
|
|
131
|
+
if (targets.has(key.toLowerCase())) continue;
|
|
132
|
+
filtered[key] = value;
|
|
133
|
+
}
|
|
134
|
+
return filtered;
|
|
135
|
+
}
|
|
136
|
+
function stripHeaders(headersRecord, names) {
|
|
137
|
+
const targets = new Set(names.map((name) => name.toLowerCase()));
|
|
138
|
+
for (const key of Object.keys(headersRecord)) if (targets.has(key.toLowerCase())) delete headersRecord[key];
|
|
139
|
+
}
|
|
140
|
+
function isNoBodyResponseStatus(status) {
|
|
141
|
+
return NO_BODY_RESPONSE_STATUSES.has(status);
|
|
142
|
+
}
|
|
143
|
+
function cancelResponseBody(response) {
|
|
144
|
+
const body = response.body;
|
|
145
|
+
if (!body || body.locked) return;
|
|
146
|
+
body.cancel().catch(() => {});
|
|
147
|
+
}
|
|
148
|
+
function isVinextStreamedHtmlResponse(response) {
|
|
149
|
+
return response.__vinextStreamedHtmlResponse === true;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Merge middleware/config headers and an optional status override into a new
|
|
153
|
+
* Web Response while preserving the original body stream when allowed.
|
|
154
|
+
* Keep this in sync with server/worker-utils.ts and the generated copy in
|
|
155
|
+
* deploy.ts.
|
|
156
|
+
*/
|
|
157
|
+
function mergeWebResponse(middlewareHeaders, response, statusOverride) {
|
|
158
|
+
const filteredMiddlewareHeaders = omitHeadersCaseInsensitive(middlewareHeaders, ["content-length"]);
|
|
159
|
+
const status = statusOverride ?? response.status;
|
|
160
|
+
const mergedHeaders = mergeResponseHeaders(filteredMiddlewareHeaders, response);
|
|
161
|
+
const shouldDropBody = isNoBodyResponseStatus(status);
|
|
162
|
+
const shouldStripStreamLength = isVinextStreamedHtmlResponse(response) && hasHeader(mergedHeaders, "content-length");
|
|
163
|
+
if (!Object.keys(filteredMiddlewareHeaders).length && statusOverride === void 0 && !shouldDropBody && !shouldStripStreamLength) return response;
|
|
164
|
+
if (shouldDropBody) {
|
|
165
|
+
cancelResponseBody(response);
|
|
166
|
+
stripHeaders(mergedHeaders, [
|
|
167
|
+
"content-encoding",
|
|
168
|
+
"content-length",
|
|
169
|
+
"content-type",
|
|
170
|
+
"transfer-encoding"
|
|
171
|
+
]);
|
|
172
|
+
return new Response(null, {
|
|
173
|
+
status,
|
|
174
|
+
statusText: status === response.status ? response.statusText : void 0,
|
|
175
|
+
headers: toWebHeaders(mergedHeaders)
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (shouldStripStreamLength) stripHeaders(mergedHeaders, ["content-length"]);
|
|
179
|
+
return new Response(response.body, {
|
|
180
|
+
status,
|
|
181
|
+
statusText: status === response.status ? response.statusText : void 0,
|
|
182
|
+
headers: toWebHeaders(mergedHeaders)
|
|
183
|
+
});
|
|
184
|
+
}
|
|
103
185
|
/**
|
|
104
186
|
* Send a compressed response if the content type is compressible and the
|
|
105
187
|
* client supports compression. Otherwise send uncompressed.
|
|
@@ -108,6 +190,7 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
|
|
|
108
190
|
const buf = typeof body === "string" ? Buffer.from(body) : body;
|
|
109
191
|
const baseType = contentType.split(";")[0].trim();
|
|
110
192
|
const encoding = compress ? negotiateEncoding(req) : null;
|
|
193
|
+
const headersWithoutBodyHeaders = omitHeadersCaseInsensitive(extraHeaders, ["content-length", "content-type"]);
|
|
111
194
|
const writeHead = (headers) => {
|
|
112
195
|
if (statusText) res.writeHead(statusCode, statusText, headers);
|
|
113
196
|
else res.writeHead(statusCode, headers);
|
|
@@ -120,7 +203,7 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
|
|
|
120
203
|
if (existingVary) varyValue = existingVary.toLowerCase().includes("accept-encoding") ? existingVary : existingVary + ", Accept-Encoding";
|
|
121
204
|
else varyValue = "Accept-Encoding";
|
|
122
205
|
writeHead({
|
|
123
|
-
...
|
|
206
|
+
...headersWithoutBodyHeaders,
|
|
124
207
|
"Content-Type": contentType,
|
|
125
208
|
"Content-Encoding": encoding,
|
|
126
209
|
Vary: varyValue
|
|
@@ -128,9 +211,8 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
|
|
|
128
211
|
compressor.end(buf);
|
|
129
212
|
pipeline(compressor, res, () => {});
|
|
130
213
|
} else {
|
|
131
|
-
const { "content-length": _cl, "Content-Length": _CL, ...headersWithoutLength } = extraHeaders;
|
|
132
214
|
writeHead({
|
|
133
|
-
...
|
|
215
|
+
...headersWithoutBodyHeaders,
|
|
134
216
|
"Content-Type": contentType,
|
|
135
217
|
"Content-Length": String(buf.length)
|
|
136
218
|
});
|
|
@@ -308,11 +390,12 @@ async function sendWebResponse(webResponse, req, res, compress) {
|
|
|
308
390
|
}
|
|
309
391
|
writeHead(nodeHeaders);
|
|
310
392
|
if (req.method === "HEAD") {
|
|
393
|
+
cancelResponseBody(webResponse);
|
|
311
394
|
res.end();
|
|
312
395
|
return;
|
|
313
396
|
}
|
|
314
397
|
const nodeStream = Readable.fromWeb(webResponse.body);
|
|
315
|
-
if (shouldCompress) pipeline(nodeStream, createCompressor(encoding), res, () => {});
|
|
398
|
+
if (shouldCompress) pipeline(nodeStream, createCompressor(encoding, "streaming"), res, () => {});
|
|
316
399
|
else pipeline(nodeStream, res, () => {});
|
|
317
400
|
}
|
|
318
401
|
/**
|
|
@@ -698,11 +781,16 @@ async function startPagesRouterServer(options) {
|
|
|
698
781
|
let response;
|
|
699
782
|
if (typeof handleApi === "function") response = await handleApi(webRequest, resolvedUrl);
|
|
700
783
|
else response = new Response("404 - API route not found", { status: 404 });
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
784
|
+
const mergedResponse = mergeWebResponse(middlewareHeaders, response, middlewareRewriteStatus);
|
|
785
|
+
if (!mergedResponse.body) {
|
|
786
|
+
await sendWebResponse(mergedResponse, req, res, compress);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const responseBody = Buffer.from(await mergedResponse.arrayBuffer());
|
|
790
|
+
const ct = mergedResponse.headers.get("content-type") ?? "application/octet-stream";
|
|
791
|
+
const responseHeaders = mergeResponseHeaders({}, mergedResponse);
|
|
792
|
+
const finalStatusText = mergedResponse.statusText || void 0;
|
|
793
|
+
sendCompressed(req, res, responseBody, ct, mergedResponse.status, responseHeaders, compress, finalStatusText);
|
|
706
794
|
return;
|
|
707
795
|
}
|
|
708
796
|
if (configRewrites.afterFiles?.length) {
|
|
@@ -735,11 +823,17 @@ async function startPagesRouterServer(options) {
|
|
|
735
823
|
res.end("404 - Not found");
|
|
736
824
|
return;
|
|
737
825
|
}
|
|
738
|
-
const
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
826
|
+
const shouldStreamPagesResponse = isVinextStreamedHtmlResponse(response);
|
|
827
|
+
const mergedResponse = mergeWebResponse(middlewareHeaders, response, middlewareRewriteStatus);
|
|
828
|
+
if (shouldStreamPagesResponse || !mergedResponse.body) {
|
|
829
|
+
await sendWebResponse(mergedResponse, req, res, compress);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
const responseBody = Buffer.from(await mergedResponse.arrayBuffer());
|
|
833
|
+
const ct = mergedResponse.headers.get("content-type") ?? "text/html";
|
|
834
|
+
const responseHeaders = mergeResponseHeaders({}, mergedResponse);
|
|
835
|
+
const finalStatusText = mergedResponse.statusText || void 0;
|
|
836
|
+
sendCompressed(req, res, responseBody, ct, mergedResponse.status, responseHeaders, compress, finalStatusText);
|
|
743
837
|
} catch (e) {
|
|
744
838
|
console.error("[vinext] Server error:", e);
|
|
745
839
|
if (!res.headersSent) {
|
|
@@ -763,6 +857,6 @@ async function startPagesRouterServer(options) {
|
|
|
763
857
|
};
|
|
764
858
|
}
|
|
765
859
|
//#endregion
|
|
766
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, mergeResponseHeaders, negotiateEncoding, nodeToWebRequest, resolveHost, sendCompressed, startProdServer, trustProxy, trustedHosts };
|
|
860
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveHost, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts };
|
|
767
861
|
|
|
768
862
|
//# sourceMappingURL=prod-server.js.map
|