@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
|
@@ -88,9 +88,20 @@ async function resolveValues(values: unknown[]): Promise<unknown[]> {
|
|
|
88
88
|
* A hung handle promise blocks the full render like any unresolved await (no
|
|
89
89
|
* handle-specific timeout). Partial / action payloads keep streaming via
|
|
90
90
|
* `handleStore.stream()`.
|
|
91
|
+
*
|
|
92
|
+
* `recordedOnly` is a PPR shell capture's render (full-payload.ts): the
|
|
93
|
+
* snapshot is cut down to the pushes the capture's record keeps
|
|
94
|
+
* (getDataForSegment's `excludeLoaderPushes`, the filter captureHandles
|
|
95
|
+
* writes the record with). A HIT restores that record and hydrates with it,
|
|
96
|
+
* so a push the record leaves out must not be in the prelude either: it
|
|
97
|
+
* rendered an element no HIT's hydration data matched (issue #1035). Which
|
|
98
|
+
* pushes a record keeps is the capture funnel's decision (shell-capture.ts
|
|
99
|
+
* deriveShellCaptureContext: today it leaves out a deferred loader push and
|
|
100
|
+
* one holding a masked promise), not this function's.
|
|
91
101
|
*/
|
|
92
102
|
export async function* resolvedHandleStream(
|
|
93
103
|
handleStore: HandleStore,
|
|
104
|
+
recordedOnly?: boolean,
|
|
94
105
|
): AsyncGenerator<HandleData, void, unknown> {
|
|
95
106
|
// Drain stream() (NOT getData()) for the converged snapshot: consuming a
|
|
96
107
|
// stream arms the store's late-push guard (LateHandlePushError for pushes
|
|
@@ -109,9 +120,34 @@ export async function* resolvedHandleStream(
|
|
|
109
120
|
for await (const data of handleStore.stream("settled")) {
|
|
110
121
|
snapshot = data;
|
|
111
122
|
}
|
|
123
|
+
if (recordedOnly) snapshot = recordedHandleData(handleStore, snapshot);
|
|
112
124
|
yield await resolveDeferredHandleValues(snapshot);
|
|
113
125
|
}
|
|
114
126
|
|
|
127
|
+
/**
|
|
128
|
+
* `snapshot` without the pushes a record leaves out. A shell capture renders
|
|
129
|
+
* (resolvedHandleStream) and waits (settleCaptureRecord) on this cut only: a
|
|
130
|
+
* promise the record drops has no slot in the prelude, and waiting for it
|
|
131
|
+
* could refuse a capture for a value the shell does not hold.
|
|
132
|
+
*/
|
|
133
|
+
export function recordedHandleData(
|
|
134
|
+
handleStore: HandleStore,
|
|
135
|
+
snapshot: HandleData,
|
|
136
|
+
): HandleData {
|
|
137
|
+
const segmentIds = new Set<string>();
|
|
138
|
+
for (const handleName in snapshot) {
|
|
139
|
+
for (const segmentId in snapshot[handleName]) segmentIds.add(segmentId);
|
|
140
|
+
}
|
|
141
|
+
const recorded: HandleData = {};
|
|
142
|
+
for (const segmentId of segmentIds) {
|
|
143
|
+
const kept = handleStore.getDataForSegment(segmentId, true);
|
|
144
|
+
for (const handleName in kept) {
|
|
145
|
+
(recorded[handleName] ??= {})[segmentId] = kept[handleName];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return recorded;
|
|
149
|
+
}
|
|
150
|
+
|
|
115
151
|
/**
|
|
116
152
|
* Resolve the deferred (Promise) values in ONE segment's handle bucket
|
|
117
153
|
* (`handleName -> entries[]`). Same shallow + drop-rejections rule as
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Turns on the suspense audit (suspense-audit.ts) in a dev build. Only this
|
|
2
|
+
// repo sets INTERNAL_RANGO_SUSPENSE_AUDIT: the e2e webServers and fixtures of
|
|
3
|
+
// the router test-app and cloudflare-basic, and the router's vitest config.
|
|
4
|
+
// A consumer's dev server and tests never print the audit's errors.
|
|
5
|
+
//
|
|
6
|
+
// Runtime fallback for non-Vite contexts (vitest, Node). In the Vite pipeline
|
|
7
|
+
// the discovery plugin replaces this module with the build-time-resolved flag
|
|
8
|
+
// (vite/inject-client-debug.ts), as it does for internal-debug.ts. Keep this
|
|
9
|
+
// module to the single export: the transform replaces the whole file.
|
|
10
|
+
export const INTERNAL_RANGO_SUSPENSE_AUDIT: boolean =
|
|
11
|
+
typeof process !== "undefined" &&
|
|
12
|
+
Boolean((process as any).env?.INTERNAL_RANGO_SUSPENSE_AUDIT);
|
package/src/loader-store.ts
CHANGED
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
* reclaim a bucket that is about to be reused, and is held until any
|
|
30
30
|
* in-flight load settles.
|
|
31
31
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* Each router's NavigationStore owns one (navigation-store.ts), and hooks reach
|
|
33
|
+
* it through NavigationStoreContext: the router store is the single source of
|
|
34
|
+
* truth. Server renders have no store — the hook falls back to
|
|
35
|
+
* `OutletContext.loaderData`.
|
|
35
36
|
*/
|
|
36
37
|
|
|
37
38
|
import { unwrapsSynchronously } from "./thenable-status.js";
|
|
@@ -597,10 +598,4 @@ export class LoaderStore {
|
|
|
597
598
|
}
|
|
598
599
|
}
|
|
599
600
|
|
|
600
|
-
/**
|
|
601
|
-
* Module-level singleton. Each browser tab gets its own; SSR never mutates it.
|
|
602
|
-
* The hook falls through to `OutletContext.loaderData` during the server render.
|
|
603
|
-
*/
|
|
604
|
-
export const loaderStore: LoaderStore = new LoaderStore();
|
|
605
|
-
|
|
606
601
|
export const EMPTY_LOADER_SNAPSHOT: LoaderEntry = EMPTY_SNAPSHOT;
|
package/src/outlet-provider.tsx
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
useContext,
|
|
5
|
+
useMemo,
|
|
6
|
+
type ComponentProps,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
} from "react";
|
|
4
9
|
import { OutletContext, type OutletContextValue } from "./outlet-context.js";
|
|
5
10
|
import type { ResolvedSegment } from "./types.js";
|
|
11
|
+
import { AuditedOutlet } from "./suspense-audit-react.js";
|
|
6
12
|
|
|
7
13
|
/**
|
|
8
14
|
* Outlet content provider — stores parent context for useLoader chain walking.
|
|
@@ -57,3 +63,21 @@ export function OutletProvider({
|
|
|
57
63
|
<OutletContext.Provider value={value}>{children}</OutletContext.Provider>
|
|
58
64
|
);
|
|
59
65
|
}
|
|
66
|
+
|
|
67
|
+
// Dev only (suspense-audit.ts): OutletProvider with the segment's mount
|
|
68
|
+
// tracked under it. Chosen where the element is created, inside a NODE_ENV
|
|
69
|
+
// test: see AuditedRouteContent (route-content-wrapper.tsx).
|
|
70
|
+
export function AuditedOutletProvider({
|
|
71
|
+
children,
|
|
72
|
+
...props
|
|
73
|
+
}: ComponentProps<typeof OutletProvider>): ReactNode {
|
|
74
|
+
return (
|
|
75
|
+
<OutletProvider {...props}>
|
|
76
|
+
{props.segment ? (
|
|
77
|
+
<AuditedOutlet segmentId={props.segment.id}>{children}</AuditedOutlet>
|
|
78
|
+
) : (
|
|
79
|
+
children
|
|
80
|
+
)}
|
|
81
|
+
</OutletProvider>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import type { ShellCacheEntry } from "../cache/types.js";
|
|
24
|
+
import type { RouterVersions } from "../router-versions.js";
|
|
24
25
|
import type { MatchResult } from "../types.js";
|
|
25
26
|
import { MemorySegmentCacheStore } from "../cache/memory-segment-store.js";
|
|
26
27
|
import {
|
|
@@ -105,13 +106,14 @@ export interface BuildShellCaptureOptions {
|
|
|
105
106
|
/** Build-time env bindings (rango plugin buildEnv), if configured. */
|
|
106
107
|
buildEnv?: unknown;
|
|
107
108
|
/**
|
|
108
|
-
* The
|
|
109
|
-
*
|
|
110
|
-
* serve-side isValidShellHit gate compares
|
|
111
|
-
* running worker's ctx.version
|
|
112
|
-
*
|
|
109
|
+
* The versions the SHIPPED build serves this router with (the build table,
|
|
110
|
+
* or the router's consumer-set version) — NOT the temp server's own
|
|
111
|
+
* version-plugin stamp. The serve-side isValidShellHit gate compares
|
|
112
|
+
* entry.buildVersion against the running worker's ctx.version, which is the
|
|
113
|
+
* document version; stamping the temp server's would make every build entry
|
|
114
|
+
* an eternal MISS.
|
|
113
115
|
*/
|
|
114
|
-
|
|
116
|
+
versions: RouterVersions;
|
|
115
117
|
/**
|
|
116
118
|
* The SSR half, composed by the plugin from the temp server's SSR
|
|
117
119
|
* environment runner (react-dom/static prerender + Flight client), with the
|
|
@@ -192,7 +194,7 @@ async function attemptBuildCapture(
|
|
|
192
194
|
cacheStore: new MemorySegmentCacheStore(),
|
|
193
195
|
themeConfig: router.themeConfig ?? null,
|
|
194
196
|
stateCookieName: router.resolvedStateCookieName,
|
|
195
|
-
|
|
197
|
+
versions: opts.versions,
|
|
196
198
|
});
|
|
197
199
|
// Scope registry lookups (root-scope/search-schema) per router during the
|
|
198
200
|
// bake, mirroring rsc/handler.ts on the request path (#762).
|
|
@@ -215,7 +217,7 @@ async function attemptBuildCapture(
|
|
|
215
217
|
|
|
216
218
|
const descriptor: ShellCaptureDescriptor = {
|
|
217
219
|
key: opts.key,
|
|
218
|
-
buildVersion: opts.
|
|
220
|
+
buildVersion: opts.versions.document,
|
|
219
221
|
ttl: opts.ttl,
|
|
220
222
|
swr: opts.swr,
|
|
221
223
|
tags: opts.tags,
|
|
@@ -448,7 +450,7 @@ async function runBuildCaptureFinal(
|
|
|
448
450
|
// buildFullPayload reads only ctx.router.* and ctx.version.
|
|
449
451
|
{
|
|
450
452
|
router,
|
|
451
|
-
version: opts.
|
|
453
|
+
version: opts.versions.document,
|
|
452
454
|
} as unknown as HandlerContext<any>,
|
|
453
455
|
url,
|
|
454
456
|
derivedCtx,
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare KV writable prerender store (`@rangojs/router/prerender/cloudflare`).
|
|
3
|
+
*
|
|
4
|
+
* Backs the durable overlay for on-demand prerender on Cloudflare. Self-contained
|
|
5
|
+
* (no CFCacheStore construction constraints): entries are stored under the
|
|
6
|
+
* versioned key `prerender:{routerId}:{version}:{routeName}:{paramHash}`, and
|
|
7
|
+
* tag invalidation uses the same timestamp-marker technique as the runtime cache
|
|
8
|
+
* — a per-tag KV marker holding the ms epoch of the last invalidation; a read
|
|
9
|
+
* treats the entry as stale (never deleted) when any of its tags' markers is at
|
|
10
|
+
* or after the entry's storedAt.
|
|
11
|
+
*
|
|
12
|
+
* Key design constraints (see docs/design/ondemand-prerender.md, Store Model):
|
|
13
|
+
* - staleAt is SOFT metadata; entries are written with NO KV expirationTtl. Hard
|
|
14
|
+
* expiry would delete the entry SWR needs and re-expose older bundled-manifest
|
|
15
|
+
* content below the overlay.
|
|
16
|
+
* - Prerender keys are route + params + variant only — never host-namespaced, so
|
|
17
|
+
* a refresh from a different host than the serve request still hits.
|
|
18
|
+
* - Invalidation is mark-stale, not delete (deleting would re-expose the manifest
|
|
19
|
+
* entry as the result of an "invalidation").
|
|
20
|
+
* - Tag markers use a prerender-specific prefix, separate from the runtime cache's
|
|
21
|
+
* `updateTag()`/`revalidateTag()` namespace, and are scoped by router id
|
|
22
|
+
* (`{prefix}{encodeURIComponent(routerId)}/{tag}`): routers sharing one KV
|
|
23
|
+
* namespace (an app behind a host router) must not mark each other's entries.
|
|
24
|
+
* - Markers carry no expiry: an entry without a `ttl` never goes stale by
|
|
25
|
+
* itself, so an expired marker would silently drop an invalidation.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { KVNamespace } from "../cache/cf/cf-cache-types.js";
|
|
29
|
+
import {
|
|
30
|
+
lowerStoredEntryStaleAt,
|
|
31
|
+
serializePrerenderKey,
|
|
32
|
+
type PrerenderKey,
|
|
33
|
+
type PrerenderStoredEntry,
|
|
34
|
+
type WritablePrerenderStore,
|
|
35
|
+
} from "./writable-store.js";
|
|
36
|
+
|
|
37
|
+
/** Prerender tag markers live in their own namespace, separate from the runtime cache. */
|
|
38
|
+
const PRERENDER_TAG_MARKER_PREFIX = "__rango_pr_tag__/";
|
|
39
|
+
|
|
40
|
+
export interface KVPrerenderStoreOptions {
|
|
41
|
+
/** Injectable clock for `markStale` markers in deterministic tests. Defaults to Date.now. */
|
|
42
|
+
now?: () => number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a {@link WritablePrerenderStore} backed by a Cloudflare KV namespace.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* import { createKVPrerenderStore } from "@rangojs/router/prerender/cloudflare";
|
|
51
|
+
*
|
|
52
|
+
* createRouter<Env>({
|
|
53
|
+
* prerender: (env) => ({
|
|
54
|
+
* store: createKVPrerenderStore(env.PRERENDER_KV),
|
|
55
|
+
* ttl: 3600,
|
|
56
|
+
* onRevalidate: (target, e) => e.PRERENDER_QUEUE.send({ target }),
|
|
57
|
+
* }),
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function createKVPrerenderStore(
|
|
62
|
+
kv: KVNamespace,
|
|
63
|
+
options: KVPrerenderStoreOptions = {},
|
|
64
|
+
): WritablePrerenderStore {
|
|
65
|
+
const now = options.now ?? (() => Date.now());
|
|
66
|
+
function markerKey(routerId: string, tag: string): string {
|
|
67
|
+
// An encoded id holds no "/", so the id/tag split is unambiguous.
|
|
68
|
+
return `${PRERENDER_TAG_MARKER_PREFIX}${encodeURIComponent(routerId)}/${tag}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function readTagMarker(
|
|
72
|
+
routerId: string,
|
|
73
|
+
tag: string,
|
|
74
|
+
): Promise<number | null> {
|
|
75
|
+
const raw = await kv.get(markerKey(routerId, tag));
|
|
76
|
+
if (!raw) return null;
|
|
77
|
+
const n = Number(raw);
|
|
78
|
+
return Number.isFinite(n) ? n : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
async get(key: PrerenderKey): Promise<PrerenderStoredEntry | null> {
|
|
83
|
+
const raw = await kv.get(serializePrerenderKey(key));
|
|
84
|
+
if (!raw) return null;
|
|
85
|
+
|
|
86
|
+
let stored: PrerenderStoredEntry;
|
|
87
|
+
try {
|
|
88
|
+
stored = JSON.parse(raw) as PrerenderStoredEntry;
|
|
89
|
+
} catch {
|
|
90
|
+
// Corrupt entry: treat as a miss rather than crash the serve path.
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Tag invalidation is mark-stale: a tag marked at or after the write
|
|
95
|
+
// forces the entry stale (it still serves). Shape is verified by the
|
|
96
|
+
// router; an already-stale entry skips the marker reads.
|
|
97
|
+
const meta = stored?.meta;
|
|
98
|
+
if (
|
|
99
|
+
meta?.tags?.length &&
|
|
100
|
+
!(meta.staleAt != null && meta.staleAt <= now())
|
|
101
|
+
) {
|
|
102
|
+
const markers = await Promise.all(
|
|
103
|
+
meta.tags.map((tag) => readTagMarker(key.routerId, tag)),
|
|
104
|
+
);
|
|
105
|
+
if (markers.some((m) => m != null && m >= meta.storedAt)) {
|
|
106
|
+
lowerStoredEntryStaleAt(stored, meta.storedAt);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return stored;
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
async set(key: PrerenderKey, stored: PrerenderStoredEntry): Promise<void> {
|
|
114
|
+
// No expirationTtl: staleAt is soft metadata (see file header).
|
|
115
|
+
await kv.put(serializePrerenderKey(key), JSON.stringify(stored));
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
async delete(key: PrerenderKey): Promise<void> {
|
|
119
|
+
await kv.delete(serializePrerenderKey(key));
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
async markStale(routerId: string, tags: string[]): Promise<void> {
|
|
123
|
+
if (tags.length === 0) return;
|
|
124
|
+
const marker = String(now());
|
|
125
|
+
await Promise.all(
|
|
126
|
+
tags.map((tag) => kv.put(markerKey(routerId, tag), marker)),
|
|
127
|
+
);
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|