vinext 0.0.39 → 0.0.41
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 -1
- package/dist/build/standalone.js +7 -0
- package/dist/build/standalone.js.map +1 -1
- package/dist/check.js +2 -2
- package/dist/check.js.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/entries/app-rsc-entry.d.ts +2 -1
- package/dist/entries/app-rsc-entry.js +185 -264
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +205 -199
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +81 -6
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +1 -1
- package/dist/init.js +2 -2
- package/dist/init.js.map +1 -1
- package/dist/plugins/fonts.js +1 -0
- package/dist/plugins/fonts.js.map +1 -1
- package/dist/plugins/server-externals-manifest.d.ts +11 -1
- package/dist/plugins/server-externals-manifest.js +10 -3
- package/dist/plugins/server-externals-manifest.js.map +1 -1
- package/dist/routing/app-router.d.ts +10 -2
- package/dist/routing/app-router.js +37 -22
- package/dist/routing/app-router.js.map +1 -1
- package/dist/server/app-page-boundary-render.d.ts +1 -0
- package/dist/server/app-page-boundary-render.js +1 -0
- package/dist/server/app-page-boundary-render.js.map +1 -1
- package/dist/server/app-page-render.d.ts +1 -0
- package/dist/server/app-page-render.js +2 -0
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-response.d.ts +4 -1
- package/dist/server/app-page-response.js +14 -8
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-route-wiring.d.ts +79 -0
- package/dist/server/app-page-route-wiring.js +167 -0
- package/dist/server/app-page-route-wiring.js.map +1 -0
- package/dist/server/app-page-stream.d.ts +4 -1
- package/dist/server/app-page-stream.js +5 -1
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-route-handler-response.js +6 -2
- package/dist/server/app-route-handler-response.js.map +1 -1
- package/dist/server/app-router-entry.d.ts +6 -1
- package/dist/server/app-router-entry.js +9 -2
- package/dist/server/app-router-entry.js.map +1 -1
- package/dist/server/app-ssr-entry.d.ts +3 -1
- package/dist/server/app-ssr-entry.js +17 -17
- package/dist/server/app-ssr-entry.js.map +1 -1
- package/dist/server/app-ssr-stream.d.ts +1 -1
- package/dist/server/app-ssr-stream.js +4 -4
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/csp.d.ts +12 -0
- package/dist/server/csp.js +46 -0
- package/dist/server/csp.js.map +1 -0
- package/dist/server/dev-server.js +20 -14
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/html.d.ts +4 -1
- package/dist/server/html.js +11 -1
- package/dist/server/html.js.map +1 -1
- package/dist/server/middleware-response-headers.d.ts +12 -0
- package/dist/server/middleware-response-headers.js +23 -0
- package/dist/server/middleware-response-headers.js.map +1 -0
- package/dist/server/pages-page-data.d.ts +1 -0
- package/dist/server/pages-page-data.js +2 -2
- package/dist/server/pages-page-data.js.map +1 -1
- package/dist/server/pages-page-response.d.ts +2 -1
- package/dist/server/pages-page-response.js +16 -14
- package/dist/server/pages-page-response.js.map +1 -1
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.js +41 -14
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/request-pipeline.d.ts +14 -1
- package/dist/server/request-pipeline.js +55 -1
- package/dist/server/request-pipeline.js.map +1 -1
- package/dist/server/worker-utils.d.ts +4 -1
- package/dist/server/worker-utils.js +31 -1
- package/dist/server/worker-utils.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +14 -5
- package/dist/shims/error-boundary.js +23 -3
- package/dist/shims/error-boundary.js.map +1 -1
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/navigation.d.ts +16 -1
- package/dist/shims/navigation.js +18 -3
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/router.js +127 -38
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/script-nonce-context.d.ts +12 -0
- package/dist/shims/script-nonce-context.js +17 -0
- package/dist/shims/script-nonce-context.js.map +1 -0
- package/dist/shims/script.js +41 -10
- package/dist/shims/script.js.map +1 -1
- package/dist/shims/server.d.ts +17 -4
- package/dist/shims/server.js +97 -74
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/slot.d.ts +28 -0
- package/dist/shims/slot.js +49 -0
- package/dist/shims/slot.js.map +1 -0
- package/dist/shims/url-safety.js +25 -4
- package/dist/shims/url-safety.js.map +1 -1
- package/package.json +7 -8
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
1
2
|
//#region src/server/app-page-response.ts
|
|
2
3
|
const STATIC_CACHE_CONTROL = "s-maxage=31536000, stale-while-revalidate";
|
|
3
4
|
const NO_STORE_CACHE_CONTROL = "no-store, must-revalidate";
|
|
@@ -13,6 +14,7 @@ function applyTimingHeader(headers, timing) {
|
|
|
13
14
|
}
|
|
14
15
|
function resolveAppPageRscResponsePolicy(options) {
|
|
15
16
|
if (options.isForceDynamic || options.dynamicUsedDuringBuild) return { cacheControl: NO_STORE_CACHE_CONTROL };
|
|
17
|
+
if (options.revalidateSeconds === 0) return { cacheControl: NO_STORE_CACHE_CONTROL };
|
|
16
18
|
if ((options.isForceStatic || options.isDynamicError) && !options.revalidateSeconds || options.revalidateSeconds === Infinity) return {
|
|
17
19
|
cacheControl: STATIC_CACHE_CONTROL,
|
|
18
20
|
cacheState: "STATIC"
|
|
@@ -28,7 +30,15 @@ function resolveAppPageHtmlResponsePolicy(options) {
|
|
|
28
30
|
cacheControl: NO_STORE_CACHE_CONTROL,
|
|
29
31
|
shouldWriteToCache: false
|
|
30
32
|
};
|
|
31
|
-
if (
|
|
33
|
+
if (options.hasScriptNonce) return {
|
|
34
|
+
cacheControl: NO_STORE_CACHE_CONTROL,
|
|
35
|
+
shouldWriteToCache: false
|
|
36
|
+
};
|
|
37
|
+
if (options.revalidateSeconds === 0) return {
|
|
38
|
+
cacheControl: NO_STORE_CACHE_CONTROL,
|
|
39
|
+
shouldWriteToCache: false
|
|
40
|
+
};
|
|
41
|
+
if ((options.isForceStatic || options.isDynamicError) && options.revalidateSeconds === null) return {
|
|
32
42
|
cacheControl: STATIC_CACHE_CONTROL,
|
|
33
43
|
cacheState: "STATIC",
|
|
34
44
|
shouldWriteToCache: false
|
|
@@ -57,11 +67,7 @@ function buildAppPageRscResponse(body, options) {
|
|
|
57
67
|
if (options.params && Object.keys(options.params).length > 0) headers.set("X-Vinext-Params", encodeURIComponent(JSON.stringify(options.params)));
|
|
58
68
|
if (options.policy.cacheControl) headers.set("Cache-Control", options.policy.cacheControl);
|
|
59
69
|
if (options.policy.cacheState) headers.set("X-Vinext-Cache", options.policy.cacheState);
|
|
60
|
-
|
|
61
|
-
const lowerKey = key.toLowerCase();
|
|
62
|
-
if (lowerKey === "set-cookie" || lowerKey === "vary") headers.append(key, value);
|
|
63
|
-
else headers.set(key, value);
|
|
64
|
-
}
|
|
70
|
+
mergeMiddlewareResponseHeaders(headers, options.middlewareContext.headers);
|
|
65
71
|
applyTimingHeader(headers, options.timing);
|
|
66
72
|
return new Response(body, {
|
|
67
73
|
status: options.middlewareContext.status ?? 200,
|
|
@@ -77,7 +83,7 @@ function buildAppPageHtmlResponse(body, options) {
|
|
|
77
83
|
if (options.policy.cacheState) headers.set("X-Vinext-Cache", options.policy.cacheState);
|
|
78
84
|
if (options.draftCookie) headers.append("Set-Cookie", options.draftCookie);
|
|
79
85
|
if (options.fontLinkHeader) headers.set("Link", options.fontLinkHeader);
|
|
80
|
-
|
|
86
|
+
mergeMiddlewareResponseHeaders(headers, options.middlewareContext.headers);
|
|
81
87
|
applyTimingHeader(headers, options.timing);
|
|
82
88
|
return new Response(body, {
|
|
83
89
|
status: options.middlewareContext.status ?? 200,
|
|
@@ -85,6 +91,6 @@ function buildAppPageHtmlResponse(body, options) {
|
|
|
85
91
|
});
|
|
86
92
|
}
|
|
87
93
|
//#endregion
|
|
88
|
-
export { buildAppPageHtmlResponse, buildAppPageRscResponse, resolveAppPageHtmlResponsePolicy, resolveAppPageRscResponsePolicy };
|
|
94
|
+
export { buildAppPageHtmlResponse, buildAppPageRscResponse, mergeMiddlewareResponseHeaders, resolveAppPageHtmlResponsePolicy, resolveAppPageRscResponsePolicy };
|
|
89
95
|
|
|
90
96
|
//# sourceMappingURL=app-page-response.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-page-response.js","names":[],"sources":["../../src/server/app-page-response.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"app-page-response.js","names":[],"sources":["../../src/server/app-page-response.ts"],"sourcesContent":["import { mergeMiddlewareResponseHeaders } from \"./middleware-response-headers.js\";\n\nexport type AppPageMiddlewareContext = {\n headers: Headers | null;\n status: number | null;\n};\n\nexport type AppPageResponseTiming = {\n compileEnd?: number;\n handlerStart: number;\n renderEnd?: number;\n responseKind: \"html\" | \"rsc\";\n};\n\nexport type AppPageResponsePolicy = {\n cacheControl?: string;\n cacheState?: \"MISS\" | \"STATIC\";\n};\n\ntype ResolveAppPageResponsePolicyBaseOptions = {\n isDynamicError: boolean;\n isForceDynamic: boolean;\n isForceStatic: boolean;\n isProduction: boolean;\n revalidateSeconds: number | null;\n};\n\nexport type ResolveAppPageRscResponsePolicyOptions = {\n dynamicUsedDuringBuild: boolean;\n} & ResolveAppPageResponsePolicyBaseOptions;\n\nexport type ResolveAppPageHtmlResponsePolicyOptions = {\n dynamicUsedDuringRender: boolean;\n hasScriptNonce: boolean;\n} & ResolveAppPageResponsePolicyBaseOptions;\n\nexport type AppPageHtmlResponsePolicy = {\n shouldWriteToCache: boolean;\n} & AppPageResponsePolicy;\n\nexport type BuildAppPageRscResponseOptions = {\n middlewareContext: AppPageMiddlewareContext;\n params?: Record<string, unknown>;\n policy: AppPageResponsePolicy;\n timing?: AppPageResponseTiming;\n};\n\nexport type BuildAppPageHtmlResponseOptions = {\n draftCookie?: string | null;\n fontLinkHeader?: string;\n middlewareContext: AppPageMiddlewareContext;\n policy: AppPageResponsePolicy;\n timing?: AppPageResponseTiming;\n};\n\nconst STATIC_CACHE_CONTROL = \"s-maxage=31536000, stale-while-revalidate\";\nconst NO_STORE_CACHE_CONTROL = \"no-store, must-revalidate\";\n\nfunction buildRevalidateCacheControl(revalidateSeconds: number): string {\n return `s-maxage=${revalidateSeconds}, stale-while-revalidate`;\n}\n\nfunction applyTimingHeader(headers: Headers, timing?: AppPageResponseTiming): void {\n if (!timing) {\n return;\n }\n\n const handlerStart = Math.round(timing.handlerStart);\n const compileMs =\n timing.compileEnd !== undefined ? Math.round(timing.compileEnd - timing.handlerStart) : -1;\n const renderMs =\n timing.responseKind === \"html\" &&\n timing.renderEnd !== undefined &&\n timing.compileEnd !== undefined\n ? Math.round(timing.renderEnd - timing.compileEnd)\n : -1;\n\n headers.set(\"x-vinext-timing\", `${handlerStart},${compileMs},${renderMs}`);\n}\n\nexport function resolveAppPageRscResponsePolicy(\n options: ResolveAppPageRscResponsePolicyOptions,\n): AppPageResponsePolicy {\n if (options.isForceDynamic || options.dynamicUsedDuringBuild) {\n return { cacheControl: NO_STORE_CACHE_CONTROL };\n }\n\n // revalidate = 0 means \"always dynamic, never cache\" — equivalent to\n // force-dynamic for caching purposes. Must be checked before the\n // isForceStatic/isDynamicError branch below, which uses !revalidateSeconds\n // and would incorrectly catch 0 as a falsy value.\n if (options.revalidateSeconds === 0) {\n return { cacheControl: NO_STORE_CACHE_CONTROL };\n }\n\n if (\n ((options.isForceStatic || options.isDynamicError) && !options.revalidateSeconds) ||\n options.revalidateSeconds === Infinity\n ) {\n return {\n cacheControl: STATIC_CACHE_CONTROL,\n cacheState: \"STATIC\",\n };\n }\n\n if (options.revalidateSeconds) {\n return {\n cacheControl: buildRevalidateCacheControl(options.revalidateSeconds),\n // Emit MISS as part of the initial RSC response shape rather than bolting\n // it on later in the cache-write block so response construction stays\n // centralized in this helper. This matches the eventual write path: the\n // first ISR-eligible production response is a cache miss.\n cacheState: options.isProduction ? \"MISS\" : undefined,\n };\n }\n\n return {};\n}\n\nexport function resolveAppPageHtmlResponsePolicy(\n options: ResolveAppPageHtmlResponsePolicyOptions,\n): AppPageHtmlResponsePolicy {\n if (options.isForceDynamic) {\n return {\n cacheControl: NO_STORE_CACHE_CONTROL,\n shouldWriteToCache: false,\n };\n }\n\n if (options.hasScriptNonce) {\n return {\n cacheControl: NO_STORE_CACHE_CONTROL,\n shouldWriteToCache: false,\n };\n }\n\n // revalidate = 0 means \"always dynamic, never cache\" — equivalent to\n // force-dynamic for caching purposes. Must be checked before the\n // isForceStatic/isDynamicError branch below, which matches revalidateSeconds\n // === 0 and would incorrectly return a static Cache-Control.\n if (options.revalidateSeconds === 0) {\n return {\n cacheControl: NO_STORE_CACHE_CONTROL,\n shouldWriteToCache: false,\n };\n }\n\n if ((options.isForceStatic || options.isDynamicError) && options.revalidateSeconds === null) {\n return {\n cacheControl: STATIC_CACHE_CONTROL,\n cacheState: \"STATIC\",\n shouldWriteToCache: false,\n };\n }\n\n if (options.dynamicUsedDuringRender) {\n return {\n cacheControl: NO_STORE_CACHE_CONTROL,\n shouldWriteToCache: false,\n };\n }\n\n if (\n options.revalidateSeconds !== null &&\n options.revalidateSeconds > 0 &&\n options.revalidateSeconds !== Infinity\n ) {\n return {\n cacheControl: buildRevalidateCacheControl(options.revalidateSeconds),\n cacheState: options.isProduction ? \"MISS\" : undefined,\n shouldWriteToCache: options.isProduction,\n };\n }\n\n if (options.revalidateSeconds === Infinity) {\n return {\n cacheControl: STATIC_CACHE_CONTROL,\n cacheState: \"STATIC\",\n shouldWriteToCache: false,\n };\n }\n\n return { shouldWriteToCache: false };\n}\n\nexport { mergeMiddlewareResponseHeaders };\n\nexport function buildAppPageRscResponse(\n body: ReadableStream,\n options: BuildAppPageRscResponseOptions,\n): Response {\n const headers = new Headers({\n \"Content-Type\": \"text/x-component; charset=utf-8\",\n Vary: \"RSC, Accept\",\n });\n\n if (options.params && Object.keys(options.params).length > 0) {\n // encodeURIComponent so non-ASCII params (e.g. Korean slugs) survive the\n // HTTP ByteString constraint — Headers.set() rejects chars above U+00FF.\n headers.set(\"X-Vinext-Params\", encodeURIComponent(JSON.stringify(options.params)));\n }\n if (options.policy.cacheControl) {\n headers.set(\"Cache-Control\", options.policy.cacheControl);\n }\n if (options.policy.cacheState) {\n headers.set(\"X-Vinext-Cache\", options.policy.cacheState);\n }\n\n mergeMiddlewareResponseHeaders(headers, options.middlewareContext.headers);\n\n applyTimingHeader(headers, options.timing);\n\n return new Response(body, {\n status: options.middlewareContext.status ?? 200,\n headers,\n });\n}\n\nexport function buildAppPageHtmlResponse(\n body: ReadableStream,\n options: BuildAppPageHtmlResponseOptions,\n): Response {\n const headers = new Headers({\n \"Content-Type\": \"text/html; charset=utf-8\",\n Vary: \"RSC, Accept\",\n });\n\n if (options.policy.cacheControl) {\n headers.set(\"Cache-Control\", options.policy.cacheControl);\n }\n if (options.policy.cacheState) {\n headers.set(\"X-Vinext-Cache\", options.policy.cacheState);\n }\n if (options.draftCookie) {\n headers.append(\"Set-Cookie\", options.draftCookie);\n }\n if (options.fontLinkHeader) {\n headers.set(\"Link\", options.fontLinkHeader);\n }\n\n mergeMiddlewareResponseHeaders(headers, options.middlewareContext.headers);\n\n applyTimingHeader(headers, options.timing);\n\n return new Response(body, {\n status: options.middlewareContext.status ?? 200,\n headers,\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { AppPageParams } from "./app-page-boundary.js";
|
|
2
|
+
import { Metadata, Viewport } from "../shims/metadata.js";
|
|
3
|
+
import { ComponentType, ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/server/app-page-route-wiring.d.ts
|
|
6
|
+
type AppPageComponentProps = {
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
error?: Error;
|
|
9
|
+
params?: unknown;
|
|
10
|
+
reset?: () => void;
|
|
11
|
+
} & Record<string, unknown>;
|
|
12
|
+
type AppPageComponent = ComponentType<AppPageComponentProps>;
|
|
13
|
+
type AppPageErrorComponent = ComponentType<{
|
|
14
|
+
error: Error;
|
|
15
|
+
reset: () => void;
|
|
16
|
+
}>;
|
|
17
|
+
type AppPageModule = Record<string, unknown> & {
|
|
18
|
+
default?: AppPageComponent | null | undefined;
|
|
19
|
+
};
|
|
20
|
+
type AppPageErrorModule = Record<string, unknown> & {
|
|
21
|
+
default?: AppPageErrorComponent | null | undefined;
|
|
22
|
+
};
|
|
23
|
+
type AppPageRouteWiringSlot<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule> = {
|
|
24
|
+
default?: TModule | null;
|
|
25
|
+
error?: TErrorModule | null;
|
|
26
|
+
layout?: TModule | null;
|
|
27
|
+
layoutIndex: number;
|
|
28
|
+
loading?: TModule | null;
|
|
29
|
+
page?: TModule | null;
|
|
30
|
+
/**
|
|
31
|
+
* Filesystem segments from the slot's root to its active page.
|
|
32
|
+
* Used to populate the LayoutSegmentProvider's segmentMap for this slot.
|
|
33
|
+
* null when the slot has no active page (showing default.tsx fallback).
|
|
34
|
+
*/
|
|
35
|
+
routeSegments?: readonly string[] | null;
|
|
36
|
+
};
|
|
37
|
+
type AppPageRouteWiringRoute<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule> = {
|
|
38
|
+
error?: TErrorModule | null;
|
|
39
|
+
errors?: readonly (TErrorModule | null | undefined)[] | null;
|
|
40
|
+
layoutTreePositions?: readonly number[] | null;
|
|
41
|
+
layouts: readonly (TModule | null | undefined)[];
|
|
42
|
+
loading?: TModule | null;
|
|
43
|
+
notFound?: TModule | null;
|
|
44
|
+
notFounds?: readonly (TModule | null | undefined)[] | null;
|
|
45
|
+
routeSegments?: readonly string[];
|
|
46
|
+
slots?: Readonly<Record<string, AppPageRouteWiringSlot<TModule, TErrorModule>>> | null;
|
|
47
|
+
templates?: readonly (TModule | null | undefined)[] | null;
|
|
48
|
+
};
|
|
49
|
+
type AppPageSlotOverride<TModule extends AppPageModule = AppPageModule> = {
|
|
50
|
+
pageModule: TModule;
|
|
51
|
+
params?: AppPageParams;
|
|
52
|
+
props?: Readonly<Record<string, unknown>>;
|
|
53
|
+
};
|
|
54
|
+
type AppPageLayoutEntry<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule> = {
|
|
55
|
+
errorModule?: TErrorModule | null | undefined;
|
|
56
|
+
id: string;
|
|
57
|
+
layoutModule?: TModule | null | undefined;
|
|
58
|
+
notFoundModule?: TModule | null | undefined;
|
|
59
|
+
treePath: string;
|
|
60
|
+
treePosition: number;
|
|
61
|
+
};
|
|
62
|
+
type BuildAppPageRouteElementOptions<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule> = {
|
|
63
|
+
element: ReactNode;
|
|
64
|
+
globalErrorModule?: TErrorModule | null;
|
|
65
|
+
makeThenableParams: (params: AppPageParams) => unknown;
|
|
66
|
+
matchedParams: AppPageParams;
|
|
67
|
+
resolvedMetadata: Metadata | null;
|
|
68
|
+
resolvedViewport: Viewport;
|
|
69
|
+
rootNotFoundModule?: TModule | null;
|
|
70
|
+
route: AppPageRouteWiringRoute<TModule, TErrorModule>;
|
|
71
|
+
slotOverrides?: Readonly<Record<string, AppPageSlotOverride<TModule>>> | null;
|
|
72
|
+
};
|
|
73
|
+
declare function createAppPageTreePath(routeSegments: readonly string[] | null | undefined, treePosition: number): string;
|
|
74
|
+
declare function createAppPageLayoutEntries<TModule extends AppPageModule, TErrorModule extends AppPageErrorModule>(route: Pick<AppPageRouteWiringRoute<TModule, TErrorModule>, "errors" | "layoutTreePositions" | "layouts" | "notFounds" | "routeSegments">): AppPageLayoutEntry<TModule, TErrorModule>[];
|
|
75
|
+
declare function resolveAppPageChildSegments(routeSegments: readonly string[], treePosition: number, params: Readonly<Record<string, string | string[] | undefined>>): string[];
|
|
76
|
+
declare function buildAppPageRouteElement<TModule extends AppPageModule, TErrorModule extends AppPageErrorModule>(options: BuildAppPageRouteElementOptions<TModule, TErrorModule>): ReactNode;
|
|
77
|
+
//#endregion
|
|
78
|
+
export { AppPageErrorModule, AppPageLayoutEntry, AppPageModule, AppPageRouteWiringRoute, AppPageRouteWiringSlot, AppPageSlotOverride, BuildAppPageRouteElementOptions, buildAppPageRouteElement, createAppPageLayoutEntries, createAppPageTreePath, resolveAppPageChildSegments };
|
|
79
|
+
//# sourceMappingURL=app-page-route-wiring.d.ts.map
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { ErrorBoundary, NotFoundBoundary } from "../shims/error-boundary.js";
|
|
2
|
+
import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
|
|
3
|
+
import { MetadataHead, ViewportHead } from "../shims/metadata.js";
|
|
4
|
+
import { Suspense } from "react";
|
|
5
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/server/app-page-route-wiring.tsx
|
|
7
|
+
function getDefaultExport(module) {
|
|
8
|
+
return module?.default ?? null;
|
|
9
|
+
}
|
|
10
|
+
function getErrorBoundaryExport(module) {
|
|
11
|
+
return module?.default ?? null;
|
|
12
|
+
}
|
|
13
|
+
function createAppPageTreePath(routeSegments, treePosition) {
|
|
14
|
+
const treePathSegments = routeSegments?.slice(0, treePosition) ?? [];
|
|
15
|
+
if (treePathSegments.length === 0) return "/";
|
|
16
|
+
return `/${treePathSegments.join("/")}`;
|
|
17
|
+
}
|
|
18
|
+
function createAppPageLayoutEntries(route) {
|
|
19
|
+
return route.layouts.map((layoutModule, index) => {
|
|
20
|
+
const treePosition = route.layoutTreePositions?.[index] ?? 0;
|
|
21
|
+
const treePath = createAppPageTreePath(route.routeSegments, treePosition);
|
|
22
|
+
return {
|
|
23
|
+
errorModule: route.errors?.[index] ?? null,
|
|
24
|
+
id: `layout:${treePath}`,
|
|
25
|
+
layoutModule,
|
|
26
|
+
notFoundModule: route.notFounds?.[index] ?? null,
|
|
27
|
+
treePath,
|
|
28
|
+
treePosition
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function resolveAppPageChildSegments(routeSegments, treePosition, params) {
|
|
33
|
+
const rawSegments = routeSegments.slice(treePosition);
|
|
34
|
+
const resolvedSegments = [];
|
|
35
|
+
for (const segment of rawSegments) {
|
|
36
|
+
if (segment.startsWith("[[...") && segment.endsWith("]]") && segment.length >= 8) {
|
|
37
|
+
const paramValue = params[segment.slice(5, -2)];
|
|
38
|
+
if (Array.isArray(paramValue) && paramValue.length === 0) continue;
|
|
39
|
+
if (paramValue === void 0) continue;
|
|
40
|
+
resolvedSegments.push(Array.isArray(paramValue) ? paramValue.join("/") : paramValue);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (segment.startsWith("[...") && segment.endsWith("]")) {
|
|
44
|
+
const paramValue = params[segment.slice(4, -1)];
|
|
45
|
+
if (Array.isArray(paramValue)) {
|
|
46
|
+
resolvedSegments.push(paramValue.join("/"));
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
resolvedSegments.push(paramValue ?? segment);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (segment.startsWith("[") && segment.endsWith("]") && !segment.includes(".")) {
|
|
53
|
+
const paramValue = params[segment.slice(1, -1)];
|
|
54
|
+
resolvedSegments.push(Array.isArray(paramValue) ? paramValue.join("/") : paramValue ?? segment);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
resolvedSegments.push(segment);
|
|
58
|
+
}
|
|
59
|
+
return resolvedSegments;
|
|
60
|
+
}
|
|
61
|
+
function buildAppPageRouteElement(options) {
|
|
62
|
+
let element = /* @__PURE__ */ jsx(LayoutSegmentProvider, {
|
|
63
|
+
segmentMap: { children: [] },
|
|
64
|
+
children: options.element
|
|
65
|
+
});
|
|
66
|
+
element = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
67
|
+
/* @__PURE__ */ jsx("meta", { charSet: "utf-8" }),
|
|
68
|
+
options.resolvedMetadata ? /* @__PURE__ */ jsx(MetadataHead, { metadata: options.resolvedMetadata }) : null,
|
|
69
|
+
/* @__PURE__ */ jsx(ViewportHead, { viewport: options.resolvedViewport }),
|
|
70
|
+
element
|
|
71
|
+
] });
|
|
72
|
+
const loadingComponent = getDefaultExport(options.route.loading);
|
|
73
|
+
if (loadingComponent) element = /* @__PURE__ */ jsx(Suspense, {
|
|
74
|
+
fallback: /* @__PURE__ */ jsx(loadingComponent, {}),
|
|
75
|
+
children: element
|
|
76
|
+
});
|
|
77
|
+
const lastLayoutErrorModule = options.route.errors && options.route.errors.length > 0 ? options.route.errors[options.route.errors.length - 1] : null;
|
|
78
|
+
const pageErrorComponent = getErrorBoundaryExport(options.route.error);
|
|
79
|
+
if (pageErrorComponent && options.route.error !== lastLayoutErrorModule) element = /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
80
|
+
fallback: pageErrorComponent,
|
|
81
|
+
children: element
|
|
82
|
+
});
|
|
83
|
+
const notFoundComponent = getDefaultExport(options.route.notFound) ?? getDefaultExport(options.rootNotFoundModule);
|
|
84
|
+
if (notFoundComponent) element = /* @__PURE__ */ jsx(NotFoundBoundary, {
|
|
85
|
+
fallback: /* @__PURE__ */ jsx(notFoundComponent, {}),
|
|
86
|
+
children: element
|
|
87
|
+
});
|
|
88
|
+
const templates = options.route.templates ?? [];
|
|
89
|
+
const routeSlots = options.route.slots ?? {};
|
|
90
|
+
const layoutEntries = createAppPageLayoutEntries(options.route);
|
|
91
|
+
const routeThenableParams = options.makeThenableParams(options.matchedParams);
|
|
92
|
+
const getEffectiveSlotParams = (slotName) => options.slotOverrides?.[slotName]?.params ?? options.matchedParams;
|
|
93
|
+
for (let index = layoutEntries.length - 1; index >= 0; index--) {
|
|
94
|
+
const layoutEntry = layoutEntries[index];
|
|
95
|
+
const layoutNotFoundComponent = getDefaultExport(layoutEntry.notFoundModule);
|
|
96
|
+
if (layoutNotFoundComponent) element = /* @__PURE__ */ jsx(NotFoundBoundary, {
|
|
97
|
+
fallback: /* @__PURE__ */ jsx(layoutNotFoundComponent, {}),
|
|
98
|
+
children: element
|
|
99
|
+
});
|
|
100
|
+
const layoutErrorComponent = getErrorBoundaryExport(layoutEntry.errorModule);
|
|
101
|
+
if (layoutErrorComponent) element = /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
102
|
+
fallback: layoutErrorComponent,
|
|
103
|
+
children: element
|
|
104
|
+
});
|
|
105
|
+
const templateComponent = getDefaultExport(templates[index]);
|
|
106
|
+
if (templateComponent) element = /* @__PURE__ */ jsx(templateComponent, {
|
|
107
|
+
params: options.matchedParams,
|
|
108
|
+
children: element
|
|
109
|
+
});
|
|
110
|
+
const layoutComponent = getDefaultExport(layoutEntry.layoutModule);
|
|
111
|
+
if (!layoutComponent) continue;
|
|
112
|
+
const layoutProps = { params: routeThenableParams };
|
|
113
|
+
for (const [slotName, slot] of Object.entries(routeSlots)) {
|
|
114
|
+
const targetIndex = slot.layoutIndex >= 0 ? slot.layoutIndex : layoutEntries.length - 1;
|
|
115
|
+
if (index !== targetIndex) continue;
|
|
116
|
+
const slotOverride = options.slotOverrides?.[slotName];
|
|
117
|
+
const slotParams = getEffectiveSlotParams(slotName);
|
|
118
|
+
const slotComponent = getDefaultExport(slotOverride?.pageModule) ?? getDefaultExport(slot.page) ?? getDefaultExport(slot.default);
|
|
119
|
+
if (!slotComponent) continue;
|
|
120
|
+
const slotProps = { params: options.makeThenableParams(slotParams) };
|
|
121
|
+
if (slotOverride?.props) Object.assign(slotProps, slotOverride.props);
|
|
122
|
+
let slotElement = /* @__PURE__ */ jsx(slotComponent, { ...slotProps });
|
|
123
|
+
const slotLayoutComponent = getDefaultExport(slot.layout);
|
|
124
|
+
if (slotLayoutComponent) slotElement = /* @__PURE__ */ jsx(slotLayoutComponent, {
|
|
125
|
+
params: options.makeThenableParams(slotParams),
|
|
126
|
+
children: slotElement
|
|
127
|
+
});
|
|
128
|
+
const slotLoadingComponent = getDefaultExport(slot.loading);
|
|
129
|
+
if (slotLoadingComponent) slotElement = /* @__PURE__ */ jsx(Suspense, {
|
|
130
|
+
fallback: /* @__PURE__ */ jsx(slotLoadingComponent, {}),
|
|
131
|
+
children: slotElement
|
|
132
|
+
});
|
|
133
|
+
const slotErrorComponent = getErrorBoundaryExport(slot.error);
|
|
134
|
+
if (slotErrorComponent) slotElement = /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
135
|
+
fallback: slotErrorComponent,
|
|
136
|
+
children: slotElement
|
|
137
|
+
});
|
|
138
|
+
layoutProps[slotName] = slotElement;
|
|
139
|
+
}
|
|
140
|
+
element = /* @__PURE__ */ jsx(layoutComponent, {
|
|
141
|
+
...layoutProps,
|
|
142
|
+
children: element
|
|
143
|
+
});
|
|
144
|
+
const segmentMap = { children: resolveAppPageChildSegments(options.route.routeSegments ?? [], layoutEntry.treePosition, options.matchedParams) };
|
|
145
|
+
for (const [slotName, slot] of Object.entries(routeSlots)) {
|
|
146
|
+
const targetIndex = slot.layoutIndex >= 0 ? slot.layoutIndex : layoutEntries.length - 1;
|
|
147
|
+
if (index !== targetIndex) continue;
|
|
148
|
+
const slotParams = getEffectiveSlotParams(slotName);
|
|
149
|
+
if (slot.routeSegments) segmentMap[slotName] = resolveAppPageChildSegments(slot.routeSegments, 0, slotParams);
|
|
150
|
+
else segmentMap[slotName] = [];
|
|
151
|
+
}
|
|
152
|
+
element = /* @__PURE__ */ jsx(LayoutSegmentProvider, {
|
|
153
|
+
segmentMap,
|
|
154
|
+
children: element
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
const globalErrorComponent = getErrorBoundaryExport(options.globalErrorModule);
|
|
158
|
+
if (globalErrorComponent) element = /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
159
|
+
fallback: globalErrorComponent,
|
|
160
|
+
children: element
|
|
161
|
+
});
|
|
162
|
+
return element;
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
export { buildAppPageRouteElement, createAppPageLayoutEntries, createAppPageTreePath, resolveAppPageChildSegments };
|
|
166
|
+
|
|
167
|
+
//# sourceMappingURL=app-page-route-wiring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-page-route-wiring.js","names":[],"sources":["../../src/server/app-page-route-wiring.tsx"],"sourcesContent":["import { Suspense, type ComponentType, type ReactNode } from \"react\";\nimport { ErrorBoundary, NotFoundBoundary } from \"../shims/error-boundary.js\";\nimport { LayoutSegmentProvider } from \"../shims/layout-segment-context.js\";\nimport { MetadataHead, ViewportHead, type Metadata, type Viewport } from \"../shims/metadata.js\";\nimport type { AppPageParams } from \"./app-page-boundary.js\";\n\ntype AppPageComponentProps = {\n children?: ReactNode;\n error?: Error;\n params?: unknown;\n reset?: () => void;\n} & Record<string, unknown>;\n\ntype AppPageComponent = ComponentType<AppPageComponentProps>;\ntype AppPageErrorComponent = ComponentType<{ error: Error; reset: () => void }>;\n\nexport type AppPageModule = Record<string, unknown> & {\n default?: AppPageComponent | null | undefined;\n};\n\nexport type AppPageErrorModule = Record<string, unknown> & {\n default?: AppPageErrorComponent | null | undefined;\n};\n\nexport type AppPageRouteWiringSlot<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n> = {\n default?: TModule | null;\n error?: TErrorModule | null;\n layout?: TModule | null;\n layoutIndex: number;\n loading?: TModule | null;\n page?: TModule | null;\n /**\n * Filesystem segments from the slot's root to its active page.\n * Used to populate the LayoutSegmentProvider's segmentMap for this slot.\n * null when the slot has no active page (showing default.tsx fallback).\n */\n routeSegments?: readonly string[] | null;\n};\n\nexport type AppPageRouteWiringRoute<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n> = {\n error?: TErrorModule | null;\n errors?: readonly (TErrorModule | null | undefined)[] | null;\n layoutTreePositions?: readonly number[] | null;\n layouts: readonly (TModule | null | undefined)[];\n loading?: TModule | null;\n notFound?: TModule | null;\n notFounds?: readonly (TModule | null | undefined)[] | null;\n routeSegments?: readonly string[];\n slots?: Readonly<Record<string, AppPageRouteWiringSlot<TModule, TErrorModule>>> | null;\n templates?: readonly (TModule | null | undefined)[] | null;\n};\n\nexport type AppPageSlotOverride<TModule extends AppPageModule = AppPageModule> = {\n pageModule: TModule;\n params?: AppPageParams;\n props?: Readonly<Record<string, unknown>>;\n};\n\nexport type AppPageLayoutEntry<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n> = {\n errorModule?: TErrorModule | null | undefined;\n id: string;\n layoutModule?: TModule | null | undefined;\n notFoundModule?: TModule | null | undefined;\n treePath: string;\n treePosition: number;\n};\n\nexport type BuildAppPageRouteElementOptions<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n> = {\n element: ReactNode;\n globalErrorModule?: TErrorModule | null;\n makeThenableParams: (params: AppPageParams) => unknown;\n matchedParams: AppPageParams;\n resolvedMetadata: Metadata | null;\n resolvedViewport: Viewport;\n rootNotFoundModule?: TModule | null;\n route: AppPageRouteWiringRoute<TModule, TErrorModule>;\n slotOverrides?: Readonly<Record<string, AppPageSlotOverride<TModule>>> | null;\n};\n\nfunction getDefaultExport<TModule extends AppPageModule>(\n module: TModule | null | undefined,\n): AppPageComponent | null {\n return module?.default ?? null;\n}\n\nfunction getErrorBoundaryExport<TModule extends AppPageErrorModule>(\n module: TModule | null | undefined,\n): AppPageErrorComponent | null {\n return module?.default ?? null;\n}\n\nexport function createAppPageTreePath(\n routeSegments: readonly string[] | null | undefined,\n treePosition: number,\n): string {\n const treePathSegments = routeSegments?.slice(0, treePosition) ?? [];\n if (treePathSegments.length === 0) {\n return \"/\";\n }\n return `/${treePathSegments.join(\"/\")}`;\n}\n\nexport function createAppPageLayoutEntries<\n TModule extends AppPageModule,\n TErrorModule extends AppPageErrorModule,\n>(\n route: Pick<\n AppPageRouteWiringRoute<TModule, TErrorModule>,\n \"errors\" | \"layoutTreePositions\" | \"layouts\" | \"notFounds\" | \"routeSegments\"\n >,\n): AppPageLayoutEntry<TModule, TErrorModule>[] {\n return route.layouts.map((layoutModule, index) => {\n const treePosition = route.layoutTreePositions?.[index] ?? 0;\n const treePath = createAppPageTreePath(route.routeSegments, treePosition);\n return {\n errorModule: route.errors?.[index] ?? null,\n id: `layout:${treePath}`,\n layoutModule,\n notFoundModule: route.notFounds?.[index] ?? null,\n treePath,\n treePosition,\n };\n });\n}\n\nexport function resolveAppPageChildSegments(\n routeSegments: readonly string[],\n treePosition: number,\n params: Readonly<Record<string, string | string[] | undefined>>,\n): string[] {\n const rawSegments = routeSegments.slice(treePosition);\n const resolvedSegments: string[] = [];\n\n for (const segment of rawSegments) {\n if (\n segment.startsWith(\"[[...\") &&\n segment.endsWith(\"]]\") &&\n segment.length >= \"[[...x]]\".length\n ) {\n const paramName = segment.slice(5, -2);\n const paramValue = params[paramName];\n if (Array.isArray(paramValue) && paramValue.length === 0) {\n continue;\n }\n if (paramValue === undefined) {\n continue;\n }\n resolvedSegments.push(Array.isArray(paramValue) ? paramValue.join(\"/\") : paramValue);\n continue;\n }\n\n if (segment.startsWith(\"[...\") && segment.endsWith(\"]\")) {\n const paramName = segment.slice(4, -1);\n const paramValue = params[paramName];\n if (Array.isArray(paramValue)) {\n resolvedSegments.push(paramValue.join(\"/\"));\n continue;\n }\n resolvedSegments.push(paramValue ?? segment);\n continue;\n }\n\n if (segment.startsWith(\"[\") && segment.endsWith(\"]\") && !segment.includes(\".\")) {\n const paramName = segment.slice(1, -1);\n const paramValue = params[paramName];\n resolvedSegments.push(\n Array.isArray(paramValue) ? paramValue.join(\"/\") : (paramValue ?? segment),\n );\n continue;\n }\n\n resolvedSegments.push(segment);\n }\n\n return resolvedSegments;\n}\n\nexport function buildAppPageRouteElement<\n TModule extends AppPageModule,\n TErrorModule extends AppPageErrorModule,\n>(options: BuildAppPageRouteElementOptions<TModule, TErrorModule>): ReactNode {\n let element: ReactNode = (\n <LayoutSegmentProvider segmentMap={{ children: [] }}>{options.element}</LayoutSegmentProvider>\n );\n\n element = (\n <>\n <meta charSet=\"utf-8\" />\n {options.resolvedMetadata ? <MetadataHead metadata={options.resolvedMetadata} /> : null}\n <ViewportHead viewport={options.resolvedViewport} />\n {element}\n </>\n );\n\n const loadingComponent = getDefaultExport(options.route.loading);\n if (loadingComponent) {\n const LoadingComponent = loadingComponent;\n element = <Suspense fallback={<LoadingComponent />}>{element}</Suspense>;\n }\n\n const lastLayoutErrorModule =\n options.route.errors && options.route.errors.length > 0\n ? options.route.errors[options.route.errors.length - 1]\n : null;\n const pageErrorComponent = getErrorBoundaryExport(options.route.error);\n if (pageErrorComponent && options.route.error !== lastLayoutErrorModule) {\n element = <ErrorBoundary fallback={pageErrorComponent}>{element}</ErrorBoundary>;\n }\n\n const notFoundComponent =\n getDefaultExport(options.route.notFound) ?? getDefaultExport(options.rootNotFoundModule);\n if (notFoundComponent) {\n const NotFoundComponent = notFoundComponent;\n element = <NotFoundBoundary fallback={<NotFoundComponent />}>{element}</NotFoundBoundary>;\n }\n\n const templates = options.route.templates ?? [];\n const routeSlots = options.route.slots ?? {};\n const layoutEntries = createAppPageLayoutEntries(options.route);\n const routeThenableParams = options.makeThenableParams(options.matchedParams);\n const getEffectiveSlotParams = (slotName: string): Record<string, string | string[]> =>\n options.slotOverrides?.[slotName]?.params ?? options.matchedParams;\n\n for (let index = layoutEntries.length - 1; index >= 0; index--) {\n const layoutEntry = layoutEntries[index];\n\n // Next.js nesting per segment (outer to inner): Layout > Template > Error > NotFound > children\n // Building bottom-up: NotFoundBoundary is the innermost wrapper, then ErrorBoundary, then Template.\n const layoutNotFoundComponent = getDefaultExport(layoutEntry.notFoundModule);\n if (layoutNotFoundComponent) {\n const LayoutNotFoundComponent = layoutNotFoundComponent;\n element = (\n <NotFoundBoundary fallback={<LayoutNotFoundComponent />}>{element}</NotFoundBoundary>\n );\n }\n\n const layoutErrorComponent = getErrorBoundaryExport(layoutEntry.errorModule);\n if (layoutErrorComponent) {\n element = <ErrorBoundary fallback={layoutErrorComponent}>{element}</ErrorBoundary>;\n }\n\n const templateComponent = getDefaultExport(templates[index]);\n if (templateComponent) {\n const TemplateComponent = templateComponent;\n element = <TemplateComponent params={options.matchedParams}>{element}</TemplateComponent>;\n }\n\n const layoutComponent = getDefaultExport(layoutEntry.layoutModule);\n if (!layoutComponent) {\n continue;\n }\n\n const layoutProps: Record<string, unknown> = {\n params: routeThenableParams,\n };\n\n for (const [slotName, slot] of Object.entries(routeSlots)) {\n const targetIndex = slot.layoutIndex >= 0 ? slot.layoutIndex : layoutEntries.length - 1;\n if (index !== targetIndex) {\n continue;\n }\n\n const slotOverride = options.slotOverrides?.[slotName];\n const slotParams = getEffectiveSlotParams(slotName);\n const slotComponent =\n getDefaultExport(slotOverride?.pageModule) ??\n getDefaultExport(slot.page) ??\n getDefaultExport(slot.default);\n if (!slotComponent) {\n continue;\n }\n\n const slotProps: Record<string, unknown> = {\n params: options.makeThenableParams(slotParams),\n };\n if (slotOverride?.props) {\n Object.assign(slotProps, slotOverride.props);\n }\n\n const SlotComponent = slotComponent;\n let slotElement: ReactNode = <SlotComponent {...slotProps} />;\n\n const slotLayoutComponent = getDefaultExport(slot.layout);\n if (slotLayoutComponent) {\n const SlotLayoutComponent = slotLayoutComponent;\n slotElement = (\n <SlotLayoutComponent params={options.makeThenableParams(slotParams)}>\n {slotElement}\n </SlotLayoutComponent>\n );\n }\n\n const slotLoadingComponent = getDefaultExport(slot.loading);\n if (slotLoadingComponent) {\n const SlotLoadingComponent = slotLoadingComponent;\n slotElement = <Suspense fallback={<SlotLoadingComponent />}>{slotElement}</Suspense>;\n }\n\n const slotErrorComponent = getErrorBoundaryExport(slot.error);\n if (slotErrorComponent) {\n slotElement = <ErrorBoundary fallback={slotErrorComponent}>{slotElement}</ErrorBoundary>;\n }\n\n layoutProps[slotName] = slotElement;\n }\n\n const LayoutComponent = layoutComponent;\n element = <LayoutComponent {...layoutProps}>{element}</LayoutComponent>;\n\n // Build the segment map for this layout level. The \"children\" key always\n // contains the route segments below this layout. Named parallel slots at\n // this layout level add their own keys with per-slot segment data.\n const segmentMap: { children: string[] } & Record<string, string[]> = {\n children: resolveAppPageChildSegments(\n options.route.routeSegments ?? [],\n layoutEntry.treePosition,\n options.matchedParams,\n ),\n };\n for (const [slotName, slot] of Object.entries(routeSlots)) {\n const targetIndex = slot.layoutIndex >= 0 ? slot.layoutIndex : layoutEntries.length - 1;\n if (index !== targetIndex) {\n continue;\n }\n const slotParams = getEffectiveSlotParams(slotName);\n if (slot.routeSegments) {\n // Slot has an active page — resolve its segments (dynamic params → values)\n segmentMap[slotName] = resolveAppPageChildSegments(\n slot.routeSegments,\n 0, // Slot segments are already relative to the slot root\n slotParams,\n );\n } else {\n // Slot is showing default.tsx or has no page — empty segments\n segmentMap[slotName] = [];\n }\n }\n element = <LayoutSegmentProvider segmentMap={segmentMap}>{element}</LayoutSegmentProvider>;\n }\n\n const globalErrorComponent = getErrorBoundaryExport(options.globalErrorModule);\n if (globalErrorComponent) {\n element = <ErrorBoundary fallback={globalErrorComponent}>{element}</ErrorBoundary>;\n }\n\n return element;\n}\n"],"mappings":"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"}
|
|
@@ -12,12 +12,15 @@ type CreateAppPageFontDataOptions = {
|
|
|
12
12
|
getStyles: () => string[];
|
|
13
13
|
};
|
|
14
14
|
type AppPageSsrHandler = {
|
|
15
|
-
handleSsr: (rscStream: ReadableStream<Uint8Array>, navigationContext: unknown, fontData: AppPageFontData
|
|
15
|
+
handleSsr: (rscStream: ReadableStream<Uint8Array>, navigationContext: unknown, fontData: AppPageFontData, options?: {
|
|
16
|
+
scriptNonce?: string;
|
|
17
|
+
}) => Promise<ReadableStream<Uint8Array>>;
|
|
16
18
|
};
|
|
17
19
|
type RenderAppPageHtmlStreamOptions = {
|
|
18
20
|
fontData: AppPageFontData;
|
|
19
21
|
navigationContext: unknown;
|
|
20
22
|
rscStream: ReadableStream<Uint8Array>;
|
|
23
|
+
scriptNonce?: string;
|
|
21
24
|
ssrHandler: AppPageSsrHandler;
|
|
22
25
|
};
|
|
23
26
|
type RenderAppPageHtmlResponseOptions = {
|
|
@@ -7,7 +7,8 @@ function createAppPageFontData(options) {
|
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
9
|
async function renderAppPageHtmlStream(options) {
|
|
10
|
-
|
|
10
|
+
const ssrOptions = options.scriptNonce === void 0 ? void 0 : { scriptNonce: options.scriptNonce };
|
|
11
|
+
return options.ssrHandler.handleSsr(options.rscStream, options.navigationContext, options.fontData, ssrOptions);
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
14
|
* Wraps a stream so that `onFlush` is called when the last byte has been read
|
|
@@ -33,6 +34,9 @@ function deferUntilStreamConsumed(stream, onFlush) {
|
|
|
33
34
|
return reader.read().then(({ done, value }) => {
|
|
34
35
|
if (done) controller.close();
|
|
35
36
|
else controller.enqueue(value);
|
|
37
|
+
}, (error) => {
|
|
38
|
+
once();
|
|
39
|
+
controller.error(error);
|
|
36
40
|
});
|
|
37
41
|
},
|
|
38
42
|
cancel(reason) {
|
|
@@ -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 type AppPageFontData = {\n links: string[];\n preloads: readonly AppPageFontPreload[];\n styles: string[];\n};\n\nexport type CreateAppPageFontDataOptions = {\n getLinks: () => string[];\n getPreloads: () => AppPageFontPreload[];\n getStyles: () => string[];\n};\n\nexport type AppPageSsrHandler = {\n handleSsr: (\n rscStream: ReadableStream<Uint8Array>,\n navigationContext: unknown,\n fontData: AppPageFontData,\n ) => Promise<ReadableStream<Uint8Array>>;\n};\n\nexport type RenderAppPageHtmlStreamOptions = {\n fontData: AppPageFontData;\n navigationContext: unknown;\n rscStream: ReadableStream<Uint8Array>;\n ssrHandler: AppPageSsrHandler;\n};\n\nexport type RenderAppPageHtmlResponseOptions = {\n clearRequestContext: () => void;\n fontLinkHeader?: string;\n status: number;\n} & RenderAppPageHtmlStreamOptions;\n\nexport type AppPageHtmlStreamRecoveryResult = {\n htmlStream: ReadableStream<Uint8Array> | null;\n response: Response | null;\n};\n\nexport type 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 type AppPageRscErrorTracker = {\n getCapturedError: () => unknown;\n onRenderError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown;\n};\n\nexport type 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
|
|
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 type AppPageFontData = {\n links: string[];\n preloads: readonly AppPageFontPreload[];\n styles: string[];\n};\n\nexport type CreateAppPageFontDataOptions = {\n getLinks: () => string[];\n getPreloads: () => AppPageFontPreload[];\n getStyles: () => string[];\n};\n\nexport type AppPageSsrHandler = {\n handleSsr: (\n rscStream: ReadableStream<Uint8Array>,\n navigationContext: unknown,\n fontData: AppPageFontData,\n options?: { scriptNonce?: string },\n ) => Promise<ReadableStream<Uint8Array>>;\n};\n\nexport type RenderAppPageHtmlStreamOptions = {\n fontData: AppPageFontData;\n navigationContext: unknown;\n rscStream: ReadableStream<Uint8Array>;\n scriptNonce?: string;\n ssrHandler: AppPageSsrHandler;\n};\n\nexport type RenderAppPageHtmlResponseOptions = {\n clearRequestContext: () => void;\n fontLinkHeader?: string;\n status: number;\n} & RenderAppPageHtmlStreamOptions;\n\nexport type AppPageHtmlStreamRecoveryResult = {\n htmlStream: ReadableStream<Uint8Array> | null;\n response: Response | null;\n};\n\nexport type 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 type AppPageRscErrorTracker = {\n getCapturedError: () => unknown;\n onRenderError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown;\n};\n\nexport type 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 const ssrOptions =\n options.scriptNonce === undefined ? undefined : { scriptNonce: options.scriptNonce };\n\n return options.ssrHandler.handleSsr(\n options.rscStream,\n options.navigationContext,\n options.fontData,\n ssrOptions,\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(\n ({ done, value }) => {\n if (done) {\n controller.close();\n } else {\n controller.enqueue(value);\n }\n },\n (error) => {\n once();\n controller.error(error);\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":";AA4DA,SAAgB,sBAAsB,SAAwD;AAC5F,QAAO;EACL,OAAO,QAAQ,UAAU;EACzB,UAAU,QAAQ,aAAa;EAC/B,QAAQ,QAAQ,WAAW;EAC5B;;AAGH,eAAsB,wBACpB,SACqC;CACrC,MAAM,aACJ,QAAQ,gBAAgB,KAAA,IAAY,KAAA,IAAY,EAAE,aAAa,QAAQ,aAAa;AAEtF,QAAO,QAAQ,WAAW,UACxB,QAAQ,WACR,QAAQ,mBACR,QAAQ,UACR,WACD;;;;;;;;;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,MAClB,EAAE,MAAM,YAAY;AACnB,QAAI,KACF,YAAW,OAAO;QAElB,YAAW,QAAQ,MAAM;OAG5B,UAAU;AACT,UAAM;AACN,eAAW,MAAM,MAAM;KAE1B;;EAEH,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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
1
2
|
//#region src/server/app-route-handler-response.ts
|
|
2
3
|
function buildRouteHandlerCacheControl(cacheState, revalidateSeconds) {
|
|
3
4
|
if (cacheState === "STALE") return "s-maxage=0, stale-while-revalidate";
|
|
@@ -6,7 +7,7 @@ function buildRouteHandlerCacheControl(cacheState, revalidateSeconds) {
|
|
|
6
7
|
function applyRouteHandlerMiddlewareContext(response, middlewareContext) {
|
|
7
8
|
if (!middlewareContext.headers && middlewareContext.status == null) return response;
|
|
8
9
|
const responseHeaders = new Headers(response.headers);
|
|
9
|
-
|
|
10
|
+
mergeMiddlewareResponseHeaders(responseHeaders, middlewareContext.headers);
|
|
10
11
|
return new Response(response.body, {
|
|
11
12
|
status: middlewareContext.status ?? response.status,
|
|
12
13
|
statusText: response.statusText,
|
|
@@ -34,8 +35,11 @@ async function buildAppRouteCacheValue(response) {
|
|
|
34
35
|
const body = await response.arrayBuffer();
|
|
35
36
|
const headers = {};
|
|
36
37
|
response.headers.forEach((value, key) => {
|
|
37
|
-
if (key
|
|
38
|
+
if (key === "set-cookie" || key === "x-vinext-cache" || key === "cache-control") return;
|
|
39
|
+
headers[key] = value;
|
|
38
40
|
});
|
|
41
|
+
const setCookies = response.headers.getSetCookie?.() ?? [];
|
|
42
|
+
if (setCookies.length > 0) headers["set-cookie"] = setCookies;
|
|
39
43
|
return {
|
|
40
44
|
kind: "APP_ROUTE",
|
|
41
45
|
body,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-route-handler-response.js","names":[],"sources":["../../src/server/app-route-handler-response.ts"],"sourcesContent":["import type { CachedRouteValue } from \"../shims/cache.js\";\n\nexport type RouteHandlerMiddlewareContext = {\n headers: Headers | null;\n status: number | null;\n};\n\nexport type BuildRouteHandlerCachedResponseOptions = {\n cacheState: \"HIT\" | \"STALE\";\n isHead: boolean;\n revalidateSeconds: number;\n};\n\nexport type FinalizeRouteHandlerResponseOptions = {\n pendingCookies: string[];\n draftCookie?: string | null;\n isHead: boolean;\n};\n\nfunction buildRouteHandlerCacheControl(\n cacheState: BuildRouteHandlerCachedResponseOptions[\"cacheState\"],\n revalidateSeconds: number,\n): string {\n if (cacheState === \"STALE\") {\n return \"s-maxage=0, stale-while-revalidate\";\n }\n\n return `s-maxage=${revalidateSeconds}, stale-while-revalidate`;\n}\n\nexport function applyRouteHandlerMiddlewareContext(\n response: Response,\n middlewareContext: RouteHandlerMiddlewareContext,\n): Response {\n if (!middlewareContext.headers && middlewareContext.status == null) {\n return response;\n }\n\n const responseHeaders = new Headers(response.headers);\n
|
|
1
|
+
{"version":3,"file":"app-route-handler-response.js","names":[],"sources":["../../src/server/app-route-handler-response.ts"],"sourcesContent":["import type { CachedRouteValue } from \"../shims/cache.js\";\nimport { mergeMiddlewareResponseHeaders } from \"./middleware-response-headers.js\";\n\nexport type RouteHandlerMiddlewareContext = {\n headers: Headers | null;\n status: number | null;\n};\n\nexport type BuildRouteHandlerCachedResponseOptions = {\n cacheState: \"HIT\" | \"STALE\";\n isHead: boolean;\n revalidateSeconds: number;\n};\n\nexport type FinalizeRouteHandlerResponseOptions = {\n pendingCookies: string[];\n draftCookie?: string | null;\n isHead: boolean;\n};\n\nfunction buildRouteHandlerCacheControl(\n cacheState: BuildRouteHandlerCachedResponseOptions[\"cacheState\"],\n revalidateSeconds: number,\n): string {\n if (cacheState === \"STALE\") {\n return \"s-maxage=0, stale-while-revalidate\";\n }\n\n return `s-maxage=${revalidateSeconds}, stale-while-revalidate`;\n}\n\nexport function applyRouteHandlerMiddlewareContext(\n response: Response,\n middlewareContext: RouteHandlerMiddlewareContext,\n): Response {\n if (!middlewareContext.headers && middlewareContext.status == null) {\n return response;\n }\n\n const responseHeaders = new Headers(response.headers);\n mergeMiddlewareResponseHeaders(responseHeaders, middlewareContext.headers);\n\n return new Response(response.body, {\n status: middlewareContext.status ?? response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n}\n\nexport function buildRouteHandlerCachedResponse(\n cachedValue: CachedRouteValue,\n options: BuildRouteHandlerCachedResponseOptions,\n): Response {\n const headers = new Headers();\n for (const [key, value] of Object.entries(cachedValue.headers)) {\n if (Array.isArray(value)) {\n for (const entry of value) {\n headers.append(key, entry);\n }\n } else {\n headers.set(key, value);\n }\n }\n headers.set(\"X-Vinext-Cache\", options.cacheState);\n headers.set(\n \"Cache-Control\",\n buildRouteHandlerCacheControl(options.cacheState, options.revalidateSeconds),\n );\n\n return new Response(options.isHead ? null : cachedValue.body, {\n status: cachedValue.status,\n headers,\n });\n}\n\nexport function applyRouteHandlerRevalidateHeader(\n response: Response,\n revalidateSeconds: number,\n): void {\n response.headers.set(\"cache-control\", buildRouteHandlerCacheControl(\"HIT\", revalidateSeconds));\n}\n\nexport function markRouteHandlerCacheMiss(response: Response): void {\n response.headers.set(\"X-Vinext-Cache\", \"MISS\");\n}\n\nexport async function buildAppRouteCacheValue(response: Response): Promise<CachedRouteValue> {\n const body = await response.arrayBuffer();\n const headers: CachedRouteValue[\"headers\"] = {};\n\n response.headers.forEach((value, key) => {\n if (key === \"set-cookie\" || key === \"x-vinext-cache\" || key === \"cache-control\") return;\n headers[key] = value;\n });\n const setCookies = response.headers.getSetCookie?.() ?? [];\n if (setCookies.length > 0) {\n headers[\"set-cookie\"] = setCookies;\n }\n\n return {\n kind: \"APP_ROUTE\",\n body,\n status: response.status,\n headers,\n };\n}\n\nexport function finalizeRouteHandlerResponse(\n response: Response,\n options: FinalizeRouteHandlerResponseOptions,\n): Response {\n const { pendingCookies, draftCookie, isHead } = options;\n if (pendingCookies.length === 0 && !draftCookie && !isHead) {\n return response;\n }\n\n const headers = new Headers(response.headers);\n for (const cookie of pendingCookies) {\n headers.append(\"Set-Cookie\", cookie);\n }\n if (draftCookie) {\n headers.append(\"Set-Cookie\", draftCookie);\n }\n\n return new Response(isHead ? null : response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n"],"mappings":";;AAoBA,SAAS,8BACP,YACA,mBACQ;AACR,KAAI,eAAe,QACjB,QAAO;AAGT,QAAO,YAAY,kBAAkB;;AAGvC,SAAgB,mCACd,UACA,mBACU;AACV,KAAI,CAAC,kBAAkB,WAAW,kBAAkB,UAAU,KAC5D,QAAO;CAGT,MAAM,kBAAkB,IAAI,QAAQ,SAAS,QAAQ;AACrD,gCAA+B,iBAAiB,kBAAkB,QAAQ;AAE1E,QAAO,IAAI,SAAS,SAAS,MAAM;EACjC,QAAQ,kBAAkB,UAAU,SAAS;EAC7C,YAAY,SAAS;EACrB,SAAS;EACV,CAAC;;AAGJ,SAAgB,gCACd,aACA,SACU;CACV,MAAM,UAAU,IAAI,SAAS;AAC7B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAAY,QAAQ,CAC5D,KAAI,MAAM,QAAQ,MAAM,CACtB,MAAK,MAAM,SAAS,MAClB,SAAQ,OAAO,KAAK,MAAM;KAG5B,SAAQ,IAAI,KAAK,MAAM;AAG3B,SAAQ,IAAI,kBAAkB,QAAQ,WAAW;AACjD,SAAQ,IACN,iBACA,8BAA8B,QAAQ,YAAY,QAAQ,kBAAkB,CAC7E;AAED,QAAO,IAAI,SAAS,QAAQ,SAAS,OAAO,YAAY,MAAM;EAC5D,QAAQ,YAAY;EACpB;EACD,CAAC;;AAGJ,SAAgB,kCACd,UACA,mBACM;AACN,UAAS,QAAQ,IAAI,iBAAiB,8BAA8B,OAAO,kBAAkB,CAAC;;AAGhG,SAAgB,0BAA0B,UAA0B;AAClE,UAAS,QAAQ,IAAI,kBAAkB,OAAO;;AAGhD,eAAsB,wBAAwB,UAA+C;CAC3F,MAAM,OAAO,MAAM,SAAS,aAAa;CACzC,MAAM,UAAuC,EAAE;AAE/C,UAAS,QAAQ,SAAS,OAAO,QAAQ;AACvC,MAAI,QAAQ,gBAAgB,QAAQ,oBAAoB,QAAQ,gBAAiB;AACjF,UAAQ,OAAO;GACf;CACF,MAAM,aAAa,SAAS,QAAQ,gBAAgB,IAAI,EAAE;AAC1D,KAAI,WAAW,SAAS,EACtB,SAAQ,gBAAgB;AAG1B,QAAO;EACL,MAAM;EACN;EACA,QAAQ,SAAS;EACjB;EACD;;AAGH,SAAgB,6BACd,UACA,SACU;CACV,MAAM,EAAE,gBAAgB,aAAa,WAAW;AAChD,KAAI,eAAe,WAAW,KAAK,CAAC,eAAe,CAAC,OAClD,QAAO;CAGT,MAAM,UAAU,IAAI,QAAQ,SAAS,QAAQ;AAC7C,MAAK,MAAM,UAAU,eACnB,SAAQ,OAAO,cAAc,OAAO;AAEtC,KAAI,YACF,SAAQ,OAAO,cAAc,YAAY;AAG3C,QAAO,IAAI,SAAS,SAAS,OAAO,SAAS,MAAM;EACjD,QAAQ,SAAS;EACjB,YAAY,SAAS;EACrB;EACD,CAAC"}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { ExecutionContextLike } from "../shims/request-context.js";
|
|
2
2
|
|
|
3
3
|
//#region src/server/app-router-entry.d.ts
|
|
4
|
+
type WorkerAssetEnv = {
|
|
5
|
+
ASSETS?: {
|
|
6
|
+
fetch(request: Request): Promise<Response> | Response;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
4
9
|
declare const _default: {
|
|
5
|
-
fetch(request: Request,
|
|
10
|
+
fetch(request: Request, env?: WorkerAssetEnv, ctx?: ExecutionContextLike): Promise<Response>;
|
|
6
11
|
};
|
|
7
12
|
//#endregion
|
|
8
13
|
export { _default as default };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runWithExecutionContext } from "../shims/request-context.js";
|
|
2
|
+
import { resolveStaticAssetSignal } from "./worker-utils.js";
|
|
2
3
|
import rscHandler from "virtual:vinext-rsc-entry";
|
|
3
4
|
//#region src/server/app-router-entry.ts
|
|
4
5
|
/**
|
|
@@ -14,11 +15,17 @@ import rscHandler from "virtual:vinext-rsc-entry";
|
|
|
14
15
|
* This file runs in the RSC environment. Configure the Cloudflare plugin with:
|
|
15
16
|
* cloudflare({ viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] } })
|
|
16
17
|
*/
|
|
17
|
-
var app_router_entry_default = { async fetch(request,
|
|
18
|
+
var app_router_entry_default = { async fetch(request, env, ctx) {
|
|
18
19
|
if (new URL(request.url).pathname.replaceAll("\\", "/").startsWith("//")) return new Response("404 Not Found", { status: 404 });
|
|
19
20
|
const handleFn = () => rscHandler(request, ctx);
|
|
20
21
|
const result = await (ctx ? runWithExecutionContext(ctx, handleFn) : handleFn());
|
|
21
|
-
if (result instanceof Response)
|
|
22
|
+
if (result instanceof Response) {
|
|
23
|
+
if (env?.ASSETS) {
|
|
24
|
+
const assetResponse = await resolveStaticAssetSignal(result, { fetchAsset: (path) => Promise.resolve(env.ASSETS.fetch(new Request(new URL(path, request.url)))) });
|
|
25
|
+
if (assetResponse) return assetResponse;
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
22
29
|
if (result === null || result === void 0) return new Response("Not Found", { status: 404 });
|
|
23
30
|
return new Response(String(result), { status: 200 });
|
|
24
31
|
} };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-router-entry.js","names":[],"sources":["../../src/server/app-router-entry.ts"],"sourcesContent":["/**\n * Default Cloudflare Worker entry point for vinext App Router.\n *\n * Use this directly in wrangler.jsonc:\n * \"main\": \"vinext/server/app-router-entry\"\n *\n * Or import and delegate to it from a custom worker:\n * import handler from \"vinext/server/app-router-entry\";\n * return handler.fetch(request, env, ctx);\n *\n * This file runs in the RSC environment. Configure the Cloudflare plugin with:\n * cloudflare({ viteEnvironment: { name: \"rsc\", childEnvironments: [\"ssr\"] } })\n */\n\n// @ts-expect-error — virtual module resolved by vinext\nimport rscHandler from \"virtual:vinext-rsc-entry\";\nimport { runWithExecutionContext, type ExecutionContextLike } from \"../shims/request-context.js\";\n\nexport default {\n async fetch(request: Request
|
|
1
|
+
{"version":3,"file":"app-router-entry.js","names":[],"sources":["../../src/server/app-router-entry.ts"],"sourcesContent":["/**\n * Default Cloudflare Worker entry point for vinext App Router.\n *\n * Use this directly in wrangler.jsonc:\n * \"main\": \"vinext/server/app-router-entry\"\n *\n * Or import and delegate to it from a custom worker:\n * import handler from \"vinext/server/app-router-entry\";\n * return handler.fetch(request, env, ctx);\n *\n * This file runs in the RSC environment. Configure the Cloudflare plugin with:\n * cloudflare({ viteEnvironment: { name: \"rsc\", childEnvironments: [\"ssr\"] } })\n */\n\n// @ts-expect-error — virtual module resolved by vinext\nimport rscHandler from \"virtual:vinext-rsc-entry\";\nimport { runWithExecutionContext, type ExecutionContextLike } from \"../shims/request-context.js\";\nimport { resolveStaticAssetSignal } from \"./worker-utils.js\";\n\ntype WorkerAssetEnv = {\n ASSETS?: {\n fetch(request: Request): Promise<Response> | Response;\n };\n};\n\nexport default {\n async fetch(\n request: Request,\n env?: WorkerAssetEnv,\n ctx?: ExecutionContextLike,\n ): Promise<Response> {\n const url = new URL(request.url);\n\n // Normalize backslashes (browsers treat /\\ as //) before any other checks.\n const rawPathname = url.pathname.replaceAll(\"\\\\\", \"/\");\n\n // Block protocol-relative URL open redirects (//evil.com/ or /\\evil.com/).\n // Check rawPathname BEFORE decode so the guard fires before normalization.\n if (rawPathname.startsWith(\"//\")) {\n return new Response(\"404 Not Found\", { status: 404 });\n }\n\n // Validate that percent-encoding is well-formed. The RSC handler performs\n // the actual decode + normalize; we only check here to return a clean 400\n // instead of letting a malformed sequence crash downstream.\n try {\n decodeURIComponent(rawPathname);\n } catch {\n // Malformed percent-encoding (e.g. /%E0%A4%A) — return 400 instead of throwing.\n return new Response(\"Bad Request\", { status: 400 });\n }\n\n // Do NOT decode/normalize the pathname here. The RSC handler\n // (virtual:vinext-rsc-entry) is the single point of decoding — it calls\n // decodeURIComponent + normalizePath on the incoming URL. Decoding here\n // AND in the handler would double-decode, causing inconsistent path\n // matching between middleware and routing.\n\n // Delegate to RSC handler (which decodes + normalizes the pathname itself),\n // wrapping in the ExecutionContext ALS scope so downstream code can reach\n // ctx.waitUntil() without having ctx threaded through every call site.\n const handleFn = () => rscHandler(request, ctx);\n const result = await (ctx ? runWithExecutionContext(ctx, handleFn) : handleFn());\n\n if (result instanceof Response) {\n if (env?.ASSETS) {\n const assetResponse = await resolveStaticAssetSignal(result, {\n fetchAsset: (path) =>\n Promise.resolve(env.ASSETS!.fetch(new Request(new URL(path, request.url)))),\n });\n if (assetResponse) return assetResponse;\n }\n return result;\n }\n\n if (result === null || result === undefined) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n return new Response(String(result), { status: 200 });\n },\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAyBA,IAAA,2BAAe,EACb,MAAM,MACJ,SACA,KACA,KACmB;AAQnB,KAPY,IAAI,IAAI,QAAQ,IAAI,CAGR,SAAS,WAAW,MAAM,IAAI,CAItC,WAAW,KAAK,CAC9B,QAAO,IAAI,SAAS,iBAAiB,EAAE,QAAQ,KAAK,CAAC;CAsBvD,MAAM,iBAAiB,WAAW,SAAS,IAAI;CAC/C,MAAM,SAAS,OAAO,MAAM,wBAAwB,KAAK,SAAS,GAAG,UAAU;AAE/E,KAAI,kBAAkB,UAAU;AAC9B,MAAI,KAAK,QAAQ;GACf,MAAM,gBAAgB,MAAM,yBAAyB,QAAQ,EAC3D,aAAa,SACX,QAAQ,QAAQ,IAAI,OAAQ,MAAM,IAAI,QAAQ,IAAI,IAAI,MAAM,QAAQ,IAAI,CAAC,CAAC,CAAC,EAC9E,CAAC;AACF,OAAI,cAAe,QAAO;;AAE5B,SAAO;;AAGT,KAAI,WAAW,QAAQ,WAAW,KAAA,EAChC,QAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;AAGnD,QAAO,IAAI,SAAS,OAAO,OAAO,EAAE,EAAE,QAAQ,KAAK,CAAC;GAEvD"}
|
|
@@ -10,7 +10,9 @@ type FontData = {
|
|
|
10
10
|
styles?: string[];
|
|
11
11
|
preloads?: FontPreload[];
|
|
12
12
|
};
|
|
13
|
-
declare function handleSsr(rscStream: ReadableStream<Uint8Array>, navContext: NavigationContext | null, fontData?: FontData
|
|
13
|
+
declare function handleSsr(rscStream: ReadableStream<Uint8Array>, navContext: NavigationContext | null, fontData?: FontData, options?: {
|
|
14
|
+
scriptNonce?: string;
|
|
15
|
+
}): Promise<ReadableStream<Uint8Array>>;
|
|
14
16
|
declare const _default: {
|
|
15
17
|
fetch(request: Request): Promise<Response>;
|
|
16
18
|
};
|