@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
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { type EntryData } from "../server/context.js";
|
|
19
19
|
import type { SearchParamsFilter } from "../cache/search-params-filter.js";
|
|
20
|
-
import type { DocumentShellCacheEntry, ShellCacheEntry, SegmentCacheStore } from "../cache/types.js";
|
|
20
|
+
import type { DocumentShellCacheEntry, ShellCacheEntry, ShellDocumentRead, ShellReadStats, ShellSnapshotFailure, ShellSnapshotRecord, SegmentCacheStore } from "../cache/types.js";
|
|
21
21
|
/** Debug/status header the browser (and e2e assertions) can read: HIT | MISS. */
|
|
22
22
|
export declare const SHELL_STATUS_HEADER = "x-rango-shell";
|
|
23
23
|
/** Partial-navigation status header set only after captured PPR segments are consumed. */
|
|
@@ -94,6 +94,41 @@ export declare function resolvePprConfig(entry: EntryData | undefined | null): R
|
|
|
94
94
|
* and lookup makes every shell request a permanent miss.
|
|
95
95
|
*/
|
|
96
96
|
export declare function buildShellKey(url: URL, filter?: SearchParamsFilter): string;
|
|
97
|
+
export { navigationShellKey, partitionShellKey, } from "./shell-capture-constants.js";
|
|
98
|
+
/**
|
|
99
|
+
* The forced-MISS query marker. A document HIT that cannot finish its tail
|
|
100
|
+
* reloads the page with it (shellReloadScript); the serve gate renders a
|
|
101
|
+
* request carrying it on axis 1, with no shell read and no capture, so the
|
|
102
|
+
* reload can never be a HIT that degrades again. The handler drops it from
|
|
103
|
+
* the request on entry (withoutShellMissMarker) and keeps only the flag
|
|
104
|
+
* (RequestContext._shellForcedMiss), so nothing else sees it.
|
|
105
|
+
*/
|
|
106
|
+
export declare const SHELL_MISS_PARAM: string;
|
|
107
|
+
/**
|
|
108
|
+
* The request without the forced-MISS marker, or undefined when it carries
|
|
109
|
+
* none. The handler calls it before anything reads the request: left in, the
|
|
110
|
+
* marker reached `ctx.request.url`, `originalUrl`, middleware, and the SSR
|
|
111
|
+
* search seed (so `useSearchParams` rendered it), and a `"use cache"` key
|
|
112
|
+
* built from the request URL was never read again. GET and HEAD only: the
|
|
113
|
+
* reload is a navigation, and a request with a body cannot be re-created
|
|
114
|
+
* without re-streaming it. The substring test keeps the URL parse off every
|
|
115
|
+
* other request.
|
|
116
|
+
*/
|
|
117
|
+
export declare function withoutShellMissMarker(request: Request): Request | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* The inline script a degraded document HIT ends with: reload once into a
|
|
120
|
+
* forced MISS. The marker in the URL is the loop bound — a request carrying
|
|
121
|
+
* it is never a HIT, so this script never runs on the reload, and if it ever
|
|
122
|
+
* did (a server that ignores the marker) it would return without touching
|
|
123
|
+
* the page. window.stop() runs only when it reloads: it keeps the half-sent
|
|
124
|
+
* Flight stream from being closed by DOMContentLoaded, which would throw
|
|
125
|
+
* React's "Connection closed" (#412) before the reload lands. `nonce` is the
|
|
126
|
+
* request's CSP nonce; a ppr route with an active nonce is axis 1
|
|
127
|
+
* (rsc-rendering.ts shellServePlan), so on a HIT it is normally absent.
|
|
128
|
+
*/
|
|
129
|
+
export declare function shellReloadScript(nonce?: string): string;
|
|
130
|
+
/** An inline script a shell HIT's tail appends, carrying the CSP nonce if any. */
|
|
131
|
+
export declare function inlineShellScript(body: string, nonce?: string): string;
|
|
97
132
|
/**
|
|
98
133
|
* The shell key's search portion (`?`-prefixed sorted search with the
|
|
99
134
|
* cache.searchParams filter applied, or "") — ALSO the string the capture and
|
|
@@ -117,16 +152,11 @@ export declare function shellSearchSeed(url: URL, filter?: SearchParamsFilter):
|
|
|
117
152
|
*/
|
|
118
153
|
export declare function isValidShellHit(entry: ShellCacheEntry, buildVersion: string): boolean;
|
|
119
154
|
/**
|
|
120
|
-
* DOCUMENT-half
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* page that never hydrates, re-served on every request until the entry ages
|
|
126
|
-
* out (no eviction path exists; failure schedules no recapture by itself).
|
|
127
|
-
* Checking here turns a corrupt entry (store-layer fault) into a plain MISS
|
|
128
|
-
* the recapture overwrites. Cost: one duplicate decode/parse per HIT, sub-ms
|
|
129
|
-
* against a prelude flush that dominates the path.
|
|
155
|
+
* DOCUMENT-half structural gate and type narrowing, without decoding the
|
|
156
|
+
* prelude: the prelude is a string and the postponed blob parses. Partial
|
|
157
|
+
* replay (which never serves the prelude) and the build-manifest read-through
|
|
158
|
+
* use it as-is; the document HIT path runs it inside
|
|
159
|
+
* {@link openShellDocument}, whose single decode is the prelude's check.
|
|
130
160
|
*
|
|
131
161
|
* navigationOnly entries store no document half (prelude/postponed absent) and
|
|
132
162
|
* therefore never pass. The partial-replay path skips this gate for them
|
|
@@ -135,8 +165,46 @@ export declare function isValidShellHit(entry: ShellCacheEntry, buildVersion: st
|
|
|
135
165
|
* capture).
|
|
136
166
|
*/
|
|
137
167
|
export declare function hasIntactShellPayload(entry: ShellCacheEntry): entry is DocumentShellCacheEntry;
|
|
138
|
-
/**
|
|
139
|
-
export
|
|
168
|
+
/** A document shell ready to serve: its prelude decoded exactly once. */
|
|
169
|
+
export interface ShellDocument {
|
|
170
|
+
entry: ShellCacheEntry;
|
|
171
|
+
prelude: Uint8Array;
|
|
172
|
+
/** React's postponed state, parse-checked (null = DATA variant). */
|
|
173
|
+
postponed: string | null;
|
|
174
|
+
/**
|
|
175
|
+
* The capture snapshot: on the entry, or still arriving on its own promise
|
|
176
|
+
* after a prelude-first read (SegmentCacheStore.readShellDocument), which
|
|
177
|
+
* only the tail awaits.
|
|
178
|
+
*/
|
|
179
|
+
snapshot: ShellSnapshotRecord[] | undefined | Promise<ShellSnapshotRecord[] | undefined>;
|
|
180
|
+
/** Why a prelude-first read's snapshot is missing (ShellDocumentRead). */
|
|
181
|
+
snapshotFailure?: Promise<ShellSnapshotFailure | undefined>;
|
|
182
|
+
/** The prelude-first read's stats, when perf metrics are on. */
|
|
183
|
+
stats?: ShellReadStats;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Pre-commit integrity gate for a document HIT, and its only prelude decode.
|
|
187
|
+
* A stored entry whose prelude is not decodable base64 or whose postponed blob
|
|
188
|
+
* is not parseable JSON would otherwise throw AFTER the 200 + full static
|
|
189
|
+
* prelude flushed (`resumeShellHTML` parses in the tail) — the client gets a
|
|
190
|
+
* visually complete page that never hydrates, re-served on every request
|
|
191
|
+
* until the entry ages out. Returning null here turns a corrupt entry
|
|
192
|
+
* (store-layer fault) into a plain MISS the recapture overwrites. The decoded
|
|
193
|
+
* bytes are what serveShellHit enqueues, so the check costs no second decode.
|
|
194
|
+
* A prelude-first `read` (SegmentCacheStore.readShellDocument) already carries
|
|
195
|
+
* the raw bytes and a pending snapshot, so only its postponed blob is checked.
|
|
196
|
+
*/
|
|
197
|
+
export declare function openShellDocument(entry: ShellCacheEntry, read?: Pick<ShellDocumentRead, "prelude" | "snapshot" | "snapshotFailure" | "stats">): ShellDocument | null;
|
|
198
|
+
/**
|
|
199
|
+
* Prelude enqueue granularity. A streaming compressor in front of the worker
|
|
200
|
+
* emits its first byte only after compressing the whole write it was handed:
|
|
201
|
+
* one 629 KB enqueue delayed the first compressed byte by the full prelude's
|
|
202
|
+
* compression time (brotli-6 5.1 ms, gzip-6 1.1 ms in Node zlib with a flush
|
|
203
|
+
* per write; workerd's CompressionStream emits nothing until the whole write is
|
|
204
|
+
* compressed), while 32 KB chunks emit within 0.1 ms and cost slightly more
|
|
205
|
+
* in total compression (0.55-0.70 ms) than one write (issue #941).
|
|
206
|
+
*/
|
|
207
|
+
export declare const SHELL_PRELUDE_CHUNK_BYTES: number;
|
|
140
208
|
/** True when the store implements the shell entry family. */
|
|
141
209
|
export declare function hasShellFamily(store: SegmentCacheStore | undefined): store is SegmentCacheStore & {
|
|
142
210
|
getShell: NonNullable<SegmentCacheStore["getShell"]>;
|
|
@@ -152,8 +220,22 @@ export declare function hasShellFamily(store: SegmentCacheStore | undefined): st
|
|
|
152
220
|
export interface ShellTailTiming {
|
|
153
221
|
key: string;
|
|
154
222
|
outcome: "complete" | "redirect" | "error";
|
|
223
|
+
/** The capture snapshot available to the tail (read and parsed). */
|
|
224
|
+
snapshotMs?: number;
|
|
225
|
+
/** Snapshot bytes read after the commit (prelude-first stores). */
|
|
226
|
+
snapshotBytes?: number;
|
|
227
|
+
/** Snapshot bytes read after the commit, excluding the parse. */
|
|
228
|
+
snapshotReadMs?: number;
|
|
229
|
+
/** Snapshot JSON.parse CPU (reads 0 on a deployed worker: see bytes). */
|
|
230
|
+
snapshotParseMs?: number;
|
|
231
|
+
/** Snapshot records by family, e.g. `segment:1/item:5` (no commas: it rides a Server-Timing desc). */
|
|
232
|
+
snapshotRecords?: string;
|
|
233
|
+
/** Records the capture pruned from the snapshot, same format (ShellCacheEntry.prunedRecords). */
|
|
234
|
+
snapshotPruned?: string;
|
|
155
235
|
/** Loader-family seed decode (only when the entry carried a snapshot). */
|
|
156
236
|
seedMs?: number;
|
|
237
|
+
/** The seed decode's own duration: Flight deserialization, CPU only. */
|
|
238
|
+
seedCpuMs?: number;
|
|
157
239
|
/** Tail router.match() settled. */
|
|
158
240
|
matchMs?: number;
|
|
159
241
|
/** Tail stream (resume output) handed to the response stream. */
|
|
@@ -168,12 +250,13 @@ export interface ShellTailTiming {
|
|
|
168
250
|
tailBytes?: number;
|
|
169
251
|
}
|
|
170
252
|
/**
|
|
171
|
-
* Compact single-line form for the console log and the
|
|
172
|
-
*
|
|
173
|
-
* escaping needed.
|
|
253
|
+
* Compact single-line form for the console log and the Server-Timing mirror's
|
|
254
|
+
* `desc`: alphanumerics and `=`, `-`, `:`, `/` only (records=segment:1 pruned=item:5),
|
|
255
|
+
* so no quoted-string escaping is needed. Offsets are from the commit; the
|
|
256
|
+
* `-cpu` fields are CPU-only durations.
|
|
174
257
|
*/
|
|
175
258
|
export declare function describeShellTailTiming(timing: ShellTailTiming): string;
|
|
176
|
-
/** Buffer one terminal tail timing for the
|
|
259
|
+
/** Buffer one terminal tail timing for the Server-Timing mirror. */
|
|
177
260
|
export declare function publishShellTailTiming(timing: ShellTailTiming): void;
|
|
178
261
|
/**
|
|
179
262
|
* Consume (read-and-clear) the buffered tail timing for `key`, so one tail
|
|
@@ -198,3 +281,43 @@ export declare function warnShellStoreMissingOnce(key: string): void;
|
|
|
198
281
|
* the missing-store warning above (an undeclared route stays silent).
|
|
199
282
|
*/
|
|
200
283
|
export declare function warnPprNonceActiveOnce(key: string): void;
|
|
284
|
+
/**
|
|
285
|
+
* Dev only (the caller gates on NODE_ENV): warn once per route that the
|
|
286
|
+
* route's cache() entry reduces an explicit `ppr.ttl` or `ppr.swr`. `shell`
|
|
287
|
+
* is what the capture stores for a record written just now
|
|
288
|
+
* (shell-capture.ts capShellWindow), so the message states the values the
|
|
289
|
+
* shell actually gets. A ppr value the cap leaves whole (or raises: the
|
|
290
|
+
* cap can move a record's stale time into the shell's swr) is silent, and
|
|
291
|
+
* so is a ppr config that sets no ttl/swr.
|
|
292
|
+
*/
|
|
293
|
+
export declare function warnPprWindowCappedOnce(routeName: string, ppr: {
|
|
294
|
+
ttl?: number;
|
|
295
|
+
swr?: number;
|
|
296
|
+
}, shell: {
|
|
297
|
+
ttl: number;
|
|
298
|
+
swr: number;
|
|
299
|
+
}, cache: {
|
|
300
|
+
ttl: number;
|
|
301
|
+
swr: number;
|
|
302
|
+
}): void;
|
|
303
|
+
/**
|
|
304
|
+
* Whether a partitioned request needs no build-shell probe: its route was
|
|
305
|
+
* already warned, or its path was already found without one.
|
|
306
|
+
*/
|
|
307
|
+
export declare function partitionBuildShellCheckDone(pathname: string, routeName: string | undefined): boolean;
|
|
308
|
+
/**
|
|
309
|
+
* Record a partitioned request's build-shell probe, and warn once per route
|
|
310
|
+
* when it found one: the route has a build-time shell its requests cannot
|
|
311
|
+
* read. The route's request partition (a `cache({ key })` enclosing it, at
|
|
312
|
+
* any depth (#970), or the store's keyGenerator) keys the shell per
|
|
313
|
+
* partition, and the build captured only
|
|
314
|
+
* the default one, so each partition captures at runtime. Same
|
|
315
|
+
* declared-intent-cannot-be-honored doctrine as the warnings above; a path
|
|
316
|
+
* with no build shell stays silent.
|
|
317
|
+
*/
|
|
318
|
+
export declare function notePartitionBuildShellCheck(pathname: string, routeName: string | undefined, found: boolean): void;
|
|
319
|
+
/**
|
|
320
|
+
* @internal Reset the serve path's once-per-key warnings and buffered tail
|
|
321
|
+
* timings. Tests only (testing/serve-shell-request.ts resetShellTestState).
|
|
322
|
+
*/
|
|
323
|
+
export declare function resetShellServeStateForTests(): void;
|
|
@@ -208,6 +208,13 @@ export interface SSRModule {
|
|
|
208
208
|
origin?: string;
|
|
209
209
|
/** Each component error the prerender reports (never the capture's own abort). */
|
|
210
210
|
onError?: (error: unknown) => void;
|
|
211
|
+
/**
|
|
212
|
+
* React's errorInfo for each task still pending at the capture's abort
|
|
213
|
+
* (its componentStack is computed when read).
|
|
214
|
+
*/
|
|
215
|
+
onAbortedTask?: (errorInfo: {
|
|
216
|
+
componentStack?: string;
|
|
217
|
+
} | undefined) => void;
|
|
211
218
|
}) => Promise<{
|
|
212
219
|
prelude: Uint8Array;
|
|
213
220
|
postponed: string | null;
|
|
@@ -106,6 +106,15 @@ export type LoaderEntry = {
|
|
|
106
106
|
* appears on navigation-lane entries.
|
|
107
107
|
*/
|
|
108
108
|
awaitBeforeFlush?: true;
|
|
109
|
+
/**
|
|
110
|
+
* loader(Def, { ssr: false }) on every DSL evaluation, document and
|
|
111
|
+
* navigation: the loader's PPR bake lane (loader-cache.ts
|
|
112
|
+
* resolveLoaderData). The bake/seed key rides on it, so a client
|
|
113
|
+
* navigation's shell replay pins exactly the loaders a document HIT pins,
|
|
114
|
+
* whatever the entry's loading(). awaitBeforeFlush cannot carry that: a
|
|
115
|
+
* navigation evaluation never has it.
|
|
116
|
+
*/
|
|
117
|
+
bake?: true;
|
|
109
118
|
};
|
|
110
119
|
/**
|
|
111
120
|
* Segments state for intercept context
|
|
@@ -408,15 +417,64 @@ export declare function getParallelSlotCount(parallels: ParallelEntries | EntryD
|
|
|
408
417
|
export declare function track(label: string, depth?: number): () => void;
|
|
409
418
|
/**
|
|
410
419
|
* Check if the current execution is inside a cache() DSL boundary.
|
|
411
|
-
* Returns false inside loader execution
|
|
412
|
-
*
|
|
420
|
+
* Returns false inside loader execution: a route cache() does not store
|
|
421
|
+
* loader values, so non-cacheable reads are safe. A loader bound with its own
|
|
422
|
+
* cache() stores its value; recordLoaderIdentityRead guards that.
|
|
413
423
|
*/
|
|
414
424
|
export declare function isInsideCacheScope(): boolean;
|
|
425
|
+
/**
|
|
426
|
+
* Throw if called during the ACTIVE background shell-capture render
|
|
427
|
+
* (`_shellCaptureRun` true on the derived request context built by
|
|
428
|
+
* shell-capture.ts). The captured shell is shared across every user hitting
|
|
429
|
+
* the URL, so a request-scoped read here would bake one user's cookies,
|
|
430
|
+
* headers, or per-request variables into markup served to others — same
|
|
431
|
+
* hazard as the cache scopes above, at the document tier. DSL segment loaders
|
|
432
|
+
* need no exemption: the live lane is masked (never executed) during capture,
|
|
433
|
+
* and the bake lane is exactly what this guard exists for.
|
|
434
|
+
*
|
|
435
|
+
* It covers everything the capture waits for, not only synchronous handler
|
|
436
|
+
* code: a promise the handler passes or pushes, an async server component,
|
|
437
|
+
* and a loader the handler awaits (`await ctx.use(Loader)`) all run under the
|
|
438
|
+
* capture context and all bake. The last one used to be exempt (the
|
|
439
|
+
* consumption-lane rule, #672/#674): while a HIT re-ran handlers, a slot
|
|
440
|
+
* handler's `ctx.use` of an identity loader rendered per visitor on the HIT.
|
|
441
|
+
* Now every HIT replays the capture's copy, so the exemption would bake the
|
|
442
|
+
* capturing request's identity into every visitor's page. The cache()
|
|
443
|
+
* guards above keep it (a cache() HIT is a separate tier).
|
|
444
|
+
*
|
|
445
|
+
* Keys off `_shellCaptureRun`, NOT the `_shellCapture` descriptor: the descriptor
|
|
446
|
+
* is also present during the FOREGROUND render (it means "a capture is wanted"),
|
|
447
|
+
* and the foreground must read cookies/headers normally to serve the real user.
|
|
448
|
+
* Only the derived capture context sets `_shellCaptureRun`.
|
|
449
|
+
*
|
|
450
|
+
* Applies only to the READ surfaces (cookies(), headers(), a { cache: false }
|
|
451
|
+
* variable, and the theme reads through tripShellCaptureGuard) whose values
|
|
452
|
+
* become markup. Response directives
|
|
453
|
+
* (invalidateClientCache(), keepClientCache()) stay callable: during capture
|
|
454
|
+
* they are header effects on a discarded response, and on the live HIT path
|
|
455
|
+
* the full pipeline runs so their headers flow to the client normally.
|
|
456
|
+
*
|
|
457
|
+
* The throw can be caught by the code that made the read, so the guard also
|
|
458
|
+
* flags the capture context; the capture refuses on the flag
|
|
459
|
+
* (shell-capture.ts refuseOnCaptureGuard), nothing is stored, and every
|
|
460
|
+
* request keeps getting the normal axis-1 render.
|
|
461
|
+
*/
|
|
462
|
+
export declare function assertNotInsideShellCapture(ctx: unknown, fnName: string): void;
|
|
463
|
+
/**
|
|
464
|
+
* True when `ctx` is the active capture render (assertNotInsideShellCapture
|
|
465
|
+
* above). On true the capture context is flagged with the read (`surface`,
|
|
466
|
+
* e.g. "cookies()", "ctx.theme") and the fix the refusal warning gives, plus
|
|
467
|
+
* the loader body (if any) that made the read, and the caller throws.
|
|
468
|
+
*/
|
|
469
|
+
export declare function tripShellCaptureGuard(ctx: unknown, surface: string, fix: string): boolean;
|
|
415
470
|
/**
|
|
416
471
|
* Read guard for a non-cacheable variable (`createVar({ cache: false })` or a
|
|
417
472
|
* `ctx.set(..., { cache: false })` write). Callers check isNonCacheable() first
|
|
418
473
|
* so ordinary reads never reach the scope lookups.
|
|
419
474
|
*
|
|
475
|
+
* Throws during a PPR shell capture (`requestCtx` is the ambient request
|
|
476
|
+
* context; assertNotInsideShellCapture): the shell is shared per host+URL.
|
|
477
|
+
*
|
|
420
478
|
* Throws inside a "use cache" body: the key does not include the value, so the
|
|
421
479
|
* first caller's value would be stored and served to later callers (#925). A
|
|
422
480
|
* loader body entered INSIDE the cached function is exempt, the same baked-copy
|
|
@@ -425,7 +483,47 @@ export declare function isInsideCacheScope(): boolean;
|
|
|
425
483
|
* records the exec scope it was entered in, so the exemption holds only while
|
|
426
484
|
* that is still the innermost one.
|
|
427
485
|
*/
|
|
428
|
-
export declare function assertNonCacheableReadAllowed(keyOrVar: string | ContextVar<unknown
|
|
486
|
+
export declare function assertNonCacheableReadAllowed(keyOrVar: string | ContextVar<unknown>, requestCtx?: unknown): void;
|
|
487
|
+
/** How the identity error words a read: a function call or a property read. */
|
|
488
|
+
export type LoaderIdentityReadVerb = "called" | "read";
|
|
489
|
+
/** A request-identity read a loader execution made (recordLoaderIdentityRead). */
|
|
490
|
+
export interface LoaderIdentityRead {
|
|
491
|
+
/**
|
|
492
|
+
* The read as the error names it: "cookies()", "headers()",
|
|
493
|
+
* `ctx.get() for a non-cacheable variable "x"`, "ctx.theme" or
|
|
494
|
+
* "getRequestContext().theme".
|
|
495
|
+
*/
|
|
496
|
+
surface: string;
|
|
497
|
+
/** "called" for a function, "read" for a property (the theme getters). */
|
|
498
|
+
verb: LoaderIdentityReadVerb;
|
|
499
|
+
/** The loader body that made the read. */
|
|
500
|
+
bodyId: string | undefined;
|
|
501
|
+
/**
|
|
502
|
+
* The cached loader whose value carries a read another loader made
|
|
503
|
+
* (loader-cache.ts identity mark): its readers read it through `via`.
|
|
504
|
+
*/
|
|
505
|
+
via?: string;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Record a request-identity read (cookies(), headers(), a non-cacheable
|
|
509
|
+
* ctx.get()) on the current loader execution (#972).
|
|
510
|
+
*
|
|
511
|
+
* isInsideCacheScope() exempts loader bodies because a route cache() never
|
|
512
|
+
* stores their values. A loader bound with its own cache() does: with no key()
|
|
513
|
+
* and no store keyGenerator its entry is keyed by loader, host, path and
|
|
514
|
+
* params only, and an enclosing route cache() key does not partition it
|
|
515
|
+
* (#974). A cookies() read there stored the first visitor's session and served
|
|
516
|
+
* it to everyone for the TTL. The read is recorded on the execution, not
|
|
517
|
+
* thrown at the call: a reader that starts the loader before its binding does
|
|
518
|
+
* runs it outside any fill, and the binding's MISS then reuses that run. The
|
|
519
|
+
* fill checks what its execution recorded, through the same links as its tags
|
|
520
|
+
* (cache-tag.ts recordedIdentityRead), so both orders fail the same way.
|
|
521
|
+
* Response directives do not record: a key cannot make a skipped body's side
|
|
522
|
+
* effect reach a HIT.
|
|
523
|
+
*/
|
|
524
|
+
export declare function recordLoaderIdentityRead(surface: string, verb?: LoaderIdentityReadVerb): void;
|
|
525
|
+
/** The error a loader cache() fill with no declared key fails with (#972). */
|
|
526
|
+
export declare function loaderCacheIdentityError(read: LoaderIdentityRead, cachedLoaderId: string): Error;
|
|
429
527
|
/**
|
|
430
528
|
* Check if the current execution is inside a DSL loader scope
|
|
431
529
|
* (wrapped by runInsideLoaderScope). Used by rendered() barrier
|
|
@@ -439,6 +537,10 @@ export declare function isInsideLoaderScope(): boolean;
|
|
|
439
537
|
* Takes scalars so the already-latched path allocates nothing.
|
|
440
538
|
*/
|
|
441
539
|
export declare function latchCachedHeaderScope(kind: CachedHeaderScope["kind"], routeKey?: string): void;
|
|
540
|
+
/** True inside ANY loader execution — DSL loader scope or a loader body
|
|
541
|
+
* (however invoked). The "loaders always re-run fresh" exemptions key off
|
|
542
|
+
* this. */
|
|
543
|
+
export declare function isInsideAnyLoaderScope(): boolean;
|
|
442
544
|
/**
|
|
443
545
|
* The one ppr opt-in predicate: a page route entry that DECLARED `ppr`
|
|
444
546
|
* (`false` and undefined mean plain axis 1). Shared by the serve path
|
|
@@ -511,11 +613,13 @@ export declare function runInsideLoaderScope<T>(fn: () => T): T;
|
|
|
511
613
|
* and handler-invoked via ctx.use) so request-scoped reads inside a loader
|
|
512
614
|
* never trip the cache-scope guards — loaders always run fresh.
|
|
513
615
|
*/
|
|
514
|
-
export declare function runInsideLoaderBodyScope<T>(fn: () => T, loaderId?: string,
|
|
616
|
+
export declare function runInsideLoaderBodyScope<T>(fn: () => T, loaderId?: string, tags?: Set<string>): T;
|
|
617
|
+
/** The innermost loader body's execution tag set (runInsideLoaderBodyScope). */
|
|
618
|
+
export declare function getLoaderBodyTags(): Set<string> | undefined;
|
|
515
619
|
/**
|
|
516
620
|
* The $$id of the loader whose body is currently executing, or undefined
|
|
517
621
|
* outside any loader body. Used by the shell-capture identity guard
|
|
518
|
-
* (
|
|
622
|
+
* (assertNotInsideShellCapture) so its refusal warning can name the loader that read
|
|
519
623
|
* cookies()/headers() instead of blaming a lane it cannot see — the old
|
|
520
624
|
* hardcoded "bake-lane loader" text misled a live-lane debugging session
|
|
521
625
|
* (issue #672, secondary).
|
|
@@ -528,16 +632,6 @@ export declare function getCurrentLoaderBodyId(): string | undefined;
|
|
|
528
632
|
* reader runs in that request only reaches the page through the replay.
|
|
529
633
|
*/
|
|
530
634
|
export declare function isInsideLoaderBody(loaderId: string): boolean;
|
|
531
|
-
/**
|
|
532
|
-
* True while a HANDLER-invoked loader body (`await ctx.use(Loader)` from a
|
|
533
|
-
* handler, not the DSL segment funnel) is executing. The consumption-lane
|
|
534
|
-
* rule keys off this: handler consumption yields a BAKED copy in every shared
|
|
535
|
-
* artifact — cache(), "use cache", and the PPR shell — so the shell-capture
|
|
536
|
-
* identity guard (cookie-store.ts) permits cookies()/headers() here, exactly
|
|
537
|
-
* like the cache-purity guards do. DSL segment loaders (live lane masked at
|
|
538
|
-
* capture, bake lane guarded) never set the flag.
|
|
539
|
-
*/
|
|
540
|
-
export declare function isInsideHandlerInvokedLoaderBody(): boolean;
|
|
541
635
|
/**
|
|
542
636
|
* Check if the current execution is inside a handle push callback (sync or an
|
|
543
637
|
* async callback's continuation). Used by the handler-to-loader deadlock guard
|
|
@@ -63,6 +63,27 @@ export interface ReadonlyHeaders {
|
|
|
63
63
|
[Symbol.iterator](): HeadersIterator<[string, string]>;
|
|
64
64
|
}
|
|
65
65
|
type HeadersIterator<T> = IterableIterator<T>;
|
|
66
|
+
/** The public reads of the visitor's theme cookie that assertThemeReadAllowed guards. */
|
|
67
|
+
export type ThemeReadSurface = "ctx.theme" | "getRequestContext().theme";
|
|
68
|
+
/**
|
|
69
|
+
* Guard for a read of the visitor's theme: the handler and middleware
|
|
70
|
+
* `ctx.theme` (handler-context.ts, middleware.ts) and `getRequestContext().theme`
|
|
71
|
+
* (request-context.ts). The theme is the visitor's theme cookie, so it is an
|
|
72
|
+
* identity read like cookies(): it refuses in the same scopes, in the same
|
|
73
|
+
* order (refusingCacheScope, then tripShellCaptureGuard in context.ts), with
|
|
74
|
+
* the same exemption (a loader body under cache(); at capture there is none).
|
|
75
|
+
* Before #971 each read was a plain cookie read, so a ppr shell, a cache()
|
|
76
|
+
* entry or a "use cache" entry stored the first visitor's theme and served it
|
|
77
|
+
* to every later visitor.
|
|
78
|
+
*
|
|
79
|
+
* A read the guards allow is recorded on the current loader execution, as a
|
|
80
|
+
* cookies() read method records it (#972, recordLoaderIdentityRead).
|
|
81
|
+
*
|
|
82
|
+
* `ctx` is the request context at read time, as cookies() reads it. The
|
|
83
|
+
* router's own payload read (payloadInitialTheme, rsc/full-payload.ts) goes
|
|
84
|
+
* through the unguarded `_readTheme()`.
|
|
85
|
+
*/
|
|
86
|
+
export declare function assertThemeReadAllowed(ctx: unknown, surface: ThemeReadSurface): void;
|
|
66
87
|
/**
|
|
67
88
|
* Get the original request headers (read-only).
|
|
68
89
|
*
|