@rangojs/router 0.17.0 → 0.19.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/event-controller.d.ts +3 -3
- package/dist/types/browser/history-state.d.ts +18 -0
- package/dist/types/browser/navigation-transaction.d.ts +20 -2
- package/dist/types/browser/partial-update.d.ts +16 -1
- package/dist/types/browser/prefetch/cache.d.ts +6 -6
- package/dist/types/browser/react/Link.d.ts +19 -9
- package/dist/types/browser/react/context.d.ts +2 -2
- package/dist/types/browser/react/location-state-shared.d.ts +95 -56
- package/dist/types/browser/react/location-state.d.ts +1 -1
- package/dist/types/browser/transition-when.d.ts +80 -0
- package/dist/types/browser/types.d.ts +104 -9
- package/dist/types/build/generate-manifest.d.ts +8 -0
- package/dist/types/cache/background-task.d.ts +24 -2
- package/dist/types/cache/cache-exec-scope.d.ts +31 -0
- package/dist/types/cache/cache-key-utils.d.ts +48 -2
- package/dist/types/cache/cache-policy.d.ts +18 -1
- package/dist/types/cache/cache-runtime.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +192 -26
- package/dist/types/cache/cache-tag.d.ts +116 -11
- 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 +224 -72
- 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 +19 -0
- package/dist/types/cache/document-cache.d.ts +10 -0
- package/dist/types/cache/handle-snapshot.d.ts +41 -8
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/invalidation-order.d.ts +23 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/request-tag-mask.d.ts +54 -0
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +141 -105
- package/dist/types/cache/tag-invalidation.d.ts +54 -3
- package/dist/types/cache/types.d.ts +267 -96
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +123 -18
- package/dist/types/client-urls/navigation.d.ts +28 -2
- package/dist/types/client-urls/server-projection.d.ts +5 -2
- package/dist/types/client-urls/types.d.ts +5 -5
- package/dist/types/client.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/route-definition/helpers-types.d.ts +11 -11
- package/dist/types/route-name.d.ts +7 -0
- package/dist/types/router/error-handling.d.ts +1 -1
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/intercept-resolution.d.ts +2 -0
- package/dist/types/router/match-context.d.ts +5 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
- package/dist/types/router/middleware-types.d.ts +6 -1
- package/dist/types/router/router-options.d.ts +4 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +58 -12
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +33 -48
- package/dist/types/router/segment-resolution/mask-nested.d.ts +73 -24
- package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
- package/dist/types/router/transition-when.d.ts +24 -10
- package/dist/types/rsc/attach-transition-when.d.ts +18 -0
- package/dist/types/rsc/full-payload.d.ts +21 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-cache-serve.d.ts +15 -12
- package/dist/types/rsc/shell-build-manifest.d.ts +12 -3
- package/dist/types/rsc/shell-capture-constants.d.ts +116 -12
- package/dist/types/rsc/shell-capture.d.ts +202 -74
- package/dist/types/rsc/shell-serve.d.ts +144 -50
- package/dist/types/rsc/types.d.ts +9 -0
- package/dist/types/segment-system.d.ts +7 -0
- package/dist/types/server/context.d.ts +155 -33
- package/dist/types/server/cookie-store.d.ts +33 -0
- package/dist/types/server/handle-store.d.ts +52 -1
- package/dist/types/server/request-context.d.ts +210 -63
- package/dist/types/server/request-headers.d.ts +44 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/ssr/index.d.ts +9 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/index.d.ts +3 -2
- package/dist/types/testing/internal/action-id.d.ts +7 -0
- package/dist/types/testing/location-state-key.d.ts +24 -0
- package/dist/types/testing/render-route.d.ts +17 -1
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/run-transition-when.d.ts +44 -69
- package/dist/types/testing/serve-shell-request.d.ts +133 -0
- package/dist/types/testing/shell-status.d.ts +44 -19
- package/dist/types/transition-when-ref.d.ts +79 -0
- package/dist/types/types/cache-types.d.ts +15 -3
- package/dist/types/types/handler-context.d.ts +14 -1
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/segments.d.ts +86 -71
- package/dist/types/urls/path-helper-types.d.ts +3 -4
- package/dist/types/urls/pattern-types.d.ts +21 -18
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1005 -105
- package/package.json +1 -1
- package/skills/breadcrumbs/SKILL.md +17 -7
- package/skills/cache-guide/SKILL.md +175 -76
- package/skills/caching/SKILL.md +361 -33
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +12 -7
- package/skills/cloudflare/SKILL.md +15 -0
- package/skills/comparison/references/framework-comparison.md +52 -37
- package/skills/defer-hydration/SKILL.md +50 -30
- package/skills/deployment-caching/SKILL.md +5 -2
- package/skills/document-cache/SKILL.md +32 -2
- package/skills/handler-use/SKILL.md +2 -0
- package/skills/hooks/SKILL.md +3 -1
- package/skills/hooks/handle-and-actions.md +3 -1
- package/skills/hooks/navigation.md +23 -2
- package/skills/hooks/state.md +101 -7
- package/skills/intercept/SKILL.md +29 -12
- package/skills/loader/SKILL.md +197 -44
- package/skills/middleware/SKILL.md +34 -9
- package/skills/migrate-nextjs/SKILL.md +68 -37
- 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 +65 -0
- package/skills/parallel/SKILL.md +13 -8
- package/skills/ppr/SKILL.md +715 -371
- package/skills/prerender/SKILL.md +30 -17
- package/skills/rango/SKILL.md +72 -34
- package/skills/route/SKILL.md +14 -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 +49 -43
- package/skills/testing/cache-prerender.md +15 -14
- package/skills/testing/client-components.md +45 -3
- package/skills/testing/loader.md +19 -1
- package/skills/testing/middleware.md +1 -0
- package/skills/testing/predicates.md +49 -49
- package/skills/testing/render-handler.md +1 -0
- package/skills/testing/serve-shell-request.md +202 -0
- package/skills/testing/server-actions.md +9 -3
- package/skills/theme/SKILL.md +56 -4
- package/skills/typesafety/env-and-bindings.md +18 -2
- package/skills/use-cache/SKILL.md +91 -23
- package/skills/vercel/SKILL.md +14 -2
- package/skills/view-transitions/SKILL.md +150 -29
- package/src/__augment-tests__/augmented.check.ts +23 -0
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/event-controller.ts +4 -4
- package/src/browser/history-state.ts +75 -1
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +77 -18
- package/src/browser/navigation-client.ts +4 -3
- package/src/browser/navigation-store.ts +43 -1
- package/src/browser/navigation-transaction.ts +31 -11
- package/src/browser/partial-update.ts +146 -35
- package/src/browser/prefetch/cache.ts +6 -6
- package/src/browser/prefetch/fetch.ts +2 -2
- package/src/browser/react/Link.tsx +38 -33
- package/src/browser/react/NavigationProvider.tsx +2 -2
- package/src/browser/react/context.ts +2 -2
- package/src/browser/react/location-state-shared.ts +241 -49
- package/src/browser/react/location-state.ts +5 -2
- package/src/browser/rsc-router.tsx +9 -0
- package/src/browser/server-action-bridge.ts +84 -15
- package/src/browser/transition-when.ts +205 -0
- package/src/browser/types.ts +124 -10
- package/src/build/generate-manifest.ts +77 -1
- package/src/cache/background-task.ts +55 -2
- package/src/cache/cache-exec-scope.ts +48 -1
- package/src/cache/cache-key-utils.ts +62 -2
- package/src/cache/cache-policy.ts +31 -4
- package/src/cache/cache-runtime.ts +209 -129
- package/src/cache/cache-scope.ts +573 -69
- package/src/cache/cache-tag.ts +475 -19
- 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 +1094 -376
- 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 +55 -40
- package/src/cache/document-cache.ts +53 -7
- package/src/cache/handle-snapshot.ts +53 -9
- package/src/cache/index.ts +2 -0
- package/src/cache/invalidation-order.ts +84 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/request-tag-mask.ts +147 -0
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +249 -277
- package/src/cache/tag-invalidation.ts +109 -18
- package/src/cache/types.ts +274 -102
- package/src/cache/vercel/vercel-cache-store.ts +561 -74
- package/src/client-urls/client-root.tsx +8 -4
- package/src/client-urls/client-urls.ts +8 -6
- package/src/client-urls/navigation.ts +55 -4
- package/src/client-urls/server-projection.ts +31 -10
- package/src/client-urls/types.ts +12 -5
- package/src/client.tsx +2 -0
- package/src/host/cookie-handler.ts +2 -1
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/loader-redirect.tsx +7 -3
- package/src/prerender/build-shell-capture.ts +173 -110
- package/src/route-definition/dsl-helpers.ts +6 -3
- package/src/route-definition/helpers-types.ts +11 -11
- package/src/route-name.ts +12 -0
- package/src/router/content-negotiation.ts +4 -1
- package/src/router/error-handling.ts +5 -2
- package/src/router/handler-context.ts +42 -5
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +30 -8
- package/src/router/loader-resolution.ts +89 -33
- package/src/router/logging.ts +5 -3
- package/src/router/match-api.ts +89 -42
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +14 -29
- package/src/router/match-middleware/background-revalidation.ts +3 -5
- package/src/router/match-middleware/cache-lookup.ts +52 -19
- package/src/router/match-middleware/cache-store.ts +36 -10
- package/src/router/match-result.ts +1 -0
- package/src/router/middleware-types.ts +6 -1
- package/src/router/middleware.ts +20 -4
- package/src/router/navigation-snapshot.ts +6 -5
- package/src/router/prerender-match.ts +10 -0
- package/src/router/request-classification.ts +7 -4
- package/src/router/router-options.ts +4 -1
- package/src/router/segment-resolution/fresh.ts +18 -13
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +366 -70
- package/src/router/segment-resolution/loader-mask.ts +3 -4
- package/src/router/segment-resolution/loader-snapshot.ts +215 -126
- package/src/router/segment-resolution/mask-nested.ts +161 -42
- package/src/router/segment-resolution/revalidation.ts +27 -13
- package/src/router/segment-resolution/view-transition-default.ts +6 -21
- package/src/router/telemetry.ts +6 -3
- package/src/router/transition-when.ts +34 -52
- package/src/router.ts +2 -1
- package/src/rsc/attach-transition-when.ts +47 -0
- package/src/rsc/full-payload.ts +47 -3
- package/src/rsc/handler.ts +24 -10
- package/src/rsc/loader-fetch.ts +2 -1
- package/src/rsc/manifest-init.ts +4 -0
- package/src/rsc/origin-guard.ts +8 -5
- package/src/rsc/progressive-enhancement.ts +10 -32
- package/src/rsc/response-cache-serve.ts +69 -78
- package/src/rsc/response-route-handler.ts +2 -2
- package/src/rsc/rsc-rendering.ts +712 -338
- package/src/rsc/server-action.ts +8 -20
- package/src/rsc/shell-build-manifest.ts +49 -29
- package/src/rsc/shell-capture-constants.ts +182 -12
- package/src/rsc/shell-capture.ts +1126 -612
- package/src/rsc/shell-serve.ts +331 -85
- package/src/rsc/ssr-setup.ts +3 -2
- package/src/rsc/types.ts +9 -0
- package/src/segment-system.tsx +40 -7
- package/src/server/context.ts +328 -72
- package/src/server/cookie-store.ts +184 -133
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +364 -100
- package/src/server/request-headers.ts +134 -0
- package/src/server.ts +4 -0
- package/src/ssr/index.tsx +96 -37
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/testing/dispatch.ts +4 -7
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/internal/action-id.ts +25 -0
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/location-state-key.ts +45 -0
- package/src/testing/render-handler.ts +1 -1
- package/src/testing/render-route.tsx +109 -16
- package/src/testing/run-client-revalidate.ts +3 -19
- package/src/testing/run-loader.ts +67 -5
- package/src/testing/run-transition-when.ts +109 -172
- package/src/testing/serve-shell-request.ts +381 -0
- package/src/testing/shell-status.ts +73 -47
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/transition-when-ref.ts +220 -0
- package/src/types/cache-types.ts +15 -3
- package/src/types/handler-context.ts +14 -1
- package/src/types/index.ts +3 -0
- package/src/types/segments.ts +94 -91
- package/src/urls/path-helper-types.ts +3 -4
- package/src/urls/pattern-types.ts +21 -18
- package/src/vite/discovery/discover-routers.ts +3 -0
- package/src/vite/discovery/shell-prerender-phase.ts +9 -2
- package/src/vite/discovery/virtual-module-codegen.ts +16 -6
- package/src/vite/plugins/transition-when-hoist.ts +1064 -0
- package/src/vite/rango.ts +2 -0
- package/src/vite/router-discovery.ts +89 -9
- package/dist/types/rsc/transition-gate.d.ts +0 -23
- package/src/rsc/transition-gate.ts +0 -86
package/src/rsc/server-action.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* wraps revalidation inside route middleware — identical to a normal render.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import { requestHeaders } from "../server/request-headers.js";
|
|
18
19
|
import {
|
|
19
20
|
getRequestContext,
|
|
20
21
|
setRequestContextParams,
|
|
@@ -22,7 +23,8 @@ import {
|
|
|
22
23
|
import { observePhase, PHASES } from "../router/instrument.js";
|
|
23
24
|
import { resolveActionRefId } from "../router/is-action.js";
|
|
24
25
|
import type { TraceSpan } from "../router/tracing.js";
|
|
25
|
-
import {
|
|
26
|
+
import { attachTransitionWhen } from "./attach-transition-when.js";
|
|
27
|
+
import { publicRouteName } from "../route-name.js";
|
|
26
28
|
import type { RscPayload } from "./types.js";
|
|
27
29
|
import {
|
|
28
30
|
hasBodyContent,
|
|
@@ -93,7 +95,7 @@ export async function executeServerAction<TEnv>(
|
|
|
93
95
|
const temporaryReferences = ctx.createTemporaryReferenceSet();
|
|
94
96
|
|
|
95
97
|
// Decode action arguments from request body
|
|
96
|
-
const contentType = request.
|
|
98
|
+
const contentType = requestHeaders(request).get("content-type") || "";
|
|
97
99
|
let args: unknown[] = [];
|
|
98
100
|
let actionFormData: FormData | undefined;
|
|
99
101
|
|
|
@@ -381,14 +383,6 @@ async function revalidateAfterActionInner<TEnv>(
|
|
|
381
383
|
): Promise<Response> {
|
|
382
384
|
const reqCtx = getRequestContext();
|
|
383
385
|
const { actionContext } = continuation;
|
|
384
|
-
// Expose the action that triggered this revalidation to the transition({ when })
|
|
385
|
-
// gate (covers both the error-boundary and success gate calls below). Mirrors
|
|
386
|
-
// the action fields a revalidate() predicate sees.
|
|
387
|
-
reqCtx._gateActionId = actionContext?.actionId;
|
|
388
|
-
reqCtx._gateActionUrl = actionContext?.actionUrl;
|
|
389
|
-
reqCtx._gateActionResult = actionContext?.actionResult;
|
|
390
|
-
reqCtx._gateFormData = actionContext?.formData;
|
|
391
|
-
|
|
392
386
|
// Mark the rest of this request as an action revalidation render. The "use
|
|
393
387
|
// cache" runtime reads this to re-execute a stale entry in the foreground
|
|
394
388
|
// (fresh data in the action response) rather than serving stale + revalidating
|
|
@@ -523,16 +517,13 @@ function renderActionBoundaryResponse<TEnv>(
|
|
|
523
517
|
// routerId exposed for the frontend (current app identity); see
|
|
524
518
|
// rsc-rendering.ts partial branch.
|
|
525
519
|
routerId: ctx.router.id,
|
|
526
|
-
segments:
|
|
527
|
-
errorBoundary.segments,
|
|
528
|
-
reqCtx,
|
|
529
|
-
ctx.router.onError,
|
|
530
|
-
),
|
|
520
|
+
segments: attachTransitionWhen(errorBoundary.segments, reqCtx),
|
|
531
521
|
isPartial: true,
|
|
532
522
|
matched: errorBoundary.matched,
|
|
533
523
|
diff: errorBoundary.diff,
|
|
534
524
|
resolvedIds: errorBoundary.resolvedIds,
|
|
535
525
|
params: errorBoundary.params,
|
|
526
|
+
routeName: publicRouteName(errorBoundary.routeName),
|
|
536
527
|
isError: true,
|
|
537
528
|
handles: handleStore.stream(),
|
|
538
529
|
version: ctx.version,
|
|
@@ -589,16 +580,13 @@ function renderRevalidationResponse<TEnv>(
|
|
|
589
580
|
// routerId exposed for the frontend (current app identity); see
|
|
590
581
|
// rsc-rendering.ts partial branch.
|
|
591
582
|
routerId: ctx.router.id,
|
|
592
|
-
segments:
|
|
593
|
-
matchResult.segments,
|
|
594
|
-
reqCtx,
|
|
595
|
-
ctx.router.onError,
|
|
596
|
-
),
|
|
583
|
+
segments: attachTransitionWhen(matchResult.segments, reqCtx),
|
|
597
584
|
isPartial: true,
|
|
598
585
|
matched: matchResult.matched,
|
|
599
586
|
diff: matchResult.diff,
|
|
600
587
|
resolvedIds: matchResult.resolvedIds,
|
|
601
588
|
params: matchResult.params,
|
|
589
|
+
routeName: publicRouteName(matchResult.routeName),
|
|
602
590
|
slots: matchResult.slots,
|
|
603
591
|
handles: handleStore.stream(),
|
|
604
592
|
version: ctx.version,
|
|
@@ -37,7 +37,11 @@ import {
|
|
|
37
37
|
hasIntactShellPayload,
|
|
38
38
|
isValidShellHit,
|
|
39
39
|
} from "./shell-serve.js";
|
|
40
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
SHELL_CAPTURE_MAX_WAIT_MS,
|
|
42
|
+
resetShellWarningsForTests,
|
|
43
|
+
warnOnce,
|
|
44
|
+
} from "./shell-capture-constants.js";
|
|
41
45
|
import { buildShellManifestKey } from "../prerender/shell-manifest-key.js";
|
|
42
46
|
|
|
43
47
|
/** One baked manifest record (the __ps asset module's default export). */
|
|
@@ -45,10 +49,8 @@ export interface BuildShellEntry {
|
|
|
45
49
|
entry: ShellCacheEntry;
|
|
46
50
|
/** Resolved ppr ttl (seconds) — drives staleness/recapture, never expiry. */
|
|
47
51
|
ttl: number;
|
|
48
|
-
swr?: number;
|
|
49
52
|
/** The putShell-barrier tag union baked at build (static + recorded). */
|
|
50
53
|
tags?: string[];
|
|
51
|
-
routeName: string;
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
interface ShellManifestModule {
|
|
@@ -81,16 +83,16 @@ function loadManifest(): Promise<ShellManifestModule | null> {
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
/**
|
|
84
|
-
* Per-spec verdict memo for the version +
|
|
86
|
+
* Per-spec verdict memo for the version + structural gates. A manifest record
|
|
85
87
|
* is immutable for the process lifetime (content-hashed asset module), so its
|
|
86
|
-
* gate verdict is constant
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
88
|
+
* gate verdict is constant: a fresh build hit never populates the runtime
|
|
89
|
+
* store, so the store MISS + read-through is the steady state, not a warm-up.
|
|
90
|
+
* Only the per-request gates (tag markers, staleness) stay outside the memo;
|
|
91
|
+
* the document HIT decodes the prelude once per serve (openShellDocument),
|
|
92
|
+
* partial replay never does. `null` memoizes a failed verdict —
|
|
93
|
+
* deterministically invalid. Spec-only keying is sound because buildVersion is
|
|
94
|
+
* process-constant on the manifest path (folded into the shipped worker; dev
|
|
95
|
+
* never loads a manifest).
|
|
94
96
|
*/
|
|
95
97
|
const validatedSpecs = new Map<string, ValidatedBuildShellEntry | null>();
|
|
96
98
|
|
|
@@ -121,15 +123,16 @@ async function validatedManifestRecord(
|
|
|
121
123
|
return verdict ?? undefined;
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* Reset the memoized manifest (unit tests swap the global loader) and the
|
|
128
|
+
* shell path's once-per-key warnings, the tag-check one included.
|
|
129
|
+
*/
|
|
125
130
|
export function resetBuildShellManifestForTests(): void {
|
|
126
131
|
manifestPromise = null;
|
|
127
132
|
validatedSpecs.clear();
|
|
133
|
+
resetShellWarningsForTests();
|
|
128
134
|
}
|
|
129
135
|
|
|
130
|
-
/** Keys already warned about a tag-check-incapable store (once per key). */
|
|
131
|
-
const warnedTagCheckUnsupported = new Set<string>();
|
|
132
|
-
|
|
133
136
|
export interface BuildShellHit {
|
|
134
137
|
entry: DocumentShellCacheEntry;
|
|
135
138
|
/** Past createdAt + ttl: serve, but schedule the runtime recapture. */
|
|
@@ -259,6 +262,24 @@ async function fetchDevShellEntry(
|
|
|
259
262
|
}
|
|
260
263
|
}
|
|
261
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Whether a build-time shell exists for `pathname` (the caller checks the
|
|
267
|
+
* request is search-less, like the entries lookupBuildShell serves): a
|
|
268
|
+
* manifest entry for the path in production, a Prerender route in dev. No
|
|
269
|
+
* asset load and no dev capture: it only decides whether a request that
|
|
270
|
+
* cannot read the build shell deserves a warning.
|
|
271
|
+
*/
|
|
272
|
+
export async function hasBuildShell(
|
|
273
|
+
pathname: string,
|
|
274
|
+
dev?: DevShellLookup,
|
|
275
|
+
): Promise<boolean> {
|
|
276
|
+
if (globalThis.__loadShellManifestModule === undefined) {
|
|
277
|
+
return dev?.isPrerenderRoute === true;
|
|
278
|
+
}
|
|
279
|
+
const mod = await loadManifest();
|
|
280
|
+
return mod?.default[buildShellManifestKey(pathname)] !== undefined;
|
|
281
|
+
}
|
|
282
|
+
|
|
262
283
|
/**
|
|
263
284
|
* Look up the baked shell entry for a request, applying every serve gate:
|
|
264
285
|
* search-less requests only (the build captured the bare pathname; a
|
|
@@ -315,20 +336,19 @@ export async function lookupBuildShell(
|
|
|
315
336
|
// memo-only answers would let the immutable asset resurrect on the
|
|
316
337
|
// next request). Declared intent that cannot be honored deserves a
|
|
317
338
|
// diagnostic; the route keeps runtime-capture semantics.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
339
|
+
warnOnce(
|
|
340
|
+
"build-shell-tag-check",
|
|
341
|
+
buildShellManifestKey(url.pathname),
|
|
342
|
+
() =>
|
|
322
343
|
`[rango] Build-time shell for "${url.pathname}" carries cache tags, but ` +
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
344
|
+
"the app cache store cannot answer tag-invalidation history durably " +
|
|
345
|
+
(typeof check !== "function"
|
|
346
|
+
? "(isTagsInvalidatedSince() is not implemented), "
|
|
347
|
+
: "(no durable tag history — a CFCacheStore without a KV namespace), ") +
|
|
348
|
+
"so updateTag() could not evict it. The entry is not served; the route " +
|
|
349
|
+
"keeps runtime shell capture. Use MemorySegmentCacheStore, a KV-backed " +
|
|
350
|
+
"CFCacheStore, or VercelCacheStore (or add the method to your custom store).",
|
|
351
|
+
);
|
|
332
352
|
return null;
|
|
333
353
|
}
|
|
334
354
|
if (await check.call(store, record.tags, entry.createdAt)) return null;
|
|
@@ -1,31 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React-free leaf of the PPR shell path: its constants, the shell key
|
|
3
|
+
* builders, the status header names and bypass reasons, and the once-per-key
|
|
4
|
+
* warning registry. The ssr graph (ssr/index.tsx), the testing barrels
|
|
5
|
+
* (testing/shell-status.ts) and cookie-store.ts import from here without
|
|
6
|
+
* pulling the capture orchestration module or shell-serve.ts (React).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { sortedSearchString } from "../cache/cache-key-utils.js";
|
|
10
|
+
import type { SearchParamsFilter } from "../cache/search-params-filter.js";
|
|
11
|
+
|
|
1
12
|
/**
|
|
2
13
|
* Default upper bound on the capture prerender wait before forcing the abort
|
|
3
14
|
* that freezes the shell — the ONLY wall-clock on the capture path, and a
|
|
4
15
|
* pathological guard: it should never fire once the caller's `quiesce` is a
|
|
5
16
|
* task-quantized, frozen-byte signal (the capture gate in shell-capture.ts).
|
|
6
|
-
*
|
|
7
|
-
* Leaf module on purpose (imports nothing): shell-capture.ts re-exports it
|
|
8
|
-
* (single import site for the rsc graph — shell-build-manifest.ts sizes the
|
|
9
|
-
* dev fetch envelope from that re-export) and ssr/index.tsx uses it as
|
|
10
|
-
* captureShellHTML's own fallback; the ssr graph must not pull the capture
|
|
11
|
-
* orchestration module for one number.
|
|
17
|
+
* ssr/index.tsx uses it as captureShellHTML's own fallback.
|
|
12
18
|
*
|
|
13
19
|
* 15s, raised from 5s: captures are background work (waitUntil), so the budget
|
|
14
20
|
* costs latency-to-HIT only — never a served response — and 5s spuriously
|
|
15
21
|
* refused legitimately-slow deferred shell material (a real storefront's meta
|
|
16
22
|
* chains settle at ~7s). Ceiling math: workerd's waitUntil lifetime is ~30s
|
|
17
|
-
* past response completion
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
+
* past response completion. An attempt that consumed the whole budget is not
|
|
24
|
+
* retried in place (shell-capture.ts CaptureAttemptStats.noShellCause), so
|
|
25
|
+
* the envelope is one budget plus store I/O; the retry follows only an
|
|
26
|
+
* attempt that ended early (a cold-module abort). Past the ceiling the
|
|
27
|
+
* platform kill degrades to the existing best-effort contract (the key stays
|
|
28
|
+
* MISS; a later request re-captures).
|
|
23
29
|
* Node/dev and build-time captures have no waitUntil ceiling. The per-route
|
|
24
30
|
* `ppr.captureTimeout` knob remains for tightening below the default. See
|
|
25
31
|
* docs/design/ppr-shell-resume.md (Cost model).
|
|
26
32
|
*/
|
|
27
33
|
export const SHELL_CAPTURE_MAX_WAIT_MS = 15_000;
|
|
28
34
|
|
|
35
|
+
/** Debug/status header on a ppr document response: `HIT` | `MISS`. */
|
|
36
|
+
export const SHELL_STATUS_HEADER: string = "x-rango-shell";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Partial-navigation replay status header, set only after the captured
|
|
40
|
+
* segment record was consumed (`HIT`) or with the reason it was not.
|
|
41
|
+
*/
|
|
42
|
+
export const PPR_REPLAY_STATUS_HEADER: string = "x-rango-ppr-replay";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Bounded reasons a partial request to a ppr route falls open to the ordinary
|
|
46
|
+
* match (`x-rango-ppr-replay: BYPASS; reason=<reason>`). One list for the
|
|
47
|
+
* producer (rsc-rendering.ts) and the parser (testing/shell-status.ts): a
|
|
48
|
+
* reason the parser does not list parses as null.
|
|
49
|
+
*/
|
|
50
|
+
export const PPR_REPLAY_BYPASS_REASONS = [
|
|
51
|
+
"method",
|
|
52
|
+
"dynamic",
|
|
53
|
+
"nonce",
|
|
54
|
+
"store-unavailable",
|
|
55
|
+
"passive-read-unsupported",
|
|
56
|
+
"no-navigation-context",
|
|
57
|
+
"prerender-store",
|
|
58
|
+
"intercept",
|
|
59
|
+
"cache-disabled",
|
|
60
|
+
"read-error",
|
|
61
|
+
"no-entry",
|
|
62
|
+
"invalid-version",
|
|
63
|
+
"corrupt-entry",
|
|
64
|
+
"no-segment-snapshot",
|
|
65
|
+
"snapshot-miss",
|
|
66
|
+
"explicit-cache-hit",
|
|
67
|
+
] as const;
|
|
68
|
+
|
|
69
|
+
/** One of {@link PPR_REPLAY_BYPASS_REASONS}. */
|
|
70
|
+
export type PprReplayBypassReason = (typeof PPR_REPLAY_BYPASS_REASONS)[number];
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The shell key's search portion (`?`-prefixed sorted search with the
|
|
74
|
+
* cache.searchParams filter applied, or "") — ALSO the string the capture and
|
|
75
|
+
* resume SSR renders seed their store location with (SSRRenderOptions.search
|
|
76
|
+
* / ShellCaptureOptions.search / ShellResumeOptions.search). Search is part
|
|
77
|
+
* of shell identity, so static-part `useSearchParams` reads render exactly
|
|
78
|
+
* what the key names; deriving seed and key from this one helper is what
|
|
79
|
+
* keeps capture, resume, and lookup byte-agreed (drift = replay mismatch or
|
|
80
|
+
* permanent MISS).
|
|
81
|
+
*/
|
|
82
|
+
export function shellSearchSeed(url: URL, filter?: SearchParamsFilter): string {
|
|
83
|
+
const sorted = sortedSearchString(url.searchParams, filter);
|
|
84
|
+
return sorted ? `?${sorted}` : "";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Shell cache key: host + pathname + sorted search + a `:shell` namespace suffix
|
|
89
|
+
* (so it can never collide with a document-cache key; the store further isolates
|
|
90
|
+
* the shell family internally).
|
|
91
|
+
*
|
|
92
|
+
* The key includes the request HOST: in a multi-tenant host-router deployment
|
|
93
|
+
* (one worker, one shared KV/runtime-cache store) a host-less key would serve
|
|
94
|
+
* tenant A's captured shell to tenant B's users.
|
|
95
|
+
*
|
|
96
|
+
* `filter` is the request's compiled `cache.searchParams` config
|
|
97
|
+
* (ctx._searchParamsFilter): excluded params collapse onto one shell slot.
|
|
98
|
+
* Callers on the serve/capture path MUST pass it -- key drift between capture
|
|
99
|
+
* and lookup makes every shell request a permanent miss. The testing helper
|
|
100
|
+
* `shellCacheKey` (testing/shell-status.ts) builds its key with it too.
|
|
101
|
+
*/
|
|
102
|
+
export function buildShellKey(url: URL, filter?: SearchParamsFilter): string {
|
|
103
|
+
return `${url.host}${url.pathname}${shellSearchSeed(url, filter)}:shell`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The shell key for one request partition (cache-scope.ts
|
|
108
|
+
* resolveShellPartition): the route cache() `key()` or store keyGenerator
|
|
109
|
+
* result that partitions the route's record partitions its shell too, so
|
|
110
|
+
* each partition captures and serves its own.
|
|
111
|
+
*
|
|
112
|
+
* The partition is URI-encoded: it is request-derived, and raw it could end
|
|
113
|
+
* in a suffix another key is built with (navigationShellKey), so a partition
|
|
114
|
+
* `gold:navigation` named gold's navigation entry and its document shell
|
|
115
|
+
* replayed to gold's navigations. Encoded, it holds no `:` or `|`.
|
|
116
|
+
*/
|
|
117
|
+
export function partitionShellKey(key: string, partition: string): string {
|
|
118
|
+
return `${key}|${encodeURIComponent(partition)}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The navigation-only entry beside a document shell key (a cold partial
|
|
123
|
+
* request's capture, rsc-rendering.ts). A suffix after the (encoded)
|
|
124
|
+
* partition: partitionShellKey keeps the two unambiguous.
|
|
125
|
+
*/
|
|
126
|
+
export function navigationShellKey(key: string): string {
|
|
127
|
+
return `${key}:navigation`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Fixed number of macrotask hops between `quiesce` resolving and the abort. These
|
|
132
|
+
* give React's fizz worker turns to flush the settled shell into the prelude and
|
|
133
|
+
* mark still-pending boundaries as POSTPONED (rather than errored) before
|
|
134
|
+
* controller.abort() lands. Not a wall-clock wait. Taken by captureShellHTML
|
|
135
|
+
* (ssr/index.tsx) and by the testing SSR stub (testing/serve-shell-request.ts),
|
|
136
|
+
* so the stub freezes the same Flight the real capture would.
|
|
137
|
+
*
|
|
138
|
+
* Why 16 and not 2: the capture's Flight input is the doc record's stored
|
|
139
|
+
* fragments (settleCaptureRecord, shell-capture.ts), already serialized, so the
|
|
140
|
+
* render emits the whole shell payload in the first tick and the gate declares
|
|
141
|
+
* quiesce within a few ms, before fizz has rendered the shell to <body>. When
|
|
142
|
+
* the capture Flight dribbled out as handlers ran, Flight-quiet meant "the shell
|
|
143
|
+
* has rendered" and 2 hops sufficed.
|
|
144
|
+
*
|
|
145
|
+
* Hops alone are NOT render-readiness: fizz cannot emit even <html> until the
|
|
146
|
+
* payload root settles, nor complete a shell whose client components are
|
|
147
|
+
* still loading — real module-runner I/O in dev (100ms+ cold), which no fixed
|
|
148
|
+
* count of near-zero-cost task hops can buy. captureShellHTML therefore
|
|
149
|
+
* awaits the payload-settled signal (SsrRootOptions.onPayloadSettled) and
|
|
150
|
+
* then the client-reference loads in flight in the isolate (captureClientLoads),
|
|
151
|
+
* deadline-bounded, between quiesce and these hops; the hops then only flush
|
|
152
|
+
* the settled tree and mark pending boundaries POSTPONED. Still task-based
|
|
153
|
+
* (masked loaders never emit, so more hops never lets a hole settle). Bounded
|
|
154
|
+
* by maxWaitMs end to end.
|
|
155
|
+
*/
|
|
156
|
+
export const POST_QUIESCE_TASK_HOPS = 16;
|
|
157
|
+
|
|
29
158
|
/**
|
|
30
159
|
* Hard cap on one capture TASK — runShellCapture end to end (both attempts +
|
|
31
160
|
* the in-place retry delay). SHELL_CAPTURE_MAX_WAIT_MS arms only inside
|
|
@@ -56,3 +185,44 @@ export const PPR_LANE_HINT: string =
|
|
|
56
185
|
"non-promise data; every other loader is live (masked at capture, fresh per " +
|
|
57
186
|
"request) and needs loading() or an inline <Suspense> above its reader. See the " +
|
|
58
187
|
'/ppr skill, "The loader lane rule" (node_modules/@rangojs/router/skills/ppr/SKILL.md).';
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Once-per-key console warnings of the PPR shell path (the capture, the serve
|
|
191
|
+
* gates, the build-shell read-through), per isolate. One registry, so one
|
|
192
|
+
* reset clears them all ({@link resetShellWarningsForTests}).
|
|
193
|
+
*/
|
|
194
|
+
const warnedShellKeys = new Set<string>();
|
|
195
|
+
|
|
196
|
+
function shellWarningId(scope: string, key: string): string {
|
|
197
|
+
return `${scope}\u0000${key}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Whether {@link warnOnce} already warned for `scope` and `key`. */
|
|
201
|
+
export function hasWarnedOnce(scope: string, key: string): boolean {
|
|
202
|
+
return warnedShellKeys.has(shellWarningId(scope, key));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* `console.warn(message())` once per `scope` and `key`; `message` is built only
|
|
207
|
+
* when it prints. Returns true when it warned.
|
|
208
|
+
*/
|
|
209
|
+
export function warnOnce(
|
|
210
|
+
scope: string,
|
|
211
|
+
key: string,
|
|
212
|
+
message: () => string,
|
|
213
|
+
): boolean {
|
|
214
|
+
const id = shellWarningId(scope, key);
|
|
215
|
+
if (warnedShellKeys.has(id)) return false;
|
|
216
|
+
warnedShellKeys.add(id);
|
|
217
|
+
console.warn(message());
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @internal Forget every once-per-key shell warning. Tests only: the capture,
|
|
223
|
+
* serve and build-shell resets (testing/serve-shell-request.ts
|
|
224
|
+
* resetShellTestState) call it.
|
|
225
|
+
*/
|
|
226
|
+
export function resetShellWarningsForTests(): void {
|
|
227
|
+
warnedShellKeys.clear();
|
|
228
|
+
}
|