@rangojs/router 0.20.0 → 0.22.0
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/bin/rango.js +39 -15
- package/dist/types/browser/event-controller.d.ts +49 -0
- package/dist/types/browser/history-state.d.ts +16 -4
- package/dist/types/browser/navigation-store.d.ts +9 -0
- package/dist/types/browser/navigation-transaction.d.ts +7 -0
- package/dist/types/browser/partial-update.d.ts +2 -1
- package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
- package/dist/types/browser/rango-state.d.ts +3 -1
- package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
- package/dist/types/browser/react/context.d.ts +25 -0
- package/dist/types/browser/react/location-state-shared.d.ts +84 -10
- package/dist/types/browser/react/location-state.d.ts +7 -2
- package/dist/types/browser/rsc-router.d.ts +13 -2
- package/dist/types/browser/types.d.ts +10 -0
- package/dist/types/build/generate-manifest.d.ts +2 -0
- package/dist/types/cache/background-task.d.ts +22 -0
- package/dist/types/cache/cache-key-utils.d.ts +50 -5
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +5 -6
- package/dist/types/cache/cf/cf-cache-store.d.ts +34 -7
- package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
- package/dist/types/cache/document-cache.d.ts +8 -5
- package/dist/types/cache/handle-snapshot.d.ts +59 -50
- package/dist/types/cache/index.d.ts +3 -1
- package/dist/types/cache/invalidation-order.d.ts +14 -0
- package/dist/types/cache/memory-segment-store.d.ts +33 -1
- package/dist/types/cache/shell-snapshot.d.ts +29 -4
- package/dist/types/cache/store-scope.d.ts +22 -0
- package/dist/types/cache/tag-invalidation.d.ts +7 -6
- package/dist/types/cache/types.d.ts +84 -24
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +35 -17
- package/dist/types/client-urls/navigation.d.ts +7 -1
- package/dist/types/client-urls/optimistic-location.d.ts +10 -4
- package/dist/types/errors.d.ts +6 -0
- package/dist/types/handles/deferred-resolution.d.ts +18 -1
- package/dist/types/internal-suspense-audit.d.ts +1 -0
- package/dist/types/loader-store.d.ts +4 -8
- package/dist/types/outlet-provider.d.ts +2 -1
- package/dist/types/prerender/build-shell-capture.d.ts +8 -6
- package/dist/types/prerender/cloudflare.d.ts +49 -0
- package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
- package/dist/types/prerender/index.d.ts +12 -0
- package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
- package/dist/types/prerender/on-demand.d.ts +272 -0
- package/dist/types/prerender/producer-guard.d.ts +33 -0
- package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
- package/dist/types/prerender/warm-request.d.ts +65 -0
- package/dist/types/prerender/warm.d.ts +65 -0
- package/dist/types/prerender/writable-store.d.ts +170 -0
- package/dist/types/prerender.d.ts +27 -0
- package/dist/types/reverse.d.ts +1 -1
- package/dist/types/route-content-wrapper.d.ts +3 -1
- package/dist/types/router/error-handling.d.ts +2 -0
- package/dist/types/router/handler-context.d.ts +1 -1
- package/dist/types/router/match-middleware/cache-lookup.d.ts +15 -1
- package/dist/types/router/navigation-snapshot.d.ts +8 -1
- package/dist/types/router/pattern-matching.d.ts +2 -0
- package/dist/types/router/prefetch-default.d.ts +4 -4
- package/dist/types/router/prerender-match.d.ts +23 -1
- package/dist/types/router/route-trie-builder.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +1 -0
- package/dist/types/router/router-interfaces.d.ts +32 -1
- package/dist/types/router/router-options.d.ts +39 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +23 -10
- package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
- package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
- package/dist/types/router/trie-matching.d.ts +2 -0
- package/dist/types/router-versions.d.ts +39 -0
- package/dist/types/rsc/handler-context.d.ts +5 -0
- package/dist/types/rsc/shell-build-manifest.d.ts +9 -6
- package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
- package/dist/types/rsc/shell-capture.d.ts +14 -1
- package/dist/types/rsc/shell-serve.d.ts +2 -2
- package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
- package/dist/types/rsc/types.d.ts +12 -6
- package/dist/types/segment-boundary-content.d.ts +17 -0
- package/dist/types/segment-loader-promise.d.ts +2 -6
- package/dist/types/server/build-version-table.d.ts +53 -0
- package/dist/types/server/context.d.ts +12 -4
- package/dist/types/server/handle-store.d.ts +89 -46
- package/dist/types/server/request-context.d.ts +84 -9
- package/dist/types/suspense-audit-react.d.ts +26 -0
- package/dist/types/suspense-audit.d.ts +157 -0
- package/dist/types/testing/action-form.d.ts +34 -0
- package/dist/types/testing/build-versions.d.ts +32 -0
- package/dist/types/testing/dom.entry.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +3 -1
- package/dist/types/testing/index.d.ts +6 -0
- package/dist/types/testing/internal/seed-vars.d.ts +7 -2
- package/dist/types/testing/internal/server-modules.d.ts +8 -0
- package/dist/types/testing/location-state-key.d.ts +5 -1
- package/dist/types/testing/render-route.d.ts +77 -6
- package/dist/types/testing/serve-shell-request.d.ts +55 -4
- package/dist/types/testing/shell-status.d.ts +14 -6
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/thenable-status.d.ts +6 -0
- package/dist/types/types/handler-context.d.ts +15 -5
- package/dist/types/types/route-entry.d.ts +5 -0
- package/dist/types/urls/path-helper.d.ts +18 -0
- package/dist/types/vite/discovery/build-versions.d.ts +393 -0
- package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
- package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
- package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
- package/dist/types/vite/discovery/state.d.ts +25 -0
- package/dist/types/vite/encryption-key.d.ts +18 -0
- package/dist/types/vite/inject-client-debug.d.ts +6 -25
- package/dist/types/vite/plugin-types.d.ts +27 -0
- package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
- package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
- package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
- package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
- package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
- package/dist/types/vite/plugins/virtual-entries.d.ts +18 -1
- package/dist/types/vite/router-discovery.d.ts +6 -0
- package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
- package/dist/types/vite/utils/client-chunks.d.ts +2 -0
- package/dist/types/vite/utils/compare-strings.d.ts +7 -0
- package/dist/types/vite/utils/package-resolution.d.ts +8 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
- package/dist/vite/index.js +1707 -931
- package/package.json +12 -3
- package/skills/cache-guide/SKILL.md +102 -37
- package/skills/caching/SKILL.md +56 -9
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +16 -0
- package/skills/cloudflare/SKILL.md +133 -1
- package/skills/comparison/references/framework-comparison.md +17 -13
- package/skills/deployment-caching/SKILL.md +33 -12
- package/skills/document-cache/SKILL.md +16 -14
- package/skills/hooks/SKILL.md +25 -0
- package/skills/hooks/handle-and-actions.md +5 -0
- package/skills/hooks/navigation.md +38 -0
- package/skills/hooks/state.md +213 -11
- package/skills/hooks/urls.md +40 -1
- package/skills/host-router/SKILL.md +31 -0
- package/skills/intercept/SKILL.md +6 -0
- package/skills/loader/SKILL.md +25 -5
- package/skills/middleware/SKILL.md +2 -0
- package/skills/migrate-nextjs/SKILL.md +82 -9
- package/skills/ppr/SKILL.md +150 -48
- package/skills/prerender/SKILL.md +651 -10
- package/skills/rango/SKILL.md +9 -7
- package/skills/response-routes/SKILL.md +5 -5
- package/skills/route/SKILL.md +29 -0
- package/skills/router-setup/SKILL.md +15 -1
- package/skills/testing/SKILL.md +32 -27
- package/skills/testing/cache-prerender.md +18 -17
- package/skills/testing/client-components.md +193 -8
- package/skills/testing/handles.md +2 -0
- package/skills/testing/loader.md +1 -1
- package/skills/testing/middleware.md +1 -1
- package/skills/testing/render-handler.md +18 -18
- package/skills/testing/serve-shell-request.md +167 -22
- package/skills/testing/server-actions.md +17 -17
- package/skills/testing/server-tree.md +1 -1
- package/skills/typesafety/generated-files-and-cli.md +1 -1
- package/skills/use-cache/SKILL.md +25 -10
- package/skills/vercel/SKILL.md +5 -3
- package/skills/view-transitions/SKILL.md +9 -4
- package/src/bin/rango.ts +1 -1
- package/src/browser/event-controller.ts +119 -2
- package/src/browser/history-state.ts +39 -17
- package/src/browser/link-interceptor.ts +2 -2
- package/src/browser/navigation-bridge.ts +104 -41
- package/src/browser/navigation-store.ts +26 -0
- package/src/browser/navigation-transaction.ts +24 -12
- package/src/browser/network-error-handler.ts +2 -0
- package/src/browser/partial-update.ts +58 -14
- package/src/browser/prefetch/default-strategy.ts +22 -42
- package/src/browser/rango-state.ts +3 -1
- package/src/browser/react/Link.tsx +37 -43
- package/src/browser/react/NavigationProvider.tsx +85 -1
- package/src/browser/react/context.ts +30 -0
- package/src/browser/react/location-state-shared.ts +200 -34
- package/src/browser/react/location-state.ts +28 -77
- package/src/browser/react/use-action.ts +4 -1
- package/src/browser/react/use-handle.ts +3 -0
- package/src/browser/react/use-link-status.ts +4 -3
- package/src/browser/react/use-navigation.ts +4 -1
- package/src/browser/react/use-params.ts +4 -1
- package/src/browser/react/use-pathname.ts +4 -1
- package/src/browser/react/use-search-params.ts +5 -3
- package/src/browser/react/use-segments.ts +36 -54
- package/src/browser/rsc-router.tsx +93 -31
- package/src/browser/segment-reconciler.ts +3 -3
- package/src/browser/server-action-bridge.ts +43 -7
- package/src/browser/types.ts +11 -0
- package/src/build/generate-manifest.ts +17 -0
- package/src/build/merge-full-manifests.ts +5 -0
- package/src/build/route-trie.ts +1 -0
- package/src/cache/background-task.ts +61 -0
- package/src/cache/cache-key-utils.ts +91 -12
- package/src/cache/cache-policy.ts +11 -2
- package/src/cache/cache-runtime.ts +23 -23
- package/src/cache/cache-scope.ts +48 -36
- package/src/cache/cf/cf-cache-store.ts +91 -66
- package/src/cache/cf/cf-cache-types.ts +12 -4
- package/src/cache/document-cache.ts +35 -14
- package/src/cache/handle-snapshot.ts +118 -86
- package/src/cache/index.ts +5 -0
- package/src/cache/invalidation-order.ts +53 -0
- package/src/cache/memory-segment-store.ts +52 -1
- package/src/cache/shell-snapshot.ts +52 -5
- package/src/cache/store-scope.ts +36 -0
- package/src/cache/tag-invalidation.ts +7 -5
- package/src/cache/types.ts +90 -24
- package/src/cache/vercel/vercel-cache-store.ts +68 -26
- package/src/client-urls/client-root.tsx +1 -0
- package/src/client-urls/navigation.ts +10 -0
- package/src/client-urls/optimistic-location.ts +10 -4
- package/src/client.tsx +40 -3
- package/src/errors.ts +13 -6
- package/src/handles/deferred-resolution.ts +36 -0
- package/src/internal-suspense-audit.ts +12 -0
- package/src/loader-store.ts +4 -9
- package/src/outlet-provider.tsx +25 -1
- package/src/prerender/build-shell-capture.ts +11 -9
- package/src/prerender/cloudflare.ts +130 -0
- package/src/prerender/create-prerender-trigger.ts +780 -0
- package/src/prerender/index.ts +39 -0
- package/src/prerender/memory-prerender-store.ts +115 -0
- package/src/prerender/on-demand.ts +339 -0
- package/src/prerender/producer-guard.ts +66 -0
- package/src/prerender/shell-manifest-key.ts +12 -17
- package/src/prerender/warm-request.ts +154 -0
- package/src/prerender/warm.ts +193 -0
- package/src/prerender/writable-store.ts +278 -0
- package/src/prerender.ts +29 -0
- package/src/reverse.ts +1 -1
- package/src/route-content-wrapper.tsx +107 -13
- package/src/router/error-handling.ts +12 -12
- package/src/router/find-match.ts +1 -0
- package/src/router/handler-context.ts +2 -0
- package/src/router/intercept-resolution.ts +15 -8
- package/src/router/loader-resolution.ts +2 -10
- package/src/router/manifest.ts +2 -0
- package/src/router/match-middleware/background-revalidation.ts +2 -0
- package/src/router/match-middleware/cache-lookup.ts +239 -54
- package/src/router/navigation-snapshot.ts +25 -6
- package/src/router/pattern-matching.ts +14 -0
- package/src/router/prefetch-default.ts +4 -4
- package/src/router/prerender-match.ts +460 -352
- package/src/router/request-classification.ts +15 -12
- package/src/router/route-trie-builder.ts +9 -0
- package/src/router/router-context.ts +1 -0
- package/src/router/router-interfaces.ts +35 -1
- package/src/router/router-options.ts +45 -5
- package/src/router/segment-resolution/fresh.ts +20 -19
- package/src/router/segment-resolution/loader-cache.ts +110 -77
- package/src/router/segment-resolution/revalidation.ts +34 -24
- package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
- package/src/router/trie-matching.ts +3 -0
- package/src/router-versions.ts +60 -0
- package/src/router.ts +107 -1
- package/src/rsc/full-payload.ts +6 -2
- package/src/rsc/handler-context.ts +5 -0
- package/src/rsc/handler.ts +64 -7
- package/src/rsc/progressive-enhancement.ts +5 -1
- package/src/rsc/response-cache-serve.ts +10 -4
- package/src/rsc/rsc-rendering.ts +65 -12
- package/src/rsc/shell-build-manifest.ts +17 -12
- package/src/rsc/shell-capture-constants.ts +39 -7
- package/src/rsc/shell-capture.ts +144 -45
- package/src/rsc/shell-serve.ts +12 -6
- package/src/rsc/ssr-module-loader.ts +26 -0
- package/src/rsc/types.ts +12 -6
- package/src/segment-boundary-content.ts +25 -0
- package/src/segment-loader-promise.ts +28 -38
- package/src/segment-system.tsx +117 -57
- package/src/server/build-version-table.ts +130 -0
- package/src/server/context.ts +40 -17
- package/src/server/cookie-store.ts +5 -0
- package/src/server/handle-store.ts +357 -127
- package/src/server/request-context.ts +149 -12
- package/src/ssr/ssr-root.tsx +32 -13
- package/src/suspense-audit-react.tsx +98 -0
- package/src/suspense-audit.ts +1030 -0
- package/src/testing/action-form.ts +66 -0
- package/src/testing/build-versions.ts +64 -0
- package/src/testing/dispatch.ts +6 -0
- package/src/testing/dom.entry.ts +1 -0
- package/src/testing/flight-runtime.d.ts +27 -0
- package/src/testing/flight.entry.ts +4 -0
- package/src/testing/index.ts +22 -0
- package/src/testing/internal/context.ts +2 -1
- package/src/testing/internal/seed-vars.ts +14 -2
- package/src/testing/internal/server-modules.ts +32 -0
- package/src/testing/location-state-key.ts +18 -8
- package/src/testing/render-handler.ts +2 -1
- package/src/testing/render-route.tsx +293 -46
- package/src/testing/serve-shell-request.ts +262 -37
- package/src/testing/shell-status.ts +17 -6
- package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
- package/src/testing/vitest-stubs/version.ts +4 -1
- package/src/thenable-status.ts +10 -0
- package/src/types/handler-context.ts +15 -5
- package/src/types/route-entry.ts +6 -0
- package/src/urls/path-helper.ts +69 -9
- package/src/use-loader.tsx +57 -30
- package/src/vite/discovery/build-versions.ts +1281 -0
- package/src/vite/discovery/bundle-postprocess.ts +4 -0
- package/src/vite/discovery/discover-routers.ts +15 -0
- package/src/vite/discovery/installed-packages.ts +159 -0
- package/src/vite/discovery/prerender-collection.ts +38 -1
- package/src/vite/discovery/router-versions-phase.ts +336 -0
- package/src/vite/discovery/shell-prerender-phase.ts +33 -43
- package/src/vite/discovery/state.ts +36 -1
- package/src/vite/discovery/virtual-module-codegen.ts +7 -2
- package/src/vite/encryption-key.ts +101 -9
- package/src/vite/inject-client-debug.ts +42 -20
- package/src/vite/plugin-types.ts +28 -0
- package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
- package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
- package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
- package/src/vite/plugins/expose-internal-ids.ts +12 -3
- package/src/vite/plugins/stable-rsc-output.ts +147 -0
- package/src/vite/plugins/version-plugin.ts +19 -24
- package/src/vite/plugins/version.d.ts +17 -2
- package/src/vite/plugins/virtual-entries.ts +39 -6
- package/src/vite/rango.ts +28 -3
- package/src/vite/router-discovery.ts +96 -13
- package/src/vite/utils/bundle-analysis.ts +39 -7
- package/src/vite/utils/client-chunks.ts +5 -1
- package/src/vite/utils/compare-strings.ts +9 -0
- package/src/vite/utils/package-resolution.ts +9 -0
- package/src/vite/utils/prerender-utils.ts +4 -1
- package/src/vite/utils/shared-utils.ts +1 -1
- package/dist/types/segment-content-promise.d.ts +0 -13
- package/src/segment-content-promise.ts +0 -67
|
@@ -170,6 +170,21 @@ export async function classifyRequest<TEnv = any>(
|
|
|
170
170
|
const willFullRender =
|
|
171
171
|
!isAction && !isLoaderFetch && !isPartialReq && !isPeRender;
|
|
172
172
|
|
|
173
|
+
// Before the version check: each router has its own version, so a tab of
|
|
174
|
+
// another app always carries one this router does not have. That request is
|
|
175
|
+
// an app switch, not a stale client.
|
|
176
|
+
const clientRouterId = url.searchParams.get("_rsc_rid");
|
|
177
|
+
if (
|
|
178
|
+
clientRouterId &&
|
|
179
|
+
clientRouterId !== deps.routerId &&
|
|
180
|
+
url.searchParams.has("_rsc_partial")
|
|
181
|
+
) {
|
|
182
|
+
return {
|
|
183
|
+
mode: "app-switch",
|
|
184
|
+
reloadUrl: stripInternalParams(url).toString(),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
173
188
|
const clientVersion = url.searchParams.get("_rsc_v");
|
|
174
189
|
if (
|
|
175
190
|
deps.routerVersion &&
|
|
@@ -195,18 +210,6 @@ export async function classifyRequest<TEnv = any>(
|
|
|
195
210
|
};
|
|
196
211
|
}
|
|
197
212
|
|
|
198
|
-
const clientRouterId = url.searchParams.get("_rsc_rid");
|
|
199
|
-
if (
|
|
200
|
-
clientRouterId &&
|
|
201
|
-
clientRouterId !== deps.routerId &&
|
|
202
|
-
url.searchParams.has("_rsc_partial")
|
|
203
|
-
) {
|
|
204
|
-
return {
|
|
205
|
-
mode: "app-switch",
|
|
206
|
-
reloadUrl: stripInternalParams(url).toString(),
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
213
|
const result = await resolveRoute<TEnv>(pathname, {
|
|
211
214
|
findMatch: deps.findMatch,
|
|
212
215
|
lite: true,
|
|
@@ -37,6 +37,12 @@ export interface TrieLeaf {
|
|
|
37
37
|
pr?: true;
|
|
38
38
|
/** Passthrough: handler kept in bundle for live fallback on unknown params */
|
|
39
39
|
pt?: true;
|
|
40
|
+
/**
|
|
41
|
+
* On-demand prerender eligible: the route opted into router.prerender()
|
|
42
|
+
* refresh. Distinct from `pr` — an od route may have no build-baked entry yet
|
|
43
|
+
* still needs a writable-overlay lookup at serve time.
|
|
44
|
+
*/
|
|
45
|
+
od?: true;
|
|
40
46
|
/** Response type for non-RSC routes (json, text, image, any) */
|
|
41
47
|
rt?: string;
|
|
42
48
|
/** Negotiate variants: response-type routes sharing this path */
|
|
@@ -72,6 +78,7 @@ export interface TrieNode {
|
|
|
72
78
|
* @param prerenderRouteNames - Optional set of prerendered route names (sets leaf.pr)
|
|
73
79
|
* @param passthroughRouteNames - Optional set of passthrough route names (sets leaf.pt)
|
|
74
80
|
* @param responseTypeRoutes - Optional map of route name to response type (sets leaf.rt)
|
|
81
|
+
* @param onDemandRouteNames - Optional set of on-demand-eligible route names (sets leaf.od)
|
|
75
82
|
*/
|
|
76
83
|
export function buildRouteTrie(
|
|
77
84
|
routeManifest: Record<string, string>,
|
|
@@ -80,6 +87,7 @@ export function buildRouteTrie(
|
|
|
80
87
|
prerenderRouteNames?: Set<string>,
|
|
81
88
|
passthroughRouteNames?: Set<string>,
|
|
82
89
|
responseTypeRoutes?: Record<string, string>,
|
|
90
|
+
onDemandRouteNames?: Set<string>,
|
|
83
91
|
): TrieNode {
|
|
84
92
|
const root: TrieNode = {};
|
|
85
93
|
|
|
@@ -99,6 +107,7 @@ export function buildRouteTrie(
|
|
|
99
107
|
...(trailingSlash ? { ts: trailingSlash } : {}),
|
|
100
108
|
...(prerenderRouteNames?.has(routeName) ? { pr: true } : {}),
|
|
101
109
|
...(passthroughRouteNames?.has(routeName) ? { pt: true } : {}),
|
|
110
|
+
...(onDemandRouteNames?.has(routeName) ? { od: true } : {}),
|
|
102
111
|
...(responseType ? { rt: responseType } : {}),
|
|
103
112
|
});
|
|
104
113
|
}
|
|
@@ -225,6 +225,29 @@ export interface Rango<
|
|
|
225
225
|
* ```
|
|
226
226
|
*/
|
|
227
227
|
fetch(request: Request, input?: RouterRequestInput<TEnv>): Promise<Response>;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Bind `{ env, ctx }` and get the on-demand (ISR-style) prerender runner, to refresh from a running app —
|
|
231
|
+
* fetch handler, cron, queue, workflow, webhook, or server action.
|
|
232
|
+
*
|
|
233
|
+
* Requestless: the producer renders with route params and env only, never the
|
|
234
|
+
* caller's cookies/headers/session. Only routes that opted in via
|
|
235
|
+
* `Prerender(..., { onDemand })` are persistable; others return a skipped
|
|
236
|
+
* result. Configure the store via the `prerender` router option.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* const prerender = router.prerender({ env, ctx });
|
|
241
|
+
* await prerender("/products/42");
|
|
242
|
+
* await prerender(
|
|
243
|
+
* { route: "products.detail", params: { id: "42" } },
|
|
244
|
+
* { throwOnError: true },
|
|
245
|
+
* );
|
|
246
|
+
* await prerender.many(targets, { concurrency: 4 });
|
|
247
|
+
* await prerender.markStale(["product:42"]);
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
prerender: import("../prerender/on-demand.js").PrerenderFn<TEnv, TRoutes>;
|
|
228
251
|
}
|
|
229
252
|
|
|
230
253
|
/**
|
|
@@ -332,6 +355,13 @@ export interface RangoInternal<
|
|
|
332
355
|
*/
|
|
333
356
|
readonly cache?: RangoOptions<TEnv>["cache"];
|
|
334
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Prerender store configuration (the writable durable overlay for on-demand
|
|
360
|
+
* prerender). Stored separately from the `prerender` trigger method so the RSC
|
|
361
|
+
* handler can resolve it per request. @internal
|
|
362
|
+
*/
|
|
363
|
+
readonly _prerenderConfig?: RangoOptions<TEnv>["prerender"];
|
|
364
|
+
|
|
335
365
|
/**
|
|
336
366
|
* Not found component to render when no route matches
|
|
337
367
|
*/
|
|
@@ -457,7 +487,8 @@ export interface RangoInternal<
|
|
|
457
487
|
readonly nonce?: NonceProvider<TEnv>;
|
|
458
488
|
|
|
459
489
|
/**
|
|
460
|
-
*
|
|
490
|
+
* The consumer-set version (createRouter({ version })), used for both of the
|
|
491
|
+
* router's versions. Undefined when the router uses its build versions.
|
|
461
492
|
*/
|
|
462
493
|
readonly version?: string;
|
|
463
494
|
|
|
@@ -619,6 +650,9 @@ export interface RangoInternal<
|
|
|
619
650
|
* Handle an RSC request.
|
|
620
651
|
*/
|
|
621
652
|
fetch(request: Request, input?: RouterRequestInput<TEnv>): Promise<Response>;
|
|
653
|
+
|
|
654
|
+
/** On-demand prerender binder: router.prerender({ env, ctx }) returns the runner. */
|
|
655
|
+
prerender: import("../prerender/on-demand.js").PrerenderFn<TEnv, TRoutes>;
|
|
622
656
|
}
|
|
623
657
|
|
|
624
658
|
/**
|
|
@@ -341,6 +341,36 @@ export interface RangoOptions<TEnv = any> {
|
|
|
341
341
|
searchParams?: CacheSearchParams;
|
|
342
342
|
});
|
|
343
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Writable prerender store for on-demand (ISR-style) prerender refresh.
|
|
346
|
+
*
|
|
347
|
+
* A durable overlay in front of the bundled build manifest. When provided,
|
|
348
|
+
* `router.prerender(target, { env, ctx })` renders a route requestlessly and
|
|
349
|
+
* writes a versioned payload here; the serve path reads the overlay before the
|
|
350
|
+
* bundled manifest. Same union shape as `cache`: a plain config object or a
|
|
351
|
+
* factory resolved per request/per call (never at `createRouter()` time), so
|
|
352
|
+
* multi-tenant apps never memoize one request's binding for another.
|
|
353
|
+
*
|
|
354
|
+
* @example Cloudflare KV overlay with SWR revalidation
|
|
355
|
+
* ```typescript
|
|
356
|
+
* import { createKVPrerenderStore } from "@rangojs/router/prerender/cloudflare";
|
|
357
|
+
*
|
|
358
|
+
* const router = createRouter<AppEnv>({
|
|
359
|
+
* prerender: (env) => ({
|
|
360
|
+
* store: createKVPrerenderStore(env.PRERENDER_KV),
|
|
361
|
+
* ttl: 3600,
|
|
362
|
+
* onRevalidate: (target, e) => e.PRERENDER_QUEUE.send({ target }),
|
|
363
|
+
* }),
|
|
364
|
+
* });
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
prerender?:
|
|
368
|
+
| import("../prerender/on-demand.js").PrerenderConfig<TEnv>
|
|
369
|
+
| ((
|
|
370
|
+
env: TEnv,
|
|
371
|
+
ctx?: ExecutionContext,
|
|
372
|
+
) => import("../prerender/on-demand.js").PrerenderConfig<TEnv>);
|
|
373
|
+
|
|
344
374
|
/**
|
|
345
375
|
* Named cache profiles for "use cache" directive.
|
|
346
376
|
* Profile names map to TTL/SWR configuration.
|
|
@@ -502,13 +532,23 @@ export interface RangoOptions<TEnv = any> {
|
|
|
502
532
|
nonce?: NonceProvider<TEnv>;
|
|
503
533
|
|
|
504
534
|
/**
|
|
505
|
-
*
|
|
506
|
-
*
|
|
535
|
+
* Version override. When set, this exact value is used for both of the
|
|
536
|
+
* router's versions: it keys the router's cached data (segments, `"use
|
|
537
|
+
* cache"` values, loader data) and its stored HTML (PPR shells,
|
|
538
|
+
* document-cache responses), is sent in payload metadata, and is what the
|
|
539
|
+
* browser's `_rsc_v` is compared with (a mismatch reloads the tab).
|
|
540
|
+
*
|
|
541
|
+
* Leave it unset for the default: rango computes two versions per router at
|
|
542
|
+
* build time, a data version from the router's server code and a document
|
|
543
|
+
* version that also covers the client assets. The same code builds to the
|
|
544
|
+
* same versions, so a deploy that does not change this router keeps its
|
|
545
|
+
* cache. In dev both are a stamp bumped on every RSC module edit.
|
|
507
546
|
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
547
|
+
* Set it to clear this router's cache on demand (change the value), or to
|
|
548
|
+
* tie the cache to your own release id (a per-deploy value clears the cache
|
|
549
|
+
* on every deploy).
|
|
510
550
|
*
|
|
511
|
-
* @default
|
|
551
|
+
* @default the router's build versions
|
|
512
552
|
*/
|
|
513
553
|
version?: string;
|
|
514
554
|
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
37
37
|
import { _getRequestContext } from "../../server/request-context.js";
|
|
38
38
|
import { getRouterContext } from "../router-context.js";
|
|
39
|
-
import {
|
|
39
|
+
import { trackStreamedHandler } from "./streamed-handler-boundary.js";
|
|
40
40
|
import { observeSegmentHandler } from "../instrument.js";
|
|
41
41
|
import {
|
|
42
42
|
track,
|
|
@@ -280,6 +280,13 @@ export interface ResolveSegmentOptions {
|
|
|
280
280
|
* #918: resolved twice on a MISS, once and discarded on a HIT).
|
|
281
281
|
*/
|
|
282
282
|
chain?: readonly EntryData[];
|
|
283
|
+
/**
|
|
284
|
+
* The matched chain entry being resolved: where the sync path catches a
|
|
285
|
+
* handler error, so a streamed handler (or slot owned by a routeless layout)
|
|
286
|
+
* recovers into the same boundary. resolveSegment sets it for the entry it
|
|
287
|
+
* resolves; nested resolutions keep the outer value.
|
|
288
|
+
*/
|
|
289
|
+
boundaryEntry?: EntryData;
|
|
283
290
|
}
|
|
284
291
|
|
|
285
292
|
/**
|
|
@@ -298,6 +305,8 @@ export async function resolveSegment<TEnv>(
|
|
|
298
305
|
options?: ResolveSegmentOptions,
|
|
299
306
|
): Promise<ResolvedSegment[]> {
|
|
300
307
|
const segments: ResolvedSegment[] = [];
|
|
308
|
+
const boundaryEntry = options?.boundaryEntry ?? entry;
|
|
309
|
+
if (!options?.boundaryEntry) options = { ...options, boundaryEntry };
|
|
301
310
|
|
|
302
311
|
if (entry.type === "layout" || entry.type === "cache") {
|
|
303
312
|
if (!options?.skipLoaders) {
|
|
@@ -398,19 +407,15 @@ export async function resolveSegment<TEnv>(
|
|
|
398
407
|
if (result instanceof Promise) {
|
|
399
408
|
warnOnStreamedResponse(result, entry.id);
|
|
400
409
|
result.finally(doneRouteHandler).catch(() => {});
|
|
401
|
-
|
|
410
|
+
component = trackStreamedHandler(deps, result, {
|
|
411
|
+
boundaryEntry,
|
|
402
412
|
segmentId: entry.shortCode,
|
|
403
413
|
segmentType: entry.type,
|
|
404
|
-
|
|
405
|
-
observeStreamedHandler(
|
|
406
|
-
tracked,
|
|
407
|
-
entry.shortCode,
|
|
408
|
-
entry.type,
|
|
409
|
-
context.pathname,
|
|
414
|
+
context,
|
|
410
415
|
routeKey,
|
|
411
416
|
params,
|
|
412
|
-
|
|
413
|
-
|
|
417
|
+
recover: !options?.throwOnError,
|
|
418
|
+
});
|
|
414
419
|
} else {
|
|
415
420
|
doneRouteHandler();
|
|
416
421
|
component = result;
|
|
@@ -679,19 +684,15 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
679
684
|
: handler;
|
|
680
685
|
if (result instanceof Promise) {
|
|
681
686
|
result.finally(doneParallelHandler).catch(() => {});
|
|
682
|
-
|
|
687
|
+
component = trackStreamedHandler(deps, result, {
|
|
688
|
+
boundaryEntry: options?.boundaryEntry ?? parallelEntry,
|
|
683
689
|
segmentId: `${parentShortCode}.${slot}`,
|
|
684
690
|
segmentType: "parallel",
|
|
685
|
-
|
|
686
|
-
observeStreamedHandler(
|
|
687
|
-
tracked,
|
|
688
|
-
`${parentShortCode}.${slot}`,
|
|
689
|
-
"parallel",
|
|
690
|
-
context.pathname,
|
|
691
|
+
context,
|
|
691
692
|
routeKey,
|
|
692
693
|
params,
|
|
693
|
-
|
|
694
|
-
|
|
694
|
+
recover: !options?.throwOnError,
|
|
695
|
+
});
|
|
695
696
|
} else {
|
|
696
697
|
doneParallelHandler();
|
|
697
698
|
component = result as ReactNode;
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* 1. options.key(requestCtx) — full override, stored namespaced as
|
|
10
10
|
* loader:{loaderId}:key:{encodeURIComponent(result)} (#1009)
|
|
11
11
|
* 2. store.keyGenerator(requestCtx, defaultKey) — store-level modification
|
|
12
|
-
* 3. loader:{loaderId}:{host}{pathname}:{sortedParams} — default
|
|
12
|
+
* 3. loader:{loaderId}:{routerId}@{host}{pathname}:{sortedParams} — default
|
|
13
|
+
* (a loader's ctx is one router's: cache-key-utils.ts, the router rule)
|
|
13
14
|
*
|
|
14
15
|
* Values are serialized via RSC Flight (serializeResult/deserializeResult),
|
|
15
16
|
* supporting ReactNode, Promises, null, and all RSC-serializable types.
|
|
@@ -50,24 +51,34 @@ import {
|
|
|
50
51
|
type LoaderIdentityRead,
|
|
51
52
|
} from "../../server/context.js";
|
|
52
53
|
import type { HandlerContext, InternalHandlerContext } from "../../types.js";
|
|
53
|
-
import type {
|
|
54
|
+
import type {
|
|
55
|
+
HandleStore,
|
|
56
|
+
RecordAuthority,
|
|
57
|
+
} from "../../server/handle-store.js";
|
|
54
58
|
import type { CacheItemResult } from "../../cache/types.js";
|
|
55
59
|
import {
|
|
56
60
|
startHandleCapture,
|
|
57
61
|
type HandleCapture,
|
|
58
62
|
} from "../../cache/handle-capture.js";
|
|
59
|
-
import {
|
|
63
|
+
import {
|
|
64
|
+
appendHandles,
|
|
65
|
+
encodeHandles,
|
|
66
|
+
decodeHandles,
|
|
67
|
+
type OwnedPushDelivery,
|
|
68
|
+
} from "../../cache/handle-snapshot.js";
|
|
69
|
+
import type { ShellLoaderSeedEntry } from "../../cache/shell-snapshot.js";
|
|
60
70
|
import { INTERNAL_RANGO_DEBUG } from "../../internal-debug.js";
|
|
61
71
|
import { runIdentityExempt } from "../../cache/cache-exec-scope.js";
|
|
62
72
|
import {
|
|
63
73
|
getRequestContext,
|
|
64
74
|
_getRequestContext,
|
|
65
75
|
runWithRequestContext,
|
|
76
|
+
type RequestContext,
|
|
66
77
|
} from "../../server/request-context.js";
|
|
67
78
|
import { observePhase, PHASES } from "../instrument.js";
|
|
68
79
|
import {
|
|
69
80
|
KEY_PART_PREFIX,
|
|
70
|
-
|
|
81
|
+
requestKeyBase,
|
|
71
82
|
} from "../../cache/cache-key-utils.js";
|
|
72
83
|
import {
|
|
73
84
|
resolveTtl,
|
|
@@ -78,6 +89,7 @@ import {
|
|
|
78
89
|
DEFAULT_ROUTE_TTL,
|
|
79
90
|
} from "../../cache/cache-policy.js";
|
|
80
91
|
import { readThroughItem } from "../../cache/read-through-swr.js";
|
|
92
|
+
import { isWarmReplace, noteWarmWrite } from "../../prerender/warm-request.js";
|
|
81
93
|
import {
|
|
82
94
|
executionStart,
|
|
83
95
|
predatesInvalidation,
|
|
@@ -128,17 +140,6 @@ function debugLoaderCacheLog(message: string): void {
|
|
|
128
140
|
}
|
|
129
141
|
}
|
|
130
142
|
|
|
131
|
-
function getDefaultLoaderCacheKey(
|
|
132
|
-
loaderId: string,
|
|
133
|
-
host: string,
|
|
134
|
-
pathname: string,
|
|
135
|
-
params: Record<string, string>,
|
|
136
|
-
): string {
|
|
137
|
-
const paramStr = sortedRouteParams(params);
|
|
138
|
-
const base = paramStr ? `${pathname}:${paramStr}` : pathname;
|
|
139
|
-
return `loader:${loaderId}:${host}${base}`;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
143
|
/**
|
|
143
144
|
* A loader's own `key()` result as its entry key (#1009). The result is
|
|
144
145
|
* often request input, and the item family (`getItem`/`setItem`) has no
|
|
@@ -149,7 +150,7 @@ function getDefaultLoaderCacheKey(
|
|
|
149
150
|
* - an encoded result holds no `:`, so the key reads unambiguously from
|
|
150
151
|
* the right (result, `key:`, loader id), and two namespaced keys are
|
|
151
152
|
* equal only for the same loader and result;
|
|
152
|
-
* - a default key `loader:<id
|
|
153
|
+
* - a default key `loader:<id>:[<router>@]<host><pathname>...` holds a `/` past its
|
|
153
154
|
* id, and an encoded result holds none, so the two are equal only if one
|
|
154
155
|
* loader's id is literally `<other id>:<host>/<path>...`. Loader ids can
|
|
155
156
|
* hold `:` (a dev id is a root-relative path, absolute `D:/...` on
|
|
@@ -178,8 +179,9 @@ async function resolveLoaderKey(
|
|
|
178
179
|
// (cache-runtime pushes ctx.url.host). Without it, a multi-tenant host router
|
|
179
180
|
// serving the same pathname for different hosts would leak one host's cached
|
|
180
181
|
// loader data to another.
|
|
181
|
-
const
|
|
182
|
-
const
|
|
182
|
+
const requestCtx = getRequestContext();
|
|
183
|
+
const host = requestCtx?.url?.host ?? "localhost";
|
|
184
|
+
const defaultKey = `loader:${loaderId}:${requestKeyBase(requestCtx, host, pathname, undefined, params)}`;
|
|
183
185
|
if (options === false) return { key: defaultKey, declared: false };
|
|
184
186
|
const keyFn = options.key;
|
|
185
187
|
return {
|
|
@@ -256,26 +258,6 @@ function chainLoaderEntries(entry: EntryData): readonly LoaderEntry[] {
|
|
|
256
258
|
return loaders;
|
|
257
259
|
}
|
|
258
260
|
|
|
259
|
-
/**
|
|
260
|
-
* The ids of the loaders a request over the matched chain `entries` runs on
|
|
261
|
-
* the live lane (no `ssr: false`), on a chain entry, its orphan layouts or
|
|
262
|
-
* its parallel slots. A document HIT tail restores the record's pushes of
|
|
263
|
-
* every other owner as authoritative (withCacheLookup, HandleStore
|
|
264
|
-
* pushRestored); a live-lane loader is a hole, so its live run replaces its
|
|
265
|
-
* recorded pushes instead. Memoized per entry.
|
|
266
|
-
*/
|
|
267
|
-
export function liveLaneLoaderIds(
|
|
268
|
-
entries: readonly EntryData[],
|
|
269
|
-
): ReadonlySet<string> {
|
|
270
|
-
const ids = new Set<string>();
|
|
271
|
-
for (const entry of entries) {
|
|
272
|
-
for (const l of chainLoaderEntries(entry)) {
|
|
273
|
-
if (!l.bake) ids.add(l.loader.$$id);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return ids;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
261
|
/**
|
|
280
262
|
* The lane of each loader the matched chain `entries` registers: "live" (no
|
|
281
263
|
* `ssr: false`) or "bake". A loader registered on both is "live". A PPR
|
|
@@ -295,6 +277,75 @@ export function routeLoaderLanes(
|
|
|
295
277
|
return lanes;
|
|
296
278
|
}
|
|
297
279
|
|
|
280
|
+
/**
|
|
281
|
+
* The pins this request serves loaders from, by loader id: the seed a
|
|
282
|
+
* document HIT tail (serveShellHit) or a navigation replay whose doc record
|
|
283
|
+
* hit (matchPartialWithPprReplay) armed from the shell entry's loader
|
|
284
|
+
* records. A capture serves none: its bake-lane loaders execute.
|
|
285
|
+
*
|
|
286
|
+
* The one answer to "is this loader served from the shell record on this
|
|
287
|
+
* request", for its value (resolveLoaderData) and for the record's copies of
|
|
288
|
+
* its handle pushes (loaderPins): both look the loader up here and nowhere
|
|
289
|
+
* else. A condition that makes pins, or one loader's pin, not apply belongs
|
|
290
|
+
* here or in the seed's decode (shell-snapshot.ts buildShellLoaderSeed),
|
|
291
|
+
* never after a lookup, or the two disagree: issues #1001 and #1003 were the
|
|
292
|
+
* pushes decided from the request's type while the value was decided from
|
|
293
|
+
* the pin.
|
|
294
|
+
*/
|
|
295
|
+
function servedPins(
|
|
296
|
+
reqCtx: RequestContext<any> | undefined,
|
|
297
|
+
): ReadonlyMap<string, ShellLoaderSeedEntry> | undefined {
|
|
298
|
+
return isShellCaptureActive(reqCtx) ? undefined : reqCtx?._shellLoaderSeed;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* What a record restored on a request over the matched chain `entries` is to
|
|
303
|
+
* each loader's handle pushes (HandleStore RecordAuthority), from the pins
|
|
304
|
+
* the request serves (servedPins). Call it when the record hits: the pins are
|
|
305
|
+
* read here, once, because a navigation replay arms its seed for the match
|
|
306
|
+
* only and the store asks while loader bodies run.
|
|
307
|
+
*
|
|
308
|
+
* Each side applies its own lane to the pin. The value is per registration
|
|
309
|
+
* (resolveLoaderData: an `ssr: false` registration is served its pin). The
|
|
310
|
+
* pushes are per loader: one the route also registers without `ssr: false`
|
|
311
|
+
* is a hole whatever its other registration's pin says, because the capture
|
|
312
|
+
* credited its pushes to the live registration's run (routeLoaderLanes).
|
|
313
|
+
*
|
|
314
|
+
* A dependency the route does not register has no pin of its own, and the
|
|
315
|
+
* record does not name the loader that ran it at capture: its copies stand
|
|
316
|
+
* only while every `ssr: false` loader is pinned (exact when the pins are
|
|
317
|
+
* all there or all gone).
|
|
318
|
+
*/
|
|
319
|
+
export function loaderPins(
|
|
320
|
+
entries: readonly EntryData[],
|
|
321
|
+
reqCtx: RequestContext<any> | undefined,
|
|
322
|
+
): OwnedPushDelivery {
|
|
323
|
+
const pins = servedPins(reqCtx);
|
|
324
|
+
let lanes: ReadonlyMap<string, "live" | "bake"> | undefined;
|
|
325
|
+
let dependency: RecordAuthority | undefined;
|
|
326
|
+
return (loaderId) => {
|
|
327
|
+
lanes ??= routeLoaderLanes(entries);
|
|
328
|
+
const lane = lanes.get(loaderId);
|
|
329
|
+
if (lane === "live") return "hole";
|
|
330
|
+
if (lane === "bake") {
|
|
331
|
+
const pin = pins?.get(loaderId);
|
|
332
|
+
return pin ? (pin.complete ? "pin" : "copies") : "hole";
|
|
333
|
+
}
|
|
334
|
+
if (dependency === undefined) {
|
|
335
|
+
let bakeLane = 0;
|
|
336
|
+
let pinned = 0;
|
|
337
|
+
for (const [id, registered] of lanes) {
|
|
338
|
+
if (registered !== "bake") continue;
|
|
339
|
+
bakeLane++;
|
|
340
|
+
if (pins?.has(id)) pinned++;
|
|
341
|
+
}
|
|
342
|
+
dependency =
|
|
343
|
+
bakeLane > 0 && pinned === bakeLane ? "copies" : "placeholders";
|
|
344
|
+
}
|
|
345
|
+
return dependency;
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
298
349
|
function getLoaderStore(
|
|
299
350
|
loaderEntry: LoaderEntry,
|
|
300
351
|
): import("../../cache/types.js").SegmentCacheStore | null {
|
|
@@ -323,11 +374,12 @@ function getLoaderStore(
|
|
|
323
374
|
* replay; and the store reads the body scope to tell a live push from a
|
|
324
375
|
* replayed one.
|
|
325
376
|
* A PPR shell capture records the replayed pushes under this loader (its
|
|
326
|
-
* push funnel reads the pushReplayed owner, shell-capture.ts), so a
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
377
|
+
* push funnel reads the pushReplayed owner, shell-capture.ts), so a replay
|
|
378
|
+
* of that shell that serves this loader from its pin restores them owned
|
|
379
|
+
* (HandleStore.pushRestored), on a document HIT and on a navigation alike.
|
|
380
|
+
* This replay still runs there: the store drops its settled values, which
|
|
381
|
+
* the record already restored, and keeps the thenable ones the record could
|
|
382
|
+
* not keep (a deferred push, the reason the loader's pin carries `runs`).
|
|
331
383
|
*/
|
|
332
384
|
async function replayLoaderHandles(
|
|
333
385
|
encoded: string | undefined,
|
|
@@ -338,34 +390,7 @@ async function replayLoaderHandles(
|
|
|
338
390
|
): Promise<void> {
|
|
339
391
|
const recorded = encoded ? await decodeHandles(encoded) : {};
|
|
340
392
|
if (!recorded) return;
|
|
341
|
-
|
|
342
|
-
const claims = (owner: string): boolean => {
|
|
343
|
-
let deliver = delivers.get(owner);
|
|
344
|
-
if (deliver === undefined) {
|
|
345
|
-
deliver = claim ? claim(owner) : true;
|
|
346
|
-
delivers.set(owner, deliver);
|
|
347
|
-
}
|
|
348
|
-
return deliver;
|
|
349
|
-
};
|
|
350
|
-
const replay = (): void => {
|
|
351
|
-
for (const key in recorded) {
|
|
352
|
-
const owner = key.slice(key.indexOf(":") + 1);
|
|
353
|
-
if (!claims(owner)) continue;
|
|
354
|
-
for (const [handleName, values] of Object.entries(recorded[key])) {
|
|
355
|
-
for (const value of values) {
|
|
356
|
-
handleStore.pushReplayed(handleName, segmentId, value, owner);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
};
|
|
361
|
-
// A hole's entry, pushes or none, takes the place of the shell copies of
|
|
362
|
-
// its pushes (HandleStore.redeliverReplays). Not once the hole delivered
|
|
363
|
-
// in this request: a run a reader started first replaces them itself.
|
|
364
|
-
if (claims(cachedLoaderId)) {
|
|
365
|
-
handleStore.redeliverReplays(cachedLoaderId, replay);
|
|
366
|
-
} else {
|
|
367
|
-
replay();
|
|
368
|
-
}
|
|
393
|
+
appendHandles(recorded, handleStore, segmentId, claim, cachedLoaderId);
|
|
369
394
|
}
|
|
370
395
|
|
|
371
396
|
/**
|
|
@@ -492,10 +517,11 @@ export function resolveLoaderData<TEnv>(
|
|
|
492
517
|
return createMaskedLoaderPromise();
|
|
493
518
|
}
|
|
494
519
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
520
|
+
// `bake`: the pin is the `ssr: false` registration's. The caller's key is
|
|
521
|
+
// passed for an unflagged loader of an entry without loading() too.
|
|
522
|
+
if (bakeSegmentKey && loaderEntry.bake) {
|
|
523
|
+
const recorded = servedPins(reqCtx)?.get(loaderEntry.loader.$$id);
|
|
524
|
+
if (recorded) {
|
|
499
525
|
if (!recorded.holes) {
|
|
500
526
|
// Pin-first (hole-free record): the pinned container is what the
|
|
501
527
|
// payload serves (recorded paths win wholesale), resolved
|
|
@@ -734,7 +760,11 @@ function executeLoaderData<TEnv>(
|
|
|
734
760
|
};
|
|
735
761
|
|
|
736
762
|
const data = await readThroughItem({
|
|
737
|
-
|
|
763
|
+
// A router.prerender() warm takes the miss path: the body runs and
|
|
764
|
+
// setItem replaces the entry.
|
|
765
|
+
getItem: isWarmReplace(requestCtxForExecute)
|
|
766
|
+
? async () => null
|
|
767
|
+
: (k) => store.getItem!(k),
|
|
738
768
|
// Handles ride the entry like "use cache" (encodeHandles: Flight, pending
|
|
739
769
|
// pushes awaited up to its timeout, the whole blob dropped on a timeout
|
|
740
770
|
// or a thrown encode). Encoded here, inside the deferred write, so a
|
|
@@ -761,7 +791,9 @@ function executeLoaderData<TEnv>(
|
|
|
761
791
|
...o,
|
|
762
792
|
tags: entryTags.length > 0 ? entryTags : undefined,
|
|
763
793
|
...(handles ? { handles } : {}),
|
|
794
|
+
startedAt: start?.at,
|
|
764
795
|
});
|
|
796
|
+
noteWarmWrite(requestCtxForExecute, "item");
|
|
765
797
|
}),
|
|
766
798
|
key,
|
|
767
799
|
execute: async () => {
|
|
@@ -800,7 +832,8 @@ function executeLoaderData<TEnv>(
|
|
|
800
832
|
});
|
|
801
833
|
|
|
802
834
|
// An entry without handles (none recorded, or dropped by an encode
|
|
803
|
-
// timeout) replays none, and still
|
|
835
|
+
// timeout) replays none, and still takes the place of the loader's
|
|
836
|
+
// placeholders (appendHandles): a claimed entry is the loader's source.
|
|
804
837
|
if (cachedRead && handleStore && owningSegmentId) {
|
|
805
838
|
await replayLoaderHandles(
|
|
806
839
|
hitHandles,
|