@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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { replayLoaderPush } from "./handle-capture.js";
|
|
10
10
|
import type { ResolvedSegment } from "../types.js";
|
|
11
|
-
import type { HandleStore } from "../server/handle-store.js";
|
|
11
|
+
import type { HandleStore, RecordAuthority } from "../server/handle-store.js";
|
|
12
12
|
import type { HandleOwners, SegmentHandleData } from "./types.js";
|
|
13
13
|
// segment-codec eagerly pulls @vitejs/plugin-rsc (a virtual: module unresolvable
|
|
14
14
|
// in plain node/vitest). It is imported LAZILY inside the two async encode/decode
|
|
@@ -134,53 +134,51 @@ export function captureHandleOwners(
|
|
|
134
134
|
return owners;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
/**
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
137
|
+
/**
|
|
138
|
+
* captureHandles and captureHandleOwners cut down to the handle arrays that
|
|
139
|
+
* hold an owned value, each whole (its unowned values too, in place), so
|
|
140
|
+
* restoreHandles rebuilds exactly those arrays over a store that already
|
|
141
|
+
* holds the rest. A prerender-served shell capture stores this: the
|
|
142
|
+
* prerender store restores the handler pushes on a HIT, and the loader
|
|
143
|
+
* pushes have no other record (shell-capture.ts captureAndStoreShell).
|
|
144
|
+
*/
|
|
145
|
+
export function captureOwnedHandles(handleStore: HandleStore): {
|
|
146
|
+
handles: HandleRecord;
|
|
147
|
+
owners?: HandleOwners;
|
|
148
|
+
} {
|
|
149
|
+
const handles: HandleRecord = {};
|
|
150
|
+
let owners: HandleOwners | undefined;
|
|
151
|
+
for (const id of handleStore.getOwnedSegmentIds()) {
|
|
152
|
+
const segOwners = handleStore.getRecordOwners(id);
|
|
153
|
+
if (!segOwners) continue;
|
|
154
|
+
const kept = handleStore.getDataForSegment(id, true);
|
|
155
|
+
handles[id] = {};
|
|
156
|
+
for (const name in segOwners) handles[id][name] = kept[name] ?? [];
|
|
157
|
+
(owners ??= {})[id] = segOwners;
|
|
158
|
+
}
|
|
159
|
+
return { handles, owners };
|
|
158
160
|
}
|
|
159
161
|
|
|
162
|
+
/**
|
|
163
|
+
* What the record being restored is to each loader's handle pushes on this
|
|
164
|
+
* request (HandleStore RecordAuthority). Built by loaderPins (loader-cache.ts)
|
|
165
|
+
* over the pins resolveLoaderData serves a loader's value from, so a value
|
|
166
|
+
* and its record copies get one answer.
|
|
167
|
+
*/
|
|
168
|
+
export type OwnedPushDelivery = (loaderId: string) => RecordAuthority;
|
|
169
|
+
|
|
160
170
|
/**
|
|
161
171
|
* Restore handle data from a cached snapshot into the handle store.
|
|
162
172
|
* Used when serving cached segments to replay their handle data: a route
|
|
163
173
|
* cache() record owns its segments' arrays, so it REPLACES them.
|
|
164
174
|
*
|
|
165
175
|
* A segment with `owners` is replaced with empty arrays and re-pushed in
|
|
166
|
-
* recorded order
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* restores as a plain replay.
|
|
173
|
-
*
|
|
174
|
-
* `owned.restore`: a document HIT tail restoring the shell's doc record
|
|
175
|
-
* (withCacheLookup). A bake-lane owner's values are the prelude's, so they go
|
|
176
|
-
* through pushRestored and stand: a run of that loader on the HIT (a
|
|
177
|
-
* promise-carrying `ssr: false` loader, one whose record asks for a run)
|
|
178
|
-
* reads the store, and its settled pushes are dropped, not swapped in.
|
|
179
|
-
* Nothing is claimed, so the loader's own cache() HIT still replays the
|
|
180
|
-
* pushes the record could not keep (the thenable ones pushRestored lets
|
|
181
|
-
* through). A loader the route also runs on the live lane is a hole: its
|
|
182
|
-
* values keep pushReplayed, unclaimed, and its live run replaces them
|
|
183
|
-
* (#936), even inside a restored loader's body (`owned.liveLane`).
|
|
176
|
+
* recorded order. A loader-owned value stands (pushRestored) where `owned`
|
|
177
|
+
* says the record supplies that loader's value too ("pin", "copies"), and is
|
|
178
|
+
* a placeholder (pushPlaceholder) everywhere else: without `owned`, every
|
|
179
|
+
* owner. Neither claims its loader, so the loader's own cache() HIT still
|
|
180
|
+
* delivers (the thenable pushes a restored copy lets through, or its entry's
|
|
181
|
+
* pushes in a placeholder's place).
|
|
184
182
|
*/
|
|
185
183
|
export function restoreHandles(
|
|
186
184
|
handles: Record<string, SegmentHandleData>,
|
|
@@ -188,8 +186,6 @@ export function restoreHandles(
|
|
|
188
186
|
owners?: HandleOwners,
|
|
189
187
|
owned?: OwnedPushDelivery,
|
|
190
188
|
): void {
|
|
191
|
-
let delivers: Map<string, boolean> | undefined;
|
|
192
|
-
if (owned?.liveLane) handleStore.markLiveLane(owned.liveLane);
|
|
193
189
|
for (const [segId, segHandles] of Object.entries(handles)) {
|
|
194
190
|
if (Object.keys(segHandles).length === 0) continue;
|
|
195
191
|
const segOwners = owners?.[segId];
|
|
@@ -208,29 +204,42 @@ export function restoreHandles(
|
|
|
208
204
|
handleStore.push(handleName, segId, values[i]);
|
|
209
205
|
continue;
|
|
210
206
|
}
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
207
|
+
const authority = owned?.(owner);
|
|
208
|
+
if (authority === "pin" || authority === "copies") {
|
|
213
209
|
handleStore.pushRestored(handleName, segId, values[i], owner);
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (hole) {
|
|
217
|
-
handleStore.pushReplayed(handleName, segId, values[i], owner);
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
delivers ??= new Map();
|
|
221
|
-
let deliver = delivers.get(owner);
|
|
222
|
-
if (deliver === undefined) {
|
|
223
|
-
deliver = owned?.claim ? owned.claim(owner) : true;
|
|
224
|
-
delivers.set(owner, deliver);
|
|
225
|
-
}
|
|
226
|
-
if (deliver) {
|
|
227
|
-
handleStore.pushReplayed(handleName, segId, values[i], owner);
|
|
210
|
+
} else {
|
|
211
|
+
handleStore.pushPlaceholder(handleName, segId, values[i], owner);
|
|
228
212
|
}
|
|
229
213
|
}
|
|
230
214
|
}
|
|
231
215
|
}
|
|
232
216
|
}
|
|
233
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Serve a doc record's handles: set its authority over the loaders' pushes
|
|
220
|
+
* (with or without a handles blob: a pinned loader whose capture pushed
|
|
221
|
+
* nothing has no copy, and its run must still add no settled push), then
|
|
222
|
+
* decode and restoreHandles. `keepIds` limits the restore to the segments a
|
|
223
|
+
* boundary-scoped scope serves. A decode failure skips the restore.
|
|
224
|
+
*/
|
|
225
|
+
export async function restoreRecordHandles(
|
|
226
|
+
handleStore: HandleStore,
|
|
227
|
+
record: { handles?: string; handleOwners?: HandleOwners },
|
|
228
|
+
owned: OwnedPushDelivery | undefined,
|
|
229
|
+
keepIds?: ReadonlySet<string>,
|
|
230
|
+
): Promise<void> {
|
|
231
|
+
if (owned) handleStore.setRecordAuthority(owned);
|
|
232
|
+
if (!record.handles) return;
|
|
233
|
+
const decoded = await decodeHandles(record.handles);
|
|
234
|
+
if (!decoded) return;
|
|
235
|
+
if (keepIds) {
|
|
236
|
+
for (const id of Object.keys(decoded)) {
|
|
237
|
+
if (!keepIds.has(id)) delete decoded[id];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
restoreHandles(decoded, handleStore, record.handleOwners, owned);
|
|
241
|
+
}
|
|
242
|
+
|
|
234
243
|
/**
|
|
235
244
|
* Append recorded handle pushes to the store, in recorded order, via push()
|
|
236
245
|
* so active captures and loader-scope tagging see them like live pushes. For
|
|
@@ -238,43 +247,66 @@ export function restoreHandles(
|
|
|
238
247
|
* restoreHandles would wipe those. `segmentId` redirects every value to one
|
|
239
248
|
* segment (the caller's).
|
|
240
249
|
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
250
|
+
* The replay of every cached unit that records by owner: a "use cache"
|
|
251
|
+
* function (useCacheRecordKey) and a loader's own cache() (loader-cache.ts
|
|
252
|
+
* recordOwnerKey, with `unitLoader` the cached loader).
|
|
253
|
+
*
|
|
254
|
+
* A `${seq}:${loaderId}` group is that loader's pushes, which reach the page
|
|
255
|
+
* once per request: `claim` (setupLoaderAccess _claimLoaderPushes) is asked
|
|
256
|
+
* once per loader and skips one that already ran or was replayed in this
|
|
257
|
+
* request; a claimed loader that runs later replaces its replayed values
|
|
258
|
+
* (HandleStore.pushReplayed). A group without an owner (`${seq}:`, or a key
|
|
259
|
+
* without ":" from a record written before owner keys, which is a segment
|
|
260
|
+
* id) is the unit's own: `unitLoader`'s, or no loader's for a function,
|
|
261
|
+
* whose pushes are plain.
|
|
262
|
+
*
|
|
263
|
+
* The claimed loaders' placeholders give way to this delivery
|
|
264
|
+
* (HandleStore.replacePlaceholders), `unitLoader`'s even when the entry
|
|
265
|
+
* recorded no push for it: the entry stands for that loader's run.
|
|
266
|
+
*
|
|
267
|
+
* No `claim` means the caller's pushes are diverted off the page (a stale
|
|
268
|
+
* refresh: cache-runtime.ts refreshView, setupLoaderAccess
|
|
269
|
+
* _runLoaderIsolated): every group is delivered, into the diverting capture,
|
|
270
|
+
* and the page's placeholders stay, or the page would lose the push.
|
|
249
271
|
*/
|
|
250
272
|
export function appendHandles(
|
|
251
273
|
handles: Record<string, SegmentHandleData>,
|
|
252
274
|
handleStore: HandleStore,
|
|
253
|
-
segmentId
|
|
254
|
-
claim
|
|
275
|
+
segmentId: string | undefined,
|
|
276
|
+
claim: ((loaderId: string) => boolean) | undefined,
|
|
277
|
+
unitLoader?: string,
|
|
255
278
|
): void {
|
|
256
|
-
|
|
257
|
-
for (const [key, segHandles] of Object.entries(handles)) {
|
|
279
|
+
const ownerOf = (key: string): string | undefined => {
|
|
258
280
|
const colon = key.indexOf(":");
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
claims.set(owner, deliver);
|
|
266
|
-
}
|
|
267
|
-
if (!deliver) continue;
|
|
281
|
+
return (colon < 0 ? "" : key.slice(colon + 1)) || unitLoader;
|
|
282
|
+
};
|
|
283
|
+
const claims = new Map<string, boolean>();
|
|
284
|
+
const ask = (owner: string | undefined): void => {
|
|
285
|
+
if (owner !== undefined && !claims.has(owner)) {
|
|
286
|
+
claims.set(owner, claim ? claim(owner) : true);
|
|
268
287
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
288
|
+
};
|
|
289
|
+
ask(unitLoader);
|
|
290
|
+
for (const key in handles) ask(ownerOf(key));
|
|
291
|
+
const deliver = (): void => {
|
|
292
|
+
for (const [key, segHandles] of Object.entries(handles)) {
|
|
293
|
+
const owner = ownerOf(key);
|
|
294
|
+
if (owner !== undefined && !claims.get(owner)) continue;
|
|
295
|
+
const target = segmentId ?? key;
|
|
296
|
+
for (const [handleName, values] of Object.entries(segHandles)) {
|
|
297
|
+
for (const value of values) {
|
|
298
|
+
if (owner !== undefined) {
|
|
299
|
+
replayLoaderPush(handleStore, handleName, target, value, owner);
|
|
300
|
+
} else {
|
|
301
|
+
handleStore.push(handleName, target, value);
|
|
302
|
+
}
|
|
276
303
|
}
|
|
277
304
|
}
|
|
278
305
|
}
|
|
306
|
+
};
|
|
307
|
+
const granted: string[] = [];
|
|
308
|
+
if (claim) {
|
|
309
|
+
for (const [owner, given] of claims) if (given) granted.push(owner);
|
|
279
310
|
}
|
|
311
|
+
handleStore.replacePlaceholders(granted, deliver);
|
|
280
312
|
}
|
package/src/cache/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type {
|
|
2
2
|
SegmentCacheStore,
|
|
3
|
+
CacheStoreScope,
|
|
3
4
|
CachedEntryData,
|
|
4
5
|
CacheGetResult,
|
|
5
6
|
CacheItemResult,
|
|
@@ -56,3 +57,7 @@ export {
|
|
|
56
57
|
} from "./document-cache.js";
|
|
57
58
|
|
|
58
59
|
export type { CacheErrorCategory } from "./cache-error.js";
|
|
60
|
+
|
|
61
|
+
// For a custom persistent store: the versions the built-in stores key with.
|
|
62
|
+
export { getCacheVersions } from "../server/build-version-table.js";
|
|
63
|
+
export type { RouterVersions } from "../router-versions.js";
|
|
@@ -43,6 +43,59 @@ export interface ExecutionStart {
|
|
|
43
43
|
readonly at: number;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* The `taggedAt` / `ta` stamp a store writes on a tagged data entry: the start
|
|
48
|
+
* of the execution that produced the value (`startedAt`), so a marker written
|
|
49
|
+
* while it ran rejects the entry on read (#1068). Write time when the writer
|
|
50
|
+
* knows no start. Never later than now: a start from a skewed clock must not
|
|
51
|
+
* stamp the future, which would make the entry look newer than every marker.
|
|
52
|
+
*/
|
|
53
|
+
export function entryStamp(startedAt: number | undefined): number {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
// 0 is "no start", not the epoch: no marker is older than it, so such an
|
|
56
|
+
// entry would never be rejected.
|
|
57
|
+
return typeof startedAt === "number" &&
|
|
58
|
+
Number.isFinite(startedAt) &&
|
|
59
|
+
startedAt > 0
|
|
60
|
+
? Math.min(startedAt, now)
|
|
61
|
+
: now;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The stamp of a data entry: `entryStamp` for a tagged entry, none otherwise. */
|
|
65
|
+
export function taggedStamp(
|
|
66
|
+
tags: readonly string[] | undefined,
|
|
67
|
+
startedAt: number | undefined,
|
|
68
|
+
): number | undefined {
|
|
69
|
+
return Array.isArray(tags) && tags.length > 0
|
|
70
|
+
? entryStamp(startedAt)
|
|
71
|
+
: undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* putResponse has no options object to carry the start and takes no new
|
|
76
|
+
* positional parameter, so the writer marks the Response it hands over.
|
|
77
|
+
* On globalThis like the order: a second evaluated copy of this module that
|
|
78
|
+
* wrote the mark must be read by the store's copy, else the stamp silently
|
|
79
|
+
* falls back to write time.
|
|
80
|
+
*/
|
|
81
|
+
const responseStarts: WeakMap<Response, number> = ((globalThis as any)[
|
|
82
|
+
Symbol.for("rangojs-router:response-execution-start")
|
|
83
|
+
] ??= new WeakMap());
|
|
84
|
+
|
|
85
|
+
/** Mark the execution start behind a Response handed to putResponse. */
|
|
86
|
+
export function markResponseStart(
|
|
87
|
+
response: Response,
|
|
88
|
+
start: ExecutionStart,
|
|
89
|
+
): Response {
|
|
90
|
+
responseStarts.set(response, start.at);
|
|
91
|
+
return response;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The start a writer marked on `response`, if any. */
|
|
95
|
+
export function responseStartedAt(response: Response): number | undefined {
|
|
96
|
+
return responseStarts.get(response);
|
|
97
|
+
}
|
|
98
|
+
|
|
46
99
|
/** Record an execution's start, before it reads its data. */
|
|
47
100
|
export function executionStart(): ExecutionStart {
|
|
48
101
|
return { seq: order.seq, at: Date.now() };
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type {
|
|
9
|
+
CacheStoreScope,
|
|
9
10
|
SegmentCacheStore,
|
|
10
11
|
CachedEntryData,
|
|
11
12
|
CacheDefaults,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
DEFAULT_FUNCTION_TTL,
|
|
24
25
|
} from "./cache-policy.js";
|
|
25
26
|
import { reportCacheError } from "./cache-error.js";
|
|
27
|
+
import { responseStartedAt } from "./invalidation-order.js";
|
|
26
28
|
|
|
27
29
|
const CACHE_REGISTRY_KEY = "__rsc_router_segment_cache_registry__";
|
|
28
30
|
const RESPONSE_CACHE_REGISTRY_KEY = "__rsc_router_response_cache_registry__";
|
|
@@ -146,6 +148,26 @@ export interface MemorySegmentCacheStoreOptions<TEnv = unknown> {
|
|
|
146
148
|
* ```
|
|
147
149
|
*/
|
|
148
150
|
maxEntries?: number;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Where an entry this store writes can be read; what `router.prerender()`
|
|
154
|
+
* checks before warming a route. Defaults to `"local"`: the maps live in one
|
|
155
|
+
* process, and the router cannot tell one long-running server from several
|
|
156
|
+
* replicas or serverless instances, so a warm into it is refused in
|
|
157
|
+
* production. Set `"global"` only when exactly one process serves the app
|
|
158
|
+
* (a single Node server); a warm then reports `warmed`. Under the Vite dev
|
|
159
|
+
* server a `"local"` instance already counts as shared.
|
|
160
|
+
*
|
|
161
|
+
* Like `defaults` and `keyGenerator`, the scope is per instance: two
|
|
162
|
+
* instances with the same `name` share their maps but each declares its own
|
|
163
|
+
* scope. Keep them identical.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* const store = new MemorySegmentCacheStore({ scope: "global" });
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
scope?: CacheStoreScope;
|
|
149
171
|
}
|
|
150
172
|
|
|
151
173
|
/** Default per-family entry cap for MemorySegmentCacheStore (FIFO eviction). */
|
|
@@ -188,6 +210,13 @@ const DEFAULT_MAX_ENTRIES = 1000;
|
|
|
188
210
|
export class MemorySegmentCacheStore<
|
|
189
211
|
TEnv = unknown,
|
|
190
212
|
> implements SegmentCacheStore<TEnv> {
|
|
213
|
+
/**
|
|
214
|
+
* SegmentCacheStore.scope: "local" unless `options.scope` says otherwise, so
|
|
215
|
+
* router.prerender() refuses to warm into these maps in production. Under
|
|
216
|
+
* the Vite dev server the gate counts a "local" instance as shared
|
|
217
|
+
* (store-scope.ts resolveWarmStoreScope).
|
|
218
|
+
*/
|
|
219
|
+
readonly scope: CacheStoreScope;
|
|
191
220
|
readonly supportsPassiveShellReads: true = true;
|
|
192
221
|
private cache: Map<string, CachedEntryData>;
|
|
193
222
|
private responseCache: Map<string, CachedResponseEntry>;
|
|
@@ -253,6 +282,7 @@ export class MemorySegmentCacheStore<
|
|
|
253
282
|
this.defaults = options?.defaults;
|
|
254
283
|
this.keyGenerator = options?.keyGenerator;
|
|
255
284
|
this.maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
285
|
+
this.scope = options?.scope ?? "local";
|
|
256
286
|
}
|
|
257
287
|
|
|
258
288
|
/**
|
|
@@ -300,6 +330,7 @@ export class MemorySegmentCacheStore<
|
|
|
300
330
|
ttl: number,
|
|
301
331
|
_swr?: number,
|
|
302
332
|
): Promise<void> {
|
|
333
|
+
if (this.invalidatedAfter(data.tags, data.taggedAt)) return;
|
|
303
334
|
const entry: CachedEntryData = {
|
|
304
335
|
...data,
|
|
305
336
|
expiresAt: Date.now() + ttl * 1000,
|
|
@@ -358,6 +389,7 @@ export class MemorySegmentCacheStore<
|
|
|
358
389
|
tags?: string[],
|
|
359
390
|
): Promise<void> {
|
|
360
391
|
try {
|
|
392
|
+
const startedAt = responseStartedAt(response);
|
|
361
393
|
const body = await response.clone().arrayBuffer();
|
|
362
394
|
const headers: [string, string][] = [];
|
|
363
395
|
response.headers.forEach((value, name) => {
|
|
@@ -368,6 +400,8 @@ export class MemorySegmentCacheStore<
|
|
|
368
400
|
const swrWindow = resolveSwrWindow(swr, this.defaults);
|
|
369
401
|
const { staleAt, expiresAt } = computeExpiration(ttl, swrWindow);
|
|
370
402
|
|
|
403
|
+
// After the last await before the write.
|
|
404
|
+
if (this.invalidatedAfter(tags, startedAt)) return;
|
|
371
405
|
const prefixedKey = `res:${key}`;
|
|
372
406
|
this.unregisterTags(prefixedKey);
|
|
373
407
|
this.evictIfNeeded(this.responseCache, key, "res");
|
|
@@ -415,6 +449,7 @@ export class MemorySegmentCacheStore<
|
|
|
415
449
|
value: string,
|
|
416
450
|
options?: CacheItemOptions,
|
|
417
451
|
): Promise<void> {
|
|
452
|
+
if (this.invalidatedAfter(options?.tags, options?.startedAt)) return;
|
|
418
453
|
const ttl = resolveTtl(options?.ttl, this.defaults, DEFAULT_FUNCTION_TTL);
|
|
419
454
|
const swrWindow = resolveSwrWindow(options?.swr, this.defaults);
|
|
420
455
|
const { staleAt, expiresAt } = computeExpiration(ttl, swrWindow);
|
|
@@ -486,7 +521,23 @@ export class MemorySegmentCacheStore<
|
|
|
486
521
|
return this.tagsInvalidatedSince(tags, sinceMs);
|
|
487
522
|
}
|
|
488
523
|
|
|
489
|
-
|
|
524
|
+
/**
|
|
525
|
+
* A tag invalidated in a millisecond after the write's execution started
|
|
526
|
+
* (#1068). Strictly after: the start's own millisecond is ambiguous, and
|
|
527
|
+
* invalidation-order.ts orders those exactly.
|
|
528
|
+
*/
|
|
529
|
+
private invalidatedAfter(
|
|
530
|
+
tags: readonly string[] | undefined,
|
|
531
|
+
startedAt: number | undefined,
|
|
532
|
+
): boolean {
|
|
533
|
+
if (!tags || tags.length === 0 || startedAt === undefined) return false;
|
|
534
|
+
return this.tagsInvalidatedSince(tags, startedAt + 1);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
private tagsInvalidatedSince(
|
|
538
|
+
tags: readonly string[],
|
|
539
|
+
sinceMs: number,
|
|
540
|
+
): boolean {
|
|
490
541
|
for (const tag of tags) {
|
|
491
542
|
const at = this.tagInvalidatedAt.get(tag);
|
|
492
543
|
// >= so a same-millisecond invalidation wins (freshness over staleness),
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
* the capture's copy of a value: it is dynamic or has its own cache. A
|
|
19
19
|
* bake-lane loader body that runs on a HIT reads the store too; its pinned
|
|
20
20
|
* container paths stay the pin's (overlayLoaderContainer), and its settled
|
|
21
|
-
* pushes stay the record's (HandleStore.pushRestored).
|
|
21
|
+
* pushes stay the record's (HandleStore.pushRestored). The two go together:
|
|
22
|
+
* a loader whose pin the entry lost runs fresh, and the record's copies of
|
|
23
|
+
* its pushes give way to that run's (restoreHandles).
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
26
|
import type {
|
|
@@ -30,6 +32,7 @@ import type {
|
|
|
30
32
|
ShellCacheEntry,
|
|
31
33
|
ShellSnapshotRecord,
|
|
32
34
|
ShellSnapshotLoaderValue,
|
|
35
|
+
ShellSnapshotHandlesValue,
|
|
33
36
|
CacheReadError,
|
|
34
37
|
} from "./types.js";
|
|
35
38
|
import { settleGrowing } from "./background-task.js";
|
|
@@ -367,17 +370,40 @@ export interface ShellLoaderSeedEntry {
|
|
|
367
370
|
* hole-free record: a hole-carrying one always runs.
|
|
368
371
|
*/
|
|
369
372
|
runs: boolean;
|
|
373
|
+
/**
|
|
374
|
+
* The capture that wrote the pin recorded every settled push of the
|
|
375
|
+
* loader's run in the doc record (CachedEntryData.handleOwners): the pin
|
|
376
|
+
* carries the `runs` bit, which captures write since they record those
|
|
377
|
+
* pushes (v0.18). A replay then drops any other settled push of the loader
|
|
378
|
+
* (HandleStore RecordAuthority "pin"). False for an older pin, whose record
|
|
379
|
+
* may hold none and leaves them to the run.
|
|
380
|
+
*/
|
|
381
|
+
complete: boolean;
|
|
370
382
|
}
|
|
371
383
|
|
|
384
|
+
/**
|
|
385
|
+
* A pin's loader. A pin is stored under its loader segment id,
|
|
386
|
+
* `${shortCode}D${index}.${loaderId}` (fresh.ts resolveLoaders), and a
|
|
387
|
+
* shortCode holds no "D" and no "." (server/context.ts getShortCode: M, L, P,
|
|
388
|
+
* R, C, I and digits), so the first `D<index>.` ends it. A loader id can hold
|
|
389
|
+
* both.
|
|
390
|
+
*/
|
|
391
|
+
const PIN_KEY_PREFIX = /^[^D.]*D\d+\./;
|
|
392
|
+
|
|
372
393
|
/**
|
|
373
394
|
* Materialize the loader-family seed from a shell snapshot for a HIT's tail
|
|
374
395
|
* render: Flight-deserialize each recorded (promise-elided) bake-lane
|
|
375
|
-
* container into a
|
|
376
|
-
*
|
|
396
|
+
* container into a loader-id -> pin Map, which serveShellHit assigns to the
|
|
397
|
+
* tail context's `_shellLoaderSeed` (loader-cache.ts servedPins).
|
|
377
398
|
* Lives here so every snapshot family is decoded in this module (the segment
|
|
378
399
|
* family via {@link SeededShellStore}); the loader family is not a store
|
|
379
400
|
* read, so it seeds the context instead of a store.
|
|
380
401
|
*
|
|
402
|
+
* Keyed by loader, not by the segment a pin was stored under: a loader runs
|
|
403
|
+
* once per capture, so every segment that registers it pinned the same
|
|
404
|
+
* container, and the loader's value and its handle pushes are both looked up
|
|
405
|
+
* by loader. A key that does not parse pins nothing.
|
|
406
|
+
*
|
|
381
407
|
* Deserializations run in parallel; a record that fails to decode is skipped
|
|
382
408
|
* (that loader drifts — the pre-snapshot behavior — instead of failing the
|
|
383
409
|
* HIT). Returns undefined when the snapshot carries no loader records, without
|
|
@@ -396,10 +422,14 @@ export async function buildShellLoaderSeed(
|
|
|
396
422
|
const entries = await Promise.all(
|
|
397
423
|
loaderRecords.map(
|
|
398
424
|
async (rec): Promise<[string, ShellLoaderSeedEntry] | null> => {
|
|
425
|
+
const prefix = PIN_KEY_PREFIX.exec(rec.key);
|
|
426
|
+
if (!prefix) return null;
|
|
399
427
|
try {
|
|
400
|
-
|
|
428
|
+
// Typed as always carrying the bits; an older record lacks them.
|
|
429
|
+
const stored = rec.value as Partial<ShellSnapshotLoaderValue> &
|
|
430
|
+
Pick<ShellSnapshotLoaderValue, "value">;
|
|
401
431
|
return [
|
|
402
|
-
rec.key,
|
|
432
|
+
rec.key.slice(prefix[0].length),
|
|
403
433
|
{
|
|
404
434
|
container: await deserializeResult(stored.value),
|
|
405
435
|
// A record stored before the bits existed (v0.17) lacks the
|
|
@@ -407,6 +437,7 @@ export async function buildShellLoaderSeed(
|
|
|
407
437
|
// so the loader body supplies them.
|
|
408
438
|
holes: stored.holes !== 0,
|
|
409
439
|
runs: stored.runs !== 0,
|
|
440
|
+
complete: stored.runs !== undefined,
|
|
410
441
|
},
|
|
411
442
|
];
|
|
412
443
|
} catch {
|
|
@@ -422,6 +453,22 @@ export async function buildShellLoaderSeed(
|
|
|
422
453
|
return seed.size > 0 ? seed : undefined;
|
|
423
454
|
}
|
|
424
455
|
|
|
456
|
+
/** The key of a snapshot's one `handles` record (ShellSnapshotHandlesValue). */
|
|
457
|
+
export const SHELL_HANDLES_RECORD_KEY = "handles";
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* The `handles` record of a prerender-served entry, which a HIT tail restores
|
|
461
|
+
* after the prerender store's handles (cache-lookup.ts yieldFromStore), or
|
|
462
|
+
* undefined: an entry with a doc record, or one written before the record
|
|
463
|
+
* existed (v0.21), which serves as it did.
|
|
464
|
+
*/
|
|
465
|
+
export function shellPrerenderHandles(
|
|
466
|
+
snapshot: readonly ShellSnapshotRecord[],
|
|
467
|
+
): ShellSnapshotHandlesValue | undefined {
|
|
468
|
+
const record = snapshot.find((r) => r?.family === "handles");
|
|
469
|
+
return record?.value as ShellSnapshotHandlesValue | undefined;
|
|
470
|
+
}
|
|
471
|
+
|
|
425
472
|
/**
|
|
426
473
|
* The implicit doc scope's store on a HIT tail and on a partial replay: it
|
|
427
474
|
* serves the snapshot's segment records, the doc record among them, and
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The store scope `router.prerender()` gates a warm on
|
|
3
|
+
* (prerender/create-prerender-trigger.ts). See
|
|
4
|
+
* docs/design/prerender-every-route.md, "The store declaration".
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { CacheStoreScope, SegmentCacheStore } from "./types.js";
|
|
8
|
+
import { MemorySegmentCacheStore } from "./memory-segment-store.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* True under the Vite dev server: only its routes-manifest virtual module sets
|
|
12
|
+
* the dev origin (vite/discovery/virtual-module-codegen.ts). A build, a preview
|
|
13
|
+
* server and a test runner answer false.
|
|
14
|
+
*/
|
|
15
|
+
export function isViteDevServer(): boolean {
|
|
16
|
+
return typeof globalThis.__PRERENDER_DEV_URL === "string";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The scope a warm treats `store` as having: its own declaration, with none
|
|
21
|
+
* or an unknown value being `"local"`.
|
|
22
|
+
*
|
|
23
|
+
* One exception: under the Vite dev server a MemorySegmentCacheStore answers
|
|
24
|
+
* `"global"`, because that one process serves every request. Production reads
|
|
25
|
+
* the class's own `scope` (`"local"`): changing that declaration is what
|
|
26
|
+
* would admit it there.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveWarmStoreScope(
|
|
29
|
+
store: SegmentCacheStore<any>,
|
|
30
|
+
devServer: boolean = isViteDevServer(),
|
|
31
|
+
): CacheStoreScope {
|
|
32
|
+
const declared = store.scope;
|
|
33
|
+
if (declared === "global" || declared === "regional") return declared;
|
|
34
|
+
if (devServer && store instanceof MemorySegmentCacheStore) return "global";
|
|
35
|
+
return "local";
|
|
36
|
+
}
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
export {
|
|
50
50
|
executionStart,
|
|
51
51
|
invalidatedSince,
|
|
52
|
+
markResponseStart,
|
|
52
53
|
type ExecutionStart,
|
|
53
54
|
} from "./invalidation-order.js";
|
|
54
55
|
|
|
@@ -147,11 +148,12 @@ function markFreshReads(stores: SegmentCacheStore[]): void {
|
|
|
147
148
|
* The write gate of every cache execution (#977): whether one of `tags` was
|
|
148
149
|
* invalidated after the execution that produced the value started.
|
|
149
150
|
*
|
|
150
|
-
*
|
|
151
|
-
* `taggedAt`, VercelCacheStore's `ta
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
151
|
+
* A store stamps an entry with the execution's start (CFCacheStore's
|
|
152
|
+
* `taggedAt`, VercelCacheStore's `ta`, #1068), but only CFCacheStore with KV
|
|
153
|
+
* markers rejects it on a later request's read, and MemorySegmentCacheStore
|
|
154
|
+
* refuses the write. The gate saves the write everywhere and is the only
|
|
155
|
+
* guard where a read compares nothing. Two answers, either one skips the
|
|
156
|
+
* write:
|
|
155
157
|
*
|
|
156
158
|
* - this isolate's order (invalidatedSince), which sees every request's
|
|
157
159
|
* updateTag()/revalidateTag() here, the calling one included;
|