@rangojs/router 0.32.0 → 0.33.1
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/bin/browser-7KIJ5HSV.js +15 -0
- package/dist/bin/chunk-3B7CKBRO.js +463 -0
- package/dist/bin/chunk-524VACCG.js +71 -0
- package/dist/bin/chunk-LD7UBHT4.js +196 -0
- package/dist/bin/chunk-TO6CRWLN.js +1429 -0
- package/dist/bin/chunk-TTXC2U3G.js +2477 -0
- package/dist/bin/client-urls-projection-DQLWPDAT.js +28 -0
- package/dist/bin/forward-user-plugins-URBZKJP4.js +62 -0
- package/dist/bin/rango.js +42 -10173
- package/dist/bin/runtime-EPUDEHO4.js +596 -0
- package/dist/bin/runtime-discovery-3HH6BP3K.js +352 -0
- package/dist/bin/state-ACD2MGKJ.js +16 -0
- package/dist/bin/temp-rsc-server-JALTIWLT.js +3190 -0
- package/dist/types/bin/rango.d.ts +1 -0
- package/dist/types/browser/action-fence.d.ts +20 -0
- package/dist/types/browser/cookie-name.d.ts +51 -10
- package/dist/types/browser/invalidate-client-cache.d.ts +8 -6
- package/dist/types/browser/navigation-bridge.d.ts +36 -1
- package/dist/types/browser/navigation-client.d.ts +19 -0
- package/dist/types/browser/navigation-store-handle.d.ts +37 -4
- package/dist/types/browser/navigation-store.d.ts +2 -2
- package/dist/types/browser/navigation-transaction.d.ts +0 -1
- package/dist/types/browser/partial-update.d.ts +13 -0
- package/dist/types/browser/prefetch/cache.d.ts +96 -8
- package/dist/types/browser/prefetch/fetch.d.ts +26 -2
- package/dist/types/browser/prefetch/queue.d.ts +3 -2
- package/dist/types/browser/rango-state.d.ts +34 -5
- package/dist/types/browser/react/Link.d.ts +15 -6
- package/dist/types/browser/types.d.ts +25 -0
- package/dist/types/build/generate-route-types.d.ts +1 -1
- package/dist/types/build/route-types/include-resolution.d.ts +9 -1
- package/dist/types/build/route-types/router-processing.d.ts +3 -1
- package/dist/types/cache/cache-key-utils.d.ts +12 -8
- package/dist/types/cache/cache-scope.d.ts +4 -2
- package/dist/types/cache/cf/cf-cache-types.d.ts +9 -5
- package/dist/types/cache/document-cache-policy.d.ts +9 -1
- package/dist/types/cache/memory-segment-store.d.ts +3 -2
- package/dist/types/cache/segment-codec.d.ts +2 -2
- package/dist/types/cache/shell-snapshot.d.ts +1 -1
- package/dist/types/cache/types.d.ts +12 -7
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +2 -1
- package/dist/types/client-urls/types.d.ts +7 -5
- package/dist/types/prefetch-markers.d.ts +9 -0
- package/dist/types/prefetch-source-scope.d.ts +66 -0
- package/dist/types/route-definition/helpers-types.d.ts +8 -5
- package/dist/types/router/intercept-resolution.d.ts +25 -11
- package/dist/types/router/match-context.d.ts +1 -0
- package/dist/types/router/match-handlers.d.ts +1 -1
- package/dist/types/router/prefetch-cache-ttl.d.ts +11 -3
- package/dist/types/router/router-context.d.ts +1 -1
- package/dist/types/router/router-options.d.ts +6 -3
- package/dist/types/router/segment-resolution/loader-cache.d.ts +8 -0
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +23 -7
- package/dist/types/router/segment-resolution/revalidation.d.ts +19 -0
- package/dist/types/router/source-reads.d.ts +53 -0
- package/dist/types/router/types.d.ts +1 -1
- package/dist/types/rsc/helpers.d.ts +5 -2
- package/dist/types/rsc/prefetch-render-error.d.ts +7 -2
- package/dist/types/rsc/request-stages.d.ts +2 -0
- package/dist/types/rsc/shell-serve.d.ts +6 -1
- package/dist/types/server/context.d.ts +23 -6
- package/dist/types/server/cookie-store.d.ts +15 -1
- package/dist/types/server/document-location-state.d.ts +5 -2
- package/dist/types/server/handle-store.d.ts +2 -0
- package/dist/types/server/request-context.d.ts +43 -7
- package/dist/types/testing/e2e/server.d.ts +16 -1
- package/dist/types/testing/render-route.d.ts +66 -2
- package/dist/types/testing/serve-shell-request.d.ts +6 -3
- package/dist/types/types/handler-context.d.ts +7 -5
- package/dist/types/types/loader-types.d.ts +7 -4
- package/dist/types/types/segments.d.ts +15 -4
- package/dist/types/urls/path-helper-types.d.ts +7 -5
- package/dist/types/vite/temp-rsc-server.d.ts +2 -2
- package/dist/vite/index.js +449 -189
- package/package.json +2 -2
- package/skills/app-structure/SKILL.md +81 -0
- package/skills/app-structure/e-commerce.md +10 -9
- package/skills/app-structure/large-app.md +18 -2
- package/skills/caching/SKILL.md +10 -5
- package/skills/client-urls/SKILL.md +29 -11
- package/skills/comparison/references/framework-comparison.md +13 -8
- package/skills/document-cache/SKILL.md +8 -0
- package/skills/hooks/navigation.md +8 -8
- package/skills/hooks/outlets.md +4 -0
- package/skills/hooks/state.md +24 -8
- package/skills/intercept/SKILL.md +33 -12
- package/skills/layout/SKILL.md +60 -0
- package/skills/links/SKILL.md +101 -4
- package/skills/loader/SKILL.md +49 -22
- package/skills/migrate-nextjs/SKILL.md +8 -2
- package/skills/migrate-react-router/data-and-actions.md +5 -0
- package/skills/ppr/SKILL.md +26 -5
- package/skills/prerender/SKILL.md +4 -1
- package/skills/route/SKILL.md +6 -6
- package/skills/router-setup/SKILL.md +2 -1
- package/skills/server-actions/SKILL.md +41 -8
- package/skills/testing/client-components.md +29 -13
- package/skills/testing/serve-shell-request.md +7 -7
- package/skills/testing/server-actions.md +8 -0
- package/skills/ux-architecture/SKILL.md +70 -19
- package/src/bin/rango.ts +39 -13
- package/src/browser/action-fence.ts +72 -0
- package/src/browser/cookie-name.ts +82 -14
- package/src/browser/invalidate-client-cache.ts +9 -7
- package/src/browser/navigation-bridge.ts +164 -32
- package/src/browser/navigation-client.ts +95 -20
- package/src/browser/navigation-store-handle.ts +67 -5
- package/src/browser/navigation-store.ts +86 -17
- package/src/browser/navigation-transaction.ts +0 -6
- package/src/browser/partial-update.ts +31 -12
- package/src/browser/prefetch/cache.ts +188 -11
- package/src/browser/prefetch/fetch.ts +211 -51
- package/src/browser/prefetch/queue.ts +3 -2
- package/src/browser/rango-state.ts +50 -8
- package/src/browser/react/Link.tsx +20 -9
- package/src/browser/react/use-navigation.ts +1 -1
- package/src/browser/rsc-router.tsx +8 -1
- package/src/browser/server-action-bridge.ts +16 -11
- package/src/browser/types.ts +25 -0
- package/src/build/generate-route-types.ts +1 -0
- package/src/build/route-types/include-resolution.ts +40 -3
- package/src/build/route-types/router-processing.ts +170 -25
- package/src/build/route-types/scan-filter.ts +1 -0
- package/src/cache/cache-key-utils.ts +12 -8
- package/src/cache/cache-runtime.ts +5 -1
- package/src/cache/cache-scope.ts +28 -7
- package/src/cache/cf/cf-cache-types.ts +9 -5
- package/src/cache/document-cache-policy.ts +27 -0
- package/src/cache/document-cache.ts +5 -0
- package/src/cache/memory-segment-store.ts +3 -2
- package/src/cache/segment-codec.ts +2 -2
- package/src/cache/shell-snapshot.ts +1 -1
- package/src/cache/types.ts +12 -7
- package/src/cache/vercel/vercel-cache-store.ts +2 -1
- package/src/client-urls/types.ts +7 -5
- package/src/prefetch-markers.ts +10 -0
- package/src/prefetch-source-scope.ts +137 -0
- package/src/route-definition/helpers-types.ts +8 -5
- package/src/router/intercept-resolution.ts +94 -50
- package/src/router/loader-resolution.ts +8 -6
- package/src/router/match-api.ts +33 -29
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +2 -1
- package/src/router/match-middleware/cache-lookup.ts +21 -13
- package/src/router/match-result.ts +39 -87
- package/src/router/navigation-snapshot.ts +18 -3
- package/src/router/prefetch-cache-ttl.ts +15 -3
- package/src/router/revalidation.ts +53 -23
- package/src/router/router-context.ts +2 -1
- package/src/router/router-options.ts +6 -3
- package/src/router/segment-resolution/loader-cache.ts +21 -3
- package/src/router/segment-resolution/prefetch-deferral.ts +38 -31
- package/src/router/segment-resolution/revalidation.ts +40 -12
- package/src/router/source-reads.ts +122 -0
- package/src/router/types.ts +2 -1
- package/src/router.ts +11 -12
- package/src/rsc/helpers.ts +8 -3
- package/src/rsc/prefetch-render-error.ts +7 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/request-stages.ts +59 -0
- package/src/rsc/rsc-rendering.ts +49 -33
- package/src/rsc/server-action.ts +2 -1
- package/src/rsc/shell-capture.ts +4 -5
- package/src/rsc/shell-serve.ts +12 -1
- package/src/segment-system.tsx +1 -1
- package/src/server/context.ts +37 -6
- package/src/server/cookie-store.ts +22 -1
- package/src/server/document-location-state.ts +23 -2
- package/src/server/handle-store.ts +2 -0
- package/src/server/request-context.ts +113 -28
- package/src/testing/dispatch.ts +3 -0
- package/src/testing/e2e/fixture.ts +4 -12
- package/src/testing/e2e/server.ts +91 -4
- package/src/testing/render-route.tsx +269 -4
- package/src/testing/serve-shell-request.ts +6 -3
- package/src/types/handler-context.ts +7 -5
- package/src/types/loader-types.ts +7 -4
- package/src/types/segments.ts +15 -4
- package/src/urls/path-helper-types.ts +7 -5
- package/src/vite/temp-rsc-server.ts +2 -2
|
@@ -493,9 +493,12 @@ export interface RangoOptions<TEnv = any> {
|
|
|
493
493
|
* TTL (in seconds) for the client-side in-memory prefetch cache.
|
|
494
494
|
*
|
|
495
495
|
* Controls how long prefetch responses are kept in the client-side
|
|
496
|
-
* in-memory cache.
|
|
497
|
-
*
|
|
498
|
-
*
|
|
496
|
+
* in-memory cache. A prefetch with nothing live in it (a document cache
|
|
497
|
+
* HIT, or every sent segment from a `cache()` record or a Prerender entry,
|
|
498
|
+
* with no loader) also carries `Cache-Control: private, max-age=<ttl>`,
|
|
499
|
+
* so the browser's HTTP cache keeps it as long. Any other prefetch carries
|
|
500
|
+
* none: a prefetch whose render failed after its 200 went out would
|
|
501
|
+
* otherwise answer later requests.
|
|
499
502
|
*
|
|
500
503
|
* The cache is automatically invalidated on server actions regardless
|
|
501
504
|
* of TTL, so this is primarily a staleness safety net.
|
|
@@ -61,6 +61,14 @@ export declare function bindsLoaderCache(entries: readonly EntryData[]): boolean
|
|
|
61
61
|
* one is live (shell-capture.ts deriveShellCaptureContext).
|
|
62
62
|
*/
|
|
63
63
|
export declare function routeLoaderLanes(entries: readonly EntryData[]): ReadonlyMap<string, "live" | "bake">;
|
|
64
|
+
/**
|
|
65
|
+
* This request serves `loaderEntry` from a pin (servedPins), so its body does
|
|
66
|
+
* not decide the value: resolveLoaderData serves a pin to an `ssr: false`
|
|
67
|
+
* registration only. A prefetch does not defer such a loader
|
|
68
|
+
* (prefetch-deferral.ts defersLoader): nothing would run, and the pin is the
|
|
69
|
+
* value the document shows.
|
|
70
|
+
*/
|
|
71
|
+
export declare function servesLoaderPin(loaderEntry: LoaderEntry, reqCtx?: RequestContext<any> | undefined): boolean;
|
|
64
72
|
/**
|
|
65
73
|
* What a record restored on a request over the matched chain `entries` is to
|
|
66
74
|
* each loader's handle pushes (HandleStore RecordAuthority), from the pins
|
|
@@ -30,11 +30,18 @@ import type { CacheScope } from "../../cache/cache-scope.js";
|
|
|
30
30
|
/**
|
|
31
31
|
* What a request says it is, read once when its context is created
|
|
32
32
|
* (RequestContext._requestKind). A fill is marked on the raw URL and wins
|
|
33
|
-
* over the prefetch header.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* over the prefetch header. `"prefetch"` is a speculative prefetch (viewport,
|
|
34
|
+
* render) and `"intent"` a hover or an explicit `prefetch()` call
|
|
35
|
+
* (PREFETCH_INTENT): both are prefetches, and only the first defers. Whether
|
|
36
|
+
* a prefetch defers anything is decided per match, by partialDeferralMode: a
|
|
37
|
+
* shell capture inherits the context of the request that scheduled it and is
|
|
38
|
+
* never a prefetch.
|
|
36
39
|
*/
|
|
37
|
-
export declare function requestKind(request: Request, rawUrl: URL):
|
|
40
|
+
export declare function requestKind(request: Request, rawUrl: URL): RequestKind | undefined;
|
|
41
|
+
/** requestKind's answer for a marked request. */
|
|
42
|
+
export type RequestKind = "prefetch" | "intent" | "fill";
|
|
43
|
+
/** A prefetch of either kind: speculative or intent. */
|
|
44
|
+
export declare function isPrefetchKind(kind: RequestKind | undefined): boolean;
|
|
38
45
|
/** Static, per matched chain. */
|
|
39
46
|
interface DeferralScope {
|
|
40
47
|
/**
|
|
@@ -158,7 +165,9 @@ export declare function resolveDeferralScope(entries: readonly EntryData[]): Def
|
|
|
158
165
|
export declare function resolveInterceptDeferralScope(intercept: InterceptEntry): DeferralScope;
|
|
159
166
|
/**
|
|
160
167
|
* Which half of the feature a partial request is, if either (`fill` and
|
|
161
|
-
* `prefetch` are requestKind's answer).
|
|
168
|
+
* `prefetch` are requestKind's answer). `prefetch` is a speculative prefetch
|
|
169
|
+
* only: an intent prefetch defers nothing, like the navigation it starts
|
|
170
|
+
* early (R16). Both halves are GETs: a request of
|
|
162
171
|
* another method that carries either marker is a normal request. A prefetch
|
|
163
172
|
* defers only on a plain GET navigation: an action re-renders what it
|
|
164
173
|
* changed, and a shell capture is never a prefetch whatever the request that
|
|
@@ -234,7 +243,7 @@ export declare function startDeferredLoader(ctx: HandlerContext<any, any>, loade
|
|
|
234
243
|
/**
|
|
235
244
|
* The segment a run of `loaderId` files its handle pushes under: the one
|
|
236
245
|
* that registers it when it is registered (registerDeferredLoader), else the
|
|
237
|
-
* segment
|
|
246
|
+
* segment whose code starts it (segmentPinOf).
|
|
238
247
|
*/
|
|
239
248
|
export declare function loaderOwner(ctx: HandlerContext<any, any>, loaderId: string): string | undefined;
|
|
240
249
|
/**
|
|
@@ -242,8 +251,15 @@ export declare function loaderOwner(ctx: HandlerContext<any, any>, loaderId: str
|
|
|
242
251
|
* does not hold that segment, and the loader is flagged itself or sits behind
|
|
243
252
|
* the fallback of a flagged entry the client does not hold either. Under a
|
|
244
253
|
* flagged layout the client holds, a new route's loaders run.
|
|
254
|
+
*
|
|
255
|
+
* `servesPin` (loader-cache.ts servesLoaderPin) answers whether this request
|
|
256
|
+
* serves the loader from a pin of a document-captured shell. Such a loader
|
|
257
|
+
* is not deferred: its body does not decide the value, and the pin is what
|
|
258
|
+
* the document shows. On every other request it would run, so it is
|
|
259
|
+
* deferred as on a route without ppr. Asked when loaders resolve, after a
|
|
260
|
+
* replay HIT has armed the pins (rsc-rendering.ts matchPartialWithPprReplay).
|
|
245
261
|
*/
|
|
246
|
-
export declare function defersLoader(ctx: HandlerContext<any, any>, loaderEntry: LoaderEntry, segmentId: string): boolean;
|
|
262
|
+
export declare function defersLoader(ctx: HandlerContext<any, any>, loaderEntry: LoaderEntry, segmentId: string, servesPin: (loaderEntry: LoaderEntry) => boolean): boolean;
|
|
247
263
|
/**
|
|
248
264
|
* This prefetch skips the handler of the entry whose segment is `segmentId`:
|
|
249
265
|
* a unit the client does not hold, or anything the client does not hold
|
|
@@ -53,6 +53,25 @@ export interface LoaderOwner {
|
|
|
53
53
|
}
|
|
54
54
|
/** A route entry as a LoaderOwner, under `shortCodeOverride` when inherited. */
|
|
55
55
|
export declare function ownerOf(entry: EntryData, shortCodeOverride?: string): LoaderOwner;
|
|
56
|
+
/**
|
|
57
|
+
* A route's loader copies inherited into a child layout with parallel slots
|
|
58
|
+
* are redundant beside the route's own loader segments: true when the route
|
|
59
|
+
* renders no loading() fallback on any request, so nothing hides the route's
|
|
60
|
+
* copy from useLoader() in the slots. A redundant copy is never resolved, on
|
|
61
|
+
* any request: the walkers check this before they inherit.
|
|
62
|
+
*
|
|
63
|
+
* Decided from the entry, never from the route segment's `loading`, which
|
|
64
|
+
* differs between requests: `loading(fallback, { ssr: false })` is `null` on
|
|
65
|
+
* a document (applyDocumentModeLoading) and the fallback on partials, and the
|
|
66
|
+
* walkers emit `loading(false)` as `null` too. A copy sent on one request and
|
|
67
|
+
* not on the next gives the layout a loader entry it did not have, so
|
|
68
|
+
* renderSegments wraps it in StreamedLoaderErrorBoundary and React remounts
|
|
69
|
+
* it with its Outlet (docs/tree-structure.md). Resolving a redundant copy and
|
|
70
|
+
* dropping it from the payload later is not enough: when the route's own
|
|
71
|
+
* loader is held (its revalidate() declines), the copy is new to the client,
|
|
72
|
+
* so it re-runs and is sent without the route's copy beside it.
|
|
73
|
+
*/
|
|
74
|
+
export declare function inheritedCopyRedundant(route: EntryData): boolean;
|
|
56
75
|
/**
|
|
57
76
|
* The walker's one loader funnel: each loader is a segment, run when the
|
|
58
77
|
* client lacks it, asked its revalidate() when held, kept outright in a fill
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recording what revalidate() rules read of the source page, so a prefetch
|
|
3
|
+
* answer a rule kept is shared between the pages with equal values of what
|
|
4
|
+
* was read (prefetch-source-scope.ts).
|
|
5
|
+
*/
|
|
6
|
+
import { type SourcePage, type SourceReads } from "../prefetch-source-scope.js";
|
|
7
|
+
/**
|
|
8
|
+
* The source group of a match whose diff a source read decided: the reads
|
|
9
|
+
* every kept decision made, unioned, and the source page they read.
|
|
10
|
+
*/
|
|
11
|
+
export interface SourceHeld extends SourcePage {
|
|
12
|
+
reads: SourceReads;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A copy of `params` whose reads land in `reads`: an enumerable getter per
|
|
16
|
+
* key, so a spread, Object.entries, JSON.stringify or structuredClone reads
|
|
17
|
+
* every key. Not a Proxy: structuredClone refuses one. A read of an absent
|
|
18
|
+
* key is not recorded and needs no record: the key set is part of the group
|
|
19
|
+
* (`reads.keys`), so a page in the group lacks that key too.
|
|
20
|
+
*/
|
|
21
|
+
export declare function recordingParams(params: Readonly<Record<string, string>>, reads: SourceReads): Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* Add `reads` to the source group of the match `context` decides for. All
|
|
24
|
+
* of one match's decisions read the same source page; a caller that reads
|
|
25
|
+
* no route name passes none.
|
|
26
|
+
*/
|
|
27
|
+
export declare function markSourceHeld(context: {
|
|
28
|
+
_sourceHeld?: SourceHeld;
|
|
29
|
+
}, reads: SourceReads, page: SourcePage): void;
|
|
30
|
+
/**
|
|
31
|
+
* The `x-rsc-prefetch-scope` of a partial response, or undefined when every
|
|
32
|
+
* source page may share it.
|
|
33
|
+
*
|
|
34
|
+
* - `source`: the exact source page's. A route an intercept targets renders
|
|
35
|
+
* the modal or the full page depending on the source, so a full page
|
|
36
|
+
* prefetched where the intercept does not apply would otherwise serve a
|
|
37
|
+
* later click where it does (#1007); a response with deferred units
|
|
38
|
+
* answers one page's prefetch (`sourceOnly`); a kept rule that read
|
|
39
|
+
* `currentUrl` can depend on anything in it; and a source-held answer that
|
|
40
|
+
* sends nothing (`sentNothing`) commits treeless on the page that adopts
|
|
41
|
+
* it, which keeps its whole tree without checking it holds the ids the
|
|
42
|
+
* answer kept (partial-update.ts): only the source is known to hold them.
|
|
43
|
+
* - `params; <label>`: a revalidate() rule read the source's params or route
|
|
44
|
+
* name and kept a segment. A page that adopted the answer would keep its
|
|
45
|
+
* own copy of that segment (#1331), which is right only where the rule
|
|
46
|
+
* decides the same: on pages with equal values of what it read. The label
|
|
47
|
+
* names them (prefetch-source-scope.ts); the browser stores the answer
|
|
48
|
+
* under it. A page that lacks a kept id refetches (the commit's
|
|
49
|
+
* missing-segment check), as for a shared answer.
|
|
50
|
+
*/
|
|
51
|
+
export declare function prefetchScopeOf(sourceOnly: boolean, held: SourceHeld | undefined, sentNothing: boolean): string | undefined;
|
|
52
|
+
/** Every param of the source read: its params must equal the page's. */
|
|
53
|
+
export declare function readsOfAllParams(params: Readonly<Record<string, string>>): SourceReads;
|
|
@@ -60,7 +60,7 @@ export interface SegmentResolutionDeps<TEnv = any> {
|
|
|
60
60
|
export interface MatchApiDeps<TEnv = any> {
|
|
61
61
|
findMatch: (pathname: string, ms?: any) => any;
|
|
62
62
|
getMetricsStore: () => any;
|
|
63
|
-
findInterceptForRoute: (
|
|
63
|
+
findInterceptForRoute: (routeKeys: string | readonly string[], parentEntry: EntryData | null, selectorContext: InterceptSelectorContext | null, isAction: boolean, held?: ReadonlySet<string>) => {
|
|
64
64
|
intercept: InterceptEntry;
|
|
65
65
|
entry: EntryData;
|
|
66
66
|
} | null;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Utility functions for RSC request handling.
|
|
5
5
|
*/
|
|
6
6
|
import type { RequestContext } from "../server/request-context.js";
|
|
7
|
+
import { type LocationStateEntry } from "../browser/react/location-state-shared.js";
|
|
7
8
|
import type { MiddlewareEntry, MiddlewareFn } from "../router/middleware.js";
|
|
8
9
|
import type { RscPayload } from "./types.js";
|
|
9
10
|
import type { HandlerContext } from "./handler-context.js";
|
|
@@ -98,9 +99,11 @@ export declare function interceptRedirectForPartial(response: Response, createRe
|
|
|
98
99
|
* is populated. A `document` payload (a page load's or a no-JS form post's
|
|
99
100
|
* bootstrap payload) that carries state marks the request
|
|
100
101
|
* `_documentLocationState`: it is the visitor's, so no shared cache keeps it
|
|
101
|
-
* (server/document-location-state.ts).
|
|
102
|
+
* (server/document-location-state.ts). `locationState` defaults to the
|
|
103
|
+
* current request context's; a shell HIT's tail passes the state set before
|
|
104
|
+
* its render (rsc/rsc-rendering.ts serveShellHit).
|
|
102
105
|
*/
|
|
103
|
-
export declare function attachLocationStateIfPresent(payload: RscPayload, document?: boolean): void;
|
|
106
|
+
export declare function attachLocationStateIfPresent(payload: RscPayload, document?: boolean, locationState?: LocationStateEntry[] | undefined): void;
|
|
104
107
|
/**
|
|
105
108
|
* Only cache successful responses. Non-200 statuses (errors, redirects) are
|
|
106
109
|
* not cached -- notFound() produces 500 in response routes, and explicit
|
|
@@ -11,8 +11,13 @@
|
|
|
11
11
|
* So the body ends cleanly with one more row, PREFETCH_FAILED_ROW, which the
|
|
12
12
|
* Flight decoder skips and teeWithCompletion (browser/response-adapter.ts)
|
|
13
13
|
* reads as a stream that did not end cleanly: the client evicts the prefetch
|
|
14
|
-
* and never respawns it.
|
|
15
|
-
*
|
|
14
|
+
* and never respawns it. The router puts no Cache-Control of its own on a
|
|
15
|
+
* prefetch with a live part, the only kind whose render can report an error
|
|
16
|
+
* here, so the browser HTTP cache keeps none of it unless the route sets one;
|
|
17
|
+
* a prefetch with nothing live in it carries `private, max-age`
|
|
18
|
+
* (request-stages.ts applyStoredPrefetchCacheControl); a prefetch
|
|
19
|
+
* scoped to its source page or carrying deferred units is sent
|
|
20
|
+
* `private, no-cache` (rsc-rendering.ts NOT_REUSED).
|
|
16
21
|
*
|
|
17
22
|
* The errors are the render's `_renderErrors`: a Flight error row, a streamed
|
|
18
23
|
* handler recovered into its boundary, a loader that threw (redirect() and
|
|
@@ -38,6 +38,8 @@ export interface StageRouter<TEnv = unknown> {
|
|
|
38
38
|
readonly onTimeout?: OnTimeoutCallback<TEnv>;
|
|
39
39
|
/** Names the cookie location state crosses a document redirect in. */
|
|
40
40
|
readonly resolvedStateCookieName: string;
|
|
41
|
+
/** The in-memory prefetch TTL in ms; also a stored prefetch's max-age. */
|
|
42
|
+
readonly prefetchCacheTTL: number;
|
|
41
43
|
}
|
|
42
44
|
/** Report an error through the router's onError. */
|
|
43
45
|
export type ReportError<TEnv = unknown> = (error: unknown, phase: ErrorPhase, context: InvokeOnErrorContext<TEnv>) => void;
|
|
@@ -99,8 +99,13 @@ export declare function withoutShellMissMarker(request: Request): Request | unde
|
|
|
99
99
|
* the page. window.stop() runs only when it reloads: it keeps the half-sent
|
|
100
100
|
* Flight stream from being closed by DOMContentLoaded, which would throw
|
|
101
101
|
* React's "Connection closed" (#412) before the reload lands.
|
|
102
|
+
*
|
|
103
|
+
* `restoreCookie` is the request's `_carriedLocationStateCookie`: the HIT's
|
|
104
|
+
* headers already cleared the location state cookie a document redirect left
|
|
105
|
+
* (server/document-location-state.ts), so the script sets it again before it
|
|
106
|
+
* reloads, and the reload carries the state.
|
|
102
107
|
*/
|
|
103
|
-
export declare function shellReloadScript(): string;
|
|
108
|
+
export declare function shellReloadScript(restoreCookie?: string): string;
|
|
104
109
|
/**
|
|
105
110
|
* An inline script a shell HIT's tail appends. It carries no CSP nonce: a
|
|
106
111
|
* request with an active per-request nonce never reaches a HIT
|
|
@@ -92,9 +92,10 @@ export type EntryPropRender = {
|
|
|
92
92
|
loading?: ReactNode | false;
|
|
93
93
|
/**
|
|
94
94
|
* loading(fallback, { prefetch: false }) with a renderable fallback: a
|
|
95
|
-
* prefetch defers what the fallback covers while the entry's
|
|
96
|
-
* new to the client
|
|
97
|
-
* on both DSL
|
|
95
|
+
* speculative prefetch defers what the fallback covers while the entry's
|
|
96
|
+
* segment is new to the client
|
|
97
|
+
* (router/segment-resolution/prefetch-deferral.ts). Set on both DSL
|
|
98
|
+
* evaluations, whatever `ssr` did to `loading`.
|
|
98
99
|
*/
|
|
99
100
|
loadingPrefetch?: false;
|
|
100
101
|
/**
|
|
@@ -136,8 +137,8 @@ export type LoaderEntry = {
|
|
|
136
137
|
*/
|
|
137
138
|
bake?: true;
|
|
138
139
|
/**
|
|
139
|
-
* loader(Def, { prefetch: false }): a prefetch defers this
|
|
140
|
-
* while its segment is new to the client
|
|
140
|
+
* loader(Def, { prefetch: false }): a speculative prefetch defers this
|
|
141
|
+
* registration while its segment is new to the client
|
|
141
142
|
* (router/segment-resolution/prefetch-deferral.ts).
|
|
142
143
|
*/
|
|
143
144
|
prefetch?: false;
|
|
@@ -174,7 +175,10 @@ export type InterceptSegmentsState = {
|
|
|
174
175
|
* @internal This type is an implementation detail and may change without notice.
|
|
175
176
|
*/
|
|
176
177
|
export type InterceptSelectorContext<TEnv = any> = {
|
|
177
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* Where the navigation comes from: the intercept source while one is open.
|
|
180
|
+
* Its `url` never carries a #fragment.
|
|
181
|
+
*/
|
|
178
182
|
from: ServerRouteLocation;
|
|
179
183
|
/** The navigation target. */
|
|
180
184
|
to: ServerRouteLocation;
|
|
@@ -818,6 +822,19 @@ export declare function findEnclosingLoaderBody(match: (loaderId: string) => boo
|
|
|
818
822
|
* reader runs in that request only reaches the page through the replay.
|
|
819
823
|
*/
|
|
820
824
|
export declare function isInsideLoaderBody(loaderId: string): boolean;
|
|
825
|
+
/**
|
|
826
|
+
* Run `fn` as segment `segmentId`'s code: a handle push, a loader kickoff or
|
|
827
|
+
* a "use cache" call it makes, before or after an await, files under
|
|
828
|
+
* `segmentId` (segmentPinOf).
|
|
829
|
+
*/
|
|
830
|
+
export declare function runAsSegment<R>(segmentId: string | undefined, fn: () => R): R;
|
|
831
|
+
/**
|
|
832
|
+
* The segment code here runs for: the innermost runAsSegment scope, else
|
|
833
|
+
* `ctx`'s pin (a handler the walker awaits).
|
|
834
|
+
*/
|
|
835
|
+
export declare function segmentPinOf(ctx: {
|
|
836
|
+
_currentSegmentId?: string;
|
|
837
|
+
}): string | undefined;
|
|
821
838
|
/**
|
|
822
839
|
* Check if the current execution is inside a handle push callback (sync or an
|
|
823
840
|
* async callback's continuation). Used by the handler-to-loader deadlock guard
|
|
@@ -134,7 +134,21 @@ export declare function invalidateClientCache(): void;
|
|
|
134
134
|
*
|
|
135
135
|
* Sets an internal response header the bridge reads. Idempotent within a
|
|
136
136
|
* request. Inert (a dev warning) outside a request context — there is no
|
|
137
|
-
* automatic invalidation to suppress.
|
|
137
|
+
* automatic invalidation to suppress. It also keeps the state's write part
|
|
138
|
+
* (runActionBody).
|
|
138
139
|
*/
|
|
139
140
|
export declare function keepClientCache(): void;
|
|
141
|
+
/**
|
|
142
|
+
* Run a server action's body (the JS call or a no-JS form post), then bump the
|
|
143
|
+
* rango state's write part for the client that called it: the response's
|
|
144
|
+
* state `Set-Cookie` keeps the client's timestamp and carries a new write part
|
|
145
|
+
* (browser/cookie-name.ts). Every later request of every tab and reload sends
|
|
146
|
+
* the new `X-Rango-State`, so a prefetch or navigation the browser HTTP cache
|
|
147
|
+
* holds from before the write (a response's own `Cache-Control: max-age`)
|
|
148
|
+
* misses, while the client's in-memory caches only go stale, as the action
|
|
149
|
+
* bridge marks them. Bumped however the body ends (returned, threw, or
|
|
150
|
+
* redirected), since the write may have committed either way, and not after
|
|
151
|
+
* keepClientCache() or invalidateClientCache() (whose rotation covers it).
|
|
152
|
+
*/
|
|
153
|
+
export declare function runActionBody<T>(body: () => T | Promise<T>): Promise<T>;
|
|
140
154
|
export {};
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
* reads it on the next GET document request that navigates to the target
|
|
15
15
|
* pathname (`Sec-Fetch-Dest` a browsing context, where sent), seeds
|
|
16
16
|
* `_locationState` with its entries (so the render attaches them to the
|
|
17
|
-
* bootstrap payload, rsc/rsc-rendering.ts) and clears it.
|
|
17
|
+
* bootstrap payload, rsc/rsc-rendering.ts) and clears it. A shell HIT that
|
|
18
|
+
* degrades into a reload after its headers cleared the cookie sets it
|
|
19
|
+
* again from script first (`_carriedLocationStateCookie`), so the reload
|
|
20
|
+
* carries the state.
|
|
18
21
|
*
|
|
19
22
|
* A document that carries location state is per visitor: it is marked
|
|
20
23
|
* `_documentLocationState`, never stored by the document cache or as a PPR
|
|
@@ -38,7 +41,7 @@ export declare function documentLocationStateCookieName(stateCookieName: string,
|
|
|
38
41
|
* expiry. A malformed value is cleared and ignored, and only `__rsc_ls_*`
|
|
39
42
|
* slots are taken from it (the value comes from the browser).
|
|
40
43
|
*/
|
|
41
|
-
export declare function consumeDocumentLocationState(reqCtx: Pick<RequestContext, "_locationState" | "setCookie">, request: Request, url: URL, stateCookieName: string): void;
|
|
44
|
+
export declare function consumeDocumentLocationState(reqCtx: Pick<RequestContext, "_locationState" | "_carriedLocationStateCookie" | "setCookie">, request: Request, url: URL, stateCookieName: string): void;
|
|
42
45
|
/**
|
|
43
46
|
* Write the cookie on a document request's redirect that set location state,
|
|
44
47
|
* and mark the redirect private. Not for a partial or action request (their
|
|
@@ -158,6 +158,8 @@ export interface HandleStore {
|
|
|
158
158
|
* so a loader that throws or skips its push shows none rather than the
|
|
159
159
|
* record's. A hole's replays go with them. Any other loader's replays stay
|
|
160
160
|
* (a dependency another entry's HIT replayed), and so do restored copies.
|
|
161
|
+
* A prefetch that defers the loader calls it too (revalidation.ts
|
|
162
|
+
* resolveOwnerLoaders): no run is made, so none of its placeholders stand.
|
|
161
163
|
*/
|
|
162
164
|
settleLoaderRun(loaderId: string): void;
|
|
163
165
|
/**
|
|
@@ -31,6 +31,7 @@ import type { RoutineTrace } from "../rsc/routine-plan.js";
|
|
|
31
31
|
import type { ActionContinuation } from "../rsc/server-action.js";
|
|
32
32
|
import type { ShellCaptureState } from "../rsc/shell-capture.js";
|
|
33
33
|
import type { FormActionOutcome } from "../rsc/progressive-enhancement.js";
|
|
34
|
+
import { type RequestKind } from "../router/segment-resolution/prefetch-deferral.js";
|
|
34
35
|
import { type ExecutionStart } from "../cache/invalidation-order.js";
|
|
35
36
|
export interface RenderForegroundCursor {
|
|
36
37
|
mode: RenderMode;
|
|
@@ -119,8 +120,12 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
119
120
|
setStatus(status: number): void;
|
|
120
121
|
/** @internal Set status bypassing cache-exec guard (for framework error handling) */
|
|
121
122
|
_setStatus(status: number): void;
|
|
122
|
-
/**
|
|
123
|
-
|
|
123
|
+
/**
|
|
124
|
+
* @internal Rotate the rango state cookie: its timestamp (the server seat of
|
|
125
|
+
* invalidateClientCache), or with `"write"` only its write part (a server
|
|
126
|
+
* action without a directive, cookie-store.ts runActionBody).
|
|
127
|
+
*/
|
|
128
|
+
_rotateStateCookie(part?: "write"): void;
|
|
124
129
|
/** @internal Set the keepClientCache() directive header on the response. */
|
|
125
130
|
_setKeepCacheDirective(): void;
|
|
126
131
|
/**
|
|
@@ -532,11 +537,28 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
532
537
|
*/
|
|
533
538
|
_payloadDeferred?: boolean;
|
|
534
539
|
/**
|
|
535
|
-
* @internal
|
|
536
|
-
*
|
|
537
|
-
*
|
|
540
|
+
* @internal Set by the RSC handler when a partial response carries an
|
|
541
|
+
* `x-rsc-prefetch-scope` (router/source-reads.ts prefetchScopeOf): its diff
|
|
542
|
+
* is right for its source page, or a group of them, only. The document
|
|
543
|
+
* cache keys a partial without its source, so it does not store that
|
|
544
|
+
* response, whatever Cache-Control it ends up with.
|
|
538
545
|
*/
|
|
539
|
-
|
|
546
|
+
_payloadSourceScoped?: boolean;
|
|
547
|
+
/**
|
|
548
|
+
* @internal Set when the answer has nothing live in it, so nothing in it can
|
|
549
|
+
* fail after the 200's headers left: the document cache middleware on a
|
|
550
|
+
* fresh HIT, and the RSC handler when the partial match came from storage
|
|
551
|
+
* (MatchResult.fromStorage). finalizeRequestResponse (rsc/request-stages.ts)
|
|
552
|
+
* sends such a prefetch the router's prefetch Cache-Control.
|
|
553
|
+
*/
|
|
554
|
+
_servedFromStorage?: boolean;
|
|
555
|
+
/**
|
|
556
|
+
* @internal What the request says it is: a speculative `<Link>` prefetch,
|
|
557
|
+
* an intent prefetch (hover, `prefetch()`) or a fill (`prefetch: false`),
|
|
558
|
+
* read once at creation (segment-resolution/prefetch-deferral.ts
|
|
559
|
+
* requestKind).
|
|
560
|
+
*/
|
|
561
|
+
_requestKind?: RequestKind;
|
|
540
562
|
/**
|
|
541
563
|
* @internal Set when a render shows the visitor's URL of a rewritten
|
|
542
564
|
* request: the payload or SSR seed (renderedLocation, rsc/full-payload.ts)
|
|
@@ -558,6 +580,11 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
558
580
|
* `Cache-Control: private, no-store` and never stored by the document cache
|
|
559
581
|
* or as a PPR shell.
|
|
560
582
|
*
|
|
583
|
+
* Call it from middleware, a handler or an action. A loader's call
|
|
584
|
+
* (through `getRequestContext()`) is not supported: the payload takes the
|
|
585
|
+
* state before loaders settle, so the write may be dropped, and a PPR
|
|
586
|
+
* shell HIT never sends it.
|
|
587
|
+
*
|
|
561
588
|
* Multiple calls accumulate entries across definitions. For one definition
|
|
562
589
|
* the last write wins: two entries of the same definition share its slot
|
|
563
590
|
* in history.state, so a later call (or a later entry in one array)
|
|
@@ -579,6 +606,15 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
579
606
|
* and it leaves `private, no-store` (server/document-location-state.ts).
|
|
580
607
|
*/
|
|
581
608
|
_documentLocationState?: boolean;
|
|
609
|
+
/**
|
|
610
|
+
* @internal The `document.cookie` line that re-creates the location state
|
|
611
|
+
* cookie this request consumed (server/document-location-state.ts). The
|
|
612
|
+
* consuming response clears the cookie in its headers; a shell HIT that
|
|
613
|
+
* degrades into a reload after those headers left writes this line first
|
|
614
|
+
* (rsc/shell-serve.ts shellReloadScript), so the reload reads the state
|
|
615
|
+
* again.
|
|
616
|
+
*/
|
|
617
|
+
_carriedLocationStateCookie?: string;
|
|
582
618
|
/**
|
|
583
619
|
* The matched route name, if the route has an explicit name.
|
|
584
620
|
* Undefined before route matching or for unnamed routes.
|
|
@@ -812,7 +848,7 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
|
|
|
812
848
|
* This is the type exported to library consumers. Internal code should
|
|
813
849
|
* use the full RequestContext interface directly.
|
|
814
850
|
*/
|
|
815
|
-
export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "_readCookie" | "_readCookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhenRefs" | "_pprReplayPostMatchReason" | "_cacheStore" | "_prerender" | "_onDemandProducer" | "_searchParamsFilter" | "_shellCaptureState" | "_shellImplicitCache" | "_shellLoaderSeed" | "_resolvedCacheKeys" | "_shellKey" | "_shellForcedMiss" | "_prerenderWarm" | "_runAtRequestEntry" | "_shellFragmentPayload" | "_explicitTaggedStores" | "_requestTags" | "_recordTagOwners" | "_recordLoaderTags" | "_cacheProfiles" | "_onResponseCallbacks" | "_pendingBackgroundTasks" | "_themeConfig" | "_readTheme" | "_documentCacheRender" | "_payloadVisitorTheme" | "_payloadDeferred" | "_requestKind" | "_payloadVisitorLocation" | "_locationState" | "_documentLocationState" | "_routeName" | "_prevRouteKey" | "_visitorMoved" | "_inActionRevalidation" | "_actionRan" | "_reportedErrors" | "_throwers" | "_printedErrors" | "_renderErrors" | "_recoveredHandlerErrors" | "_renderBarrier" | "_resolveRenderBarrier" | "_renderBarrierSegmentOrder" | "_treeHasStreaming" | "_renderBarrierWaiters" | "_handlerLoaderDeps" | "_awaitBeforeFlushLoaderIds" | "_renderBarrierHandleSnapshot" | "_renderBarrierGuardClosed" | "_reportBackgroundError" | "_debugPerformance" | "_metricsStore" | "_renderForeground" | "_activeRoutine" | "_renderDiagnosticsEnabled" | "_handlerStart" | "_handlerTiming" | "_tracing" | "_basename" | "_routerId" | "_versions" | "_setStatus" | "_rotateStateCookie" | "_setKeepCacheDirective" | "_freshReads" | "_setFreshReadsCookie" | "_requestRoot" | "_requestStart" | "_responseSent" | "_variables" | "_classifiedRoute" | "_requestMode" | "_cacheSignal" | "_dynamic" | "res">;
|
|
851
|
+
export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "_readCookie" | "_readCookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhenRefs" | "_pprReplayPostMatchReason" | "_cacheStore" | "_prerender" | "_onDemandProducer" | "_searchParamsFilter" | "_shellCaptureState" | "_shellImplicitCache" | "_shellLoaderSeed" | "_resolvedCacheKeys" | "_shellKey" | "_shellForcedMiss" | "_prerenderWarm" | "_runAtRequestEntry" | "_shellFragmentPayload" | "_explicitTaggedStores" | "_requestTags" | "_recordTagOwners" | "_recordLoaderTags" | "_cacheProfiles" | "_onResponseCallbacks" | "_pendingBackgroundTasks" | "_themeConfig" | "_readTheme" | "_documentCacheRender" | "_payloadVisitorTheme" | "_payloadDeferred" | "_payloadSourceScoped" | "_servedFromStorage" | "_requestKind" | "_payloadVisitorLocation" | "_locationState" | "_documentLocationState" | "_carriedLocationStateCookie" | "_routeName" | "_prevRouteKey" | "_visitorMoved" | "_inActionRevalidation" | "_actionRan" | "_reportedErrors" | "_throwers" | "_printedErrors" | "_renderErrors" | "_recoveredHandlerErrors" | "_renderBarrier" | "_resolveRenderBarrier" | "_renderBarrierSegmentOrder" | "_treeHasStreaming" | "_renderBarrierWaiters" | "_handlerLoaderDeps" | "_awaitBeforeFlushLoaderIds" | "_renderBarrierHandleSnapshot" | "_renderBarrierGuardClosed" | "_reportBackgroundError" | "_debugPerformance" | "_metricsStore" | "_renderForeground" | "_activeRoutine" | "_renderDiagnosticsEnabled" | "_handlerStart" | "_handlerTiming" | "_tracing" | "_basename" | "_routerId" | "_versions" | "_setStatus" | "_rotateStateCookie" | "_setKeepCacheDirective" | "_freshReads" | "_setFreshReadsCookie" | "_requestRoot" | "_requestStart" | "_responseSent" | "_variables" | "_classifiedRoute" | "_requestMode" | "_cacheSignal" | "_dynamic" | "res">;
|
|
816
852
|
/**
|
|
817
853
|
* Marker for a waitUntil-scheduled fn whose task promise must NOT enter
|
|
818
854
|
* _pendingBackgroundTasks. Used by the PPR shell capture for its own task:
|
|
@@ -11,7 +11,13 @@ export interface RunCliHandle {
|
|
|
11
11
|
*/
|
|
12
12
|
exitCode: Promise<number | null>;
|
|
13
13
|
findPort: (timeoutMs?: number) => Promise<number>;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Stop the process and what it spawned (the whole process group). SIGTERM by
|
|
16
|
+
* default; `stopServer` escalates to SIGKILL for a process that outlives the
|
|
17
|
+
* wait.
|
|
18
|
+
*/
|
|
19
|
+
kill: (signal?: NodeJS.Signals) => void;
|
|
20
|
+
readonly pid: number | undefined;
|
|
15
21
|
stdout: () => string;
|
|
16
22
|
stderr: () => string;
|
|
17
23
|
}
|
|
@@ -19,6 +25,15 @@ export declare function runCli(options: {
|
|
|
19
25
|
command: string;
|
|
20
26
|
label?: string;
|
|
21
27
|
} & SpawnOptions): RunCliHandle;
|
|
28
|
+
/**
|
|
29
|
+
* Stop a server started by `runCli` and wait for its whole process group to
|
|
30
|
+
* exit: SIGTERM to the group, then SIGKILL (and a log naming the pid) for a
|
|
31
|
+
* process that outlives the wait, so a leaked server is visible rather than
|
|
32
|
+
* left holding its port.
|
|
33
|
+
*/
|
|
34
|
+
export declare function stopServer(server: RunCliHandle, options?: {
|
|
35
|
+
exitWaitMs?: number;
|
|
36
|
+
}): Promise<void>;
|
|
22
37
|
export declare function tailOutput(text: string, maxChars?: number): string;
|
|
23
38
|
export declare function createIsolatedViteCacheDir(cwd: string, projectName: string, mode: "dev" | "build" | undefined): string;
|
|
24
39
|
export declare function waitForReady(url: string, getOutput?: () => {
|
|
@@ -35,6 +35,16 @@
|
|
|
35
35
|
* not modeled). navigate(url, { streamUntil }) gives the navigation a
|
|
36
36
|
* response stream that stays open past the commit (isStreaming).
|
|
37
37
|
* useAction().state stays idle: no action runs.
|
|
38
|
+
* - prefetch(url) keeps a page as a prefetch made now carries it, and
|
|
39
|
+
* action() lands a server action's response: the page on screen
|
|
40
|
+
* re-renders with its post-write loader data, and, without
|
|
41
|
+
* `keepClientCache`, production's store marks every history entry and
|
|
42
|
+
* prefetch stale (browser/navigation-store.ts). A navigate(url) to a
|
|
43
|
+
* prefetched page commits the prefetch's data; when an action made it
|
|
44
|
+
* stale, the page then revalidates once the navigation settled, in the
|
|
45
|
+
* lane production's Back uses (useNavigation() idle), with the loader
|
|
46
|
+
* data current then. The prefetch request, its decode and the HTTP cache
|
|
47
|
+
* are e2e territory.
|
|
38
48
|
* - A spec's transition({ when }) decides every commit with production's
|
|
39
49
|
* browser decision: navigate() and useRouter().push model kind "push",
|
|
40
50
|
* useRouter().replace kind "replace", refresh() kind "revalidate", and a
|
|
@@ -49,8 +59,8 @@
|
|
|
49
59
|
* written there, and another page is on screen, its page is restored
|
|
50
60
|
* from the history cache, with the handle values it had, as production's
|
|
51
61
|
* restore is; otherwise the tree keeps its location. A stale entry's
|
|
52
|
-
* revalidation, a restore that fetches, a server action's
|
|
53
|
-
* redirect's and an intercept's are e2e territory.
|
|
62
|
+
* revalidation on Back, a restore that fetches, a server action's
|
|
63
|
+
* location state, a redirect's and an intercept's are e2e territory.
|
|
54
64
|
* What it DOES cover: client hooks that read NavigationProvider /
|
|
55
65
|
* OutletContext — useParams, useReverse, useHref, useMount, useNavigation,
|
|
56
66
|
* useRouter, usePathname, useSearchParams, Outlet/useOutlet nesting and seeded
|
|
@@ -420,6 +430,60 @@ export interface TestRouterHandle {
|
|
|
420
430
|
* transition({ when }) decides it with kind "revalidate" (`to` is `from`).
|
|
421
431
|
*/
|
|
422
432
|
refresh(): Promise<void>;
|
|
433
|
+
/**
|
|
434
|
+
* Prefetch `url`, as a `<Link>` hover or viewport trigger does: the page is
|
|
435
|
+
* kept with the loader data its response would carry now (the render-time
|
|
436
|
+
* seeds, `options.loaders` merged over). A later `navigate(url)` without
|
|
437
|
+
* `loaders` commits that data, whatever the seeds are by then.
|
|
438
|
+
*
|
|
439
|
+
* The prefetch is stamped with production's stale epoch
|
|
440
|
+
* (browser/prefetch/cache.ts): an `action()` without `keepClientCache`
|
|
441
|
+
* makes it stale. A click on a stale prefetch commits it at once, then the
|
|
442
|
+
* page revalidates once the navigation settled (its `streamUntil` ended),
|
|
443
|
+
* in Back's stale-revalidation lane: `useNavigation().state` stays "idle"
|
|
444
|
+
* and `isNavigating` reads true while its request is open, `useLinkStatus`
|
|
445
|
+
* shows no pending state, and the commit runs through commitInTransition
|
|
446
|
+
* with kind "revalidate", reading the loader data current then. A newer
|
|
447
|
+
* navigation aborts it.
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* router.prefetch("/cart"); // carries count 2
|
|
451
|
+
* await router.action({ loaders: [[BasketLoader, { count: 3 }]] });
|
|
452
|
+
* await router.navigate("/cart"); // count 2, at once
|
|
453
|
+
* await waitFor(() => expect(count()).toBe("3")); // revalidated
|
|
454
|
+
*/
|
|
455
|
+
prefetch(url: string, options?: Pick<RenderRouteOptions, "loaders">): void;
|
|
456
|
+
/**
|
|
457
|
+
* Land a server action's response on the page on screen. `loaders` are the
|
|
458
|
+
* loader values after the action's write: they become the data every
|
|
459
|
+
* later response carries, and the page on screen re-renders with them in
|
|
460
|
+
* the action lane (commitInTransition, kind "action", unless
|
|
461
|
+
* transition({ when }) gates it off). Without `keepClientCache`,
|
|
462
|
+
* production's store then marks every history entry and every prefetch
|
|
463
|
+
* stale (`markCacheAsStaleAndBroadcast()`), as the action bridge does:
|
|
464
|
+
* nothing is flushed and the state cookie is not rotated.
|
|
465
|
+
* `keepClientCache: true` is the `keepClientCache()` directive: caches stay
|
|
466
|
+
* fresh. The action's own lifecycle (`useAction()`), its follow-up and its
|
|
467
|
+
* redirect are e2e territory.
|
|
468
|
+
*/
|
|
469
|
+
action(options?: Pick<RenderRouteOptions, "loaders"> & {
|
|
470
|
+
keepClientCache?: boolean;
|
|
471
|
+
}): Promise<void>;
|
|
472
|
+
/**
|
|
473
|
+
* The background revalidations a click on a stale prefetch started, in
|
|
474
|
+
* order: the page's pathname and search, and the segment ids the request
|
|
475
|
+
* holds, as production's bridge plans them
|
|
476
|
+
* (navigation-bridge.ts scheduleStalePrefetchRevalidation). A click on a
|
|
477
|
+
* fresh prefetch, or after a `keepClientCache` action, starts none.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* await router.navigate("/cart"); // a stale prefetch
|
|
481
|
+
* await waitFor(() => expect(router.revalidations()).toHaveLength(1));
|
|
482
|
+
*/
|
|
483
|
+
revalidations(): ReadonlyArray<{
|
|
484
|
+
url: string;
|
|
485
|
+
segmentIds: string[];
|
|
486
|
+
}>;
|
|
423
487
|
/** The current committed pathname. */
|
|
424
488
|
pathname(): string;
|
|
425
489
|
/** The current committed params. */
|
|
@@ -65,9 +65,12 @@ export interface ServeShellRequestOptions<TEnv = any> {
|
|
|
65
65
|
* (default none), advertising fragment passthrough. The replay decision is
|
|
66
66
|
* `result.replayStatus`.
|
|
67
67
|
*
|
|
68
|
-
* `prefetch: true` sends
|
|
69
|
-
* `X-Rango-Prefetch`
|
|
70
|
-
* entries are deferred: `result.readDeferred()`
|
|
68
|
+
* `prefetch: true` sends the speculative kind a viewport or render
|
|
69
|
+
* `<Link>` prefetch sends (`X-Rango-Prefetch: 1`), so `prefetch: false`
|
|
70
|
+
* loaders and `loading()` entries are deferred: `result.readDeferred()`
|
|
71
|
+
* lists them. A hover's or `prefetch()`'s intent prefetch defers nothing;
|
|
72
|
+
* serve it with `prefetch: true` and `headers: { "X-Rango-Prefetch":
|
|
73
|
+
* "intent" }`, which `prefetch: true` leaves as given. `fill: true`
|
|
71
74
|
* sends the request the browser follows an adopted prefetch with
|
|
72
75
|
* (`_rsc_fill`): pass the ids the prefetch delivered, without the deferred
|
|
73
76
|
* ones, as `segments`, and the same URL as `from`. `interceptSource` sends
|
|
@@ -400,11 +400,13 @@ export type InternalHandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch exte
|
|
|
400
400
|
/**
|
|
401
401
|
* @internal A revalidate() predicate that read the source page
|
|
402
402
|
* (`currentParams`, `currentUrl`, `fromRouteName`) kept a held segment, so
|
|
403
|
-
* this match's diff is right for
|
|
404
|
-
*
|
|
405
|
-
*
|
|
403
|
+
* this match's diff is right only for pages with equal values of what the
|
|
404
|
+
* kept decisions read (#1331). Set by evaluateRevalidation
|
|
405
|
+
* (router/revalidation.ts) and the stored-entry keep
|
|
406
|
+
* (match-middleware/cache-lookup.ts); match-result.ts turns it into
|
|
407
|
+
* `sourceHeld`, which scopes a prefetch of the response to that group.
|
|
406
408
|
*/
|
|
407
|
-
_sourceHeld?:
|
|
409
|
+
_sourceHeld?: import("../router/source-reads.js").SourceHeld;
|
|
408
410
|
/**
|
|
409
411
|
* @internal DSL loaders registered and not started, by `$$id`: a prefetch
|
|
410
412
|
* deferred them, or a pass that skips loaders (`skipLoaders`) did not run
|
|
@@ -549,7 +551,7 @@ export type IsActionFn = (...actions: ActionRef[]) => boolean;
|
|
|
549
551
|
export type ShouldRevalidateFn<TParams = GenericParams, TEnv = any> = (args: {
|
|
550
552
|
/** Route params from the page being navigated away from. */
|
|
551
553
|
currentParams: TParams;
|
|
552
|
-
/** Full URL of the page being navigated away from. */
|
|
554
|
+
/** Full URL of the page being navigated away from, without its #fragment. */
|
|
553
555
|
currentUrl: URL;
|
|
554
556
|
/** Route params for the navigation target. */
|
|
555
557
|
nextParams: TParams;
|