@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
|
@@ -2,36 +2,30 @@ import type { ResolvedSegment } from "./types.js";
|
|
|
2
2
|
import { INTERNAL_RANGO_DEBUG } from "./internal-debug.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* loaders that resolve to primitive data are memoized too. The per-key array
|
|
12
|
-
* is capped (MAX_ENTRIES_PER_KEY, oldest evicted) so a long session under a
|
|
13
|
-
* stable first-ref does not grow it without bound.
|
|
5
|
+
* Browser: a boundary is handed the settled value. React treats a promise as
|
|
6
|
+
* fulfilled only after it has read it, and a boundary on screen handed a
|
|
7
|
+
* promise it has not read, in a render that cannot wait, shows its fallback.
|
|
8
|
+
* So a cache entry records its array once the aggregate fulfils and later
|
|
9
|
+
* calls return that same array; until then (and when it rejects) they return
|
|
10
|
+
* the promise. A boundary with no loaders gets one shared empty array.
|
|
14
11
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* (object keys only).
|
|
12
|
+
* Server: a fresh promise per call, so Suspense emits the fallback in the
|
|
13
|
+
* streamed HTML. A shared resolved promise would carry React's `.status`
|
|
14
|
+
* across requests and skip it.
|
|
19
15
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* and skip the fallback. The zero-loader case is especially prone because
|
|
25
|
-
* every empty-loader site would otherwise share one promise across requests.
|
|
16
|
+
* Entries are keyed on the first loader's `loaderData` reference (object refs
|
|
17
|
+
* in a WeakMap, primitives in a Map), so reconciliation's fresh segment
|
|
18
|
+
* objects keep hitting them. Each key's entry list is capped (see
|
|
19
|
+
* MAX_ENTRIES_PER_KEY).
|
|
26
20
|
*/
|
|
27
21
|
const IS_BROWSER = typeof window !== "undefined";
|
|
28
22
|
|
|
29
23
|
interface LoaderCacheEntry {
|
|
30
24
|
sources: any[];
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
// (Promise<any[]> | any[]) to mirror its siblings.
|
|
25
|
+
// Resolves to the same array it records in `value`; a rejection passes
|
|
26
|
+
// through untouched.
|
|
34
27
|
promise: Promise<any[]>;
|
|
28
|
+
value?: any[];
|
|
35
29
|
}
|
|
36
30
|
|
|
37
31
|
// Cap the per-key entries array. A stable first-ref (e.g. a layout loader whose
|
|
@@ -51,12 +45,8 @@ const primitiveLoaderCache = IS_BROWSER
|
|
|
51
45
|
? new Map<unknown, LoaderCacheEntry[]>()
|
|
52
46
|
: null;
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// state across renders. On the server it would leak `.status = "fulfilled"`
|
|
57
|
-
// across requests and skip the Suspense fallback, so we rebuild on each call.
|
|
58
|
-
const SHARED_EMPTY_LOADER_PROMISE: Promise<any[]> | null = IS_BROWSER
|
|
59
|
-
? Promise.resolve([])
|
|
48
|
+
const SHARED_EMPTY_LOADERS: any[] | null = IS_BROWSER
|
|
49
|
+
? (Object.freeze([]) as unknown as any[])
|
|
60
50
|
: null;
|
|
61
51
|
|
|
62
52
|
function hasSameReferences(a: any[], b: any[]): boolean {
|
|
@@ -117,12 +107,8 @@ function isObjectLike(value: unknown): value is object {
|
|
|
117
107
|
}
|
|
118
108
|
|
|
119
109
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* loader.loaderData ref changes — keeps React's `use()` in "known fulfilled"
|
|
123
|
-
* state and prevents a fresh Promise.all from suspending (and briefly
|
|
124
|
-
* committing the Suspense fallback) on every partial update that doesn't
|
|
125
|
-
* actually change loader data.
|
|
110
|
+
* The aggregate for a set of loader segments, memoized on their `loaderData`
|
|
111
|
+
* refs: the settled array once it has fulfilled, the promise before that.
|
|
126
112
|
*
|
|
127
113
|
* @internal
|
|
128
114
|
*/
|
|
@@ -130,7 +116,7 @@ export function getMemoizedLoaderPromise(
|
|
|
130
116
|
loaders: ResolvedSegment[],
|
|
131
117
|
): Promise<any[]> | any[] {
|
|
132
118
|
if (loaders.length === 0) {
|
|
133
|
-
return
|
|
119
|
+
return SHARED_EMPTY_LOADERS ?? buildLoaderPromise(loaders);
|
|
134
120
|
}
|
|
135
121
|
if (!objectLoaderCache || !primitiveLoaderCache) {
|
|
136
122
|
return buildLoaderPromise(loaders);
|
|
@@ -145,13 +131,17 @@ export function getMemoizedLoaderPromise(
|
|
|
145
131
|
if (entries) {
|
|
146
132
|
for (const entry of entries) {
|
|
147
133
|
if (hasSameReferences(entry.sources, sources)) {
|
|
148
|
-
return entry.promise;
|
|
134
|
+
return entry.value ?? entry.promise;
|
|
149
135
|
}
|
|
150
136
|
}
|
|
151
137
|
}
|
|
152
138
|
|
|
153
|
-
const
|
|
154
|
-
const
|
|
139
|
+
const newEntry = { sources } as LoaderCacheEntry;
|
|
140
|
+
const promise = buildLoaderPromise(loaders).then((values) => {
|
|
141
|
+
newEntry.value = values;
|
|
142
|
+
return values;
|
|
143
|
+
});
|
|
144
|
+
newEntry.promise = promise;
|
|
155
145
|
if (entries) {
|
|
156
146
|
// Bound the array: drop the oldest entry before appending when at the cap.
|
|
157
147
|
if (entries.length >= MAX_ENTRIES_PER_KEY) {
|
package/src/segment-system.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
type ComponentType,
|
|
6
6
|
isValidElement,
|
|
7
7
|
} from "react";
|
|
8
|
-
import { OutletProvider } from "./outlet-provider.js";
|
|
8
|
+
import { AuditedOutletProvider, OutletProvider } from "./outlet-provider.js";
|
|
9
9
|
import { withOptimisticCommitNone } from "./browser/optimistic-commit.js";
|
|
10
10
|
import { MountContextProvider } from "./browser/react/mount-context.js";
|
|
11
11
|
import type { ResolvedSegment, RootLayoutProps } from "./types.js";
|
|
@@ -13,17 +13,25 @@ import { decodeLoaderResults } from "./decode-loader-results.js";
|
|
|
13
13
|
import { LoaderRedirect } from "./loader-redirect.js";
|
|
14
14
|
import { invariant } from "./errors.js";
|
|
15
15
|
import {
|
|
16
|
+
AuditedLoaderBoundary,
|
|
17
|
+
AuditedRouteContent,
|
|
16
18
|
RouteContentWrapper,
|
|
17
19
|
LoaderBoundary,
|
|
18
20
|
StreamedLoaderErrorBoundary,
|
|
19
21
|
} from "./route-content-wrapper.js";
|
|
20
22
|
import { RootErrorBoundary } from "./root-error-boundary.js";
|
|
21
23
|
import { INTERNAL_RANGO_DEBUG } from "./internal-debug.js";
|
|
22
|
-
import {
|
|
24
|
+
import { INTERNAL_RANGO_SUSPENSE_AUDIT } from "./internal-suspense-audit.js";
|
|
25
|
+
import { getBoundaryContent } from "./segment-boundary-content.js";
|
|
23
26
|
import {
|
|
24
27
|
buildLoaderPromise,
|
|
25
28
|
getMemoizedLoaderPromise,
|
|
26
29
|
} from "./segment-loader-promise.js";
|
|
30
|
+
import {
|
|
31
|
+
auditSegmentElement,
|
|
32
|
+
finishTreeAudit,
|
|
33
|
+
startTreeAudit,
|
|
34
|
+
} from "./suspense-audit.js";
|
|
27
35
|
|
|
28
36
|
/**
|
|
29
37
|
* Debug log for the segment tree build, gated on the baked flag. Runs on BOTH
|
|
@@ -212,13 +220,25 @@ function wrapDefaultOutletContent(
|
|
|
212
220
|
} as any);
|
|
213
221
|
}
|
|
214
222
|
|
|
215
|
-
if (
|
|
223
|
+
if (
|
|
224
|
+
content.type ===
|
|
225
|
+
(process.env.NODE_ENV !== "production" && INTERNAL_RANGO_SUSPENSE_AUDIT
|
|
226
|
+
? AuditedOutletProvider
|
|
227
|
+
: OutletProvider) &&
|
|
228
|
+
props.segment?.type === "layout"
|
|
229
|
+
) {
|
|
216
230
|
return React.cloneElement(content, {
|
|
217
231
|
content: wrapDefaultOutletContent(props.content, transition),
|
|
218
232
|
} as any);
|
|
219
233
|
}
|
|
220
234
|
|
|
221
|
-
if (
|
|
235
|
+
if (
|
|
236
|
+
content.type ===
|
|
237
|
+
(process.env.NODE_ENV !== "production" && INTERNAL_RANGO_SUSPENSE_AUDIT
|
|
238
|
+
? AuditedLoaderBoundary
|
|
239
|
+
: LoaderBoundary) &&
|
|
240
|
+
props.segment?.type === "layout"
|
|
241
|
+
) {
|
|
222
242
|
return React.cloneElement(content, {
|
|
223
243
|
outletContent: wrapDefaultOutletContent(props.outletContent, transition),
|
|
224
244
|
} as any);
|
|
@@ -376,6 +396,10 @@ export async function renderSegments(
|
|
|
376
396
|
}
|
|
377
397
|
// Separate segments by type, passing intercept segments for explicit injection
|
|
378
398
|
const tree = segmentTreeWalk(normalizedSegments, normalizedInterceptSegments);
|
|
399
|
+
// Dev only: the wrapper chain per segment, compared with the previous
|
|
400
|
+
// tree's (suspense-audit.ts, I5).
|
|
401
|
+
const treeAudit =
|
|
402
|
+
process.env.NODE_ENV !== "production" ? startTreeAudit() : undefined;
|
|
379
403
|
|
|
380
404
|
// A route is "in a transition scope" when its own segment OR any layout in
|
|
381
405
|
// its matched chain declares transition(). Both transition() forms land here:
|
|
@@ -473,34 +497,33 @@ export async function renderSegments(
|
|
|
473
497
|
|
|
474
498
|
let nodeContent: ReactNode = null;
|
|
475
499
|
if (!clientGroup && isRenderableLoading(loading)) {
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
// instead of a pending promise, which Suspender renders synchronously. This
|
|
484
|
-
// mirrors the forceAwait loaderData unwrap above; a CLIENT component that
|
|
485
|
-
// suspends on mount inside the content still reveals a fallback (it is not
|
|
486
|
-
// pre-resolved).
|
|
487
|
-
const contentPromise = getMemoizedContentPromise(resolvedComponent);
|
|
488
|
-
let loadingContent: Promise<ReactNode> | ReactNode = contentPromise;
|
|
500
|
+
// The wrapper tree is the same on every path; only `content` differs. A
|
|
501
|
+
// settled component is handed over as the node (getBoundaryContent), a
|
|
502
|
+
// pending one as its promise, so only pending work shows the loading()
|
|
503
|
+
// fallback. forceAwait also awaits a pending component here. A CLIENT
|
|
504
|
+
// component that suspends on mount inside the content still reveals a
|
|
505
|
+
// fallback.
|
|
506
|
+
let loadingContent: ReactNode = getBoundaryContent(resolvedComponent);
|
|
489
507
|
if (forceAwait) {
|
|
490
508
|
const contentAwaitStart = segDebug ? performance.now() : 0;
|
|
491
|
-
loadingContent = await
|
|
509
|
+
loadingContent = await loadingContent;
|
|
492
510
|
if (segDebug) {
|
|
493
511
|
segDebugLog(`segment ${id}: content awaited (forceAwait)`, {
|
|
494
512
|
ms: Math.round(performance.now() - contentAwaitStart),
|
|
495
513
|
});
|
|
496
514
|
}
|
|
497
515
|
}
|
|
498
|
-
nodeContent = createElement(
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
516
|
+
nodeContent = createElement(
|
|
517
|
+
process.env.NODE_ENV !== "production" && INTERNAL_RANGO_SUSPENSE_AUDIT
|
|
518
|
+
? AuditedRouteContent
|
|
519
|
+
: RouteContentWrapper,
|
|
520
|
+
{
|
|
521
|
+
key: `suspense-loading-${id}`,
|
|
522
|
+
content: loadingContent,
|
|
523
|
+
fallback: loading,
|
|
524
|
+
segmentId: id,
|
|
525
|
+
},
|
|
526
|
+
);
|
|
504
527
|
} else {
|
|
505
528
|
// [VT-DIAG] Gated behind INTERNAL_RANGO_DEBUG. A segment in the no-loading()
|
|
506
529
|
// branch whose component decodes as a Promise/lazy gets registered into
|
|
@@ -593,7 +616,10 @@ export async function renderSegments(
|
|
|
593
616
|
let boundaryAwaitedLoaderIds: string[] | undefined;
|
|
594
617
|
if (forceAwait || isAction) {
|
|
595
618
|
const awaitStart = segDebug ? performance.now() : 0;
|
|
596
|
-
boundaryLoaderData =
|
|
619
|
+
boundaryLoaderData =
|
|
620
|
+
loaderDataPromise instanceof Promise
|
|
621
|
+
? await loaderDataPromise
|
|
622
|
+
: loaderDataPromise;
|
|
597
623
|
if (segDebug) {
|
|
598
624
|
segDebugLog(`segment ${id}: loaders awaited (forceAwait/action)`, {
|
|
599
625
|
loaderIds,
|
|
@@ -621,28 +647,38 @@ export async function renderSegments(
|
|
|
621
647
|
);
|
|
622
648
|
}
|
|
623
649
|
}
|
|
624
|
-
content = createElement(
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
650
|
+
content = createElement(
|
|
651
|
+
process.env.NODE_ENV !== "production" && INTERNAL_RANGO_SUSPENSE_AUDIT
|
|
652
|
+
? AuditedLoaderBoundary
|
|
653
|
+
: LoaderBoundary,
|
|
654
|
+
{
|
|
655
|
+
key: `loader-boundary-${key}`,
|
|
656
|
+
loaderDataPromise: boundaryLoaderData,
|
|
657
|
+
loaderIds,
|
|
658
|
+
loaderStreams: boundaryLoaderStreams,
|
|
659
|
+
awaitedLoaderIds: boundaryAwaitedLoaderIds,
|
|
660
|
+
fallback: loading,
|
|
661
|
+
outletKey: key,
|
|
662
|
+
outletContent,
|
|
663
|
+
segment: node.segment,
|
|
664
|
+
parallel: node.parallel,
|
|
665
|
+
children: nodeContent,
|
|
666
|
+
},
|
|
667
|
+
);
|
|
637
668
|
} else if (loaderEntries.length === 0) {
|
|
638
|
-
content = createElement(
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
669
|
+
content = createElement(
|
|
670
|
+
process.env.NODE_ENV !== "production" && INTERNAL_RANGO_SUSPENSE_AUDIT
|
|
671
|
+
? AuditedOutletProvider
|
|
672
|
+
: OutletProvider,
|
|
673
|
+
{
|
|
674
|
+
key,
|
|
675
|
+
content: outletContent,
|
|
676
|
+
segment: node.segment,
|
|
677
|
+
parallel: node.parallel,
|
|
678
|
+
pending: outletPending,
|
|
679
|
+
children: nodeContent,
|
|
680
|
+
},
|
|
681
|
+
);
|
|
646
682
|
} else {
|
|
647
683
|
const layoutLoaders = loaderEntries.filter((l) => !l.parallelLoading);
|
|
648
684
|
const parallelOwnedLoaders = loaderEntries.filter(
|
|
@@ -776,17 +812,23 @@ export async function renderSegments(
|
|
|
776
812
|
}
|
|
777
813
|
}
|
|
778
814
|
|
|
779
|
-
content = createElement(
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
815
|
+
content = createElement(
|
|
816
|
+
process.env.NODE_ENV !== "production" && INTERNAL_RANGO_SUSPENSE_AUDIT
|
|
817
|
+
? AuditedOutletProvider
|
|
818
|
+
: OutletProvider,
|
|
819
|
+
{
|
|
820
|
+
key,
|
|
821
|
+
content: outletContent,
|
|
822
|
+
segment: node.segment,
|
|
823
|
+
parallel: node.parallel,
|
|
824
|
+
loaderData:
|
|
825
|
+
Object.keys(loaderData).length > 0 ? loaderData : undefined,
|
|
826
|
+
loaderStreams,
|
|
827
|
+
awaitedLoaderIds,
|
|
828
|
+
pending: outletPending,
|
|
829
|
+
children: errorFallback ?? nodeContent,
|
|
830
|
+
},
|
|
831
|
+
);
|
|
790
832
|
}
|
|
791
833
|
|
|
792
834
|
// Wrap with MountContextProvider for include() scoped components.
|
|
@@ -800,6 +842,22 @@ export async function renderSegments(
|
|
|
800
842
|
});
|
|
801
843
|
}
|
|
802
844
|
|
|
845
|
+
if (process.env.NODE_ENV !== "production") {
|
|
846
|
+
auditSegmentElement(
|
|
847
|
+
treeAudit,
|
|
848
|
+
id,
|
|
849
|
+
node.segment.type,
|
|
850
|
+
key,
|
|
851
|
+
content,
|
|
852
|
+
node.segment.type === "layout" &&
|
|
853
|
+
!!ReactViewTransition &&
|
|
854
|
+
!!transition &&
|
|
855
|
+
transition.viewTransition !== false,
|
|
856
|
+
node.parallel.map((p) => p.id),
|
|
857
|
+
[node.segment, ...node.parallel, ...node.loaders],
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
|
|
803
861
|
if (segDebug) {
|
|
804
862
|
segDebugLog(`segment ${id} built`, {
|
|
805
863
|
type: node.segment.type,
|
|
@@ -849,6 +907,8 @@ export async function renderSegments(
|
|
|
849
907
|
});
|
|
850
908
|
}
|
|
851
909
|
|
|
910
|
+
if (process.env.NODE_ENV !== "production") finishTreeAudit(treeAudit, result);
|
|
911
|
+
|
|
852
912
|
return result;
|
|
853
913
|
}
|
|
854
914
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/// <reference path="../vite/plugins/version.d.ts" />
|
|
2
|
+
/**
|
|
3
|
+
* Runtime side of the per-router cache versions: binds the
|
|
4
|
+
* `@rangojs/router:version` virtual module and answers "which versions does
|
|
5
|
+
* this router, or this request, use".
|
|
6
|
+
*
|
|
7
|
+
* Design: docs/design/per-app-cache-version.md.
|
|
8
|
+
*/
|
|
9
|
+
import { ROUTER_VERSIONS, VERSION } from "@rangojs/router:version";
|
|
10
|
+
import {
|
|
11
|
+
resolveVersionsFrom,
|
|
12
|
+
uniformVersions,
|
|
13
|
+
type RouterVersions,
|
|
14
|
+
type RouterVersionsTable,
|
|
15
|
+
} from "../router-versions.js";
|
|
16
|
+
import { _getRequestContext } from "./request-context.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The build's table. Module state only so the testing primitive
|
|
20
|
+
* (testing/build-versions.ts) can install one where the build's is absent;
|
|
21
|
+
* production never reassigns it.
|
|
22
|
+
*/
|
|
23
|
+
let table: RouterVersionsTable | undefined = ROUTER_VERSIONS;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The versions a router serves with, in order:
|
|
27
|
+
*
|
|
28
|
+
* 1. `override`, a consumer-set `version` (createRouter or createRSCHandler):
|
|
29
|
+
* that exact value for both.
|
|
30
|
+
* 2. The router's entry in the build table, else the whole-build entry.
|
|
31
|
+
* 3. `VERSION` for both: the dev stamp, bumped on every RSC module edit.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveRouterVersions(
|
|
34
|
+
routerId: string | undefined,
|
|
35
|
+
override?: string,
|
|
36
|
+
): RouterVersions {
|
|
37
|
+
return (
|
|
38
|
+
resolveVersionsFrom(table, routerId, override) ?? uniformVersions(VERSION)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The version an on-demand prerender overlay key carries: the router's data
|
|
44
|
+
* version, because the overlay stores segment data, as the segment cache does
|
|
45
|
+
* (a client-only deploy keeps both). The write side (`router.prerender`,
|
|
46
|
+
* router.ts) and the read side (rsc/handler.ts) both call this with
|
|
47
|
+
* `createRouter({ version })`, the only override the trigger can see, so they
|
|
48
|
+
* cannot disagree; a `createRSCHandler({ version })` does not move it.
|
|
49
|
+
*/
|
|
50
|
+
export function resolvePrerenderVersion(
|
|
51
|
+
routerId: string | undefined,
|
|
52
|
+
routerVersion: string | undefined,
|
|
53
|
+
): string {
|
|
54
|
+
return resolveRouterVersions(routerId, routerVersion).data;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The table gives some router a pair of its own. */
|
|
58
|
+
function hasRouterVersions(): boolean {
|
|
59
|
+
if (!table) return false;
|
|
60
|
+
const pairs = new Set(
|
|
61
|
+
Object.values(table).map(([data, document]) => `${data}\0${document}`),
|
|
62
|
+
);
|
|
63
|
+
return pairs.size > 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let warnedOutsideRequest = false;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The versions of the router serving the current request, for a cache store
|
|
70
|
+
* building a key. A store operation with no request context (a detached task
|
|
71
|
+
* that lost the ALS) gets the whole-build versions: it misses the request's
|
|
72
|
+
* entries instead of reading another version's. When routers have versions of
|
|
73
|
+
* their own that is a write no router reads back, so it is logged, once per
|
|
74
|
+
* process: `ctx.waitUntil()` re-enters the request context for every task it
|
|
75
|
+
* runs (server/request-context.ts), and this line in a production log means
|
|
76
|
+
* some other path lost it.
|
|
77
|
+
*
|
|
78
|
+
* Public from `@rangojs/router/cache`, for a custom persistent store to key
|
|
79
|
+
* the way the built-in ones do. Prefix cached RSC data (segment entries, items) with
|
|
80
|
+
* `data` and stored HTML or responses (shells, document responses) with
|
|
81
|
+
* `document`, and leave tag-invalidation records unversioned; an empty string
|
|
82
|
+
* means "no version" (tests). Call it per operation, inside the request, not
|
|
83
|
+
* in the store's constructor: the cache factory runs before the request
|
|
84
|
+
* context exists.
|
|
85
|
+
*/
|
|
86
|
+
export function getCacheVersions(): RouterVersions {
|
|
87
|
+
const versions = _getRequestContext()?._versions;
|
|
88
|
+
if (versions) return versions;
|
|
89
|
+
if (!warnedOutsideRequest && hasRouterVersions()) {
|
|
90
|
+
warnedOutsideRequest = true;
|
|
91
|
+
console.warn(
|
|
92
|
+
"[rango] A cache key was built outside a request, so it carries the whole-build cache " +
|
|
93
|
+
"version instead of a router's. This build has per-router versions: an entry written " +
|
|
94
|
+
"under that key is not read by a router with its own version, and an entry that router " +
|
|
95
|
+
"wrote is not found from here. A store operation ran in a task that lost the request's " +
|
|
96
|
+
"async context. Logged once per process.",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return resolveRouterVersions(undefined);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Which of the serving router's versions a store key carries; `null`: none. */
|
|
103
|
+
export type KeyVersion = keyof RouterVersions | null;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The `v/{version}/` prefix of a persistent store key, or "" for an
|
|
107
|
+
* unversioned one. A store-level `version` is used for every versioned kind;
|
|
108
|
+
* otherwise the version is the serving router's, read per operation because
|
|
109
|
+
* the cache factory builds the store before the request context exists.
|
|
110
|
+
*/
|
|
111
|
+
export function versionKeyPrefix(
|
|
112
|
+
storeVersion: string | undefined,
|
|
113
|
+
kind: KeyVersion,
|
|
114
|
+
): string {
|
|
115
|
+
if (kind === null) return "";
|
|
116
|
+
const version = storeVersion ?? getCacheVersions()[kind];
|
|
117
|
+
return version ? `v/${version}/` : "";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @internal Install a table, or restore the build's with `undefined`. A
|
|
122
|
+
* request handler resolves its router's versions once, when it is created, so
|
|
123
|
+
* a test that swaps the table (a "deploy") needs new handlers.
|
|
124
|
+
*/
|
|
125
|
+
export function installRouterVersionsTable(
|
|
126
|
+
next: RouterVersionsTable | undefined,
|
|
127
|
+
): void {
|
|
128
|
+
table = next ?? ROUTER_VERSIONS;
|
|
129
|
+
warnedOutsideRequest = false;
|
|
130
|
+
}
|
package/src/server/context.ts
CHANGED
|
@@ -15,6 +15,10 @@ import { invariant, DslContextError } from "../errors";
|
|
|
15
15
|
import type { DefaultRouteName } from "../types/global-namespace.js";
|
|
16
16
|
import type { ContextVar } from "../context-var.js";
|
|
17
17
|
import { PPR_LANE_HINT } from "../rsc/shell-capture-constants.js";
|
|
18
|
+
import {
|
|
19
|
+
noteWarmIdentityRead,
|
|
20
|
+
type WarmContext,
|
|
21
|
+
} from "../prerender/warm-request.js";
|
|
18
22
|
import {
|
|
19
23
|
endIdentityExempt,
|
|
20
24
|
isInsideCacheExecScope,
|
|
@@ -261,8 +265,13 @@ export type EntryData =
|
|
|
261
265
|
/** Original PrerenderHandlerDefinition (for build-time getParams access) */
|
|
262
266
|
prerenderDef?: {
|
|
263
267
|
getParams?: (ctx: any) => Promise<any[]> | any[];
|
|
264
|
-
options?: {
|
|
268
|
+
options?: {
|
|
269
|
+
concurrency?: number;
|
|
270
|
+
onDemand?: import("../prerender/on-demand.js").OnDemandOption;
|
|
271
|
+
};
|
|
265
272
|
};
|
|
273
|
+
/** Set when the route opted into on-demand prerender (Prerender(..., { onDemand })) */
|
|
274
|
+
isOnDemand?: true;
|
|
266
275
|
/** Set when route is wrapped with Passthrough() — has a separate live handler */
|
|
267
276
|
isPassthrough?: true;
|
|
268
277
|
/** Live handler for runtime fallback (only set on Passthrough routes) */
|
|
@@ -428,6 +437,7 @@ export const getContext = (): {
|
|
|
428
437
|
getStore: () => HelperContext;
|
|
429
438
|
getParent: () => EntryData | null;
|
|
430
439
|
getOrCreateStore: (forRoute?: string) => HelperContext;
|
|
440
|
+
runIsolated: <T>(forRoute: string, callback: () => T) => T;
|
|
431
441
|
getNextIndex: (
|
|
432
442
|
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate",
|
|
433
443
|
) => string;
|
|
@@ -447,26 +457,29 @@ export const getContext = (): {
|
|
|
447
457
|
) => T;
|
|
448
458
|
} => {
|
|
449
459
|
const context = RangoContext;
|
|
460
|
+
const createStore = (forRoute?: string): HelperContext => ({
|
|
461
|
+
manifest: new Map<string, EntryData>(),
|
|
462
|
+
namespace: "",
|
|
463
|
+
parent: null,
|
|
464
|
+
forRoute,
|
|
465
|
+
counters: {},
|
|
466
|
+
patterns: new Map<string, string>(),
|
|
467
|
+
patternsByPrefix: new Map<string, Map<string, string>>(),
|
|
468
|
+
trailingSlash: new Map<string, "never" | "always" | "ignore">(),
|
|
469
|
+
searchSchemas: new Map<string, Record<string, string>>(),
|
|
470
|
+
});
|
|
450
471
|
|
|
451
472
|
return {
|
|
452
473
|
context,
|
|
453
474
|
getOrCreateStore: (forRoute?: string): HelperContext => {
|
|
454
475
|
let store = RangoContext.getStore();
|
|
455
476
|
if (!store) {
|
|
456
|
-
store =
|
|
457
|
-
manifest: new Map<string, EntryData>(),
|
|
458
|
-
namespace: "",
|
|
459
|
-
parent: null,
|
|
460
|
-
forRoute,
|
|
461
|
-
counters: {},
|
|
462
|
-
patterns: new Map<string, string>(),
|
|
463
|
-
patternsByPrefix: new Map<string, Map<string, string>>(),
|
|
464
|
-
trailingSlash: new Map<string, "never" | "always" | "ignore">(),
|
|
465
|
-
searchSchemas: new Map<string, Record<string, string>>(),
|
|
466
|
-
} satisfies HelperContext;
|
|
477
|
+
store = createStore(forRoute);
|
|
467
478
|
}
|
|
468
479
|
return store;
|
|
469
480
|
},
|
|
481
|
+
runIsolated: <T>(forRoute: string, callback: () => T): T =>
|
|
482
|
+
context.run(createStore(forRoute), callback),
|
|
470
483
|
getStore: (): HelperContext => {
|
|
471
484
|
const store = context.getStore();
|
|
472
485
|
if (!store) {
|
|
@@ -959,19 +972,20 @@ export interface IdentityReadWording {
|
|
|
959
972
|
* nothing and are not captured reads: they take refuseInCacheScope alone.
|
|
960
973
|
*/
|
|
961
974
|
export function guardIdentityRead(
|
|
962
|
-
ctx:
|
|
975
|
+
ctx: WarmContext | null | undefined,
|
|
963
976
|
surface: string,
|
|
964
977
|
wording: IdentityReadWording,
|
|
965
978
|
): void {
|
|
966
979
|
if (isInsideIdentityExempt()) return;
|
|
967
980
|
const { verb, fix } = wording;
|
|
968
981
|
if (tripShellCaptureGuard(ctx, surface, fix.warning)) {
|
|
982
|
+
noteWarmIdentityRead(ctx, surface);
|
|
969
983
|
throw new Error(
|
|
970
984
|
`${surface} cannot be ${verb} while capturing a shared shell ` +
|
|
971
985
|
`(ppr shell capture). ${fix.capture}`,
|
|
972
986
|
);
|
|
973
987
|
}
|
|
974
|
-
refuseInCacheScope(surface, wording);
|
|
988
|
+
refuseInCacheScope(surface, wording, ctx);
|
|
975
989
|
if (wording.record !== false) recordLoaderIdentityRead(surface, verb);
|
|
976
990
|
}
|
|
977
991
|
|
|
@@ -982,13 +996,16 @@ export function guardIdentityRead(
|
|
|
982
996
|
export function refuseInCacheScope(
|
|
983
997
|
surface: string,
|
|
984
998
|
{ verb, fix }: IdentityReadWording,
|
|
999
|
+
ctx?: WarmContext | null,
|
|
985
1000
|
): void {
|
|
986
1001
|
if (isInsideCacheExecScope()) {
|
|
1002
|
+
noteWarmIdentityRead(ctx, surface);
|
|
987
1003
|
throw new Error(
|
|
988
1004
|
`${surface} cannot be ${verb} inside a "use cache" function. ${fix.useCache}`,
|
|
989
1005
|
);
|
|
990
1006
|
}
|
|
991
1007
|
if (isInsideCacheScope()) {
|
|
1008
|
+
noteWarmIdentityRead(ctx, surface);
|
|
992
1009
|
throw new Error(
|
|
993
1010
|
`${surface} cannot be ${verb} inside a cache() boundary. ${fix.cacheScope}`,
|
|
994
1011
|
);
|
|
@@ -1074,7 +1091,7 @@ const NON_CACHEABLE_READ: IdentityReadWording = {
|
|
|
1074
1091
|
*/
|
|
1075
1092
|
export function assertNonCacheableReadAllowed(
|
|
1076
1093
|
keyOrVar: string | ContextVar<unknown>,
|
|
1077
|
-
requestCtx?:
|
|
1094
|
+
requestCtx?: WarmContext | null,
|
|
1078
1095
|
): void {
|
|
1079
1096
|
const name = typeof keyOrVar === "string" ? ` "${keyOrVar}"` : "";
|
|
1080
1097
|
guardIdentityRead(
|
|
@@ -1276,7 +1293,10 @@ export function isInsideAnyLoaderScope(): boolean {
|
|
|
1276
1293
|
|
|
1277
1294
|
/**
|
|
1278
1295
|
* The one ppr opt-in predicate: a page route entry that DECLARED `ppr`
|
|
1279
|
-
* (`false
|
|
1296
|
+
* (`false`, undefined, and on-demand routes mean plain axis 1). A writable
|
|
1297
|
+
* prerender refresh cannot atomically replace the separate document shell, so
|
|
1298
|
+
* combining those lanes would pair a fresh tail with a stale prelude. Shared by
|
|
1299
|
+
* the serve path
|
|
1280
1300
|
* (rsc/shell-serve.ts resolvePprConfig) and the header-write latch below so
|
|
1281
1301
|
* the two layers can never drift on what counts as a ppr route.
|
|
1282
1302
|
*/
|
|
@@ -1285,7 +1305,10 @@ export function isPprEntry(entry: EntryData): entry is EntryData & {
|
|
|
1285
1305
|
ppr: true | import("../urls/pattern-types.js").PartialPrerenderProps;
|
|
1286
1306
|
} {
|
|
1287
1307
|
return (
|
|
1288
|
-
entry.type === "route" &&
|
|
1308
|
+
entry.type === "route" &&
|
|
1309
|
+
entry.isOnDemand !== true &&
|
|
1310
|
+
entry.ppr !== undefined &&
|
|
1311
|
+
entry.ppr !== false
|
|
1289
1312
|
);
|
|
1290
1313
|
}
|
|
1291
1314
|
|