@rangojs/router 0.17.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 +3 -2
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/history-state.d.ts +16 -0
- 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 +98 -14
- 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-snapshot.d.ts +4 -3
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- 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/types.d.ts +207 -43
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/middleware-types.d.ts +6 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +40 -11
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +21 -10
- package/dist/types/router/segment-resolution/mask-nested.d.ts +65 -24
- package/dist/types/rsc/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +53 -6
- package/dist/types/rsc/shell-capture.d.ts +146 -15
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/types.d.ts +7 -0
- package/dist/types/server/context.d.ts +109 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/request-context.d.ts +159 -33
- package/dist/types/ssr/index.d.ts +9 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/testing/flight.entry.d.ts +7 -0
- 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/types/handler-context.d.ts +14 -1
- package/dist/types/urls/pattern-types.d.ts +22 -18
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +46 -9
- package/package.json +1 -1
- package/skills/breadcrumbs/SKILL.md +17 -7
- package/skills/cache-guide/SKILL.md +123 -51
- package/skills/caching/SKILL.md +253 -23
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +11 -6
- package/skills/cloudflare/SKILL.md +15 -0
- package/skills/comparison/references/framework-comparison.md +44 -32
- package/skills/defer-hydration/SKILL.md +50 -30
- package/skills/deployment-caching/SKILL.md +5 -2
- package/skills/document-cache/SKILL.md +8 -2
- package/skills/handler-use/SKILL.md +2 -0
- package/skills/hooks/handle-and-actions.md +3 -1
- package/skills/hooks/state.md +3 -1
- package/skills/loader/SKILL.md +161 -39
- package/skills/middleware/SKILL.md +16 -9
- package/skills/migrate-nextjs/SKILL.md +67 -36
- package/skills/migrate-react-router/SKILL.md +2 -1
- package/skills/migrate-react-router/component-migration.md +3 -1
- package/skills/migrate-react-router/data-and-actions.md +3 -1
- package/skills/migrate-react-router/route-mapping.md +7 -4
- package/skills/observability/SKILL.md +64 -0
- package/skills/parallel/SKILL.md +13 -8
- package/skills/ppr/SKILL.md +655 -363
- package/skills/prerender/SKILL.md +27 -14
- package/skills/rango/SKILL.md +52 -33
- package/skills/route/SKILL.md +12 -7
- package/skills/router-setup/SKILL.md +4 -2
- package/skills/scripts/SKILL.md +8 -1
- package/skills/server-actions/SKILL.md +11 -0
- package/skills/shell-manifest/SKILL.md +21 -12
- package/skills/testing/SKILL.md +25 -21
- package/skills/testing/cache-prerender.md +15 -14
- package/skills/testing/loader.md +18 -1
- package/skills/testing/serve-shell-request.md +188 -0
- package/skills/theme/SKILL.md +52 -4
- package/skills/typesafety/env-and-bindings.md +10 -2
- package/skills/use-cache/SKILL.md +39 -16
- package/skills/vercel/SKILL.md +14 -2
- package/skills/view-transitions/SKILL.md +1 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- package/src/browser/rsc-router.tsx +5 -0
- package/src/cache/cache-key-utils.ts +22 -2
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +34 -7
- package/src/cache/cache-scope.ts +345 -33
- 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/handle-snapshot.ts +4 -3
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +104 -19
- package/src/cache/types.ts +211 -43
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- package/src/prerender/build-shell-capture.ts +65 -11
- package/src/route-definition/dsl-helpers.ts +4 -3
- package/src/router/handler-context.ts +38 -4
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +19 -7
- package/src/router/loader-resolution.ts +63 -32
- package/src/router/match-api.ts +28 -9
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/cache-lookup.ts +17 -0
- package/src/router/match-middleware/cache-store.ts +31 -11
- package/src/router/middleware-types.ts +6 -1
- package/src/router/middleware.ts +23 -4
- package/src/router/prerender-match.ts +6 -0
- package/src/router/segment-resolution/fresh.ts +18 -9
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +227 -56
- package/src/router/segment-resolution/loader-snapshot.ts +203 -51
- package/src/router/segment-resolution/mask-nested.ts +136 -42
- package/src/router/segment-resolution/revalidation.ts +27 -10
- 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/response-cache-serve.ts +9 -6
- package/src/rsc/rsc-rendering.ts +601 -181
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +60 -6
- package/src/rsc/shell-capture.ts +1001 -250
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/types.ts +7 -0
- package/src/server/context.ts +219 -28
- package/src/server/cookie-store.ts +87 -62
- package/src/server/request-context.ts +263 -38
- package/src/ssr/index.tsx +96 -37
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/run-loader.ts +63 -5
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/types/handler-context.ts +14 -1
- package/src/urls/pattern-types.ts +22 -18
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/router-discovery.ts +14 -4
|
@@ -104,6 +104,40 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
104
104
|
_rotateStateCookie(): void;
|
|
105
105
|
/** @internal Set the keepClientCache() directive header on the response. */
|
|
106
106
|
_setKeepCacheDirective(): void;
|
|
107
|
+
/**
|
|
108
|
+
* @internal This request carries the fresh-reads cookie: it follows an
|
|
109
|
+
* updateTag()/revalidateTag() by the same user, so the stores read tag
|
|
110
|
+
* markers and shells from the store, not from an isolate memo.
|
|
111
|
+
*/
|
|
112
|
+
_freshReads?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* @internal Set the fresh-reads cookie on this response for `maxAgeMs`
|
|
115
|
+
* (rounded up to whole seconds; the longest stays). Called by
|
|
116
|
+
* updateTag()/revalidateTag().
|
|
117
|
+
*/
|
|
118
|
+
_setFreshReadsCookie(maxAgeMs: number): void;
|
|
119
|
+
/**
|
|
120
|
+
* @internal The context createRequestContext() returned. A context derived
|
|
121
|
+
* from it with Object.create (a PPR HIT tail, a shell capture, a background
|
|
122
|
+
* revalidation render) inherits it, so per-request invalidation state
|
|
123
|
+
* (tag-invalidation.ts, the stores' masks) is keyed by the request, not by
|
|
124
|
+
* whichever derived object called or read (#973).
|
|
125
|
+
*/
|
|
126
|
+
_requestRoot?: RequestContext<any, any>;
|
|
127
|
+
/**
|
|
128
|
+
* @internal The tags this request invalidated with
|
|
129
|
+
* updateTag()/revalidateTag(), each with its position in the invalidation
|
|
130
|
+
* order (tag-invalidation.ts currentCacheSeq). Kept on _requestRoot. A
|
|
131
|
+
* "use cache" execution that started before one of its own tags was
|
|
132
|
+
* invalidated is neither joined nor written (cache-runtime.ts, #973).
|
|
133
|
+
*/
|
|
134
|
+
_tagInvalidations?: Map<string, number>;
|
|
135
|
+
/**
|
|
136
|
+
* @internal The handler has handed the response to the host (rsc/handler.ts
|
|
137
|
+
* `rango.response`): header and cookie writes after this are lost, as from
|
|
138
|
+
* a streaming loader or render.
|
|
139
|
+
*/
|
|
140
|
+
_responseSent?: boolean;
|
|
107
141
|
/**
|
|
108
142
|
* Access loader data or push handle data.
|
|
109
143
|
*
|
|
@@ -191,6 +225,44 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
191
225
|
* property of the capture's derived context only.
|
|
192
226
|
*/
|
|
193
227
|
_shellCaptureLoaderRecords?: Map<string, Promise<unknown>>;
|
|
228
|
+
/**
|
|
229
|
+
* @internal Set on a capture's derived context when a loader pushed a
|
|
230
|
+
* value the record cannot keep (a deferred push, masked nested promises,
|
|
231
|
+
* or a push outside a loader body): the loader records then carry
|
|
232
|
+
* `runs: 1`, so a HIT still runs their bodies for those pushes.
|
|
233
|
+
*/
|
|
234
|
+
_shellCaptureUnrecordedLoaderPush?: true;
|
|
235
|
+
/**
|
|
236
|
+
* @internal Record keys resolved for this request (CacheScope
|
|
237
|
+
* resolveKeyFrom): by the scope's `key()` (one result) or its store's
|
|
238
|
+
* keyGenerator (one per default key). A shell capture's derived context
|
|
239
|
+
* reads it through the prototype chain, so the capture reuses the
|
|
240
|
+
* partition its request resolved instead of running `key()` again.
|
|
241
|
+
*/
|
|
242
|
+
_resolvedCacheKeys?: Map<unknown, Map<string, Promise<string>>>;
|
|
243
|
+
/**
|
|
244
|
+
* @internal The shell store key the PPR serve path resolved for this
|
|
245
|
+
* document request (rsc-rendering.ts shellServePlan), request partition
|
|
246
|
+
* included. Read by the testing helper serveShellRequest.
|
|
247
|
+
*/
|
|
248
|
+
_shellKey?: string;
|
|
249
|
+
/**
|
|
250
|
+
* @internal The request carried the forced-MISS reload marker
|
|
251
|
+
* (shell-serve.ts SHELL_MISS_PARAM). The handler strips the marker from the
|
|
252
|
+
* request on entry and sets this; the shell gate renders the request like a
|
|
253
|
+
* cache miss.
|
|
254
|
+
*/
|
|
255
|
+
_shellForcedMiss?: true;
|
|
256
|
+
/**
|
|
257
|
+
* @internal The freshness window of the route cache() records this
|
|
258
|
+
* request read or wrote (cache-scope.ts noteRouteRecordWindow), as ms
|
|
259
|
+
* timestamps: `freshUntil` is the record's `expiresAt`, `staleUntil` adds
|
|
260
|
+
* its scope's swr. The narrowest record wins. A shell capture caps its
|
|
261
|
+
* entry's ttl and ttl+swr to it (shell-capture.ts capShellWindow), so a
|
|
262
|
+
* shell is never fresher, or served longer, than the route cache() entry
|
|
263
|
+
* it replayed or wrote.
|
|
264
|
+
*/
|
|
265
|
+
_routeRecordWindow?: RouteRecordWindow;
|
|
194
266
|
/**
|
|
195
267
|
* @internal Loader-family snapshot seed for a shell HIT's tail render:
|
|
196
268
|
* segment-key -> the capture's elided container (already Flight-deserialized
|
|
@@ -198,21 +270,24 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
198
270
|
* overlays it onto the fresh run's container (recorded paths pinned,
|
|
199
271
|
* hole-marker paths keep the fresh nested promises) so the payload's baked
|
|
200
272
|
* bytes match the frozen prelude; a hole-free entry resolves pin-first
|
|
201
|
-
*
|
|
202
|
-
* context
|
|
273
|
+
* and does not run the loader. Two owners: serveShellHit sets it on the
|
|
274
|
+
* HIT tail's derived context, and matchPartialWithPprReplay sets it on the
|
|
275
|
+
* request context of a PPR partial replay once its doc record hits, and
|
|
276
|
+
* restores the previous value after the match.
|
|
203
277
|
*/
|
|
204
278
|
_shellLoaderSeed?: Map<string, import("../cache/shell-snapshot.js").ShellLoaderSeedEntry>;
|
|
205
279
|
/**
|
|
206
|
-
* @internal
|
|
207
|
-
*
|
|
280
|
+
* @internal The doc record marker: makes the next match treat the whole
|
|
281
|
+
* matched route as an implicit doc-level cache() boundary (see
|
|
208
282
|
* resolveShellImplicitCacheScope in cache/cache-scope.ts). Set ONLY on
|
|
209
283
|
* (a) the capture's derived context — with a record-only store so the
|
|
210
284
|
* capture's cacheRoute write lands in the snapshot, never the real store —
|
|
211
|
-
* (b) a HIT tail's seeded context, and (c) an eligible
|
|
212
|
-
* replay, where `store` is a request-local segment
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
285
|
+
* (b) a document HIT tail's seeded context (`docTail`), and (c) an eligible
|
|
286
|
+
* normal-route partial replay, where `store` is a request-local segment
|
|
287
|
+
* overlay.
|
|
288
|
+
* A document HIT tail replays the record even for a route with its own
|
|
289
|
+
* cache() config (the gate before the commit already evaluated that
|
|
290
|
+
* scope's opt-outs). Elsewhere the route's scope stays authoritative. On the
|
|
216
291
|
* navigation-replay serve path (`onExplicitHit` set) the marker COMPOSES with
|
|
217
292
|
* such a scope instead of being ignored: the seeded doc record supplies the match
|
|
218
293
|
* only when the explicit tier misses (withCacheLookup). cache(false) and a
|
|
@@ -229,12 +304,17 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
229
304
|
* segment record even when the triggering request is partial.
|
|
230
305
|
*/
|
|
231
306
|
keyPrefix?: "doc";
|
|
232
|
-
/**
|
|
233
|
-
|
|
307
|
+
/**
|
|
308
|
+
* @internal Called only after the implicit cache hit decodes
|
|
309
|
+
* successfully; the lookup awaits it before returning the hit, so work
|
|
310
|
+
* it starts lands before the route's loaders resolve.
|
|
311
|
+
*/
|
|
312
|
+
onHit?: () => void | Promise<void>;
|
|
234
313
|
/**
|
|
235
314
|
* @internal Called when the seeded document record fails server-side
|
|
236
315
|
* deserialization. Partial replay uses it to replace the enclosing shell
|
|
237
|
-
* snapshot after serving the fresh fallback.
|
|
316
|
+
* snapshot after serving the fresh fallback. A document HIT tail does not
|
|
317
|
+
* set it: withCacheLookup throws ShellRecordUnavailableError instead.
|
|
238
318
|
*/
|
|
239
319
|
onCorrupt?: () => void;
|
|
240
320
|
/**
|
|
@@ -245,6 +325,21 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
245
325
|
* require the exact consumable record instead of "any segment record".
|
|
246
326
|
*/
|
|
247
327
|
docKey?: string;
|
|
328
|
+
/**
|
|
329
|
+
* @internal Set on a document HIT tail (serveShellHit). The implicit doc
|
|
330
|
+
* scope replaces any route-derived cache() scope for the tail's match
|
|
331
|
+
* (resolveShellImplicitCacheScope), and a lookup that does not hit throws
|
|
332
|
+
* ShellRecordUnavailableError instead of resolving segments
|
|
333
|
+
* (withCacheLookup): a HIT never runs a handler.
|
|
334
|
+
*/
|
|
335
|
+
docTail?: true;
|
|
336
|
+
/**
|
|
337
|
+
* @internal The key a document HIT tail looks the doc record up by: the
|
|
338
|
+
* entry's own `docKey`. Recomputing it could name another key (a store
|
|
339
|
+
* keyGenerator folding request data, a build-time capture's synthetic
|
|
340
|
+
* host) and miss the record.
|
|
341
|
+
*/
|
|
342
|
+
fixedDocKey?: string;
|
|
248
343
|
/**
|
|
249
344
|
* @internal Set ONLY by matchPartialWithPprReplay on the navigation-replay
|
|
250
345
|
* serve path. Its presence arms the explicit-scope composition in
|
|
@@ -286,30 +381,19 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
286
381
|
*/
|
|
287
382
|
_shellFragmentPayload?: boolean;
|
|
288
383
|
/**
|
|
289
|
-
* @internal
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
* pushes (liveness unknowable at the barrier); (c) a handler-invoked loader
|
|
294
|
-
* executing during the capture (loader-resolution.ts — its consumption-lane
|
|
295
|
-
* value would freeze on a handler-free HIT). captureAndStoreShell folds it
|
|
296
|
-
* into ShellCacheEntry.handlerLiveHoles at the putShell barrier. Own
|
|
297
|
-
* property of the capture's derived context only.
|
|
298
|
-
*/
|
|
299
|
-
_shellCaptureHandleLiveness?: {
|
|
300
|
-
holes: boolean;
|
|
301
|
-
pendingPushes: number;
|
|
302
|
-
handlerInvokedLoader: boolean;
|
|
303
|
-
};
|
|
304
|
-
/**
|
|
305
|
-
* @internal Set (to the offending fn name) by the cookies()/headers()
|
|
306
|
-
* capture guard when it throws DURING a capture render. Load-bearing for the
|
|
384
|
+
* @internal Set by the capture guard (server/context.ts
|
|
385
|
+
* tripShellCaptureGuard) when it throws DURING a capture render: the read
|
|
386
|
+
* ("cookies()", "headers()", "ctx.get()", "ctx.theme") and the fix the
|
|
387
|
+
* refusal warning gives for it. Load-bearing for the
|
|
307
388
|
* bake lane: a guard throw inside an executing loader is swallowed by
|
|
308
389
|
* wrapLoaderPromise into per-loader error UI, which would otherwise bake
|
|
309
390
|
* silently into the shared shell — the capture checks this flag after the
|
|
310
391
|
* render and refuses instead. Deterministic, so the capture does not retry.
|
|
311
392
|
*/
|
|
312
|
-
_shellCaptureGuardTripped?:
|
|
393
|
+
_shellCaptureGuardTripped?: {
|
|
394
|
+
surface: string;
|
|
395
|
+
fix: string;
|
|
396
|
+
};
|
|
313
397
|
/**
|
|
314
398
|
* @internal The loader $$id whose BODY was executing when the capture guard
|
|
315
399
|
* tripped (read off the loader-body ALS scope at trip time), or undefined
|
|
@@ -336,6 +420,21 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
336
420
|
* content used and can therefore be invalidated by updateTag()/revalidateTag().
|
|
337
421
|
*/
|
|
338
422
|
_requestTags: Set<string>;
|
|
423
|
+
/**
|
|
424
|
+
* @internal This request can write a route cache() record: its match
|
|
425
|
+
* resolved a cache scope (match-api.ts). Arms the record tag owners
|
|
426
|
+
* (cache-tag.ts armRecordTagOwners); off, the segment scopes, loader links
|
|
427
|
+
* and owned tag sets are skipped entirely. A capture's or background
|
|
428
|
+
* re-render's derived context (Object.create) inherits it.
|
|
429
|
+
*/
|
|
430
|
+
_recordTagOwners?: true;
|
|
431
|
+
/**
|
|
432
|
+
* @internal This request resolves a loader bound with its own cache()
|
|
433
|
+
* (match-api.ts, or the binding's funnel). Arms the per-execution loader
|
|
434
|
+
* tag sets (cache-tag.ts armLoaderTagSets); off, a loader execution
|
|
435
|
+
* allocates no set and a read links nothing. Derived contexts inherit it.
|
|
436
|
+
*/
|
|
437
|
+
_recordLoaderTags?: true;
|
|
339
438
|
/** @internal Cache profiles for "use cache" profile resolution (per-router) */
|
|
340
439
|
_cacheProfiles?: Record<string, import("../cache/profile-registry.js").CacheProfile>;
|
|
341
440
|
/**
|
|
@@ -376,6 +475,11 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
376
475
|
* Returns the theme value from the cookie, or the default theme if not set.
|
|
377
476
|
* This is the user's preference ("light", "dark", or "system"), not the resolved value.
|
|
378
477
|
*
|
|
478
|
+
* The visitor's cookie, so it is guarded like `cookies()`: reading it
|
|
479
|
+
* refuses a ppr shell capture and throws inside a `cache()` boundary or a
|
|
480
|
+
* `"use cache"` function (#971). A non-enumerable getter: a spread or
|
|
481
|
+
* `Object.assign` copy of the context does not carry it.
|
|
482
|
+
*
|
|
379
483
|
* @example
|
|
380
484
|
* ```typescript
|
|
381
485
|
* route("settings", (ctx) => {
|
|
@@ -384,7 +488,7 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
384
488
|
* });
|
|
385
489
|
* ```
|
|
386
490
|
*/
|
|
387
|
-
theme?: Theme;
|
|
491
|
+
readonly theme?: Theme;
|
|
388
492
|
/**
|
|
389
493
|
* Set the theme (only available when theme is enabled in router config)
|
|
390
494
|
*
|
|
@@ -405,6 +509,14 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
405
509
|
setTheme?: (theme: Theme) => void;
|
|
406
510
|
/** @internal Theme configuration (null if theme not enabled) */
|
|
407
511
|
_themeConfig?: ResolvedThemeConfig | null;
|
|
512
|
+
/**
|
|
513
|
+
* @internal The theme cookie (or the default) read with no identity guard.
|
|
514
|
+
* The guarded reads (`theme` here, the handler and middleware `ctx.theme`)
|
|
515
|
+
* call it after assertThemeReadAllowed; the router's own payload read goes
|
|
516
|
+
* through payloadInitialTheme (rsc/full-payload.ts). Undefined without
|
|
517
|
+
* theme config.
|
|
518
|
+
*/
|
|
519
|
+
_readTheme(): Theme | undefined;
|
|
408
520
|
/**
|
|
409
521
|
* Attach location state entries to the current response.
|
|
410
522
|
*
|
|
@@ -595,13 +707,27 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
595
707
|
*/
|
|
596
708
|
_cacheSignal?: import("../router/telemetry.js").CacheSegmentSignal[];
|
|
597
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* @internal The freshness window of a route cache() record, as ms
|
|
712
|
+
* timestamps: fresh until `freshUntil` (its `expiresAt`), stale until
|
|
713
|
+
* `staleUntil` (plus its scope's swr). `ttl`, `swr` and `written` describe
|
|
714
|
+
* the record that sets `staleUntil`: its cache() config, and whether this
|
|
715
|
+
* request wrote it (cacheRoute) rather than read it.
|
|
716
|
+
*/
|
|
717
|
+
export interface RouteRecordWindow {
|
|
718
|
+
freshUntil: number;
|
|
719
|
+
staleUntil: number;
|
|
720
|
+
ttl: number;
|
|
721
|
+
swr: number;
|
|
722
|
+
written: boolean;
|
|
723
|
+
}
|
|
598
724
|
/**
|
|
599
725
|
* Public view of RequestContext, without internal methods and fields.
|
|
600
726
|
*
|
|
601
727
|
* This is the type exported to library consumers. Internal code should
|
|
602
728
|
* use the full RequestContext interface directly.
|
|
603
729
|
*/
|
|
604
|
-
export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhen" | "_pprTransitionDecisions" | "_pprReplayPostMatchReason" | "_cacheStore" | "_searchParamsFilter" | "_shellCaptureRun" | "_shellImplicitCache" | "_shellLoaderSeed" | "_shellCaptureLoaderRecords" | "
|
|
730
|
+
export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhen" | "_pprTransitionDecisions" | "_pprReplayPostMatchReason" | "_cacheStore" | "_searchParamsFilter" | "_shellCaptureRun" | "_shellImplicitCache" | "_shellLoaderSeed" | "_shellCaptureLoaderRecords" | "_shellCaptureUnrecordedLoaderPush" | "_resolvedCacheKeys" | "_shellKey" | "_shellForcedMiss" | "_routeRecordWindow" | "_shellFragmentPayload" | "_shellCaptureGuardTripped" | "_shellCaptureGuardTrippedLoaderId" | "_explicitTaggedStores" | "_requestTags" | "_recordTagOwners" | "_recordLoaderTags" | "_cacheProfiles" | "_onResponseCallbacks" | "_pendingBackgroundTasks" | "_themeConfig" | "_readTheme" | "_locationState" | "_routeName" | "_prevRouteKey" | "_gateCurrentUrl" | "_gateCurrentParams" | "_gateActionId" | "_gateActionUrl" | "_gateActionResult" | "_gateFormData" | "_inActionRevalidation" | "_reportedErrors" | "_renderErrors" | "_renderBarrier" | "_resolveRenderBarrier" | "_renderBarrierSegmentOrder" | "_treeHasStreaming" | "_renderBarrierWaiters" | "_handlerLoaderDeps" | "_awaitBeforeFlushLoaderIds" | "_renderBarrierHandleSnapshot" | "_renderBarrierGuardClosed" | "_reportBackgroundError" | "_debugPerformance" | "_metricsStore" | "_renderForeground" | "_activeRoutine" | "_renderDiagnosticsEnabled" | "_handlerStart" | "_tracing" | "_basename" | "_routerId" | "_setStatus" | "_rotateStateCookie" | "_setKeepCacheDirective" | "_freshReads" | "_setFreshReadsCookie" | "_requestRoot" | "_tagInvalidations" | "_responseSent" | "_variables" | "_classifiedRoute" | "_requestMode" | "_cacheSignal" | "_dynamic" | "res">;
|
|
605
731
|
/**
|
|
606
732
|
* Marker for a waitUntil-scheduled fn whose task promise must NOT enter
|
|
607
733
|
* _pendingBackgroundTasks. Used by the PPR shell capture for its own task:
|
|
@@ -238,6 +238,15 @@ interface ShellCaptureOptions {
|
|
|
238
238
|
* refuses to store the shell (issue #915).
|
|
239
239
|
*/
|
|
240
240
|
onError?: (error: unknown) => void;
|
|
241
|
+
/**
|
|
242
|
+
* Called with React's errorInfo for each task still pending when the
|
|
243
|
+
* capture's own abort froze the prelude; its componentStack is computed
|
|
244
|
+
* when read. The dev no-shell warning prints the stacks: one of them is what
|
|
245
|
+
* suspended above <body> (issue #941).
|
|
246
|
+
*/
|
|
247
|
+
onAbortedTask?: (errorInfo: {
|
|
248
|
+
componentStack?: string;
|
|
249
|
+
} | undefined) => void;
|
|
241
250
|
}
|
|
242
251
|
/**
|
|
243
252
|
* Result of a successful shell capture. `prelude` is the raw prelude bytes;
|
|
@@ -54,14 +54,15 @@ export interface SsrRootOptions {
|
|
|
54
54
|
nonce?: string;
|
|
55
55
|
/**
|
|
56
56
|
* Fires once when the Flight payload root settles (resolve OR reject) — the
|
|
57
|
-
* signal that
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* a
|
|
64
|
-
*
|
|
57
|
+
* signal that fizz can start emitting the tree. A client component used as
|
|
58
|
+
* an element type resolves lazily, so its module load can still be in
|
|
59
|
+
* flight (captureShellHTML waits for those separately, issue #949). The
|
|
60
|
+
* capture pass gates its abort on this: a fully REPLAYED (prerendered)
|
|
61
|
+
* route's Flight stream goes byte-quiet in ~1-3ms, but fizz cannot render
|
|
62
|
+
* even <html> until the module loads finish (real module-runner I/O in dev;
|
|
63
|
+
* 100ms+ on a cold graph), so an abort gated on Flight quiet alone fires
|
|
64
|
+
* first and freezes a zero-byte prelude. Masked-loader holes do NOT block
|
|
65
|
+
* this signal — they postpone below the root.
|
|
65
66
|
*/
|
|
66
67
|
onPayloadSettled?: () => void;
|
|
67
68
|
/**
|
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
* element tree, so you can assert typed prop fidelity across the client boundary
|
|
19
19
|
* (a `Date` comes back a `Date`) and detect inlined-vs-island. Serialize +
|
|
20
20
|
* deserialize only — no hydration/interaction (that is the e2e tier).
|
|
21
|
+
*
|
|
22
|
+
* `serveShellRequest` serves one request through the router's production
|
|
23
|
+
* handler, so a `ppr` route's MISS runs the real shell capture and a later
|
|
24
|
+
* request is a real HIT; only the HTML step is stubbed. `resetShellTestState`
|
|
25
|
+
* clears the per-isolate PPR state between tests.
|
|
21
26
|
*/
|
|
22
27
|
export { renderToFlightString, normalizeFlight, assertFlightRuntimeAvailable, } from "./flight.js";
|
|
23
28
|
export type { RenderToFlightStringOptions } from "./flight.js";
|
|
@@ -25,3 +30,5 @@ export { renderServerTree, findClientBoundaries, findElements, textContent, asse
|
|
|
25
30
|
export type { RenderServerTreeOptions, RenderServerTreeResult, ClientBoundary, BoundarySelector, FoundElement, ElementSelector, } from "./flight-tree.js";
|
|
26
31
|
export { renderHandler } from "./render-handler.js";
|
|
27
32
|
export type { TestableHandler, RenderHandlerOptions, RenderHandlerResult, StateCookieSeed, } from "./render-handler.js";
|
|
33
|
+
export { serveShellRequest, resetShellTestState, } from "./serve-shell-request.js";
|
|
34
|
+
export type { ServeShellRequestOptions, ServeShellRequestResult, } from "./serve-shell-request.js";
|
|
@@ -38,6 +38,7 @@ import { type Handle } from "../handle.js";
|
|
|
38
38
|
import type { ThemeConfig } from "../theme/types.js";
|
|
39
39
|
import type { SegmentCacheStore } from "../cache/types.js";
|
|
40
40
|
import type { CacheProfile } from "../cache/profile-registry.js";
|
|
41
|
+
import type { PartialCacheOptions } from "../types/cache-types.js";
|
|
41
42
|
import { type VarsInit, type StateCookieSeed } from "./internal/context.js";
|
|
42
43
|
/**
|
|
43
44
|
* The loader context surfaced to a `runLoader` body. It mirrors the runtime
|
|
@@ -81,9 +82,11 @@ export interface RunLoaderOptions<TEnv = any> {
|
|
|
81
82
|
basename?: string;
|
|
82
83
|
/**
|
|
83
84
|
* Theme config in the same shape `createRouter({ theme })` takes (e.g. `true`
|
|
84
|
-
* or `{ themes: [...] }`). Seeds the request's theme config so
|
|
85
|
-
*
|
|
86
|
-
*
|
|
85
|
+
* or `{ themes: [...] }`). Seeds the request's theme config, so the loader
|
|
86
|
+
* body reads the visitor's theme (the Cookie on `request`, or the default)
|
|
87
|
+
* with `getRequestContext().theme`. That read is guarded like `cookies()`: it
|
|
88
|
+
* throws inside a `"use cache"` function (#971). The loader ctx has no
|
|
89
|
+
* `theme`/`setTheme` (those are handler/middleware-only).
|
|
87
90
|
*/
|
|
88
91
|
theme?: ThemeConfig | true;
|
|
89
92
|
/** Environment bindings surfaced as `ctx.env`. */
|
|
@@ -121,6 +124,18 @@ export interface RunLoaderOptions<TEnv = any> {
|
|
|
121
124
|
cacheStore?: SegmentCacheStore;
|
|
122
125
|
/** Cache profiles (the `createRouter({ cacheProfiles })` shape). */
|
|
123
126
|
cacheProfiles?: Record<string, CacheProfile>;
|
|
127
|
+
/**
|
|
128
|
+
* Run the loader through the `cache()` a route binds it with — the options
|
|
129
|
+
* `loader(Loader, () => [cache({...})])` takes — using the production
|
|
130
|
+
* read-through against `cache.store`, else `cacheStore`. A HIT returns the
|
|
131
|
+
* stored value without running the body and records the tags the body
|
|
132
|
+
* recorded when the entry was written, so `updateTag()` of any of them drops
|
|
133
|
+
* the entry. The write is a background task the call does not await (spy
|
|
134
|
+
* on the store's `setItem` to wait for it). A raw loader body is keyed by
|
|
135
|
+
* its function reference. `handlePushes` lists the body's pushes, so a HIT
|
|
136
|
+
* reports none: the replay into the page's handle store is not modeled.
|
|
137
|
+
*/
|
|
138
|
+
cache?: PartialCacheOptions;
|
|
124
139
|
/**
|
|
125
140
|
* Customize the rango state cookie a loader that calls
|
|
126
141
|
* `invalidateClientCache()` rotates (the name is always seeded — default
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* serveShellRequest — one request through the production PPR serve path.
|
|
3
|
+
*
|
|
4
|
+
* Builds the router's request handler as `router.fetch` does
|
|
5
|
+
* (`createRSCHandler` with the router's `nonce`, `version` and `cache`
|
|
6
|
+
* config) and serves one GET through it, so middleware, the shell serve path
|
|
7
|
+
* and a MISS's background capture all run production code, on real Flight
|
|
8
|
+
* (the vendored react-server-dom serializer). The call settles every
|
|
9
|
+
* background task the request scheduled before it returns, so a MISS's
|
|
10
|
+
* `putShell` has landed.
|
|
11
|
+
*
|
|
12
|
+
* The one stub is the SSR module (the HTML step): `react-dom/server` refuses
|
|
13
|
+
* to load under the react-server condition this project runs with.
|
|
14
|
+
* - `renderHTML` passes the Flight stream through: a document body is its
|
|
15
|
+
* Flight payload.
|
|
16
|
+
* - `captureShellHTML` returns, as the prelude, the Flight text the capture
|
|
17
|
+
* rendered before `quiesce` (plus the post-quiesce task hops the real
|
|
18
|
+
* capture takes). `postponed` is always null. A capture that does not
|
|
19
|
+
* quiesce within what `ppr.captureTimeout` leaves after the handler output
|
|
20
|
+
* settled returns no shell; the real one goes on to its abort and returns
|
|
21
|
+
* whatever prelude rendered by then.
|
|
22
|
+
* - `resumeShellHTML` passes the tail's Flight stream through: a HIT body is
|
|
23
|
+
* the stored prelude followed by the tail's Flight payload.
|
|
24
|
+
* So there is no HTML: the prelude's `<body` sanity gate, SSR render errors
|
|
25
|
+
* (production refuses the capture on one), fizz resume of the holes,
|
|
26
|
+
* bootstrap scripts and nonce injection stay e2e-only. A route whose real
|
|
27
|
+
* capture refuses for a root postpone (a live loader read with no boundary
|
|
28
|
+
* above it) or an SSR render error is captured here.
|
|
29
|
+
*
|
|
30
|
+
* Must run under the `react-server` condition (the rsc Vitest project), with
|
|
31
|
+
* `rangoTestAliases()` resolving `@vitejs/plugin-rsc/rsc/server` to the stub
|
|
32
|
+
* that carries the Flight runtime.
|
|
33
|
+
*/
|
|
34
|
+
import { type Rango } from "../router/router-interfaces.js";
|
|
35
|
+
import type { SegmentCacheStore, ShellCacheEntry } from "../cache/types.js";
|
|
36
|
+
import { type PprReplayStatus, type ShellStatus } from "./shell-status.js";
|
|
37
|
+
/** Options for {@link serveShellRequest}. */
|
|
38
|
+
export interface ServeShellRequestOptions<TEnv = any> {
|
|
39
|
+
/**
|
|
40
|
+
* The shell and segment store for this request, in place of the router's
|
|
41
|
+
* `createRouter({ cache })` store. The rest of that config (`enabled`,
|
|
42
|
+
* `searchParams`) still applies. Omit it to serve through the router's own
|
|
43
|
+
* store.
|
|
44
|
+
*/
|
|
45
|
+
cacheStore?: SegmentCacheStore;
|
|
46
|
+
/** Environment bindings, as `router.fetch(request, { env })` passes them. */
|
|
47
|
+
env?: TEnv;
|
|
48
|
+
/**
|
|
49
|
+
* Request headers. A document request defaults `accept` to `text/html`.
|
|
50
|
+
*/
|
|
51
|
+
headers?: HeadersInit;
|
|
52
|
+
/**
|
|
53
|
+
* Serve the client navigation the browser sends (`?_rsc_partial`) instead
|
|
54
|
+
* of a document GET: from `from` (default `/`) with `segments` mounted
|
|
55
|
+
* (default none), advertising fragment passthrough. The replay decision is
|
|
56
|
+
* `result.replayStatus`.
|
|
57
|
+
*/
|
|
58
|
+
partial?: true | {
|
|
59
|
+
from?: string;
|
|
60
|
+
segments?: readonly string[];
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Result of {@link serveShellRequest}. */
|
|
64
|
+
export interface ServeShellRequestResult {
|
|
65
|
+
/** The handler's Response. Its body is already read: see `body`. */
|
|
66
|
+
response: Response;
|
|
67
|
+
/** The response body text. */
|
|
68
|
+
body: string;
|
|
69
|
+
/** `x-rango-shell` (`HIT` | `MISS`), or null when the header is absent. */
|
|
70
|
+
shellStatus: ShellStatus | null;
|
|
71
|
+
/** `x-rango-ppr-replay` on a partial request, or null. */
|
|
72
|
+
replayStatus: PprReplayStatus | null;
|
|
73
|
+
/**
|
|
74
|
+
* The prelude a HIT served: the capture's Flight text at quiesce (the
|
|
75
|
+
* shell as captured). Undefined unless `shellStatus` is `HIT`.
|
|
76
|
+
*/
|
|
77
|
+
prelude: string | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* The Flight payload this request rendered: a HIT's tail, a document's
|
|
80
|
+
* render, or a Flight response. Undefined when no Flight was rendered (a
|
|
81
|
+
* redirect or a middleware response).
|
|
82
|
+
*/
|
|
83
|
+
flight: string | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* The shell key production resolved for this request: for a document the
|
|
86
|
+
* serve path read and captured (a MISS or a HIT), the key it used, request
|
|
87
|
+
* partition included (a route partitioned by `cache({ key })` or the
|
|
88
|
+
* store's `keyGenerator`). Otherwise (a route without `ppr`, a request
|
|
89
|
+
* passed to axis 1 before its key was resolved, a partial request) the
|
|
90
|
+
* document URL's key without a partition, as `shellCacheKey` builds it.
|
|
91
|
+
*/
|
|
92
|
+
key: string;
|
|
93
|
+
/**
|
|
94
|
+
* Read the document shell entry under `key` from the request's store
|
|
95
|
+
* (a passive `getShell`), or null. A read, so on a store with a shell memo
|
|
96
|
+
* (CFCacheStore, VercelCacheStore) it warms the memo like any other read.
|
|
97
|
+
*/
|
|
98
|
+
readEntry(): Promise<ShellCacheEntry | null>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Reset the per-isolate PPR state requests leave behind, so the next test
|
|
102
|
+
* starts as a fresh worker would: the capture's stampede guard, its backoff
|
|
103
|
+
* (a refused capture backs its URL off for later tests too), the capture's
|
|
104
|
+
* and the serve path's once-per-key warnings, the build-shell manifest memo,
|
|
105
|
+
* and CFCacheStore's isolate memos (shells, tag markers, tag hints), which
|
|
106
|
+
* every CFCacheStore in the process shares by namespace and URL. Call it in
|
|
107
|
+
* `beforeEach`, never while a request is in flight. VercelCacheStore's memos
|
|
108
|
+
* live on the `cache` handle it is given: a new handle starts empty.
|
|
109
|
+
*/
|
|
110
|
+
export declare function resetShellTestState(): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Serve one GET for `url` through the router's production request handler
|
|
113
|
+
* and settle the background tasks it scheduled (a MISS's capture and
|
|
114
|
+
* `putShell` included). The first request for a `ppr` route is a MISS; the
|
|
115
|
+
* next one with the same store is a HIT from the stored shell. The HTML step
|
|
116
|
+
* is stubbed (see the module header), so `prelude` and `flight` are Flight
|
|
117
|
+
* text. The request starts in a later millisecond than the call: a store
|
|
118
|
+
* refuses a capture that starts in the millisecond of a tag invalidation, so
|
|
119
|
+
* an `updateTag()` made just before the call never refuses its recapture.
|
|
120
|
+
* Rejects when the handler throws or the response stream errors (a failing
|
|
121
|
+
* HIT tail), after settling the tasks scheduled so far.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* const store = new MemorySegmentCacheStore();
|
|
125
|
+
* const miss = await serveShellRequest(router, "/product/1", { cacheStore: store });
|
|
126
|
+
* expect(miss.shellStatus).toBe("MISS");
|
|
127
|
+
* const hit = await serveShellRequest(router, "/product/1", { cacheStore: store });
|
|
128
|
+
* expect(hit.shellStatus).toBe("HIT");
|
|
129
|
+
* expect(hit.prelude).toContain("Widget"); // captured shell
|
|
130
|
+
* expect(hit.flight).toContain("in stock"); // live hole, rendered now
|
|
131
|
+
*/
|
|
132
|
+
export declare function serveShellRequest<TEnv = any>(router: Rango<TEnv, any>, url: string | URL, options?: ServeShellRequestOptions<TEnv>): Promise<ServeShellRequestResult>;
|
|
@@ -18,11 +18,15 @@
|
|
|
18
18
|
* Secondary unit signal: `store.getShell(shellCacheKey(url))` after a real
|
|
19
19
|
* capture flush (background `putShell`). There is no Flight flag for shell HIT.
|
|
20
20
|
*
|
|
21
|
-
* 3. **
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* 3. **A real MISS → capture → HIT in unit tests:** `serveShellRequest`
|
|
22
|
+
* (`@rangojs/router/testing/flight`, serve-shell-request.ts) serves one
|
|
23
|
+
* request through the router's production handler and settles its
|
|
24
|
+
* background capture; a later request with the same store is a real HIT or
|
|
25
|
+
* partial replay. Its only stub is the HTML step (the SSR module).
|
|
26
|
+
* **Stays e2e-only:** real HTML (the prelude's `<body` gate, fizz resume of
|
|
27
|
+
* holes), browser resume of holes, live Cloudflare/Vercel runtimes (store
|
|
28
|
+
* test doubles work in unit), and build-time producer B bake. The unit layer
|
|
29
|
+
* must never stub a shell HIT response.
|
|
26
30
|
*
|
|
27
31
|
* Import from `@rangojs/router/testing` (Vitest) or `@rangojs/router/testing/e2e`
|
|
28
32
|
* (Playwright — same pure helpers, no Vite virtuals).
|
|
@@ -34,7 +38,7 @@ export declare const SHELL_STATUS_HEADER: string;
|
|
|
34
38
|
export declare const PPR_REPLAY_STATUS_HEADER: string;
|
|
35
39
|
/** Values the serve path writes on `x-rango-shell`. */
|
|
36
40
|
export type ShellStatus = "HIT" | "MISS";
|
|
37
|
-
declare const PPR_REPLAY_BYPASS_REASONS: readonly ["method", "dynamic", "nonce", "store-unavailable", "passive-read-unsupported", "no-navigation-context", "prerender-store", "intercept", "cache-disabled", "read-error", "no-entry", "invalid-version", "corrupt-entry", "
|
|
41
|
+
declare const PPR_REPLAY_BYPASS_REASONS: readonly ["method", "dynamic", "nonce", "store-unavailable", "passive-read-unsupported", "no-navigation-context", "prerender-store", "intercept", "cache-disabled", "read-error", "no-entry", "invalid-version", "corrupt-entry", "no-segment-snapshot", "snapshot-miss", "explicit-cache-hit", "stale-build-entry"];
|
|
38
42
|
/** Bounded reasons a PPR partial request can fall open to ordinary matching. */
|
|
39
43
|
export type PprReplayBypassReason = (typeof PPR_REPLAY_BYPASS_REASONS)[number];
|
|
40
44
|
/** Parsed `x-rango-ppr-replay` value. */
|
|
@@ -52,15 +56,24 @@ export type ShellStatusTarget = Response | {
|
|
|
52
56
|
/**
|
|
53
57
|
* Shell store key for a document URL — same formula as production
|
|
54
58
|
* `buildShellKey` in `rsc/shell-serve.ts` (host + pathname + sorted search +
|
|
55
|
-
* `:shell`)
|
|
56
|
-
*
|
|
59
|
+
* `:shell`), then the production `partitionShellKey` itself (the leaf
|
|
60
|
+
* `rsc/shell-capture-constants.ts`). The base is kept here so the testing
|
|
61
|
+
* barrel never imports the shell-serve module (it pulls React). A parity test
|
|
62
|
+
* pins the two implementations.
|
|
57
63
|
*
|
|
58
64
|
* Accepts a `URL` or an absolute/relative request URL string (relative strings
|
|
59
65
|
* resolve against `http://localhost`). Pass the router's `cache.searchParams`
|
|
60
66
|
* config as `searchParams` when the app under test sets one — the production
|
|
61
|
-
* key applies it, so the expected key must too.
|
|
67
|
+
* key applies it, so the expected key must too. When the route's record is
|
|
68
|
+
* partitioned by the request — its `cache({ key })`, or the store's
|
|
69
|
+
* `keyGenerator` — its shell is too: pass the key that function returns for
|
|
70
|
+
* the request as `partition` (appended URI-encoded; a result equal to the
|
|
71
|
+
* default key partitions nothing, so pass none). Under nested keyed
|
|
72
|
+
* `cache()` scopes, pass their `key()` results as an array, outermost first:
|
|
73
|
+
* they compose as the production record key composes them
|
|
74
|
+
* (`composeCacheKeys`).
|
|
62
75
|
*/
|
|
63
|
-
export declare function shellCacheKey(url: URL | string, searchParams?: CacheSearchParams): string;
|
|
76
|
+
export declare function shellCacheKey(url: URL | string, searchParams?: CacheSearchParams, partition?: string | readonly string[]): string;
|
|
64
77
|
/**
|
|
65
78
|
* Read `x-rango-shell` from a response. Returns `null` when the header is
|
|
66
79
|
* absent (axis-1 / non-ppr / ineligible request).
|
|
@@ -235,6 +235,12 @@ export type HandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch extends Sear
|
|
|
235
235
|
* Current theme (from cookie or default).
|
|
236
236
|
* Only available when theme is enabled in router config.
|
|
237
237
|
*
|
|
238
|
+
* The visitor's cookie, so it is guarded like `cookies()`: reading it on a
|
|
239
|
+
* `ppr` route refuses the shell capture, and inside a `cache()` boundary or
|
|
240
|
+
* a `"use cache"` function it throws (#971). Read it with `useTheme()` in a
|
|
241
|
+
* client component, or in a live loader, on those routes. A read-only,
|
|
242
|
+
* non-enumerable getter: `{ ...ctx }` does not carry it.
|
|
243
|
+
*
|
|
238
244
|
* @example
|
|
239
245
|
* ```typescript
|
|
240
246
|
* route("settings", (ctx) => {
|
|
@@ -243,7 +249,7 @@ export type HandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch extends Sear
|
|
|
243
249
|
* });
|
|
244
250
|
* ```
|
|
245
251
|
*/
|
|
246
|
-
theme?: Theme;
|
|
252
|
+
readonly theme?: Theme;
|
|
247
253
|
/**
|
|
248
254
|
* Set the theme (only available when theme is enabled in router config).
|
|
249
255
|
* Sets a cookie with the new theme value.
|
|
@@ -349,6 +355,13 @@ export type InternalHandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch exte
|
|
|
349
355
|
* from the page. See setupLoaderAccess.
|
|
350
356
|
*/
|
|
351
357
|
_runLoaderIsolated?: (loader: any) => Promise<any>;
|
|
358
|
+
/**
|
|
359
|
+
* @internal A loader's cache() binding started with these cache() tags: an
|
|
360
|
+
* execution of that loader outside the binding (a reader started it first,
|
|
361
|
+
* or a stale refresh runs it) answers for them too (#964). See
|
|
362
|
+
* setupLoaderAccess.
|
|
363
|
+
*/
|
|
364
|
+
_bindLoaderCacheTags?: (loaderId: string, tags: Set<string>) => void;
|
|
352
365
|
};
|
|
353
366
|
/**
|
|
354
367
|
* Generic params type - flexible object with string keys
|