@rangojs/router 0.16.0 → 0.18.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/README.md +255 -89
- package/dist/testing/vitest.js +252 -0
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/history-state.d.ts +16 -0
- package/dist/types/browser/navigation-transaction.d.ts +6 -0
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
- package/dist/types/browser/rsc-router.d.ts +1 -1
- package/dist/types/browser/scroll-restoration.d.ts +7 -0
- package/dist/types/cache/cache-exec-scope.d.ts +18 -1
- package/dist/types/cache/cache-key-utils.d.ts +17 -2
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +132 -19
- package/dist/types/cache/cache-tag.d.ts +92 -7
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
- package/dist/types/cache/handle-capture.d.ts +41 -3
- package/dist/types/cache/handle-snapshot.d.ts +54 -13
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/segment-codec.d.ts +10 -3
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +110 -21
- package/dist/types/cache/tag-invalidation.d.ts +30 -3
- package/dist/types/cache/taint.d.ts +1 -1
- package/dist/types/cache/types.d.ts +220 -44
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- package/dist/types/client.d.ts +4 -4
- package/dist/types/client.rsc.d.ts +4 -5
- package/dist/types/components/DefaultDocument.d.ts +5 -2
- package/dist/types/context-var.d.ts +6 -5
- package/dist/types/escape-script.d.ts +1 -1
- package/dist/types/handles/MetaTags.d.ts +5 -4
- package/dist/types/handles/Scripts.d.ts +8 -7
- package/dist/types/handles/meta.d.ts +2 -2
- package/dist/types/handles/script.d.ts +17 -16
- package/dist/types/href-client.d.ts +1 -1
- package/dist/types/html.d.ts +12 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +3 -2
- package/dist/types/loader-store.d.ts +7 -4
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/reverse.d.ts +36 -4
- package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
- package/dist/types/route-definition/helpers-types.d.ts +33 -9
- package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
- package/dist/types/route-types.d.ts +1 -1
- package/dist/types/router/debug-manifest.d.ts +3 -2
- package/dist/types/router/error-handling.d.ts +20 -0
- package/dist/types/router/handler-context.d.ts +6 -0
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
- package/dist/types/router/middleware-types.d.ts +9 -3
- package/dist/types/router/router-context.d.ts +3 -1
- package/dist/types/router/router-interfaces.d.ts +24 -2
- package/dist/types/router/router-options.d.ts +8 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +66 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
- package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
- package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
- package/dist/types/router/segment-wrappers.d.ts +1 -1
- package/dist/types/rsc/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-route-handler.d.ts +1 -1
- package/dist/types/rsc/rsc-rendering.d.ts +1 -2
- package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
- package/dist/types/rsc/shell-capture.d.ts +151 -19
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/ssr-setup.d.ts +6 -6
- package/dist/types/rsc/types.d.ts +17 -0
- package/dist/types/server/context.d.ts +139 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/handle-store.d.ts +26 -2
- package/dist/types/server/request-context.d.ts +182 -53
- package/dist/types/ssr/index.d.ts +24 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/static-handler.d.ts +4 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/generated-routes.d.ts +2 -2
- package/dist/types/testing/internal/context.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +32 -6
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/serve-shell-request.d.ts +132 -0
- package/dist/types/testing/shell-status.d.ts +23 -10
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
- package/dist/types/testing/vitest.d.ts +35 -1
- package/dist/types/theme/ThemeScript.d.ts +3 -3
- package/dist/types/types/cache-types.d.ts +14 -10
- package/dist/types/types/handler-context.d.ts +28 -1
- package/dist/types/types/loader-types.d.ts +8 -6
- package/dist/types/urls/pattern-types.d.ts +22 -18
- package/dist/types/urls/response-types.d.ts +3 -12
- package/dist/types/use-loader.d.ts +12 -3
- package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
- package/dist/types/vite/rango.d.ts +1 -1
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1420 -1348
- package/package.json +2 -2
- package/skills/api-client/SKILL.md +22 -10
- package/skills/breadcrumbs/SKILL.md +75 -22
- package/skills/bundle-analysis/SKILL.md +22 -22
- package/skills/cache-guide/SKILL.md +306 -124
- package/skills/caching/SKILL.md +530 -98
- package/skills/catalog.json +16 -16
- package/skills/client-urls/SKILL.md +65 -38
- package/skills/cloudflare/SKILL.md +87 -20
- package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
- package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
- package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
- package/skills/comparison/SKILL.md +5 -0
- package/skills/comparison/references/framework-comparison.md +75 -57
- package/skills/composability/SKILL.md +38 -29
- package/skills/css/SKILL.md +10 -13
- package/skills/debug-manifest/SKILL.md +74 -31
- package/skills/defer-hydration/SKILL.md +52 -31
- package/skills/deployment-caching/SKILL.md +11 -3
- package/skills/document-cache/SKILL.md +98 -28
- package/skills/fonts/SKILL.md +16 -8
- package/skills/handler-use/SKILL.md +55 -17
- package/skills/hooks/SKILL.md +28 -20
- package/skills/hooks/data.md +23 -3
- package/skills/hooks/handle-and-actions.md +34 -17
- package/skills/hooks/navigation.md +39 -10
- package/skills/hooks/outlets.md +13 -6
- package/skills/hooks/state.md +39 -15
- package/skills/hooks/urls.md +7 -5
- package/skills/host-router/SKILL.md +64 -12
- package/skills/i18n/SKILL.md +60 -34
- package/skills/intercept/SKILL.md +161 -26
- package/skills/layout/SKILL.md +106 -50
- package/skills/links/SKILL.md +51 -19
- package/skills/loader/SKILL.md +386 -128
- package/skills/middleware/SKILL.md +129 -26
- package/skills/migrate-nextjs/SKILL.md +156 -70
- package/skills/migrate-nextjs/backend-host-swap.md +5 -2
- package/skills/migrate-react-router/SKILL.md +51 -39
- package/skills/migrate-react-router/component-migration.md +28 -7
- package/skills/migrate-react-router/data-and-actions.md +21 -6
- package/skills/migrate-react-router/route-mapping.md +19 -10
- package/skills/mime-routes/SKILL.md +12 -7
- package/skills/observability/SKILL.md +92 -14
- package/skills/parallel/SKILL.md +88 -47
- package/skills/ppr/SKILL.md +866 -379
- package/skills/prerender/SKILL.md +210 -108
- package/skills/rango/SKILL.md +115 -65
- package/skills/react-compiler/SKILL.md +6 -5
- package/skills/response-routes/SKILL.md +133 -25
- package/skills/route/SKILL.md +170 -60
- package/skills/router-setup/SKILL.md +420 -240
- package/skills/scripts/SKILL.md +39 -21
- package/skills/server-actions/SKILL.md +231 -59
- package/skills/shell-manifest/SKILL.md +28 -14
- package/skills/streams-and-websockets/SKILL.md +32 -14
- package/skills/tailwind/SKILL.md +37 -33
- package/skills/testing/SKILL.md +73 -33
- package/skills/testing/cache-prerender.md +60 -33
- package/skills/testing/client-components.md +59 -6
- package/skills/testing/e2e-parity.md +12 -2
- package/skills/testing/flight.md +19 -11
- package/skills/testing/handles.md +8 -5
- package/skills/testing/loader.md +26 -6
- package/skills/testing/middleware.md +8 -3
- package/skills/testing/predicates.md +120 -0
- package/skills/testing/render-handler.md +70 -22
- package/skills/testing/response-routes.md +11 -4
- package/skills/testing/reverse-and-types.md +20 -14
- package/skills/testing/serve-shell-request.md +188 -0
- package/skills/testing/server-actions.md +12 -3
- package/skills/testing/server-tree.md +9 -0
- package/skills/testing/setup.md +9 -5
- package/skills/theme/SKILL.md +155 -30
- package/skills/typesafety/SKILL.md +20 -9
- package/skills/typesafety/env-and-bindings.md +137 -50
- package/skills/typesafety/generated-files-and-cli.md +26 -14
- package/skills/typesafety/params-and-search.md +44 -19
- package/skills/typesafety/route-types.md +50 -14
- package/skills/use-cache/SKILL.md +224 -83
- package/skills/vercel/SKILL.md +35 -13
- package/skills/view-transitions/SKILL.md +42 -21
- package/src/__augment-tests__/augmented.check.ts +26 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- package/src/browser/index.ts +2 -2
- package/src/browser/navigation-bridge.ts +3 -4
- package/src/browser/navigation-transaction.ts +22 -2
- package/src/browser/partial-update.ts +35 -25
- package/src/browser/react/ScrollRestoration.tsx +6 -6
- package/src/browser/rsc-router.tsx +6 -1
- package/src/browser/scroll-restoration.ts +53 -6
- package/src/cache/cache-exec-scope.ts +37 -4
- package/src/cache/cache-key-utils.ts +23 -8
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +264 -68
- package/src/cache/cache-scope.ts +419 -43
- package/src/cache/cache-tag.ts +375 -11
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +990 -348
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
- package/src/cache/document-cache.ts +15 -0
- package/src/cache/handle-capture.ts +90 -10
- package/src/cache/handle-snapshot.ts +133 -28
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/segment-codec.ts +26 -7
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +108 -22
- package/src/cache/taint.ts +1 -1
- package/src/cache/types.ts +225 -44
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- package/src/client.rsc.tsx +10 -9
- package/src/client.tsx +5 -4
- package/src/components/DefaultDocument.tsx +7 -2
- package/src/context-var.ts +6 -5
- package/src/escape-script.ts +1 -1
- package/src/handles/MetaTags.tsx +13 -11
- package/src/handles/Scripts.tsx +11 -10
- package/src/handles/meta.ts +2 -2
- package/src/handles/script.ts +17 -16
- package/src/href-client.ts +1 -1
- package/src/html.ts +12 -0
- package/src/index.rsc.ts +5 -0
- package/src/index.ts +1 -0
- package/src/loader-store.ts +7 -4
- package/src/prerender/build-shell-capture.ts +73 -22
- package/src/reverse.ts +44 -4
- package/src/route-definition/dsl-helpers.ts +121 -33
- package/src/route-definition/helpers-types.ts +33 -9
- package/src/route-definition/resolve-handler-use.ts +121 -6
- package/src/route-types.ts +0 -3
- package/src/router/debug-manifest.ts +10 -2
- package/src/router/error-handling.ts +51 -15
- package/src/router/handler-context.ts +71 -28
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +141 -43
- package/src/router/loader-resolution.ts +115 -39
- package/src/router/match-api.ts +40 -26
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/background-revalidation.ts +161 -96
- package/src/router/match-middleware/cache-lookup.ts +110 -10
- package/src/router/match-middleware/cache-store.ts +48 -101
- package/src/router/middleware-types.ts +9 -6
- package/src/router/middleware.ts +75 -13
- package/src/router/prerender-match.ts +59 -19
- package/src/router/route-snapshot.ts +11 -1
- package/src/router/router-context.ts +2 -0
- package/src/router/router-interfaces.ts +25 -2
- package/src/router/router-options.ts +8 -5
- package/src/router/segment-resolution/fresh.ts +62 -21
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +407 -76
- package/src/router/segment-resolution/loader-mask.ts +12 -22
- package/src/router/segment-resolution/loader-snapshot.ts +208 -56
- package/src/router/segment-resolution/mask-nested.ts +138 -44
- package/src/router/segment-resolution/revalidation.ts +61 -16
- package/src/router/segment-wrappers.ts +4 -0
- package/src/rsc/full-payload.ts +25 -1
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/render-pipeline.ts +3 -0
- package/src/rsc/response-cache-serve.ts +9 -6
- package/src/rsc/response-route-handler.ts +11 -3
- package/src/rsc/rsc-rendering.ts +608 -225
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +73 -6
- package/src/rsc/shell-capture.ts +1068 -296
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/ssr-setup.ts +14 -8
- package/src/rsc/types.ts +18 -0
- package/src/server/context.ts +300 -32
- package/src/server/cookie-store.ts +87 -61
- package/src/server/handle-store.ts +232 -7
- package/src/server/request-context.ts +297 -67
- package/src/ssr/index.tsx +136 -44
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/static-handler.ts +4 -1
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/generated-routes.ts +2 -2
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/internal/flight-client-globals.ts +7 -5
- package/src/testing/render-route.tsx +85 -24
- package/src/testing/run-loader.ts +68 -6
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
- package/src/testing/vitest.ts +62 -1
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/theme/ThemeScript.tsx +3 -3
- package/src/types/cache-types.ts +14 -10
- package/src/types/handler-context.ts +28 -1
- package/src/types/loader-types.ts +8 -6
- package/src/urls/pattern-types.ts +22 -18
- package/src/urls/response-types.ts +3 -23
- package/src/use-loader.tsx +12 -3
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/plugins/use-cache-transform.ts +118 -74
- package/src/vite/rango.ts +7 -3
- package/src/vite/router-discovery.ts +14 -4
|
@@ -17,6 +17,7 @@ import type { CookieOptions } from "../router/middleware.js";
|
|
|
17
17
|
import {
|
|
18
18
|
KEEP_CACHE_HEADER,
|
|
19
19
|
getRawCookieValue,
|
|
20
|
+
freshReadsCookieName,
|
|
20
21
|
mintStateValue,
|
|
21
22
|
serializeStateCookie,
|
|
22
23
|
} from "../browser/cookie-name.js";
|
|
@@ -61,9 +62,10 @@ import type { RoutineTrace } from "../rsc/routine-plan.js";
|
|
|
61
62
|
import { NOCACHE_SYMBOL, assertNotInsideCacheExec } from "../cache/taint.js";
|
|
62
63
|
import {
|
|
63
64
|
assertCachedHeaderWriteAllowed,
|
|
65
|
+
assertNonCacheableReadAllowed,
|
|
64
66
|
clearPprHeaderScope,
|
|
65
|
-
isInsideCacheScope,
|
|
66
67
|
} from "./context.js";
|
|
68
|
+
import { assertThemeReadAllowed } from "./cookie-store.js";
|
|
67
69
|
import {
|
|
68
70
|
createReverseFunction,
|
|
69
71
|
stripInternalParams,
|
|
@@ -165,6 +167,40 @@ export interface RequestContext<
|
|
|
165
167
|
_rotateStateCookie(): void;
|
|
166
168
|
/** @internal Set the keepClientCache() directive header on the response. */
|
|
167
169
|
_setKeepCacheDirective(): void;
|
|
170
|
+
/**
|
|
171
|
+
* @internal This request carries the fresh-reads cookie: it follows an
|
|
172
|
+
* updateTag()/revalidateTag() by the same user, so the stores read tag
|
|
173
|
+
* markers and shells from the store, not from an isolate memo.
|
|
174
|
+
*/
|
|
175
|
+
_freshReads?: boolean;
|
|
176
|
+
/**
|
|
177
|
+
* @internal Set the fresh-reads cookie on this response for `maxAgeMs`
|
|
178
|
+
* (rounded up to whole seconds; the longest stays). Called by
|
|
179
|
+
* updateTag()/revalidateTag().
|
|
180
|
+
*/
|
|
181
|
+
_setFreshReadsCookie(maxAgeMs: number): void;
|
|
182
|
+
/**
|
|
183
|
+
* @internal The context createRequestContext() returned. A context derived
|
|
184
|
+
* from it with Object.create (a PPR HIT tail, a shell capture, a background
|
|
185
|
+
* revalidation render) inherits it, so per-request invalidation state
|
|
186
|
+
* (tag-invalidation.ts, the stores' masks) is keyed by the request, not by
|
|
187
|
+
* whichever derived object called or read (#973).
|
|
188
|
+
*/
|
|
189
|
+
_requestRoot?: RequestContext<any, any>;
|
|
190
|
+
/**
|
|
191
|
+
* @internal The tags this request invalidated with
|
|
192
|
+
* updateTag()/revalidateTag(), each with its position in the invalidation
|
|
193
|
+
* order (tag-invalidation.ts currentCacheSeq). Kept on _requestRoot. A
|
|
194
|
+
* "use cache" execution that started before one of its own tags was
|
|
195
|
+
* invalidated is neither joined nor written (cache-runtime.ts, #973).
|
|
196
|
+
*/
|
|
197
|
+
_tagInvalidations?: Map<string, number>;
|
|
198
|
+
/**
|
|
199
|
+
* @internal The handler has handed the response to the host (rsc/handler.ts
|
|
200
|
+
* `rango.response`): header and cookie writes after this are lost, as from
|
|
201
|
+
* a streaming loader or render.
|
|
202
|
+
*/
|
|
203
|
+
_responseSent?: boolean;
|
|
168
204
|
|
|
169
205
|
/**
|
|
170
206
|
* Access loader data or push handle data.
|
|
@@ -197,8 +233,13 @@ export interface RequestContext<
|
|
|
197
233
|
/** HTTP method (GET, POST, PUT, PATCH, DELETE, etc.) */
|
|
198
234
|
method: string;
|
|
199
235
|
|
|
200
|
-
/**
|
|
201
|
-
|
|
236
|
+
/**
|
|
237
|
+
* @internal Handle store for tracking handle data across segments.
|
|
238
|
+
* Readonly: a background render that needs its own store derives a context
|
|
239
|
+
* (rerenderAndCacheRoute, shell-capture.ts) instead of swapping this field,
|
|
240
|
+
* which the foreground still reads while it produces the page.
|
|
241
|
+
*/
|
|
242
|
+
readonly _handleStore: HandleStore;
|
|
202
243
|
|
|
203
244
|
/**
|
|
204
245
|
* @internal transition({ when }) predicates for segments matched this request,
|
|
@@ -250,16 +291,58 @@ export interface RequestContext<
|
|
|
250
291
|
/**
|
|
251
292
|
* @internal Bake-lane loader containers collected DURING a shell capture:
|
|
252
293
|
* segment-key -> the loader's (pre-wrap) result promise. Populated by
|
|
253
|
-
* resolveLoaderData for loaders
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
* shared shell). Own property of the capture's derived context only.
|
|
294
|
+
* resolveLoaderData for bake-lane loaders (lane rule: see its JSDoc in
|
|
295
|
+
* loader-cache.ts). Drained by captureAndStoreShell after the shell
|
|
296
|
+
* quiesces: settled containers are promise-elided, Flight-serialized, and
|
|
297
|
+
* pinned into the snapshot's loader family; a REJECTED container refuses
|
|
298
|
+
* the capture (error UI must never bake into the shared shell). Own
|
|
299
|
+
* property of the capture's derived context only.
|
|
260
300
|
*/
|
|
261
301
|
_shellCaptureLoaderRecords?: Map<string, Promise<unknown>>;
|
|
262
302
|
|
|
303
|
+
/**
|
|
304
|
+
* @internal Set on a capture's derived context when a loader pushed a
|
|
305
|
+
* value the record cannot keep (a deferred push, masked nested promises,
|
|
306
|
+
* or a push outside a loader body): the loader records then carry
|
|
307
|
+
* `runs: 1`, so a HIT still runs their bodies for those pushes.
|
|
308
|
+
*/
|
|
309
|
+
_shellCaptureUnrecordedLoaderPush?: true;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* @internal Record keys resolved for this request (CacheScope
|
|
313
|
+
* resolveKeyFrom): by the scope's `key()` (one result) or its store's
|
|
314
|
+
* keyGenerator (one per default key). A shell capture's derived context
|
|
315
|
+
* reads it through the prototype chain, so the capture reuses the
|
|
316
|
+
* partition its request resolved instead of running `key()` again.
|
|
317
|
+
*/
|
|
318
|
+
_resolvedCacheKeys?: Map<unknown, Map<string, Promise<string>>>;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* @internal The shell store key the PPR serve path resolved for this
|
|
322
|
+
* document request (rsc-rendering.ts shellServePlan), request partition
|
|
323
|
+
* included. Read by the testing helper serveShellRequest.
|
|
324
|
+
*/
|
|
325
|
+
_shellKey?: string;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* @internal The request carried the forced-MISS reload marker
|
|
329
|
+
* (shell-serve.ts SHELL_MISS_PARAM). The handler strips the marker from the
|
|
330
|
+
* request on entry and sets this; the shell gate renders the request like a
|
|
331
|
+
* cache miss.
|
|
332
|
+
*/
|
|
333
|
+
_shellForcedMiss?: true;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* @internal The freshness window of the route cache() records this
|
|
337
|
+
* request read or wrote (cache-scope.ts noteRouteRecordWindow), as ms
|
|
338
|
+
* timestamps: `freshUntil` is the record's `expiresAt`, `staleUntil` adds
|
|
339
|
+
* its scope's swr. The narrowest record wins. A shell capture caps its
|
|
340
|
+
* entry's ttl and ttl+swr to it (shell-capture.ts capShellWindow), so a
|
|
341
|
+
* shell is never fresher, or served longer, than the route cache() entry
|
|
342
|
+
* it replayed or wrote.
|
|
343
|
+
*/
|
|
344
|
+
_routeRecordWindow?: RouteRecordWindow;
|
|
345
|
+
|
|
263
346
|
/**
|
|
264
347
|
* @internal Loader-family snapshot seed for a shell HIT's tail render:
|
|
265
348
|
* segment-key -> the capture's elided container (already Flight-deserialized
|
|
@@ -267,8 +350,10 @@ export interface RequestContext<
|
|
|
267
350
|
* overlays it onto the fresh run's container (recorded paths pinned,
|
|
268
351
|
* hole-marker paths keep the fresh nested promises) so the payload's baked
|
|
269
352
|
* bytes match the frozen prelude; a hole-free entry resolves pin-first
|
|
270
|
-
*
|
|
271
|
-
* context
|
|
353
|
+
* and does not run the loader. Two owners: serveShellHit sets it on the
|
|
354
|
+
* HIT tail's derived context, and matchPartialWithPprReplay sets it on the
|
|
355
|
+
* request context of a PPR partial replay once its doc record hits, and
|
|
356
|
+
* restores the previous value after the match.
|
|
272
357
|
*/
|
|
273
358
|
_shellLoaderSeed?: Map<
|
|
274
359
|
string,
|
|
@@ -276,16 +361,17 @@ export interface RequestContext<
|
|
|
276
361
|
>;
|
|
277
362
|
|
|
278
363
|
/**
|
|
279
|
-
* @internal
|
|
280
|
-
*
|
|
364
|
+
* @internal The doc record marker: makes the next match treat the whole
|
|
365
|
+
* matched route as an implicit doc-level cache() boundary (see
|
|
281
366
|
* resolveShellImplicitCacheScope in cache/cache-scope.ts). Set ONLY on
|
|
282
367
|
* (a) the capture's derived context — with a record-only store so the
|
|
283
368
|
* capture's cacheRoute write lands in the snapshot, never the real store —
|
|
284
|
-
* (b) a HIT tail's seeded context, and (c) an eligible
|
|
285
|
-
* replay, where `store` is a request-local segment
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
369
|
+
* (b) a document HIT tail's seeded context (`docTail`), and (c) an eligible
|
|
370
|
+
* normal-route partial replay, where `store` is a request-local segment
|
|
371
|
+
* overlay.
|
|
372
|
+
* A document HIT tail replays the record even for a route with its own
|
|
373
|
+
* cache() config (the gate before the commit already evaluated that
|
|
374
|
+
* scope's opt-outs). Elsewhere the route's scope stays authoritative. On the
|
|
289
375
|
* navigation-replay serve path (`onExplicitHit` set) the marker COMPOSES with
|
|
290
376
|
* such a scope instead of being ignored: the seeded doc record supplies the match
|
|
291
377
|
* only when the explicit tier misses (withCacheLookup). cache(false) and a
|
|
@@ -302,12 +388,17 @@ export interface RequestContext<
|
|
|
302
388
|
* segment record even when the triggering request is partial.
|
|
303
389
|
*/
|
|
304
390
|
keyPrefix?: "doc";
|
|
305
|
-
/**
|
|
306
|
-
|
|
391
|
+
/**
|
|
392
|
+
* @internal Called only after the implicit cache hit decodes
|
|
393
|
+
* successfully; the lookup awaits it before returning the hit, so work
|
|
394
|
+
* it starts lands before the route's loaders resolve.
|
|
395
|
+
*/
|
|
396
|
+
onHit?: () => void | Promise<void>;
|
|
307
397
|
/**
|
|
308
398
|
* @internal Called when the seeded document record fails server-side
|
|
309
399
|
* deserialization. Partial replay uses it to replace the enclosing shell
|
|
310
|
-
* snapshot after serving the fresh fallback.
|
|
400
|
+
* snapshot after serving the fresh fallback. A document HIT tail does not
|
|
401
|
+
* set it: withCacheLookup throws ShellRecordUnavailableError instead.
|
|
311
402
|
*/
|
|
312
403
|
onCorrupt?: () => void;
|
|
313
404
|
/**
|
|
@@ -318,6 +409,21 @@ export interface RequestContext<
|
|
|
318
409
|
* require the exact consumable record instead of "any segment record".
|
|
319
410
|
*/
|
|
320
411
|
docKey?: string;
|
|
412
|
+
/**
|
|
413
|
+
* @internal Set on a document HIT tail (serveShellHit). The implicit doc
|
|
414
|
+
* scope replaces any route-derived cache() scope for the tail's match
|
|
415
|
+
* (resolveShellImplicitCacheScope), and a lookup that does not hit throws
|
|
416
|
+
* ShellRecordUnavailableError instead of resolving segments
|
|
417
|
+
* (withCacheLookup): a HIT never runs a handler.
|
|
418
|
+
*/
|
|
419
|
+
docTail?: true;
|
|
420
|
+
/**
|
|
421
|
+
* @internal The key a document HIT tail looks the doc record up by: the
|
|
422
|
+
* entry's own `docKey`. Recomputing it could name another key (a store
|
|
423
|
+
* keyGenerator folding request data, a build-time capture's synthetic
|
|
424
|
+
* host) and miss the record.
|
|
425
|
+
*/
|
|
426
|
+
fixedDocKey?: string;
|
|
321
427
|
/**
|
|
322
428
|
* @internal Set ONLY by matchPartialWithPprReplay on the navigation-replay
|
|
323
429
|
* serve path. Its presence arms the explicit-scope composition in
|
|
@@ -361,43 +467,16 @@ export interface RequestContext<
|
|
|
361
467
|
_shellFragmentPayload?: boolean;
|
|
362
468
|
|
|
363
469
|
/**
|
|
364
|
-
* @internal
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
* pushes (liveness unknowable at the barrier); (c) a handler-invoked loader
|
|
369
|
-
* executing during the capture (loader-resolution.ts — its consumption-lane
|
|
370
|
-
* value would freeze on a handler-free HIT). captureAndStoreShell folds it
|
|
371
|
-
* into ShellCacheEntry.handlerLiveHoles at the putShell barrier. Own
|
|
372
|
-
* property of the capture's derived context only.
|
|
373
|
-
*/
|
|
374
|
-
_shellCaptureHandleLiveness?: {
|
|
375
|
-
holes: boolean;
|
|
376
|
-
pendingPushes: number;
|
|
377
|
-
handlerInvokedLoader: boolean;
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* @internal Handle values pushed from a DSL loader scope DURING a shell
|
|
382
|
-
* capture (identity set; populated by the capture push wrapper in
|
|
383
|
-
* shell-capture.ts). cacheRoute threads it into captureHandles so those
|
|
384
|
-
* values stay out of cache-write handle records — loaders re-run fresh on
|
|
385
|
-
* every HIT, so replaying their captured (masked) values would duplicate
|
|
386
|
-
* the fresh push and stall the Flight handle encode. Own property of the
|
|
387
|
-
* capture's derived context only; render-time handle consumers are
|
|
388
|
-
* unaffected (the exclusion applies only at the captureHandles call site).
|
|
389
|
-
*/
|
|
390
|
-
_shellCaptureLoaderHandleValues?: WeakSet<object>;
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* @internal Set (to the offending fn name) by the cookies()/headers()
|
|
394
|
-
* capture guard when it throws DURING a capture render. Load-bearing for the
|
|
470
|
+
* @internal Set by the capture guard (server/context.ts
|
|
471
|
+
* tripShellCaptureGuard) when it throws DURING a capture render: the read
|
|
472
|
+
* ("cookies()", "headers()", "ctx.get()", "ctx.theme") and the fix the
|
|
473
|
+
* refusal warning gives for it. Load-bearing for the
|
|
395
474
|
* bake lane: a guard throw inside an executing loader is swallowed by
|
|
396
475
|
* wrapLoaderPromise into per-loader error UI, which would otherwise bake
|
|
397
476
|
* silently into the shared shell — the capture checks this flag after the
|
|
398
477
|
* render and refuses instead. Deterministic, so the capture does not retry.
|
|
399
478
|
*/
|
|
400
|
-
_shellCaptureGuardTripped?: string;
|
|
479
|
+
_shellCaptureGuardTripped?: { surface: string; fix: string };
|
|
401
480
|
|
|
402
481
|
/**
|
|
403
482
|
* @internal The loader $$id whose BODY was executing when the capture guard
|
|
@@ -428,6 +507,23 @@ export interface RequestContext<
|
|
|
428
507
|
*/
|
|
429
508
|
_requestTags: Set<string>;
|
|
430
509
|
|
|
510
|
+
/**
|
|
511
|
+
* @internal This request can write a route cache() record: its match
|
|
512
|
+
* resolved a cache scope (match-api.ts). Arms the record tag owners
|
|
513
|
+
* (cache-tag.ts armRecordTagOwners); off, the segment scopes, loader links
|
|
514
|
+
* and owned tag sets are skipped entirely. A capture's or background
|
|
515
|
+
* re-render's derived context (Object.create) inherits it.
|
|
516
|
+
*/
|
|
517
|
+
_recordTagOwners?: true;
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* @internal This request resolves a loader bound with its own cache()
|
|
521
|
+
* (match-api.ts, or the binding's funnel). Arms the per-execution loader
|
|
522
|
+
* tag sets (cache-tag.ts armLoaderTagSets); off, a loader execution
|
|
523
|
+
* allocates no set and a read links nothing. Derived contexts inherit it.
|
|
524
|
+
*/
|
|
525
|
+
_recordLoaderTags?: true;
|
|
526
|
+
|
|
431
527
|
/** @internal Cache profiles for "use cache" profile resolution (per-router) */
|
|
432
528
|
_cacheProfiles?: Record<
|
|
433
529
|
string,
|
|
@@ -475,6 +571,11 @@ export interface RequestContext<
|
|
|
475
571
|
* Returns the theme value from the cookie, or the default theme if not set.
|
|
476
572
|
* This is the user's preference ("light", "dark", or "system"), not the resolved value.
|
|
477
573
|
*
|
|
574
|
+
* The visitor's cookie, so it is guarded like `cookies()`: reading it
|
|
575
|
+
* refuses a ppr shell capture and throws inside a `cache()` boundary or a
|
|
576
|
+
* `"use cache"` function (#971). A non-enumerable getter: a spread or
|
|
577
|
+
* `Object.assign` copy of the context does not carry it.
|
|
578
|
+
*
|
|
478
579
|
* @example
|
|
479
580
|
* ```typescript
|
|
480
581
|
* route("settings", (ctx) => {
|
|
@@ -483,7 +584,7 @@ export interface RequestContext<
|
|
|
483
584
|
* });
|
|
484
585
|
* ```
|
|
485
586
|
*/
|
|
486
|
-
theme?: Theme;
|
|
587
|
+
readonly theme?: Theme;
|
|
487
588
|
|
|
488
589
|
/**
|
|
489
590
|
* Set the theme (only available when theme is enabled in router config)
|
|
@@ -507,6 +608,15 @@ export interface RequestContext<
|
|
|
507
608
|
/** @internal Theme configuration (null if theme not enabled) */
|
|
508
609
|
_themeConfig?: ResolvedThemeConfig | null;
|
|
509
610
|
|
|
611
|
+
/**
|
|
612
|
+
* @internal The theme cookie (or the default) read with no identity guard.
|
|
613
|
+
* The guarded reads (`theme` here, the handler and middleware `ctx.theme`)
|
|
614
|
+
* call it after assertThemeReadAllowed; the router's own payload read goes
|
|
615
|
+
* through payloadInitialTheme (rsc/full-payload.ts). Undefined without
|
|
616
|
+
* theme config.
|
|
617
|
+
*/
|
|
618
|
+
_readTheme(): Theme | undefined;
|
|
619
|
+
|
|
510
620
|
/**
|
|
511
621
|
* Attach location state entries to the current response.
|
|
512
622
|
*
|
|
@@ -650,6 +760,17 @@ export interface RequestContext<
|
|
|
650
760
|
/** @internal Per-request error dedup set for onError reporting */
|
|
651
761
|
_reportedErrors: WeakSet<object>;
|
|
652
762
|
|
|
763
|
+
/**
|
|
764
|
+
* @internal Errors this response's Flight or Fizz render reported through
|
|
765
|
+
* onError: a component that threw does not fail the stream, which completes
|
|
766
|
+
* with an error row or an errored Suspense boundary. Whole-response captures
|
|
767
|
+
* (document-cache.ts, captureAndStoreShell) store nothing while it is
|
|
768
|
+
* non-empty (issue #915). An array so a render under a derived context (the
|
|
769
|
+
* shell HIT tail) pushes into the request's own list through the prototype;
|
|
770
|
+
* deriveShellCaptureContext gives the capture its own.
|
|
771
|
+
*/
|
|
772
|
+
_renderErrors?: unknown[];
|
|
773
|
+
|
|
653
774
|
/**
|
|
654
775
|
* @internal Report a non-fatal background error through the router's
|
|
655
776
|
* onError callback. Wired by the RSC handler / router during request
|
|
@@ -723,6 +844,21 @@ export interface RequestContext<
|
|
|
723
844
|
_cacheSignal?: import("../router/telemetry.js").CacheSegmentSignal[];
|
|
724
845
|
}
|
|
725
846
|
|
|
847
|
+
/**
|
|
848
|
+
* @internal The freshness window of a route cache() record, as ms
|
|
849
|
+
* timestamps: fresh until `freshUntil` (its `expiresAt`), stale until
|
|
850
|
+
* `staleUntil` (plus its scope's swr). `ttl`, `swr` and `written` describe
|
|
851
|
+
* the record that sets `staleUntil`: its cache() config, and whether this
|
|
852
|
+
* request wrote it (cacheRoute) rather than read it.
|
|
853
|
+
*/
|
|
854
|
+
export interface RouteRecordWindow {
|
|
855
|
+
freshUntil: number;
|
|
856
|
+
staleUntil: number;
|
|
857
|
+
ttl: number;
|
|
858
|
+
swr: number;
|
|
859
|
+
written: boolean;
|
|
860
|
+
}
|
|
861
|
+
|
|
726
862
|
/**
|
|
727
863
|
* Public view of RequestContext, without internal methods and fields.
|
|
728
864
|
*
|
|
@@ -748,17 +884,23 @@ export type PublicRequestContext<
|
|
|
748
884
|
| "_shellImplicitCache"
|
|
749
885
|
| "_shellLoaderSeed"
|
|
750
886
|
| "_shellCaptureLoaderRecords"
|
|
751
|
-
| "
|
|
752
|
-
| "
|
|
887
|
+
| "_shellCaptureUnrecordedLoaderPush"
|
|
888
|
+
| "_resolvedCacheKeys"
|
|
889
|
+
| "_shellKey"
|
|
890
|
+
| "_shellForcedMiss"
|
|
891
|
+
| "_routeRecordWindow"
|
|
753
892
|
| "_shellFragmentPayload"
|
|
754
893
|
| "_shellCaptureGuardTripped"
|
|
755
894
|
| "_shellCaptureGuardTrippedLoaderId"
|
|
756
895
|
| "_explicitTaggedStores"
|
|
757
896
|
| "_requestTags"
|
|
897
|
+
| "_recordTagOwners"
|
|
898
|
+
| "_recordLoaderTags"
|
|
758
899
|
| "_cacheProfiles"
|
|
759
900
|
| "_onResponseCallbacks"
|
|
760
901
|
| "_pendingBackgroundTasks"
|
|
761
902
|
| "_themeConfig"
|
|
903
|
+
| "_readTheme"
|
|
762
904
|
| "_locationState"
|
|
763
905
|
| "_routeName"
|
|
764
906
|
| "_prevRouteKey"
|
|
@@ -770,6 +912,7 @@ export type PublicRequestContext<
|
|
|
770
912
|
| "_gateFormData"
|
|
771
913
|
| "_inActionRevalidation"
|
|
772
914
|
| "_reportedErrors"
|
|
915
|
+
| "_renderErrors"
|
|
773
916
|
| "_renderBarrier"
|
|
774
917
|
| "_resolveRenderBarrier"
|
|
775
918
|
| "_renderBarrierSegmentOrder"
|
|
@@ -792,6 +935,11 @@ export type PublicRequestContext<
|
|
|
792
935
|
| "_setStatus"
|
|
793
936
|
| "_rotateStateCookie"
|
|
794
937
|
| "_setKeepCacheDirective"
|
|
938
|
+
| "_freshReads"
|
|
939
|
+
| "_setFreshReadsCookie"
|
|
940
|
+
| "_requestRoot"
|
|
941
|
+
| "_tagInvalidations"
|
|
942
|
+
| "_responseSent"
|
|
795
943
|
| "_variables"
|
|
796
944
|
| "_classifiedRoute"
|
|
797
945
|
| "_requestMode"
|
|
@@ -988,6 +1136,10 @@ export function createRequestContext<TEnv>(
|
|
|
988
1136
|
} = options;
|
|
989
1137
|
const cookieHeader = request.headers.get("Cookie");
|
|
990
1138
|
let rangoStateRotated = false;
|
|
1139
|
+
const freshCookie = stateCookieName
|
|
1140
|
+
? freshReadsCookieName(stateCookieName)
|
|
1141
|
+
: undefined;
|
|
1142
|
+
let freshCookieMaxAge = 0;
|
|
991
1143
|
let parsedCookies: Record<string, string> | null = null;
|
|
992
1144
|
|
|
993
1145
|
let stubResponse = initialResponse
|
|
@@ -1140,12 +1292,8 @@ export function createRequestContext<TEnv>(
|
|
|
1140
1292
|
},
|
|
1141
1293
|
_dynamic: false,
|
|
1142
1294
|
get: ((keyOrVar: any) => {
|
|
1143
|
-
if (isNonCacheable(variables, keyOrVar)
|
|
1144
|
-
|
|
1145
|
-
`ctx.get() for a non-cacheable variable cannot be called inside a cache() boundary. ` +
|
|
1146
|
-
`The variable was created with { cache: false } or set with { cache: false }, ` +
|
|
1147
|
-
`and its value would be stale on cache hit. Move the read outside the cached scope.`,
|
|
1148
|
-
);
|
|
1295
|
+
if (isNonCacheable(variables, keyOrVar)) {
|
|
1296
|
+
assertNonCacheableReadAllowed(keyOrVar, _getRequestContext());
|
|
1149
1297
|
}
|
|
1150
1298
|
return contextGet(variables, keyOrVar);
|
|
1151
1299
|
}) as RequestContext<TEnv>["get"],
|
|
@@ -1255,6 +1403,48 @@ export function createRequestContext<TEnv>(
|
|
|
1255
1403
|
rawStubHeaders.set(KEEP_CACHE_HEADER, "1");
|
|
1256
1404
|
},
|
|
1257
1405
|
|
|
1406
|
+
// The fresh-reads cookie (browser/cookie-name.ts): one Set-Cookie per
|
|
1407
|
+
// response, the longest Max-Age any invalidation asked for. HttpOnly (the
|
|
1408
|
+
// client never reads it), SameSite=Lax, Path=/. rawStubHeaders: an
|
|
1409
|
+
// internal writer, allowed wherever updateTag()/revalidateTag() are. A
|
|
1410
|
+
// shared cache never stores it (Set-Cookie is a per-client signal).
|
|
1411
|
+
_setFreshReadsCookie(maxAgeMs: number): void {
|
|
1412
|
+
if (!freshCookie) return;
|
|
1413
|
+
const maxAge = Math.ceil(maxAgeMs / 1000);
|
|
1414
|
+
// Already set for at least this long (an earlier invalidation in this
|
|
1415
|
+
// request): nothing to add, sent or not.
|
|
1416
|
+
if (maxAge <= freshCookieMaxAge) return;
|
|
1417
|
+
if (ctx._responseSent) {
|
|
1418
|
+
// A streaming loader or render: the headers already left. The
|
|
1419
|
+
// invalidation itself still ran; only the mutating user's bypass of
|
|
1420
|
+
// the stores' isolate memos is lost.
|
|
1421
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1422
|
+
console.warn(
|
|
1423
|
+
`[rango] updateTag()/revalidateTag() ran after the response ` +
|
|
1424
|
+
`headers were sent (from a streaming loader or render), so the ` +
|
|
1425
|
+
`${freshCookie} cookie was not set: this user's next requests ` +
|
|
1426
|
+
`can be served from a store's isolate memo for a few seconds. ` +
|
|
1427
|
+
`Invalidate in a server action, a route handler, or middleware.`,
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
const serialized = (age: number) =>
|
|
1433
|
+
`${freshCookie}=1; Max-Age=${age}; Path=/; HttpOnly; SameSite=Lax` +
|
|
1434
|
+
(url.protocol === "https:" ? "; Secure" : "");
|
|
1435
|
+
if (freshCookieMaxAge > 0) {
|
|
1436
|
+
const prior = serialized(freshCookieMaxAge);
|
|
1437
|
+
const kept = rawStubHeaders
|
|
1438
|
+
.getSetCookie()
|
|
1439
|
+
.filter((cookie) => cookie !== prior);
|
|
1440
|
+
rawStubHeaders.delete("Set-Cookie");
|
|
1441
|
+
for (const cookie of kept) rawStubHeaders.append("Set-Cookie", cookie);
|
|
1442
|
+
}
|
|
1443
|
+
freshCookieMaxAge = maxAge;
|
|
1444
|
+
rawStubHeaders.append("Set-Cookie", serialized(maxAge));
|
|
1445
|
+
invalidateResponseCookieCache();
|
|
1446
|
+
},
|
|
1447
|
+
|
|
1258
1448
|
setStatus(status: number): void {
|
|
1259
1449
|
assertNotInsideCacheExec(ctx, "setStatus");
|
|
1260
1450
|
assertResponseWriteAllowed("setStatus");
|
|
@@ -1276,6 +1466,9 @@ export function createRequestContext<TEnv>(
|
|
|
1276
1466
|
_explicitTaggedStores: explicitTaggedStores,
|
|
1277
1467
|
_requestTags: new Set<string>(),
|
|
1278
1468
|
_cacheProfiles: cacheProfiles,
|
|
1469
|
+
_freshReads:
|
|
1470
|
+
freshCookie !== undefined &&
|
|
1471
|
+
getRawCookieValue(cookieHeader, freshCookie) !== null,
|
|
1279
1472
|
|
|
1280
1473
|
waitUntil(fn: () => Promise<void>): void {
|
|
1281
1474
|
if (ctx.build) return;
|
|
@@ -1317,9 +1510,8 @@ export function createRequestContext<TEnv>(
|
|
|
1317
1510
|
this._onResponseCallbacks.push(callback);
|
|
1318
1511
|
},
|
|
1319
1512
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
},
|
|
1513
|
+
// theme: a guarded, non-enumerable getter defined after this literal.
|
|
1514
|
+
_readTheme: getTheme,
|
|
1323
1515
|
setTheme: themeConfig
|
|
1324
1516
|
? (theme: Theme) => {
|
|
1325
1517
|
assertNotInsideCacheExec(ctx, "setTheme");
|
|
@@ -1338,6 +1530,7 @@ export function createRequestContext<TEnv>(
|
|
|
1338
1530
|
_locationState: undefined,
|
|
1339
1531
|
|
|
1340
1532
|
_reportedErrors: new WeakSet<object>(),
|
|
1533
|
+
_renderErrors: [],
|
|
1341
1534
|
_metricsStore: undefined,
|
|
1342
1535
|
_renderForeground: undefined,
|
|
1343
1536
|
_activeRoutine: undefined,
|
|
@@ -1359,7 +1552,39 @@ export function createRequestContext<TEnv>(
|
|
|
1359
1552
|
getContext: () => ctx,
|
|
1360
1553
|
});
|
|
1361
1554
|
|
|
1555
|
+
// getRequestContext().theme is the visitor's cookie, guarded like cookies()
|
|
1556
|
+
// (#971); before it was a plain read that a ppr shell, a cache() entry or a
|
|
1557
|
+
// "use cache" entry stored for every visitor. Non-enumerable like the
|
|
1558
|
+
// handler ctx.theme (handler-context.ts THEME_DESCRIPTOR): serializing the
|
|
1559
|
+
// context (React dev debug info) must not count as a read. The guard reads
|
|
1560
|
+
// the ambient context, as cookies() does, and falls back to the receiver.
|
|
1561
|
+
// _readTheme is the unguarded read: non-enumerable too, so a spread copy of
|
|
1562
|
+
// the context (the fetchable-loader ctx, loader-fetch.ts) cannot carry it.
|
|
1563
|
+
Object.defineProperties(ctx, {
|
|
1564
|
+
theme: {
|
|
1565
|
+
get(this: RequestContext<TEnv>): Theme | undefined {
|
|
1566
|
+
if (!themeConfig) return undefined;
|
|
1567
|
+
assertThemeReadAllowed(
|
|
1568
|
+
_getRequestContext() ?? this,
|
|
1569
|
+
"getRequestContext().theme",
|
|
1570
|
+
);
|
|
1571
|
+
return getTheme();
|
|
1572
|
+
},
|
|
1573
|
+
enumerable: false,
|
|
1574
|
+
configurable: true,
|
|
1575
|
+
},
|
|
1576
|
+
_readTheme: { enumerable: false },
|
|
1577
|
+
});
|
|
1578
|
+
|
|
1362
1579
|
(ctx as any)[NOCACHE_SYMBOL] = true;
|
|
1580
|
+
// Non-enumerable: a self-reference would make the context circular for
|
|
1581
|
+
// JSON.stringify, spreads and React's dev debug-info serialization of props.
|
|
1582
|
+
Object.defineProperty(ctx, "_requestRoot", {
|
|
1583
|
+
value: ctx,
|
|
1584
|
+
enumerable: false,
|
|
1585
|
+
configurable: true,
|
|
1586
|
+
writable: true,
|
|
1587
|
+
});
|
|
1363
1588
|
return ctx;
|
|
1364
1589
|
}
|
|
1365
1590
|
|
|
@@ -1627,7 +1852,9 @@ export function createUseFunction<TEnv>(
|
|
|
1627
1852
|
? parseSearchParams(ctx.searchParams, searchSchema)
|
|
1628
1853
|
: {};
|
|
1629
1854
|
|
|
1630
|
-
const loaderCtx: LoaderContext<Record<string, string | undefined>, TEnv>
|
|
1855
|
+
const loaderCtx: LoaderContext<Record<string, string | undefined>, TEnv> & {
|
|
1856
|
+
_routeName?: string;
|
|
1857
|
+
} = {
|
|
1631
1858
|
params: ctx.params,
|
|
1632
1859
|
routeParams: (ctx.params ?? {}) as Record<string, string>,
|
|
1633
1860
|
request: ctx.request,
|
|
@@ -1666,6 +1893,7 @@ export function createUseFunction<TEnv>(
|
|
|
1666
1893
|
? isRouteRootScoped(ctx._routeName, ctx._routerId)
|
|
1667
1894
|
: undefined,
|
|
1668
1895
|
),
|
|
1896
|
+
_routeName: ctx._routeName,
|
|
1669
1897
|
rendered: () => {
|
|
1670
1898
|
throw new Error(
|
|
1671
1899
|
`ctx.rendered() is only available in DSL loaders (registered via loader() in urls()). ` +
|
|
@@ -1673,6 +1901,8 @@ export function createUseFunction<TEnv>(
|
|
|
1673
1901
|
);
|
|
1674
1902
|
},
|
|
1675
1903
|
};
|
|
1904
|
+
// "use cache" keys it by route, like the request ctx it reads (#940).
|
|
1905
|
+
(loaderCtx as any)[NOCACHE_SYMBOL] = true;
|
|
1676
1906
|
|
|
1677
1907
|
// Meter through the same unified phase API as the loader-resolution funnel
|
|
1678
1908
|
// (observePhase), so a loader resolved via this base request-context ctx.use
|