@rangojs/router 0.20.0 → 0.22.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/dist/bin/rango.js +39 -15
- package/dist/types/browser/event-controller.d.ts +49 -0
- package/dist/types/browser/history-state.d.ts +16 -4
- package/dist/types/browser/navigation-store.d.ts +9 -0
- package/dist/types/browser/navigation-transaction.d.ts +7 -0
- package/dist/types/browser/partial-update.d.ts +2 -1
- package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
- package/dist/types/browser/rango-state.d.ts +3 -1
- package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
- package/dist/types/browser/react/context.d.ts +25 -0
- package/dist/types/browser/react/location-state-shared.d.ts +84 -10
- package/dist/types/browser/react/location-state.d.ts +7 -2
- package/dist/types/browser/rsc-router.d.ts +13 -2
- package/dist/types/browser/types.d.ts +10 -0
- package/dist/types/build/generate-manifest.d.ts +2 -0
- package/dist/types/cache/background-task.d.ts +22 -0
- package/dist/types/cache/cache-key-utils.d.ts +50 -5
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +5 -6
- package/dist/types/cache/cf/cf-cache-store.d.ts +34 -7
- package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
- package/dist/types/cache/document-cache.d.ts +8 -5
- package/dist/types/cache/handle-snapshot.d.ts +59 -50
- package/dist/types/cache/index.d.ts +3 -1
- package/dist/types/cache/invalidation-order.d.ts +14 -0
- package/dist/types/cache/memory-segment-store.d.ts +33 -1
- package/dist/types/cache/shell-snapshot.d.ts +29 -4
- package/dist/types/cache/store-scope.d.ts +22 -0
- package/dist/types/cache/tag-invalidation.d.ts +7 -6
- package/dist/types/cache/types.d.ts +84 -24
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +35 -17
- package/dist/types/client-urls/navigation.d.ts +7 -1
- package/dist/types/client-urls/optimistic-location.d.ts +10 -4
- package/dist/types/errors.d.ts +6 -0
- package/dist/types/handles/deferred-resolution.d.ts +18 -1
- package/dist/types/internal-suspense-audit.d.ts +1 -0
- package/dist/types/loader-store.d.ts +4 -8
- package/dist/types/outlet-provider.d.ts +2 -1
- package/dist/types/prerender/build-shell-capture.d.ts +8 -6
- package/dist/types/prerender/cloudflare.d.ts +49 -0
- package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
- package/dist/types/prerender/index.d.ts +12 -0
- package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
- package/dist/types/prerender/on-demand.d.ts +272 -0
- package/dist/types/prerender/producer-guard.d.ts +33 -0
- package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
- package/dist/types/prerender/warm-request.d.ts +65 -0
- package/dist/types/prerender/warm.d.ts +65 -0
- package/dist/types/prerender/writable-store.d.ts +170 -0
- package/dist/types/prerender.d.ts +27 -0
- package/dist/types/reverse.d.ts +1 -1
- package/dist/types/route-content-wrapper.d.ts +3 -1
- package/dist/types/router/error-handling.d.ts +2 -0
- package/dist/types/router/handler-context.d.ts +1 -1
- package/dist/types/router/match-middleware/cache-lookup.d.ts +15 -1
- package/dist/types/router/navigation-snapshot.d.ts +8 -1
- package/dist/types/router/pattern-matching.d.ts +2 -0
- package/dist/types/router/prefetch-default.d.ts +4 -4
- package/dist/types/router/prerender-match.d.ts +23 -1
- package/dist/types/router/route-trie-builder.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +1 -0
- package/dist/types/router/router-interfaces.d.ts +32 -1
- package/dist/types/router/router-options.d.ts +39 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +23 -10
- package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
- package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
- package/dist/types/router/trie-matching.d.ts +2 -0
- package/dist/types/router-versions.d.ts +39 -0
- package/dist/types/rsc/handler-context.d.ts +5 -0
- package/dist/types/rsc/shell-build-manifest.d.ts +9 -6
- package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
- package/dist/types/rsc/shell-capture.d.ts +14 -1
- package/dist/types/rsc/shell-serve.d.ts +2 -2
- package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
- package/dist/types/rsc/types.d.ts +12 -6
- package/dist/types/segment-boundary-content.d.ts +17 -0
- package/dist/types/segment-loader-promise.d.ts +2 -6
- package/dist/types/server/build-version-table.d.ts +53 -0
- package/dist/types/server/context.d.ts +12 -4
- package/dist/types/server/handle-store.d.ts +89 -46
- package/dist/types/server/request-context.d.ts +84 -9
- package/dist/types/suspense-audit-react.d.ts +26 -0
- package/dist/types/suspense-audit.d.ts +157 -0
- package/dist/types/testing/action-form.d.ts +34 -0
- package/dist/types/testing/build-versions.d.ts +32 -0
- package/dist/types/testing/dom.entry.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +3 -1
- package/dist/types/testing/index.d.ts +6 -0
- package/dist/types/testing/internal/seed-vars.d.ts +7 -2
- package/dist/types/testing/internal/server-modules.d.ts +8 -0
- package/dist/types/testing/location-state-key.d.ts +5 -1
- package/dist/types/testing/render-route.d.ts +77 -6
- package/dist/types/testing/serve-shell-request.d.ts +55 -4
- package/dist/types/testing/shell-status.d.ts +14 -6
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/thenable-status.d.ts +6 -0
- package/dist/types/types/handler-context.d.ts +15 -5
- package/dist/types/types/route-entry.d.ts +5 -0
- package/dist/types/urls/path-helper.d.ts +18 -0
- package/dist/types/vite/discovery/build-versions.d.ts +393 -0
- package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
- package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
- package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
- package/dist/types/vite/discovery/state.d.ts +25 -0
- package/dist/types/vite/encryption-key.d.ts +18 -0
- package/dist/types/vite/inject-client-debug.d.ts +6 -25
- package/dist/types/vite/plugin-types.d.ts +27 -0
- package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
- package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
- package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
- package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
- package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
- package/dist/types/vite/plugins/virtual-entries.d.ts +18 -1
- package/dist/types/vite/router-discovery.d.ts +6 -0
- package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
- package/dist/types/vite/utils/client-chunks.d.ts +2 -0
- package/dist/types/vite/utils/compare-strings.d.ts +7 -0
- package/dist/types/vite/utils/package-resolution.d.ts +8 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
- package/dist/vite/index.js +1707 -931
- package/package.json +12 -3
- package/skills/cache-guide/SKILL.md +102 -37
- package/skills/caching/SKILL.md +56 -9
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +16 -0
- package/skills/cloudflare/SKILL.md +133 -1
- package/skills/comparison/references/framework-comparison.md +17 -13
- package/skills/deployment-caching/SKILL.md +33 -12
- package/skills/document-cache/SKILL.md +16 -14
- package/skills/hooks/SKILL.md +25 -0
- package/skills/hooks/handle-and-actions.md +5 -0
- package/skills/hooks/navigation.md +38 -0
- package/skills/hooks/state.md +213 -11
- package/skills/hooks/urls.md +40 -1
- package/skills/host-router/SKILL.md +31 -0
- package/skills/intercept/SKILL.md +6 -0
- package/skills/loader/SKILL.md +25 -5
- package/skills/middleware/SKILL.md +2 -0
- package/skills/migrate-nextjs/SKILL.md +82 -9
- package/skills/ppr/SKILL.md +150 -48
- package/skills/prerender/SKILL.md +651 -10
- package/skills/rango/SKILL.md +9 -7
- package/skills/response-routes/SKILL.md +5 -5
- package/skills/route/SKILL.md +29 -0
- package/skills/router-setup/SKILL.md +15 -1
- package/skills/testing/SKILL.md +32 -27
- package/skills/testing/cache-prerender.md +18 -17
- package/skills/testing/client-components.md +193 -8
- package/skills/testing/handles.md +2 -0
- package/skills/testing/loader.md +1 -1
- package/skills/testing/middleware.md +1 -1
- package/skills/testing/render-handler.md +18 -18
- package/skills/testing/serve-shell-request.md +167 -22
- package/skills/testing/server-actions.md +17 -17
- package/skills/testing/server-tree.md +1 -1
- package/skills/typesafety/generated-files-and-cli.md +1 -1
- package/skills/use-cache/SKILL.md +25 -10
- package/skills/vercel/SKILL.md +5 -3
- package/skills/view-transitions/SKILL.md +9 -4
- package/src/bin/rango.ts +1 -1
- package/src/browser/event-controller.ts +119 -2
- package/src/browser/history-state.ts +39 -17
- package/src/browser/link-interceptor.ts +2 -2
- package/src/browser/navigation-bridge.ts +104 -41
- package/src/browser/navigation-store.ts +26 -0
- package/src/browser/navigation-transaction.ts +24 -12
- package/src/browser/network-error-handler.ts +2 -0
- package/src/browser/partial-update.ts +58 -14
- package/src/browser/prefetch/default-strategy.ts +22 -42
- package/src/browser/rango-state.ts +3 -1
- package/src/browser/react/Link.tsx +37 -43
- package/src/browser/react/NavigationProvider.tsx +85 -1
- package/src/browser/react/context.ts +30 -0
- package/src/browser/react/location-state-shared.ts +200 -34
- package/src/browser/react/location-state.ts +28 -77
- package/src/browser/react/use-action.ts +4 -1
- package/src/browser/react/use-handle.ts +3 -0
- package/src/browser/react/use-link-status.ts +4 -3
- package/src/browser/react/use-navigation.ts +4 -1
- package/src/browser/react/use-params.ts +4 -1
- package/src/browser/react/use-pathname.ts +4 -1
- package/src/browser/react/use-search-params.ts +5 -3
- package/src/browser/react/use-segments.ts +36 -54
- package/src/browser/rsc-router.tsx +93 -31
- package/src/browser/segment-reconciler.ts +3 -3
- package/src/browser/server-action-bridge.ts +43 -7
- package/src/browser/types.ts +11 -0
- package/src/build/generate-manifest.ts +17 -0
- package/src/build/merge-full-manifests.ts +5 -0
- package/src/build/route-trie.ts +1 -0
- package/src/cache/background-task.ts +61 -0
- package/src/cache/cache-key-utils.ts +91 -12
- package/src/cache/cache-policy.ts +11 -2
- package/src/cache/cache-runtime.ts +23 -23
- package/src/cache/cache-scope.ts +48 -36
- package/src/cache/cf/cf-cache-store.ts +91 -66
- package/src/cache/cf/cf-cache-types.ts +12 -4
- package/src/cache/document-cache.ts +35 -14
- package/src/cache/handle-snapshot.ts +118 -86
- package/src/cache/index.ts +5 -0
- package/src/cache/invalidation-order.ts +53 -0
- package/src/cache/memory-segment-store.ts +52 -1
- package/src/cache/shell-snapshot.ts +52 -5
- package/src/cache/store-scope.ts +36 -0
- package/src/cache/tag-invalidation.ts +7 -5
- package/src/cache/types.ts +90 -24
- package/src/cache/vercel/vercel-cache-store.ts +68 -26
- package/src/client-urls/client-root.tsx +1 -0
- package/src/client-urls/navigation.ts +10 -0
- package/src/client-urls/optimistic-location.ts +10 -4
- package/src/client.tsx +40 -3
- package/src/errors.ts +13 -6
- package/src/handles/deferred-resolution.ts +36 -0
- package/src/internal-suspense-audit.ts +12 -0
- package/src/loader-store.ts +4 -9
- package/src/outlet-provider.tsx +25 -1
- package/src/prerender/build-shell-capture.ts +11 -9
- package/src/prerender/cloudflare.ts +130 -0
- package/src/prerender/create-prerender-trigger.ts +780 -0
- package/src/prerender/index.ts +39 -0
- package/src/prerender/memory-prerender-store.ts +115 -0
- package/src/prerender/on-demand.ts +339 -0
- package/src/prerender/producer-guard.ts +66 -0
- package/src/prerender/shell-manifest-key.ts +12 -17
- package/src/prerender/warm-request.ts +154 -0
- package/src/prerender/warm.ts +193 -0
- package/src/prerender/writable-store.ts +278 -0
- package/src/prerender.ts +29 -0
- package/src/reverse.ts +1 -1
- package/src/route-content-wrapper.tsx +107 -13
- package/src/router/error-handling.ts +12 -12
- package/src/router/find-match.ts +1 -0
- package/src/router/handler-context.ts +2 -0
- package/src/router/intercept-resolution.ts +15 -8
- package/src/router/loader-resolution.ts +2 -10
- package/src/router/manifest.ts +2 -0
- package/src/router/match-middleware/background-revalidation.ts +2 -0
- package/src/router/match-middleware/cache-lookup.ts +239 -54
- package/src/router/navigation-snapshot.ts +25 -6
- package/src/router/pattern-matching.ts +14 -0
- package/src/router/prefetch-default.ts +4 -4
- package/src/router/prerender-match.ts +460 -352
- package/src/router/request-classification.ts +15 -12
- package/src/router/route-trie-builder.ts +9 -0
- package/src/router/router-context.ts +1 -0
- package/src/router/router-interfaces.ts +35 -1
- package/src/router/router-options.ts +45 -5
- package/src/router/segment-resolution/fresh.ts +20 -19
- package/src/router/segment-resolution/loader-cache.ts +110 -77
- package/src/router/segment-resolution/revalidation.ts +34 -24
- package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
- package/src/router/trie-matching.ts +3 -0
- package/src/router-versions.ts +60 -0
- package/src/router.ts +107 -1
- package/src/rsc/full-payload.ts +6 -2
- package/src/rsc/handler-context.ts +5 -0
- package/src/rsc/handler.ts +64 -7
- package/src/rsc/progressive-enhancement.ts +5 -1
- package/src/rsc/response-cache-serve.ts +10 -4
- package/src/rsc/rsc-rendering.ts +65 -12
- package/src/rsc/shell-build-manifest.ts +17 -12
- package/src/rsc/shell-capture-constants.ts +39 -7
- package/src/rsc/shell-capture.ts +144 -45
- package/src/rsc/shell-serve.ts +12 -6
- package/src/rsc/ssr-module-loader.ts +26 -0
- package/src/rsc/types.ts +12 -6
- package/src/segment-boundary-content.ts +25 -0
- package/src/segment-loader-promise.ts +28 -38
- package/src/segment-system.tsx +117 -57
- package/src/server/build-version-table.ts +130 -0
- package/src/server/context.ts +40 -17
- package/src/server/cookie-store.ts +5 -0
- package/src/server/handle-store.ts +357 -127
- package/src/server/request-context.ts +149 -12
- package/src/ssr/ssr-root.tsx +32 -13
- package/src/suspense-audit-react.tsx +98 -0
- package/src/suspense-audit.ts +1030 -0
- package/src/testing/action-form.ts +66 -0
- package/src/testing/build-versions.ts +64 -0
- package/src/testing/dispatch.ts +6 -0
- package/src/testing/dom.entry.ts +1 -0
- package/src/testing/flight-runtime.d.ts +27 -0
- package/src/testing/flight.entry.ts +4 -0
- package/src/testing/index.ts +22 -0
- package/src/testing/internal/context.ts +2 -1
- package/src/testing/internal/seed-vars.ts +14 -2
- package/src/testing/internal/server-modules.ts +32 -0
- package/src/testing/location-state-key.ts +18 -8
- package/src/testing/render-handler.ts +2 -1
- package/src/testing/render-route.tsx +293 -46
- package/src/testing/serve-shell-request.ts +262 -37
- package/src/testing/shell-status.ts +17 -6
- package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
- package/src/testing/vitest-stubs/version.ts +4 -1
- package/src/thenable-status.ts +10 -0
- package/src/types/handler-context.ts +15 -5
- package/src/types/route-entry.ts +6 -0
- package/src/urls/path-helper.ts +69 -9
- package/src/use-loader.tsx +57 -30
- package/src/vite/discovery/build-versions.ts +1281 -0
- package/src/vite/discovery/bundle-postprocess.ts +4 -0
- package/src/vite/discovery/discover-routers.ts +15 -0
- package/src/vite/discovery/installed-packages.ts +159 -0
- package/src/vite/discovery/prerender-collection.ts +38 -1
- package/src/vite/discovery/router-versions-phase.ts +336 -0
- package/src/vite/discovery/shell-prerender-phase.ts +33 -43
- package/src/vite/discovery/state.ts +36 -1
- package/src/vite/discovery/virtual-module-codegen.ts +7 -2
- package/src/vite/encryption-key.ts +101 -9
- package/src/vite/inject-client-debug.ts +42 -20
- package/src/vite/plugin-types.ts +28 -0
- package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
- package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
- package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
- package/src/vite/plugins/expose-internal-ids.ts +12 -3
- package/src/vite/plugins/stable-rsc-output.ts +147 -0
- package/src/vite/plugins/version-plugin.ts +19 -24
- package/src/vite/plugins/version.d.ts +17 -2
- package/src/vite/plugins/virtual-entries.ts +39 -6
- package/src/vite/rango.ts +28 -3
- package/src/vite/router-discovery.ts +96 -13
- package/src/vite/utils/bundle-analysis.ts +39 -7
- package/src/vite/utils/client-chunks.ts +5 -1
- package/src/vite/utils/compare-strings.ts +9 -0
- package/src/vite/utils/package-resolution.ts +9 -0
- package/src/vite/utils/prerender-utils.ts +4 -1
- package/src/vite/utils/shared-utils.ts +1 -1
- package/dist/types/segment-content-promise.d.ts +0 -13
- package/src/segment-content-promise.ts +0 -67
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `router.prerender()` trigger factory.
|
|
3
|
+
*
|
|
4
|
+
* Builds the `router.prerender` binder: `(runtime) => runner`, the runner being
|
|
5
|
+
* the per-target callable (plus `.many` / `.remove` / `.markStale`) from router-supplied
|
|
6
|
+
* deps. Pure and testable: all router internals (reverse, match, the producer,
|
|
7
|
+
* the request handler) arrive as injected functions, so this module has no RSC
|
|
8
|
+
* imports and can be unit-tested with fakes.
|
|
9
|
+
*
|
|
10
|
+
* One verb, dispatched by route after the match:
|
|
11
|
+
* - A `Prerender(..., { onDemand })` route takes the requestless render into
|
|
12
|
+
* the prerender store (docs/design/ondemand-prerender.md), then, when the
|
|
13
|
+
* app cache store is shared and an origin resolves, a warm request that
|
|
14
|
+
* rebuilds the runtime caches on top of the new entry.
|
|
15
|
+
* - Every other route is warmed: a cookie-free GET through the router's own
|
|
16
|
+
* handler in which every runtime cache read misses and every write replaces
|
|
17
|
+
* the entry (docs/design/prerender-every-route.md).
|
|
18
|
+
*
|
|
19
|
+
* Contract highlights:
|
|
20
|
+
* - Requestless: the producer render never inherits the caller's request state.
|
|
21
|
+
* - A refresh always renders and replaces; `onlyIfStale` is the cron-sweep
|
|
22
|
+
* opt-in and the only path that returns `already-fresh`.
|
|
23
|
+
* - A failed render/store keeps the previous entry (replace-on-success).
|
|
24
|
+
* - A `notFound()` in the render is not a failure: it stores a "removed"
|
|
25
|
+
* marker in place of the page, stamped with the route's ttl and tags so it
|
|
26
|
+
* is rechecked like a page. `prerender.remove()` stores one without
|
|
27
|
+
* rendering, which never goes stale. A Passthrough build handler that
|
|
28
|
+
* declines (`ctx.passthrough()`) stores one too, so its live handler answers.
|
|
29
|
+
* - A page and a render's marker (a `notFound()` or a decline) carry the
|
|
30
|
+
* render's start as `storedAt`, so a `markStale()` that lands during the
|
|
31
|
+
* render marks the result; `staleAt` counts the ttl from the write. A
|
|
32
|
+
* `remove()` marker renders nothing and carries its write time.
|
|
33
|
+
* - An on-demand target is path-only (its key carries no search); a warm
|
|
34
|
+
* target keeps its search params, which cache keys carry.
|
|
35
|
+
* - A warm writes only to a store shared beyond the place the call runs.
|
|
36
|
+
*/
|
|
37
|
+
import type { SerializedSegmentData } from "../cache/types.js";
|
|
38
|
+
import type { HandlerCacheConfig } from "../rsc/types.js";
|
|
39
|
+
import type { ExecutionContext } from "../types/request-scope.js";
|
|
40
|
+
import type { OnDemandRouteConfig, PrerenderConfig, PrerenderFn, PrerenderResult } from "./on-demand.js";
|
|
41
|
+
/** Route metadata the trigger needs, resolved from a pathname by the router. */
|
|
42
|
+
export interface PrerenderMatchInfo {
|
|
43
|
+
routeName: string;
|
|
44
|
+
params: Record<string, string>;
|
|
45
|
+
/** True when the route opted into on-demand (Prerender(..., { onDemand })). */
|
|
46
|
+
isOnDemand: boolean;
|
|
47
|
+
/** True when the route is wrapped in Passthrough(). */
|
|
48
|
+
isPassthrough: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Raw producer output (matchForPrerender's main-variant shape). `onDemandConfig`
|
|
52
|
+
* is the route's `onDemand: { ttl, tags }` object (the `tags` callback can't
|
|
53
|
+
* ride the serialized trie), read from the loaded route entry during production.
|
|
54
|
+
*/
|
|
55
|
+
export interface ProducerOutput {
|
|
56
|
+
segments: SerializedSegmentData[];
|
|
57
|
+
handles: string;
|
|
58
|
+
routeName: string;
|
|
59
|
+
params: Record<string, string>;
|
|
60
|
+
passthrough?: true;
|
|
61
|
+
/** The render hit `notFound()` and nothing else failed: no page to store. */
|
|
62
|
+
notFound?: true;
|
|
63
|
+
onDemandConfig?: OnDemandRouteConfig;
|
|
64
|
+
}
|
|
65
|
+
export interface PrerenderTriggerDeps<TEnv = any> {
|
|
66
|
+
routerId: string;
|
|
67
|
+
/**
|
|
68
|
+
* The key version: the router's data version (resolvePrerenderVersion),
|
|
69
|
+
* resolved per refresh (once per `.many()` batch), as the request handler
|
|
70
|
+
* resolves it per request.
|
|
71
|
+
*/
|
|
72
|
+
resolveVersion: () => string;
|
|
73
|
+
/** True when running under Vite dev (drives the producer context's `dev`). */
|
|
74
|
+
isDev: () => boolean;
|
|
75
|
+
/**
|
|
76
|
+
* True under the Vite dev server (isViteDevServer): the one place the
|
|
77
|
+
* memory store counts as shared, and so where the not-shared warning fires.
|
|
78
|
+
*/
|
|
79
|
+
isViteDevServer: () => boolean;
|
|
80
|
+
/** Load the per-router manifest/trie before matching, as router.fetch does. */
|
|
81
|
+
ensureManifest: () => Promise<void>;
|
|
82
|
+
/** Resolve the env-scoped prerender config (factory or object); undefined when unconfigured. */
|
|
83
|
+
resolveConfig: (env: TEnv, ctx: ExecutionContext | undefined) => PrerenderConfig<TEnv> | undefined;
|
|
84
|
+
/** Reverse an object target to a pathname; undefined when the route name is unknown. */
|
|
85
|
+
reverse: (route: string, params: Record<string, string>) => string | undefined;
|
|
86
|
+
/** Match a pathname to route metadata; null when nothing matches. */
|
|
87
|
+
matchRoute: (pathname: string) => PrerenderMatchInfo | null | Promise<PrerenderMatchInfo | null>;
|
|
88
|
+
/** Run the requestless producer (matchForPrerender with onDemand). May throw. */
|
|
89
|
+
runProducer: (input: {
|
|
90
|
+
pathname: string;
|
|
91
|
+
isPassthrough: boolean;
|
|
92
|
+
env: TEnv;
|
|
93
|
+
dev: boolean;
|
|
94
|
+
}) => Promise<ProducerOutput | null>;
|
|
95
|
+
/**
|
|
96
|
+
* Resolve `createRouter({ cache })` (factory or object) for a warm;
|
|
97
|
+
* undefined when the router has none. May throw, like the prerender factory.
|
|
98
|
+
*/
|
|
99
|
+
resolveCacheConfig: (env: TEnv, ctx: ExecutionContext) => HandlerCacheConfig | undefined;
|
|
100
|
+
/** The router's own request handler (`router.fetch`): where a warm is dispatched. */
|
|
101
|
+
fetch: (request: Request, input: {
|
|
102
|
+
env: TEnv;
|
|
103
|
+
ctx: ExecutionContext;
|
|
104
|
+
}) => Promise<Response>;
|
|
105
|
+
/** The origin of the request the runner is called from, when there is one. */
|
|
106
|
+
ambientOrigin: () => string | undefined;
|
|
107
|
+
}
|
|
108
|
+
/** Thrown by the trigger when `throwOnError: true` and the operation did not succeed. */
|
|
109
|
+
export declare class PrerenderError extends Error {
|
|
110
|
+
readonly result: Extract<PrerenderResult, {
|
|
111
|
+
ok: false;
|
|
112
|
+
}>;
|
|
113
|
+
/** `operation` is the runner call that failed; the message names it. */
|
|
114
|
+
constructor(result: Extract<PrerenderResult, {
|
|
115
|
+
ok: false;
|
|
116
|
+
}>, operation?: "prerender" | "remove");
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Create the `router.prerender` callable. See {@link PrerenderTriggerDeps}.
|
|
120
|
+
*/
|
|
121
|
+
export declare function createPrerenderTrigger<TEnv = any, TRoutes = {}>(deps: PrerenderTriggerDeps<TEnv>): PrerenderFn<TEnv, TRoutes>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public on-demand prerender surface (`@rangojs/router/prerender`).
|
|
3
|
+
*
|
|
4
|
+
* Store contracts, config/result/target types, and the in-memory store (usable
|
|
5
|
+
* as a durable overlay in single-process node/Vercel functions, and as a test
|
|
6
|
+
* fake). The Cloudflare KV adapter lives in `@rangojs/router/prerender/cloudflare`.
|
|
7
|
+
*/
|
|
8
|
+
export { PrerenderError } from "./create-prerender-trigger.js";
|
|
9
|
+
export { createMemoryPrerenderStore, type MemoryPrerenderStore, type MemoryPrerenderStoreOptions, } from "./memory-prerender-store.js";
|
|
10
|
+
export { serializePrerenderKey, type PrerenderKey, type PrerenderStoredEntry, type WritablePrerenderStore, } from "./writable-store.js";
|
|
11
|
+
export type { OnDemandOption, OnDemandRouteConfig, PrerenderConfig, PrerenderFn, PrerenderResult, PrerenderWarmCaches, PrerenderManyOptions, PrerenderRunner, PrerenderRunOptions, PrerenderRuntime, PrerenderTarget, PrerenderTargetObject, } from "./on-demand.js";
|
|
12
|
+
export type { PrerenderEntry } from "./store.js";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory writable prerender store.
|
|
3
|
+
*
|
|
4
|
+
* Backs the zero-config dev overlay and the `@rangojs/router/testing` fake.
|
|
5
|
+
* A plain Map — no per-isolate null memoization (a later set() must be visible
|
|
6
|
+
* after an earlier miss), no React/RSC deps.
|
|
7
|
+
*/
|
|
8
|
+
import { type PrerenderKey, type PrerenderStoredEntry, type WritablePrerenderStore } from "./writable-store.js";
|
|
9
|
+
export interface MemoryPrerenderStore extends WritablePrerenderStore {
|
|
10
|
+
delete(key: PrerenderKey): Promise<void>;
|
|
11
|
+
markStale(routerId: string, tags: string[]): Promise<void>;
|
|
12
|
+
/** Synchronous read by structured key (tests). */
|
|
13
|
+
peek(key: PrerenderKey): PrerenderStoredEntry | null;
|
|
14
|
+
/** All stored [serializedKey, entry] pairs (tests). */
|
|
15
|
+
entries(): [string, PrerenderStoredEntry][];
|
|
16
|
+
/** Drop every entry. */
|
|
17
|
+
clear(): void;
|
|
18
|
+
readonly size: number;
|
|
19
|
+
}
|
|
20
|
+
export interface MemoryPrerenderStoreOptions {
|
|
21
|
+
/** Injectable clock for `markStale` in deterministic tests. Defaults to Date.now. */
|
|
22
|
+
now?: () => number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create an in-memory {@link WritablePrerenderStore} with test-inspection
|
|
26
|
+
* helpers. Invalidation is mark-stale (sets `staleAt`), never delete — matching
|
|
27
|
+
* the durable v1 contract so the entry keeps serving until refreshed.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createMemoryPrerenderStore(options?: MemoryPrerenderStoreOptions): MemoryPrerenderStore;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type surface for on-demand (ISR-style) prerender: the router
|
|
3
|
+
* `prerender` config option, the `router.prerender({ env, ctx })` runner, its typed
|
|
4
|
+
* target, and the inspectable result union.
|
|
5
|
+
*
|
|
6
|
+
* Types only — safe to import from router options, the public interfaces, the
|
|
7
|
+
* testing barrel, and platform adapters without pulling RSC deps.
|
|
8
|
+
*/
|
|
9
|
+
import type { ExecutionContext } from "../types/request-scope.js";
|
|
10
|
+
import type { IsEmptyObject, ParamsFor } from "../reverse.js";
|
|
11
|
+
import type { WritablePrerenderStore } from "./writable-store.js";
|
|
12
|
+
import type { ShellCaptureRefusal } from "../rsc/shell-capture-constants.js";
|
|
13
|
+
declare const prerenderTargetBrand: unique symbol;
|
|
14
|
+
/**
|
|
15
|
+
* The target `onRevalidate` receives: a plain `{ route, params }` that goes
|
|
16
|
+
* straight into a queue message. The type-only brand (never present at
|
|
17
|
+
* runtime) lets the runner (`router.prerender({ env })(target)` / `.many()`) accepts it with no cast on a
|
|
18
|
+
* router typed with named routes, while a hand-written `{ route: "typo" }`
|
|
19
|
+
* stays a type error there. After a JSON round trip, assert it back:
|
|
20
|
+
* `JSON.parse(raw) as PrerenderTargetObject`.
|
|
21
|
+
*/
|
|
22
|
+
export interface PrerenderTargetObject {
|
|
23
|
+
route: string;
|
|
24
|
+
params: Record<string, string>;
|
|
25
|
+
readonly [prerenderTargetBrand]: true;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Route map used to type the object target. When the fluent `TRoutes` phantom is
|
|
29
|
+
* empty (the common `createRouter().routes(...)` case leaves it `{}`), fall back
|
|
30
|
+
* to the generated global route map — the same fallback `Prerender<"name">` and
|
|
31
|
+
* `reverse` use — so `router.prerender({ route, params })` stays typed without an
|
|
32
|
+
* explicit route-type parameter.
|
|
33
|
+
*/
|
|
34
|
+
type PrerenderRouteMap<TRoutes> = keyof TRoutes extends never ? keyof Rango.GeneratedRouteMap extends never ? {} : Rango.GeneratedRouteMap : TRoutes;
|
|
35
|
+
/**
|
|
36
|
+
* Typed object target, derived from the router's route map the same way
|
|
37
|
+
* `reverse` is. No-param routes may omit `params`.
|
|
38
|
+
*/
|
|
39
|
+
type PrerenderRouteTarget<TRoutes> = {
|
|
40
|
+
[TName in keyof PrerenderRouteMap<TRoutes> & string]: IsEmptyObject<ParamsFor<PrerenderRouteMap<TRoutes>, TName>> extends true ? {
|
|
41
|
+
route: TName;
|
|
42
|
+
params?: Record<string, never>;
|
|
43
|
+
} : {
|
|
44
|
+
route: TName;
|
|
45
|
+
params: ParamsFor<PrerenderRouteMap<TRoutes>, TName>;
|
|
46
|
+
};
|
|
47
|
+
}[keyof PrerenderRouteMap<TRoutes> & string];
|
|
48
|
+
/**
|
|
49
|
+
* A prerender target: a typed named-route object, a path-like string/URL
|
|
50
|
+
* (`router.reverse()` output composes for free), or the `onRevalidate` target.
|
|
51
|
+
*/
|
|
52
|
+
export type PrerenderTarget<TRoutes = {}> = string | URL | PrerenderRouteTarget<TRoutes> | PrerenderTargetObject;
|
|
53
|
+
/** Env + platform capabilities bound once by `router.prerender({ env, ctx })`. */
|
|
54
|
+
export interface PrerenderRuntime<TEnv = any> {
|
|
55
|
+
/** The live env: the producer's `ctx.env` (not the build's `buildEnv`). */
|
|
56
|
+
env: TEnv;
|
|
57
|
+
/** Cloudflare `ExecutionContext`; absent on node/Vercel. */
|
|
58
|
+
ctx?: ExecutionContext;
|
|
59
|
+
/**
|
|
60
|
+
* The origin a warm requests a path or `{ route, params }` target on, for
|
|
61
|
+
* example `"https://shop.example"`. Cache keys carry the host, so a warm
|
|
62
|
+
* must request the host visitors use. A full-URL target uses its own
|
|
63
|
+
* origin; without this option the runner uses the origin of the request it
|
|
64
|
+
* is called from (an action, a route handler, `onRevalidate`), and with
|
|
65
|
+
* neither a warm returns `skipped-no-origin`. An on-demand route's
|
|
66
|
+
* requestless render never needs it.
|
|
67
|
+
*/
|
|
68
|
+
origin?: string;
|
|
69
|
+
}
|
|
70
|
+
/** Per-call options of the bound runner. */
|
|
71
|
+
export interface PrerenderRunOptions {
|
|
72
|
+
/** Throw on failure instead of returning an `{ ok: false }` result. */
|
|
73
|
+
throwOnError?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Only refresh what is cold or stale (cron-sweep opt-in), and the only
|
|
76
|
+
* path that can return `already-fresh`. On an on-demand route the stored
|
|
77
|
+
* entry is rendered only when stale. On any other route the warm reads its
|
|
78
|
+
* caches normally instead of forcing misses, so a fresh entry is left
|
|
79
|
+
* alone, a stale one refreshes and a missing one is written.
|
|
80
|
+
*/
|
|
81
|
+
onlyIfStale?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/** Per-call options of the bound runner's `.many()`. */
|
|
84
|
+
export interface PrerenderManyOptions extends PrerenderRunOptions {
|
|
85
|
+
/** Max concurrent renders in a batch (default 1; any invalid value is 1). */
|
|
86
|
+
concurrency?: number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* What a warm request wrote, by cache. On a result of the warm path, and on
|
|
90
|
+
* an on-demand result whose route was also warmed.
|
|
91
|
+
*/
|
|
92
|
+
export interface PrerenderWarmCaches {
|
|
93
|
+
/**
|
|
94
|
+
* Store writes that landed, by store family: `record` (route `cache()`),
|
|
95
|
+
* `item` (`"use cache"` and loader `cache()`), `response` (the document
|
|
96
|
+
* cache and response routes), `shell` (the `ppr` shell).
|
|
97
|
+
*/
|
|
98
|
+
writes: {
|
|
99
|
+
record: number;
|
|
100
|
+
item: number;
|
|
101
|
+
response: number;
|
|
102
|
+
shell: number;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* The `ppr` shell; absent on a route without `ppr`. `fresh` is
|
|
106
|
+
* `onlyIfStale` finding a servable shell; `not-eligible` is a request the
|
|
107
|
+
* shell path passed on (an active nonce, a store without shells, the
|
|
108
|
+
* route's own `cache()` opt-out, a buffered response).
|
|
109
|
+
*/
|
|
110
|
+
shell?: "stored" | "fresh" | "refused" | "no-shell" | "not-eligible" | "skipped-capacity" | "skipped-queue-timeout" | "error";
|
|
111
|
+
/** Why the capture refused to store the shell. */
|
|
112
|
+
refusal?: ShellCaptureRefusal;
|
|
113
|
+
/** The document cache, when `createDocumentCacheMiddleware` ran. */
|
|
114
|
+
document?: "stored" | "not-cacheable";
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Inspectable result, one per target. The trigger does not force callers
|
|
118
|
+
* into try/catch; `throwOnError: true` opts into throwing for admin/CI
|
|
119
|
+
* endpoints. `path` says what ran: `on-demand` is the requestless render of a
|
|
120
|
+
* `Prerender(..., { onDemand })` route into the prerender store, `warm` is a
|
|
121
|
+
* request through the router's handler that rewrites the route's runtime
|
|
122
|
+
* caches.
|
|
123
|
+
*/
|
|
124
|
+
export type PrerenderResult = {
|
|
125
|
+
ok: true;
|
|
126
|
+
path: "on-demand";
|
|
127
|
+
/**
|
|
128
|
+
* `already-fresh` only occurs with `onlyIfStale: true`.
|
|
129
|
+
*
|
|
130
|
+
* `removed`: a "removed" marker is stored in place of the page, and
|
|
131
|
+
* requests no longer get the page or its build-time entry. It is
|
|
132
|
+
* `ok: true`, so count it in sweep results. Written by
|
|
133
|
+
* `prerender.remove()` (permanent until a refresh without
|
|
134
|
+
* `onlyIfStale`; `tags` empty, no `ttl`), or by a refresh in which a
|
|
135
|
+
* `notFound()` was the only thing that went wrong, anywhere in the
|
|
136
|
+
* render: the route handler, a layout or parallel slot handler, any
|
|
137
|
+
* server component. That marker has the route's `tags` and `ttl` and is
|
|
138
|
+
* rechecked like a page. `onlyIfStale` finding a fresh marker reports
|
|
139
|
+
* `removed` too. When the data source may be failing, throw anything
|
|
140
|
+
* else: that is `render-failed` and keeps the page.
|
|
141
|
+
*/
|
|
142
|
+
status: "rendered" | "already-fresh" | "removed";
|
|
143
|
+
target: string;
|
|
144
|
+
routeName: string;
|
|
145
|
+
/** Opaque, for debugging/logs only — not a stable format. */
|
|
146
|
+
key: string;
|
|
147
|
+
tags: string[];
|
|
148
|
+
/** Absent = never stale. */
|
|
149
|
+
ttl?: number;
|
|
150
|
+
/**
|
|
151
|
+
* The warm request that followed the store write. Absent when the app
|
|
152
|
+
* store is not shared, no origin resolved, or nothing was rendered.
|
|
153
|
+
*/
|
|
154
|
+
caches?: PrerenderWarmCaches;
|
|
155
|
+
} | {
|
|
156
|
+
ok: true;
|
|
157
|
+
path: "warm";
|
|
158
|
+
/** `already-fresh` only occurs with `onlyIfStale: true`. */
|
|
159
|
+
status: "warmed" | "already-fresh";
|
|
160
|
+
/** The absolute URL that was requested. */
|
|
161
|
+
target: string;
|
|
162
|
+
routeName: string;
|
|
163
|
+
/** The warm response's HTTP status. */
|
|
164
|
+
responseStatus: number;
|
|
165
|
+
caches: PrerenderWarmCaches;
|
|
166
|
+
} | {
|
|
167
|
+
ok: false;
|
|
168
|
+
/** Absent when no route matched: every other result is past the match. */
|
|
169
|
+
path?: "on-demand" | "warm";
|
|
170
|
+
status: "no-match" | "no-store" | "skipped-personalized" | "skipped-unsupported-target" | "skipped-passthrough" | "skipped-not-on-demand" | "skipped-store-not-shared" | "skipped-no-origin" | "shell-not-stored" | "skipped-uncached" | "render-failed" | "store-failed";
|
|
171
|
+
target: string;
|
|
172
|
+
routeName?: string;
|
|
173
|
+
/** The warm response's HTTP status, when a warm request ran. */
|
|
174
|
+
responseStatus?: number;
|
|
175
|
+
caches?: PrerenderWarmCaches;
|
|
176
|
+
error?: unknown;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Per-route on-demand opt-in, carried inside `PrerenderOptions.onDemand`. Any
|
|
180
|
+
* truthy spelling works (literal, spread, imported const): producer retention
|
|
181
|
+
* is driven by the evaluated route manifest, not a textual scan of the call.
|
|
182
|
+
*/
|
|
183
|
+
export interface OnDemandRouteConfig {
|
|
184
|
+
/** Soft TTL (seconds) for entries refreshed for this route. Overrides the router `ttl`. */
|
|
185
|
+
ttl?: number;
|
|
186
|
+
/**
|
|
187
|
+
* Tags stamped on the stored entry, addressable via
|
|
188
|
+
* the runner's `markStale()`. A separate namespace from `cacheTag()` /
|
|
189
|
+
* `updateTag()` / `revalidateTag()`, which never reach the prerender store.
|
|
190
|
+
*/
|
|
191
|
+
tags?: string[] | ((target: {
|
|
192
|
+
params: Record<string, string>;
|
|
193
|
+
}) => string[]);
|
|
194
|
+
}
|
|
195
|
+
export type OnDemandOption = boolean | OnDemandRouteConfig;
|
|
196
|
+
/**
|
|
197
|
+
* Env-resolved prerender store configuration. Same union shape as the `cache`
|
|
198
|
+
* option: a plain object or a factory resolved per request/per call, never at
|
|
199
|
+
* `createRouter()` time.
|
|
200
|
+
*/
|
|
201
|
+
export interface PrerenderConfig<TEnv = any> {
|
|
202
|
+
store: WritablePrerenderStore;
|
|
203
|
+
/**
|
|
204
|
+
* Default soft TTL (seconds) for a route whose `onDemand` sets none. Absent
|
|
205
|
+
* = never stale. Soft: a stale entry keeps serving; staleness only decides
|
|
206
|
+
* whether `onRevalidate` is scheduled. Entries never expire.
|
|
207
|
+
*/
|
|
208
|
+
ttl?: number;
|
|
209
|
+
/**
|
|
210
|
+
* Stale-while-revalidate: its presence is the opt-in. Scheduled through
|
|
211
|
+
* `waitUntil` on a stale overlay hit (the stale entry still serves) with
|
|
212
|
+
* the JSON-serializable target and the live env. Runs at most once per
|
|
213
|
+
* stale key per isolate while one is in flight (one running longer than
|
|
214
|
+
* 15 s counts as finished, so a hung call cannot block the key), so
|
|
215
|
+
* `(target, env, ctx) => router.prerender({ env, ctx })(target, { onlyIfStale: true })`
|
|
216
|
+
* is safe in a single process; across isolates, point it at a queue, which
|
|
217
|
+
* owns dedup. Pass `onlyIfStale` in the call that runs later: a job queued
|
|
218
|
+
* for a page that `prerender.remove()` then removed finds the marker and
|
|
219
|
+
* renders nothing, where a plain refresh would bring the page back.
|
|
220
|
+
*/
|
|
221
|
+
onRevalidate?: (target: PrerenderTargetObject, env: TEnv,
|
|
222
|
+
/** The stale request's execution context; absent where none exists. */
|
|
223
|
+
ctx?: ExecutionContext) => void | Promise<void>;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* @internal Per-request resolution of the prerender config plus the key
|
|
227
|
+
* version. Threaded onto the request context by the RSC handler and read by
|
|
228
|
+
* the serve-path overlay lookup.
|
|
229
|
+
*/
|
|
230
|
+
export interface ResolvedPrerender<TEnv = any> {
|
|
231
|
+
/** The resolved config; the store is `config.store`. */
|
|
232
|
+
config: PrerenderConfig<TEnv>;
|
|
233
|
+
routerId: string;
|
|
234
|
+
/** The router's cache version for overlay keys (resolvePrerenderVersion). */
|
|
235
|
+
version: string;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* The runner `router.prerender({ env, ctx })` returns: callable per target,
|
|
239
|
+
* plus `.many` / `.remove` / `.markStale`. Typing rides the phantom `TRoutes`
|
|
240
|
+
* accumulator, like `reverse`.
|
|
241
|
+
*/
|
|
242
|
+
export interface PrerenderRunner<TRoutes = {}> {
|
|
243
|
+
(target: PrerenderTarget<TRoutes>, options?: PrerenderRunOptions): Promise<PrerenderResult>;
|
|
244
|
+
many(targets: ReadonlyArray<PrerenderTarget<TRoutes>>, options?: PrerenderManyOptions): Promise<PrerenderResult[]>;
|
|
245
|
+
/**
|
|
246
|
+
* Remove an on-demand route's page without rendering: a "removed" marker is
|
|
247
|
+
* stored in place of it. Requests then get a 404 (a `Passthrough` route: its
|
|
248
|
+
* live handler), never the build-time entry. Permanent: no `ttl`, no
|
|
249
|
+
* `onRevalidate`, left by `onlyIfStale`, until a `prerender(target)`
|
|
250
|
+
* without `onlyIfStale` renders the page again. It is a write like a
|
|
251
|
+
* refresh's, and the last write wins. `{ ok: true, status: "removed" }`; a
|
|
252
|
+
* route that is not on-demand is `skipped-not-on-demand`. `.many` is the
|
|
253
|
+
* batch form.
|
|
254
|
+
*/
|
|
255
|
+
remove: {
|
|
256
|
+
(target: PrerenderTarget<TRoutes>, options?: Pick<PrerenderRunOptions, "throwOnError">): Promise<PrerenderResult>;
|
|
257
|
+
many(targets: ReadonlyArray<PrerenderTarget<TRoutes>>, options?: Omit<PrerenderManyOptions, "onlyIfStale">): Promise<PrerenderResult[]>;
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* Mark this router's stored entries carrying one of `tags` stale. Marking only: the
|
|
261
|
+
* entries keep serving, and a stale hit schedules `onRevalidate` when one is
|
|
262
|
+
* configured. Nothing is deleted or re-rendered here.
|
|
263
|
+
*/
|
|
264
|
+
markStale(tags: string[]): Promise<void>;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* The `router.prerender` function: binds the runtime (`env`, `ctx`) and returns
|
|
268
|
+
* the {@link PrerenderRunner}. Binding does no work; the config factory is
|
|
269
|
+
* resolved per call.
|
|
270
|
+
*/
|
|
271
|
+
export type PrerenderFn<TEnv = any, TRoutes = {}> = (runtime: PrerenderRuntime<TEnv>) => PrerenderRunner<TRoutes>;
|
|
272
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requestless-prerender guard.
|
|
3
|
+
*
|
|
4
|
+
* The on-demand producer renders a route with only route params + env — never
|
|
5
|
+
* the triggering request's cookies, headers, geo, or session (see the design's
|
|
6
|
+
* Producer Semantics). Personalized reads during a producer render would bake
|
|
7
|
+
* one visitor's data into a payload shared with everyone for that route.
|
|
8
|
+
*
|
|
9
|
+
* The producer marks its own request context with `_onDemandProducer: true`. The
|
|
10
|
+
* standalone request-scoped helpers a handler reaches for — `cookies()`,
|
|
11
|
+
* `headers()`, `invalidateClientCache()`, `keepClientCache()` — call
|
|
12
|
+
* {@link assertNotInsidePrerenderProducer}, which throws
|
|
13
|
+
* {@link PrerenderPersonalizationError}; the producer catches it and maps to a
|
|
14
|
+
* `skipped-personalized` result, leaving any existing entry untouched. (The
|
|
15
|
+
* producer context's own `ctx.cookie`/`header`/etc. remain the pre-existing
|
|
16
|
+
* build-time no-op stubs over empty synthetic state — the guard covers the
|
|
17
|
+
* standalone helpers, which are the reads that would otherwise see real request
|
|
18
|
+
* data.)
|
|
19
|
+
*/
|
|
20
|
+
/** Thrown when a producer render touches request-specific state. */
|
|
21
|
+
export declare class PrerenderPersonalizationError extends Error {
|
|
22
|
+
/** Cross-realm-safe brand (instanceof can fail across bundle/realm boundaries). */
|
|
23
|
+
readonly isPrerenderPersonalizationError: true;
|
|
24
|
+
constructor(api: string);
|
|
25
|
+
}
|
|
26
|
+
export declare function isPrerenderPersonalizationError(value: unknown): value is PrerenderPersonalizationError;
|
|
27
|
+
/**
|
|
28
|
+
* Throw {@link PrerenderPersonalizationError} when `ctx` is a requestless
|
|
29
|
+
* on-demand producer context. A no-op for the live request path and the
|
|
30
|
+
* build/dev prerender paths (which do not set `_onDemandProducer`), so existing
|
|
31
|
+
* callers are unaffected.
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertNotInsidePrerenderProducer(ctx: unknown, api: string): void;
|
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Build shell manifest key, shared by the producer (the build's shell
|
|
3
|
-
* prerender phase, vite/discovery/shell-prerender-phase.ts) and the consumer
|
|
4
|
-
* (the runtime read-through, rsc/shell-build-manifest.ts) so the format
|
|
5
|
-
* cannot drift. Dependency-free: the producer runs node-side in the plugin,
|
|
6
|
-
* the consumer in the RSC runtime.
|
|
7
|
-
*
|
|
8
|
-
* PATHNAME-ONLY by design. Host-free because the build knows no request
|
|
9
|
-
* host. Router-free because the router id ($$id) is a hash of
|
|
10
|
-
* filePath:lineNumber of the TRANSFORMED source, which differs between the
|
|
11
|
-
* discovery temp server's dev-style transform chain and the main build's —
|
|
12
|
-
* a temp-realm router id can never be looked up by the shipped worker. The
|
|
13
|
-
* producer instead detects pathname collisions across routers at build time
|
|
14
|
-
* and declines both entries (loudly), keeping the key unambiguous. This is
|
|
15
|
-
* a manifest namespace, never a store keyspace: the runtime shell key
|
|
16
|
-
* (host + pathname + search + ":shell") stays untouched.
|
|
17
|
-
*/
|
|
18
|
-
export declare function buildShellManifestKey(pathname: string): string;
|
|
1
|
+
export declare function buildShellManifestKey(routerId: string, pathname: string): string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mark and record of a `router.prerender()` warm request
|
|
3
|
+
* (docs/design/prerender-every-route.md, "The synthetic request"). The
|
|
4
|
+
* trigger marks the `Request` it dispatches, the handler puts the record on
|
|
5
|
+
* the request context (`RequestContext._prerenderWarm`), and each cache layer
|
|
6
|
+
* reads the context where it reads its store. No runtime imports: the cache
|
|
7
|
+
* layers import this without pulling the trigger.
|
|
8
|
+
*/
|
|
9
|
+
import type { HandlerCacheConfig } from "../rsc/types.js";
|
|
10
|
+
import type { ShellCaptureDebugEvent } from "../rsc/shell-capture.js";
|
|
11
|
+
import type { PrerenderWarmCaches } from "./on-demand.js";
|
|
12
|
+
/** What one warm request did: written by the layers, read by the trigger. */
|
|
13
|
+
export interface PrerenderWarmRecord extends PrerenderWarmCaches {
|
|
14
|
+
/**
|
|
15
|
+
* `replace`: every runtime cache read misses and the writes replace the
|
|
16
|
+
* entries. `fill` (`onlyIfStale`): reads are normal; the record only reports.
|
|
17
|
+
*/
|
|
18
|
+
mode: "replace" | "fill";
|
|
19
|
+
/**
|
|
20
|
+
* The `createRouter({ cache })` config the trigger gated on its scope. The
|
|
21
|
+
* handler uses it instead of resolving the option again, so the store that
|
|
22
|
+
* was checked is the store that is written.
|
|
23
|
+
*/
|
|
24
|
+
cacheConfig: HandlerCacheConfig;
|
|
25
|
+
/** The surface an identity guard refused (server/context.ts guardIdentityRead). */
|
|
26
|
+
identity?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The request's render errors: the handler adopts this list as
|
|
29
|
+
* `RequestContext._renderErrors`, so what the layers push lands here.
|
|
30
|
+
*/
|
|
31
|
+
renderErrors: unknown[];
|
|
32
|
+
}
|
|
33
|
+
/** What the helpers read; a derived capture context inherits the record. */
|
|
34
|
+
export interface WarmContext {
|
|
35
|
+
_prerenderWarm?: PrerenderWarmRecord;
|
|
36
|
+
_shellCaptureRun?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function createWarmRecord(mode: PrerenderWarmRecord["mode"], cacheConfig: HandlerCacheConfig): PrerenderWarmRecord;
|
|
39
|
+
/** The mark is the object: a clone or a rebuilt request carries none. */
|
|
40
|
+
export declare function markWarmRequest(request: Request, record: PrerenderWarmRecord): void;
|
|
41
|
+
export declare function readWarmMark(request: Request): PrerenderWarmRecord | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* True when this request must read every runtime cache as a miss. A flag read
|
|
44
|
+
* at each read point, not a store wrapper: a route's or loader's own
|
|
45
|
+
* `cache({ store })` bypasses the app store (CacheScope.getStore), and the
|
|
46
|
+
* "use cache" in-flight dedup lives outside the store, so a wrapper would
|
|
47
|
+
* miss both.
|
|
48
|
+
*
|
|
49
|
+
* False in the warm's own shell capture (`_shellCaptureRun`): it runs after
|
|
50
|
+
* the warm's writes settled and must replay them, or it would render every
|
|
51
|
+
* handler a second time and could bake another generation than the one just
|
|
52
|
+
* written.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isWarmReplace(ctx: WarmContext | null | undefined): boolean;
|
|
55
|
+
/** Count a store write that landed. No-op outside a warm. */
|
|
56
|
+
export declare function noteWarmWrite(ctx: WarmContext | null | undefined, family: keyof PrerenderWarmCaches["writes"]): void;
|
|
57
|
+
/** Record the document cache's outcome; its store write counts through noteWarmWrite. */
|
|
58
|
+
export declare function noteWarmDocument(ctx: WarmContext | null | undefined, outcome: NonNullable<PrerenderWarmCaches["document"]>): void;
|
|
59
|
+
/** Record the surface an identity guard (guardIdentityRead) is about to refuse. */
|
|
60
|
+
export declare function noteWarmIdentityRead(ctx: WarmContext | null | undefined, surface: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* Fold one capture event into the record: the last attempt's outcome stands.
|
|
63
|
+
* The skips a forced capture never takes and the `backoff` notice leave it.
|
|
64
|
+
*/
|
|
65
|
+
export declare function noteWarmShellEvent(record: PrerenderWarmRecord, event: ShellCaptureDebugEvent): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One `router.prerender()` warm request: build the synthetic visitor request,
|
|
3
|
+
* dispatch it through the router's own handler, wait for everything it
|
|
4
|
+
* scheduled, and read back what it wrote
|
|
5
|
+
* (docs/design/prerender-every-route.md, "The synthetic request"). The handler
|
|
6
|
+
* is an injected `fetch`, so unit tests drive this with a fake.
|
|
7
|
+
*/
|
|
8
|
+
import { type CollectingExecutionContext } from "../cache/background-task.js";
|
|
9
|
+
import type { HandlerCacheConfig } from "../rsc/types.js";
|
|
10
|
+
import type { ExecutionContext } from "../types/request-scope.js";
|
|
11
|
+
import type { PrerenderResult, PrerenderWarmCaches } from "./on-demand.js";
|
|
12
|
+
import { type PrerenderWarmRecord } from "./warm-request.js";
|
|
13
|
+
/**
|
|
14
|
+
* How long a warm waits for its response body and background work: a capture
|
|
15
|
+
* that waited out the queue budget, then ran to its hard cap.
|
|
16
|
+
*/
|
|
17
|
+
export declare const WARM_SETTLE_BUDGET_MS: number;
|
|
18
|
+
export interface WarmRequestInput<TEnv> {
|
|
19
|
+
/** The absolute URL a visitor requests. */
|
|
20
|
+
url: URL;
|
|
21
|
+
mode: PrerenderWarmRecord["mode"];
|
|
22
|
+
/** The gated `createRouter({ cache })` config; the handler writes to its store. */
|
|
23
|
+
cacheConfig: HandlerCacheConfig;
|
|
24
|
+
env: TEnv;
|
|
25
|
+
/** The context `cacheConfig` was resolved with. */
|
|
26
|
+
ctx: CollectingExecutionContext;
|
|
27
|
+
/** `router.fetch`. */
|
|
28
|
+
fetch: (request: Request, input: {
|
|
29
|
+
env: TEnv;
|
|
30
|
+
ctx: ExecutionContext;
|
|
31
|
+
}) => Promise<Response>;
|
|
32
|
+
/** Tests shorten the wait. */
|
|
33
|
+
settleBudgetMs?: number;
|
|
34
|
+
}
|
|
35
|
+
export interface WarmRequestOutcome {
|
|
36
|
+
record: PrerenderWarmRecord;
|
|
37
|
+
/** Absent when the handler threw before answering. */
|
|
38
|
+
responseStatus?: number;
|
|
39
|
+
/** The handler's throw, a body that failed mid-stream, or a wait that ran out. */
|
|
40
|
+
error?: unknown;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Dispatch one warm: a GET with `accept: text/html` and nothing else, so it
|
|
44
|
+
* lands in the partition a header-less visitor lands in. The response is
|
|
45
|
+
* drained and dropped: what it rendered reaches a visitor only through a
|
|
46
|
+
* store.
|
|
47
|
+
*/
|
|
48
|
+
export declare function runWarmRequest<TEnv>(input: WarmRequestInput<TEnv>): Promise<WarmRequestOutcome>;
|
|
49
|
+
/** The per-cache detail a result carries. */
|
|
50
|
+
export declare function warmCaches(record: PrerenderWarmRecord): PrerenderWarmCaches;
|
|
51
|
+
/**
|
|
52
|
+
* A warm's status from what it recorded, first match wins. An identity
|
|
53
|
+
* refusal comes ahead of the render failure it usually also causes (the guard
|
|
54
|
+
* throws). `record.shell` is set only for a `ppr` route (shellServePlan).
|
|
55
|
+
*/
|
|
56
|
+
export declare function warmStatus(outcome: WarmRequestOutcome): WarmStatus;
|
|
57
|
+
type WarmStatus = Extract<PrerenderResult, {
|
|
58
|
+
ok: true;
|
|
59
|
+
path: "warm";
|
|
60
|
+
}>["status"] | Extract<PrerenderResult, {
|
|
61
|
+
ok: false;
|
|
62
|
+
}>["status"];
|
|
63
|
+
/** The `PrerenderResult` of a finished warm. */
|
|
64
|
+
export declare function composeWarmResult(outcome: WarmRequestOutcome, target: string, routeName: string): PrerenderResult;
|
|
65
|
+
export {};
|