@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
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
* A bake-lane loader (lane rule: see resolveLoaderData, loader-cache.ts)
|
|
6
6
|
* executes during shell capture; its settled container bakes into the prelude
|
|
7
|
-
* while nested promises postpone as holes. To keep a HIT's fresh
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* while nested promises postpone as holes. To keep a HIT's fresh payload in
|
|
8
|
+
* agreement with that frozen prelude, the capture pins the container in the
|
|
9
|
+
* shell snapshot. Everything outside a hole is pinned; the one exception is a
|
|
10
|
+
* server-component element holding a promise, which is a hole as a whole (see
|
|
11
|
+
* elideLoaderContainer):
|
|
10
12
|
*
|
|
11
13
|
* - elide: deep-walk the settled container; a PENDING nested promise is a
|
|
12
14
|
* hole, replaced by {@link LOADER_HOLE_KEY} — and since
|
|
@@ -27,7 +29,8 @@
|
|
|
27
29
|
* storefront PDP regression) — and fresh-only paths pass through
|
|
28
30
|
* (they cannot contradict prelude bytes that never rendered them).
|
|
29
31
|
*/
|
|
30
|
-
|
|
32
|
+
import { maskNestedContainerThenables } from "./mask-nested.js";
|
|
33
|
+
export { maskNestedContainerThenables };
|
|
31
34
|
/**
|
|
32
35
|
* Marker object standing in for a pending nested promise in a recorded loader
|
|
33
36
|
* container. Shape-checked (not identity-checked) because the record round-trips
|
|
@@ -70,21 +73,29 @@ export type ElideResult = {
|
|
|
70
73
|
* promises pin their value behind a settled marker (they baked, but consumers
|
|
71
74
|
* hold a promise-shaped key); pending ones become hole markers; a REJECTED
|
|
72
75
|
* nested promise poisons the record (error UI must never bake into a shared
|
|
73
|
-
* shell) — the caller refuses the capture.
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
76
|
+
* shell) — the caller refuses the capture. Plain data objects, arrays and the
|
|
77
|
+
* props of host, Suspense/Fragment and client-reference elements are
|
|
78
|
+
* traversed copy-on-write (an element through cloneElementWithProps); a shared
|
|
79
|
+
* subtree or a cycle resolves to the same elided result. An element Flight
|
|
80
|
+
* renders by calling user code (see rendersOnServer) is a leaf, or ONE hole
|
|
81
|
+
* marker when a thenable sits anywhere in its props: the pin encode calls that
|
|
82
|
+
* component, so a marker inside its props would reach it. Anything else
|
|
83
|
+
* (Date, Map, class instance, lazy node) is a pinned leaf.
|
|
77
84
|
*
|
|
78
85
|
* The ROOT container promise-chain unwraps with NO marker: loader-cache
|
|
79
86
|
* overlays against the AWAITED fresh container, so the recorded root must be
|
|
80
87
|
* the unwrapped structure. Everything below it goes through elideNested.
|
|
81
88
|
*/
|
|
82
|
-
export declare function elideLoaderContainer(value: unknown, seen?:
|
|
89
|
+
export declare function elideLoaderContainer(value: unknown, seen?: Map<object, ElideResult>): Promise<ElideResult>;
|
|
83
90
|
/**
|
|
84
91
|
* Overlay a recorded (elided) container onto the fresh run's container for a
|
|
85
92
|
* shell HIT. Recorded wins per path; hole markers take the fresh value (the
|
|
86
93
|
* live nested promise); fresh-only object keys pass through. Where the shapes
|
|
87
94
|
* disagree structurally, recorded wins wholesale — it is what the prelude
|
|
88
|
-
* froze, and parity beats freshness inside the shell.
|
|
95
|
+
* froze, and parity beats freshness inside the shell. Copy-on-write: a
|
|
96
|
+
* recorded subtree without markers comes back by identity. A recorded element
|
|
97
|
+
* is rebuilt through cloneElementWithProps only at its marker paths, so its
|
|
98
|
+
* other props stay pinned; props the fresh element adds do not pass through
|
|
99
|
+
* (the prelude rendered the element without them).
|
|
89
100
|
*/
|
|
90
101
|
export declare function overlayLoaderContainer(fresh: unknown, recorded: unknown): unknown;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Capture-side nested
|
|
3
|
-
* shape is the liveness declaration" for
|
|
4
|
-
* (loader-cache.ts) and
|
|
5
|
-
* push wrap in rsc/shell-capture.ts).
|
|
2
|
+
* Capture-side nested thenables. Masking is the mechanism behind "nested-
|
|
3
|
+
* promise shape is the liveness declaration" for the loader lane: bake-lane
|
|
4
|
+
* loader containers (loader-cache.ts) and DSL loader handle pushes (the
|
|
5
|
+
* capture handle store's push wrap in rsc/shell-capture.ts). Handler handle
|
|
6
|
+
* pushes are handler output and bake: the capture waits for their nested
|
|
7
|
+
* promises instead (settleNestedThenables).
|
|
6
8
|
*
|
|
7
9
|
* Deliberately a LEAF module: loader-mask (the other natural home) imports
|
|
8
10
|
* request-context, so any funnel importing the mask from there inherits that
|
|
9
|
-
* graph. This module imports only is-thenable.
|
|
11
|
+
* graph. This module imports only is-thenable and react.
|
|
10
12
|
*/
|
|
13
|
+
import { type ReactElement } from "react";
|
|
11
14
|
/**
|
|
12
15
|
* A promise that never settles — the masked stand-in for a per-request value
|
|
13
16
|
* during shell capture. The consuming Suspense subtree suspends forever, so
|
|
@@ -18,11 +21,55 @@
|
|
|
18
21
|
*/
|
|
19
22
|
export declare function createMaskedLoaderPromise<T = unknown>(): Promise<T>;
|
|
20
23
|
/**
|
|
21
|
-
*
|
|
24
|
+
* A data object the container walks descend into: prototype Object.prototype
|
|
25
|
+
* or null, and no symbol `$$typeof`. React elements, lazy nodes and references
|
|
26
|
+
* are plain objects too, told apart by that symbol (Flight switches on it).
|
|
27
|
+
* They are React's, not data: an element's dev-only fields (`_debugStack`,
|
|
28
|
+
* `_debugTask`, `_debugInfo`, `ref`) are non-enumerable, so a key-by-key copy
|
|
29
|
+
* drops them and dev Flight refuses it ("Attempted to render <x> without
|
|
30
|
+
* development properties", issue #942).
|
|
31
|
+
*/
|
|
32
|
+
export declare function isPlainDataObject(value: unknown): value is Record<string, unknown>;
|
|
33
|
+
/**
|
|
34
|
+
* cloneElement with only the given props replaced, keeping React's dev key
|
|
35
|
+
* validation. React 19 dev cloneElement resets `_store.validated`, so a
|
|
36
|
+
* keyless child of a static children list (validated at creation) would warn
|
|
37
|
+
* "Each child in a list should have a unique key" once cloned. Pass only the
|
|
38
|
+
* props that change: cloneElement re-parents the element (owner) when its
|
|
39
|
+
* config carries `ref`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function cloneElementWithProps(element: ReactElement, props: Record<string, unknown>): ReactElement;
|
|
42
|
+
/**
|
|
43
|
+
* Resolves once `value` and every thenable reachable from it through plain
|
|
44
|
+
* data objects, arrays and React element props has settled, following what
|
|
45
|
+
* each thenable resolves to. A rejection counts as settled: the consumer that
|
|
46
|
+
* encodes the value reports it. Other objects are leaves (the same walk as
|
|
47
|
+
* maskNestedContainerThenables), so a promise inside a Map is not waited for.
|
|
48
|
+
* An element is walked through its props only (`<X data={promise} />` in a
|
|
49
|
+
* handle value is handler output too), never its owner or type, and is never
|
|
50
|
+
* rebuilt: nothing is copied, only awaited.
|
|
51
|
+
*
|
|
52
|
+
* The capture waits on this for HANDLER handle pushes (shell-capture.ts):
|
|
53
|
+
* handler output is baked, so its promises must settle before the doc record
|
|
54
|
+
* encodes the handles every HIT replays.
|
|
55
|
+
*/
|
|
56
|
+
export declare function settleNestedThenables(value: unknown): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Optional single-walk report for maskNestedContainerThenables: `thenable`
|
|
59
|
+
* flips true when the walk masked at least one thenable. The capture push
|
|
60
|
+
* funnel reads it for a loader push: a container with a masked nested
|
|
61
|
+
* promise keeps its loader tag, so the handle encode never waits on a mask.
|
|
62
|
+
* Same single-pass shape as elideLoaderContainer's `hasHole`.
|
|
63
|
+
*/
|
|
64
|
+
export interface MaskReport {
|
|
65
|
+
thenable: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Copy a container with every NESTED thenable replaced by a masked
|
|
22
69
|
* (never-resolving) promise. Applied during shell capture to (a) bake-lane
|
|
23
|
-
* loader containers (loader-cache.ts) and (b)
|
|
24
|
-
* (shell-capture.ts capture store push wrap) — the
|
|
25
|
-
*
|
|
70
|
+
* loader containers (loader-cache.ts) and (b) DSL loader handle pushes
|
|
71
|
+
* (shell-capture.ts capture store push wrap) — the rango-owned funnels where
|
|
72
|
+
* a loader declares per-request data by promise SHAPE.
|
|
26
73
|
*
|
|
27
74
|
* Why: a nested promise that happened to SETTLE before the capture's quiet
|
|
28
75
|
* window closed used to bake its value into the SHARED shell (and, for
|
|
@@ -34,20 +81,14 @@ export declare function createMaskedLoaderPromise<T = unknown>(): Promise<T>;
|
|
|
34
81
|
* Masking makes the consuming subtree postpone as a hole no matter when the
|
|
35
82
|
* promise settles: liveness by declaration, not by racing the window.
|
|
36
83
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* declares per-request data, and a shell entry whose HANDLER layer made such
|
|
47
|
-
* a declaration cannot be served handler-free (the hole would never fill).
|
|
48
|
-
* Same single-pass shape as elideLoaderContainer's `hasHole`.
|
|
84
|
+
* Plain data objects, arrays and React element props are traversed: JSX
|
|
85
|
+
* carrying a promise (`<Reviews data={fetchReviews()} />`) declares
|
|
86
|
+
* per-request data the same way a plain container does. Other values, lazy
|
|
87
|
+
* nodes included, are leaves. Copy-on-write: a subtree without a thenable
|
|
88
|
+
* comes back by identity, so an element is rebuilt only when its props hold
|
|
89
|
+
* one, and then through cloneElementWithProps, which keeps its type, key,
|
|
90
|
+
* owner, dev fields and key validation. The INPUT IS NEVER MUTATED — handler-side loader consumption (the
|
|
91
|
+
* consumption-lane rule, semantic-matrix PPR3) shares the raw container and
|
|
92
|
+
* must keep real values. Cycles are preserved as cycles in the copy.
|
|
49
93
|
*/
|
|
50
|
-
export interface MaskReport {
|
|
51
|
-
thenable: boolean;
|
|
52
|
-
}
|
|
53
94
|
export declare function maskNestedContainerThenables(value: unknown, seen?: Map<object, unknown>, report?: MaskReport): unknown;
|
|
@@ -12,6 +12,7 @@ import type { RscPayload } from "./types.js";
|
|
|
12
12
|
import type { HandlerContext } from "./handler-context.js";
|
|
13
13
|
import type { RequestContext } from "../server/request-context.js";
|
|
14
14
|
import type { HandleStore } from "../server/handle-store.js";
|
|
15
|
+
import type { Theme } from "../theme/types.js";
|
|
15
16
|
/**
|
|
16
17
|
* Build the metadata payload for a full (non-partial) document render.
|
|
17
18
|
*
|
|
@@ -20,3 +21,16 @@ import type { HandleStore } from "../server/handle-store.js";
|
|
|
20
21
|
* derived-context store.
|
|
21
22
|
*/
|
|
22
23
|
export declare function buildFullPayload(m: MatchResult, ctx: HandlerContext<any>, url: URL, reqCtx: RequestContext<any>, handleStore: HandleStore): RscPayload;
|
|
24
|
+
/**
|
|
25
|
+
* The theme a document payload carries as `metadata.initialTheme` (the
|
|
26
|
+
* ThemeProvider's first state), read with no identity guard: it is the
|
|
27
|
+
* router's own read, not handler code (#971).
|
|
28
|
+
*
|
|
29
|
+
* A shell capture carries the no-cookie default, not the capturing visitor's
|
|
30
|
+
* theme. The shell and its ShellCacheEntry.initialTheme serve every visitor,
|
|
31
|
+
* and ThemeProvider re-syncs only from an explicitly stored theme, so a
|
|
32
|
+
* visitor with none kept the capturer's `useTheme().theme` for the page's
|
|
33
|
+
* lifetime. A visitor with a stored theme gets it pre-paint (theme script)
|
|
34
|
+
* and in the provider after mount.
|
|
35
|
+
*/
|
|
36
|
+
export declare function payloadInitialTheme(reqCtx: Pick<RequestContext<any>, "_themeConfig" | "_shellCaptureRun" | "_readTheme">): Theme | undefined;
|
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { CreateRSCHandlerOptions } from "./types.js";
|
|
8
8
|
import type { RouterRequestInput } from "../router/router-interfaces.js";
|
|
9
|
-
export declare function createRSCHandler<TEnv = unknown, TRoutes extends Record<string, string> = Record<string, string>>(options: CreateRSCHandlerOptions<TEnv, TRoutes>): (
|
|
9
|
+
export declare function createRSCHandler<TEnv = unknown, TRoutes extends Record<string, string> = Record<string, string>>(options: CreateRSCHandlerOptions<TEnv, TRoutes>): (incomingRequest: Request, input?: RouterRequestInput<TEnv>) => Promise<Response>;
|
|
@@ -73,6 +73,14 @@ export interface DevShellLookup {
|
|
|
73
73
|
/** Resolved `ppr.captureTimeout` (ms) — the endpoint's capture honors it. */
|
|
74
74
|
captureTimeout?: number;
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Whether a build-time shell exists for `pathname` (the caller checks the
|
|
78
|
+
* request is search-less, like the entries lookupBuildShell serves): a
|
|
79
|
+
* manifest entry for the path in production, a Prerender route in dev. No
|
|
80
|
+
* asset load and no dev capture: it only decides whether a request that
|
|
81
|
+
* cannot read the build shell deserves a warning.
|
|
82
|
+
*/
|
|
83
|
+
export declare function hasBuildShell(pathname: string, dev?: DevShellLookup): Promise<boolean>;
|
|
76
84
|
/**
|
|
77
85
|
* Look up the baked shell entry for a request, applying every serve gate:
|
|
78
86
|
* search-less requests only (the build captured the bare pathname; a
|
|
@@ -14,17 +14,64 @@
|
|
|
14
14
|
* costs latency-to-HIT only — never a served response — and 5s spuriously
|
|
15
15
|
* refused legitimately-slow deferred shell material (a real storefront's meta
|
|
16
16
|
* chains settle at ~7s). Ceiling math: workerd's waitUntil lifetime is ~30s
|
|
17
|
-
* past response completion
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* past response completion. An attempt that consumed the whole budget is not
|
|
18
|
+
* retried in place (shell-capture.ts noShellCauses), so the envelope is one
|
|
19
|
+
* budget plus store I/O; the retry follows only an attempt that ended early
|
|
20
|
+
* (a cold-module abort). Past the ceiling the platform kill degrades to the
|
|
21
|
+
* existing best-effort contract (the key stays MISS; a later request
|
|
22
|
+
* re-captures).
|
|
23
23
|
* Node/dev and build-time captures have no waitUntil ceiling. The per-route
|
|
24
24
|
* `ppr.captureTimeout` knob remains for tightening below the default. See
|
|
25
25
|
* docs/design/ppr-shell-resume.md (Cost model).
|
|
26
26
|
*/
|
|
27
27
|
export declare const SHELL_CAPTURE_MAX_WAIT_MS = 15000;
|
|
28
|
+
/**
|
|
29
|
+
* The shell key for one request partition (cache-scope.ts
|
|
30
|
+
* resolveShellPartition): the route cache() `key()` or store keyGenerator
|
|
31
|
+
* result that partitions the route's record partitions its shell too, so
|
|
32
|
+
* each partition captures and serves its own. Here, in the leaf module, so
|
|
33
|
+
* the testing helper `shellCacheKey` (testing/shell-status.ts) calls it
|
|
34
|
+
* without pulling shell-serve.ts.
|
|
35
|
+
*
|
|
36
|
+
* The partition is URI-encoded: it is request-derived, and raw it could end
|
|
37
|
+
* in a suffix another key is built with (navigationShellKey), so a partition
|
|
38
|
+
* `gold:navigation` named gold's navigation entry and its document shell
|
|
39
|
+
* replayed to gold's navigations. Encoded, it holds no `:` or `|`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function partitionShellKey(key: string, partition: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* The navigation-only entry beside a document shell key (a cold partial
|
|
44
|
+
* request's capture, rsc-rendering.ts). A suffix after the (encoded)
|
|
45
|
+
* partition: partitionShellKey keeps the two unambiguous.
|
|
46
|
+
*/
|
|
47
|
+
export declare function navigationShellKey(key: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Fixed number of macrotask hops between `quiesce` resolving and the abort. These
|
|
50
|
+
* give React's fizz worker turns to flush the settled shell into the prelude and
|
|
51
|
+
* mark still-pending boundaries as POSTPONED (rather than errored) before
|
|
52
|
+
* controller.abort() lands. Not a wall-clock wait. Taken by captureShellHTML
|
|
53
|
+
* (ssr/index.tsx) and by the testing SSR stub (testing/serve-shell-request.ts),
|
|
54
|
+
* so the stub freezes the same Flight the real capture would.
|
|
55
|
+
*
|
|
56
|
+
* Why 16 and not the original 2: under the REPLAY-ONLY capture model
|
|
57
|
+
* (docs/design/ppr-shell-resume.md), the capture Flight render serializes ring-3
|
|
58
|
+
* cached segments that are ALREADY serialized, so it emits the whole shell payload
|
|
59
|
+
* in the first tick and the gate declares quiesce almost immediately (~a few ms).
|
|
60
|
+
* On the old fresh-execution path the Flight dribbled out as handlers ran, so
|
|
61
|
+
* Flight-quiet effectively meant "the shell has rendered" and 2 hops sufficed.
|
|
62
|
+
*
|
|
63
|
+
* Hops alone are NOT render-readiness: fizz cannot emit even <html> until the
|
|
64
|
+
* payload root settles, nor complete a shell whose client components are
|
|
65
|
+
* still loading — real module-runner I/O in dev (100ms+ cold), which no fixed
|
|
66
|
+
* count of near-zero-cost task hops can buy. captureShellHTML therefore
|
|
67
|
+
* awaits the payload-settled signal (SsrRootOptions.onPayloadSettled) and
|
|
68
|
+
* then the client-reference loads in flight in the isolate (captureClientLoads),
|
|
69
|
+
* deadline-bounded, between quiesce and these hops; the hops then only flush
|
|
70
|
+
* the settled tree and mark pending boundaries POSTPONED. Still task-based
|
|
71
|
+
* (masked loaders never emit, so more hops never lets a hole settle). Bounded
|
|
72
|
+
* by maxWaitMs end to end.
|
|
73
|
+
*/
|
|
74
|
+
export declare const POST_QUIESCE_TASK_HOPS = 16;
|
|
28
75
|
/**
|
|
29
76
|
* Hard cap on one capture TASK — runShellCapture end to end (both attempts +
|
|
30
77
|
* the in-place retry delay). SHELL_CAPTURE_MAX_WAIT_MS arms only inside
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
* the triggering request, and the derived context inherits its post-middleware
|
|
16
16
|
* state (variables, cache store). Guarding is serve-time.
|
|
17
17
|
*/
|
|
18
|
+
import { type MatchResult } from "../types.js";
|
|
18
19
|
import { SHELL_CAPTURE_MAX_WAIT_MS } from "./shell-capture-constants.js";
|
|
19
|
-
import { type RequestContext } from "../server/request-context.js";
|
|
20
|
+
import { type RequestContext, type RouteRecordWindow } from "../server/request-context.js";
|
|
20
21
|
import { type HandleStore } from "../server/handle-store.js";
|
|
21
22
|
import type { SegmentCacheStore } from "../cache/types.js";
|
|
22
23
|
import type { HandlerContext } from "./handler-context.js";
|
|
@@ -91,6 +92,13 @@ declare function isCaptureBackedOff(key: string): boolean;
|
|
|
91
92
|
declare function markCaptureBackoff(key: string): void;
|
|
92
93
|
/** Clear any backoff for a key that just captured successfully. */
|
|
93
94
|
declare function clearCaptureBackoff(key: string): void;
|
|
95
|
+
/**
|
|
96
|
+
* Default limit (bytes) on a whole stored shell entry — prelude, postponed
|
|
97
|
+
* state, and snapshot — when the store declares none
|
|
98
|
+
* (SegmentCacheStore.maxShellEntryBytes). Cloudflare KV's 25 MiB value limit:
|
|
99
|
+
* the tightest common store tier a shell entry lands in.
|
|
100
|
+
*/
|
|
101
|
+
export declare const DEFAULT_SHELL_ENTRY_MAX_BYTES: number;
|
|
94
102
|
/**
|
|
95
103
|
* Default cap (serialized UTF-8 bytes) on the capture data snapshot riding
|
|
96
104
|
* inside a shell entry, when the route's `ppr` option does not set
|
|
@@ -100,8 +108,9 @@ declare function clearCaptureBackoff(key: string): void;
|
|
|
100
108
|
* while still fitting any sane pinned-ring payload. Applied ONLY in
|
|
101
109
|
* captureAndStoreShell (the single defaulting site — resolvePprConfig passes
|
|
102
110
|
* the option through undefaulted), so every producer and direct caller gets
|
|
103
|
-
* the same policy.
|
|
104
|
-
*
|
|
111
|
+
* the same policy. The doc record is exempt; over the cap the other records
|
|
112
|
+
* are dropped (shell still stored and replayed; the pins' loaders read the
|
|
113
|
+
* live store — see PartialPrerenderProps.maxSnapshotBytes).
|
|
105
114
|
*/
|
|
106
115
|
export declare const DEFAULT_PPR_MAX_SNAPSHOT_BYTES: number;
|
|
107
116
|
/**
|
|
@@ -116,10 +125,13 @@ export interface ShellCaptureDebugEvent {
|
|
|
116
125
|
key: string;
|
|
117
126
|
/**
|
|
118
127
|
* What happened:
|
|
119
|
-
* - stored / redirect / no-shell / refused: one capture
|
|
120
|
-
* (see CaptureAttemptOutcome for the semantics of each)
|
|
128
|
+
* - stored / redirect / no-shell / refused / expired: one capture
|
|
129
|
+
* ATTEMPT's outcome (see CaptureAttemptOutcome for the semantics of each)
|
|
121
130
|
* - error: the capture task failed with a genuine error (also routed through
|
|
122
131
|
* reportCacheError; the key is backed off)
|
|
132
|
+
* - skip-stored: a capture for the key stored after the scheduling request
|
|
133
|
+
* read the store and missed (ShellCaptureDescriptor.storedSeqAtRead), so
|
|
134
|
+
* the shell that request missed exists now
|
|
123
135
|
* - skip-in-flight: scheduleShellCapture found a capture already running for
|
|
124
136
|
* the key (stampede guard) and scheduled nothing
|
|
125
137
|
* - skip-backoff: the key is inside its refused-capture backoff window and
|
|
@@ -137,7 +149,7 @@ export interface ShellCaptureDebugEvent {
|
|
|
137
149
|
* - backoff: the key entered (or escalated) backoff after a terminal
|
|
138
150
|
* no-shell — carries the new backoff state
|
|
139
151
|
*/
|
|
140
|
-
outcome: "stored" | "redirect" | "no-shell" | "refused" | "error" | "skip-in-flight" | "skip-backoff" | "skip-capacity" | "skip-inert-store" | "skip-queue-timeout" | "backoff";
|
|
152
|
+
outcome: "stored" | "redirect" | "no-shell" | "refused" | "expired" | "error" | "skip-in-flight" | "skip-stored" | "skip-backoff" | "skip-capacity" | "skip-inert-store" | "skip-queue-timeout" | "backoff";
|
|
141
153
|
/** Attempt number (1 = first, 2 = in-place retry). Absent on skips. */
|
|
142
154
|
attempt?: number;
|
|
143
155
|
/** Wall-clock ms of the whole attempt (barrier + render + drain + put). */
|
|
@@ -184,6 +196,14 @@ export interface ShellCaptureDebugEvent {
|
|
|
184
196
|
* it attributable. Absent when nothing held the gate.
|
|
185
197
|
*/
|
|
186
198
|
bakeWaitMs?: number;
|
|
199
|
+
/**
|
|
200
|
+
* How long the capture waited for its doc record: the handler layer's
|
|
201
|
+
* promises, async server components and handle pushes settling, then the
|
|
202
|
+
* record's encode (settleCaptureRecord). Part of the one capture deadline.
|
|
203
|
+
*/
|
|
204
|
+
recordSettleMs?: number;
|
|
205
|
+
/** Bytes of the stored entry: prelude, postponed state, and snapshot. */
|
|
206
|
+
entryBytes?: number;
|
|
187
207
|
}
|
|
188
208
|
/**
|
|
189
209
|
* Debug sink for the capture pipeline, mirroring {@link CFCacheDebug}: `true`
|
|
@@ -330,7 +350,17 @@ export interface ShellCaptureDescriptor {
|
|
|
330
350
|
debugSink?: (event: ShellCaptureDebugEvent) => void;
|
|
331
351
|
/** Store the snapshot for navigation replay, never the captured HTML prelude. */
|
|
332
352
|
navigationOnly?: true;
|
|
353
|
+
/**
|
|
354
|
+
* lastStoredCaptureSeq(key) when a document request read the store and
|
|
355
|
+
* missed. The request schedules its capture only after rendering the MISS,
|
|
356
|
+
* so a capture another request started can store in between; a later
|
|
357
|
+
* sequence number then skips the redundant one (`skip-stored`), which would
|
|
358
|
+
* re-render the page and drop the isolate's memo of the shell just stored.
|
|
359
|
+
*/
|
|
360
|
+
storedSeqAtRead?: number;
|
|
333
361
|
}
|
|
362
|
+
/** The latest stored capture's sequence number for `key` in this isolate, or 0. */
|
|
363
|
+
export declare function lastStoredCaptureSeq(key: string): number;
|
|
334
364
|
/**
|
|
335
365
|
* Schedule the background shell capture for a served document. Stampede-guarded:
|
|
336
366
|
* one capture per key per isolate. Runs via runBackground (waitUntil on workerd,
|
|
@@ -343,6 +373,26 @@ export interface ShellCaptureDescriptor {
|
|
|
343
373
|
* capture enters the guarded background queue, never on response latency.
|
|
344
374
|
*/
|
|
345
375
|
export declare function scheduleShellCapture(ctx: HandlerContext<any>, request: Request, env: any, url: URL, reqCtx: RequestContext<any>, ssrModule: SSRModule | ((request: Request, url: URL) => Promise<SSRModule | null>), descriptor: ShellCaptureDescriptor): void;
|
|
376
|
+
/**
|
|
377
|
+
* The ttl/swr a shell is stored with: the route's ppr policy, capped by the
|
|
378
|
+
* route cache() records the capture replayed or wrote
|
|
379
|
+
* (RequestContext._routeRecordWindow). A HIT replays the capture's handler
|
|
380
|
+
* output, while a client navigation reads the route's cache() tier first, so
|
|
381
|
+
* a shell that outlived its record would show a document request other
|
|
382
|
+
* handler output than a navigation of the same URL. Freshness and total
|
|
383
|
+
* lifetime are capped separately, like the record's own SWR: the shell is
|
|
384
|
+
* fresh while the record is (at most `ttl`), and servable while the record
|
|
385
|
+
* is (at most `ttl + swr`), so a record already inside its swr window yields
|
|
386
|
+
* a shell that is stale from the start and serves while it recaptures.
|
|
387
|
+
*
|
|
388
|
+
* Whole seconds, rounded up (the SegmentCacheStore.putShell contract), so the
|
|
389
|
+
* shell may outlast the record by under a second. Null when the record's
|
|
390
|
+
* total lifetime has run out: the shell would be dead on arrival.
|
|
391
|
+
*/
|
|
392
|
+
export declare function capShellWindow(ttl: number, swr: number, record: Pick<RouteRecordWindow, "freshUntil" | "staleUntil">, now: number): {
|
|
393
|
+
ttl: number;
|
|
394
|
+
swr: number;
|
|
395
|
+
} | null;
|
|
346
396
|
/**
|
|
347
397
|
* The outcome of one capture attempt.
|
|
348
398
|
* - `stored`: a usable shell was captured (and a putShell was attempted; a store
|
|
@@ -350,10 +400,16 @@ export declare function scheduleShellCapture(ctx: HandlerContext<any>, request:
|
|
|
350
400
|
* the capture itself worked).
|
|
351
401
|
* - `redirect`: the matched route redirects, so there is no shell to capture.
|
|
352
402
|
* - `no-shell`: captureShellHTML returned null because the prelude was unusable
|
|
353
|
-
* or its private capture abort landed before the shell completed
|
|
354
|
-
*
|
|
403
|
+
* or its private capture abort landed before the shell completed, or the
|
|
404
|
+
* capture ran out of ppr.captureTimeout. Retried once in place, when no
|
|
405
|
+
* deadline ran out (noShellCauses).
|
|
406
|
+
* - `expired`: the route cache() record the capture read (not wrote) ran out
|
|
407
|
+
* before the store (capShellWindow null): it was near its end (its age,
|
|
408
|
+
* queue wait). Retried once in place, where a fresh match reads or renders
|
|
409
|
+
* a newer record; a second `expired`, or no hard-cap budget left for the
|
|
410
|
+
* retry, is terminal `no-shell` (backoff, warned once per route).
|
|
355
411
|
*/
|
|
356
|
-
type CaptureAttemptOutcome = "stored" | "redirect" | "no-shell" | "refused";
|
|
412
|
+
type CaptureAttemptOutcome = "stored" | "redirect" | "no-shell" | "refused" | "expired";
|
|
357
413
|
/**
|
|
358
414
|
* Per-attempt observability fields, filled along the capture path (barrier in
|
|
359
415
|
* attemptCapture, the rest in captureAndStoreShell) and folded into the
|
|
@@ -361,7 +417,7 @@ type CaptureAttemptOutcome = "stored" | "redirect" | "no-shell" | "refused";
|
|
|
361
417
|
* bag, not a return value: captureAndStoreShell's outcome type stays a string
|
|
362
418
|
* union its existing callers (producer B, tests) consume unchanged.
|
|
363
419
|
*/
|
|
364
|
-
type CaptureAttemptStats = Pick<ShellCaptureDebugEvent, "barrierWaitMs" | "writeSettleMs" | "preludeBytes" | "snapshotBytes" | "snapshotSkipped" | "untaggedBake" | "storeWrite" | "bakeWaitMs">;
|
|
420
|
+
type CaptureAttemptStats = Pick<ShellCaptureDebugEvent, "barrierWaitMs" | "writeSettleMs" | "preludeBytes" | "snapshotBytes" | "snapshotSkipped" | "untaggedBake" | "storeWrite" | "bakeWaitMs" | "recordSettleMs" | "entryBytes">;
|
|
365
421
|
/**
|
|
366
422
|
* Run the shell capture with a single in-place retry, then store the result.
|
|
367
423
|
*
|
|
@@ -373,26 +429,93 @@ type CaptureAttemptStats = Pick<ShellCaptureDebugEvent, "barrierWaitMs" | "write
|
|
|
373
429
|
* when we froze the shell; the attempt itself warmed the module graph, so a second
|
|
374
430
|
* attempt a short beat later usually completes the shell in the SAME background
|
|
375
431
|
* task. That kills the old multi-request warmup where the caller had to re-issue
|
|
376
|
-
* several HTTP requests before a capture stuck. We retry ONLY on `no-shell
|
|
377
|
-
*
|
|
432
|
+
* several HTTP requests before a capture stuck. We retry ONLY on `no-shell`
|
|
433
|
+
* that did not run out of ppr.captureTimeout (noShellCauses); a genuine render
|
|
434
|
+
* error is NOT retried — it propagates to scheduleShellCapture's
|
|
378
435
|
* reportCacheError. See docs/design/ppr-shell-resume.md.
|
|
379
436
|
*
|
|
380
437
|
* `retryDelayMs` is a parameter (defaulting to the module const) so unit tests can
|
|
381
438
|
* drive the retry without a real 400ms wall-clock wait.
|
|
382
439
|
*/
|
|
383
440
|
declare function runShellCapture(ctx: HandlerContext<any>, request: Request, env: any, url: URL, reqCtx: RequestContext<any>, ssrModule: SSRModule, descriptor: ShellCaptureDescriptor, retryDelayMs?: number): Promise<CaptureAttemptOutcome>;
|
|
441
|
+
/**
|
|
442
|
+
* What the record-first step produced ({@link settleCaptureRecord}):
|
|
443
|
+
* - `record`: the doc record settled; `match` carries its fragments in place
|
|
444
|
+
* of the handler layer's elements, so the capture's Flight payload is the
|
|
445
|
+
* bytes every HIT replays.
|
|
446
|
+
* - `prerender`: the prerender store supplied the handler layer (a
|
|
447
|
+
* Prerender route); its HIT tail replays the same build-time segments, so
|
|
448
|
+
* the capture renders the match as is.
|
|
449
|
+
* - `timeout`: handler output did not settle within the capture budget.
|
|
450
|
+
* - `refused`: a deterministic refusal (a capture guard tripped, the route
|
|
451
|
+
* opted out, or the handler output failed); the warning already fired.
|
|
452
|
+
*/
|
|
453
|
+
export type CaptureRecordOutcome = {
|
|
454
|
+
kind: "record" | "prerender";
|
|
455
|
+
match: MatchResult;
|
|
456
|
+
} | {
|
|
457
|
+
kind: "timeout";
|
|
458
|
+
reason: string;
|
|
459
|
+
} | {
|
|
460
|
+
kind: "refused";
|
|
461
|
+
};
|
|
462
|
+
/**
|
|
463
|
+
* Record-first capture: the doc record is both the settle signal and the
|
|
464
|
+
* parity source.
|
|
465
|
+
*
|
|
466
|
+
* Writing the record Flight-serializes every non-loader segment
|
|
467
|
+
* (serializeSegments), which runs each handler's async server components and
|
|
468
|
+
* waits for every promise the handler output carries — the capture's "is the
|
|
469
|
+
* handler layer done" signal. Live-lane loader data is not in the record
|
|
470
|
+
* (loader segments are excluded and their masked promises never settle), so
|
|
471
|
+
* the wait never depends on a hole.
|
|
472
|
+
*
|
|
473
|
+
* The capture then renders its OWN Flight payload from the record's
|
|
474
|
+
* fragments (fragmentSegments, the function a HIT tail uses) instead of from
|
|
475
|
+
* the elements the match produced. Server components therefore render once
|
|
476
|
+
* per capture, inside the record's encode: before this, the capture's Flight
|
|
477
|
+
* render and the record's encode each ran them, so an uncached async
|
|
478
|
+
* component showed one value in the prelude and another on every HIT.
|
|
479
|
+
*
|
|
480
|
+
* Order, all bounded by the one capture deadline (`ppr.captureTimeout`):
|
|
481
|
+
* top-level handle promises, the handler pushes' nested promises, and the
|
|
482
|
+
* bake-lane loader containers settle first (the record encodes the handles
|
|
483
|
+
* a HIT restores, and its handle encode has its own 5s timeout that must
|
|
484
|
+
* never be the one that fires); then the capture's onResponse callbacks fire
|
|
485
|
+
* with a synthetic 200 (the implicit doc scope's cacheRoute, an explicit
|
|
486
|
+
* scope's own write), and the deferred writes settle.
|
|
487
|
+
*/
|
|
488
|
+
export declare function settleCaptureRecord(match: MatchResult, derivation: CaptureContextDerivation, capture: Pick<ShellCaptureDescriptor, "key">, deadline: number): Promise<CaptureRecordOutcome>;
|
|
489
|
+
/**
|
|
490
|
+
* Race `work` against an absolute deadline, keeping its value. The losing
|
|
491
|
+
* work is not cancelled (a router match has no abort signal); callers that
|
|
492
|
+
* lose do not start it again (runShellCapture skips its in-place retry).
|
|
493
|
+
*/
|
|
494
|
+
export declare function withinDeadline<T>(work: Promise<T>, deadline: number): Promise<{
|
|
495
|
+
done: true;
|
|
496
|
+
value: T;
|
|
497
|
+
} | {
|
|
498
|
+
done: false;
|
|
499
|
+
}>;
|
|
384
500
|
/**
|
|
385
501
|
* The derived capture context and its fresh (mask-funneled) handle store,
|
|
386
502
|
* shared by BOTH shell producers: the runtime background capture
|
|
387
503
|
* (attemptCapture, producer A) and the build-time prerender shell capture
|
|
388
504
|
* (prerender/build-shell-capture.ts, producer B — issue #699). One
|
|
389
|
-
* implementation so the capture semantics — the
|
|
390
|
-
*
|
|
391
|
-
* scope — cannot drift between producers.
|
|
505
|
+
* implementation so the capture semantics — the handle push funnel (handler
|
|
506
|
+
* pushes settled, loader pushes masked), the capture guards, snapshot
|
|
507
|
+
* recording, the implicit doc-cache scope — cannot drift between producers.
|
|
392
508
|
*/
|
|
393
509
|
export interface CaptureContextDerivation {
|
|
394
510
|
derivedCtx: RequestContext;
|
|
395
511
|
freshHandleStore: HandleStore;
|
|
512
|
+
/**
|
|
513
|
+
* Resolves once every handler handle push has settled at any depth of
|
|
514
|
+
* plain objects and arrays (top-level promise, then the promises nested
|
|
515
|
+
* in what it resolved to). The record-first step awaits it before the doc
|
|
516
|
+
* record encodes its handles, so that encode never races a pending value.
|
|
517
|
+
*/
|
|
518
|
+
handlerPushesSettled: () => Promise<void>;
|
|
396
519
|
}
|
|
397
520
|
/**
|
|
398
521
|
* Derive the capture request context from a base context. Producer A passes
|
|
@@ -423,3 +546,11 @@ export declare function deriveShellCaptureContext(reqCtx: RequestContext<any>, d
|
|
|
423
546
|
declare function captureAndStoreShell(ssrModule: SSRModule, rscStream: ReadableStream<Uint8Array>, handleStore: HandleStore, reqCtx: RequestContext<any>, capture: ShellCaptureDescriptor, stats?: CaptureAttemptStats, captureStartedAt?: number): Promise<Exclude<CaptureAttemptOutcome, "redirect">>;
|
|
424
547
|
export { runShellCapture, captureAndStoreShell, delay, SHELL_CAPTURE_RETRY_DELAY_MS, };
|
|
425
548
|
export { isCaptureBackedOff, markCaptureBackoff, clearCaptureBackoff, REFUSED_CAPTURE_BASE_MS, REFUSED_CAPTURE_MAX_MS, REFUSED_CAPTURE_DEV_MAX_MS, };
|
|
549
|
+
/**
|
|
550
|
+
* @internal Reset this module's per-isolate state: the capture stampede
|
|
551
|
+
* guard, the refused-capture backoff, the recorded no-shell causes (which
|
|
552
|
+
* also skip a timed-out capture's in-place retry), the once-per-key warnings
|
|
553
|
+
* and the buffered debug events. Tests only (testing/serve-shell-request.ts
|
|
554
|
+
* resetShellTestState), between requests, never while a capture runs.
|
|
555
|
+
*/
|
|
556
|
+
export declare function resetShellCaptureStateForTests(): void;
|