@rangojs/router 0.0.0-experimental.32 → 0.0.0-experimental.3232cd17
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/AGENTS.md +4 -0
- package/README.md +198 -44
- package/dist/bin/rango.js +287 -105
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3248 -1117
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +73 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +107 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +245 -21
- package/skills/caching/SKILL.md +302 -6
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +270 -30
- package/skills/host-router/SKILL.md +82 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +49 -5
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +294 -30
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +203 -7
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +250 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +97 -5
- package/skills/router-setup/SKILL.md +90 -5
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +124 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +92 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/skills/typesafety/SKILL.md +329 -27
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +67 -40
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +86 -147
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +148 -19
- package/src/browser/navigation-client.ts +187 -67
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/partial-update.ts +123 -94
- package/src/browser/prefetch/cache.ts +214 -36
- package/src/browser/prefetch/fetch.ts +260 -38
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +93 -11
- package/src/browser/react/NavigationProvider.tsx +115 -34
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +49 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +166 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +22 -5
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +215 -76
- package/src/browser/scroll-restoration.ts +46 -39
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +176 -50
- package/src/browser/types.ts +95 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +137 -32
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +278 -96
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +149 -43
- package/src/cache/cache-scope.ts +148 -81
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2550 -93
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +78 -27
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +6 -30
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +33 -100
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +799 -0
- package/src/client.rsc.tsx +6 -21
- package/src/client.tsx +108 -290
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/errors.ts +30 -4
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/breadcrumbs.ts +16 -5
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +52 -26
- package/src/index.ts +100 -38
- package/src/internal-debug.ts +2 -4
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +437 -274
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +52 -10
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +108 -9
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +83 -41
- package/src/router/intercept-resolution.ts +25 -23
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +213 -30
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- package/src/router/match-api.ts +120 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +27 -6
- package/src/router/match-middleware/cache-lookup.ts +205 -249
- package/src/router/match-middleware/cache-store.ts +45 -32
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -40
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +173 -143
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +109 -63
- package/src/router/prerender-match.ts +190 -54
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +63 -55
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +6 -28
- package/src/router/router-interfaces.ts +100 -35
- package/src/router/router-options.ts +91 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +242 -75
- package/src/router/segment-resolution/helpers.ts +63 -24
- package/src/router/segment-resolution/loader-cache.ts +41 -37
- package/src/router/segment-resolution/revalidation.ts +456 -372
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +2 -3
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +91 -46
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +134 -43
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +492 -383
- package/src/rsc/helpers.ts +162 -46
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +30 -3
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +90 -63
- package/src/rsc/rsc-rendering.ts +56 -54
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +74 -67
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +25 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +134 -0
- package/src/segment-system.tsx +272 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +309 -61
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +26 -24
- package/src/server/loader-registry.ts +10 -28
- package/src/server/request-context.ts +338 -126
- package/src/ssr/index.tsx +23 -15
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +618 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +232 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +348 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +330 -0
- package/src/testing/render-route.tsx +566 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +233 -81
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +107 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -14
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +192 -99
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +51 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin-types.ts +187 -69
- package/src/vite/plugins/cjs-to-esm.ts +8 -18
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/plugins/expose-action-id.ts +49 -98
- package/src/vite/plugins/expose-id-utils.ts +11 -50
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
- package/src/vite/plugins/expose-internal-ids.ts +554 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +41 -20
- package/src/vite/plugins/virtual-entries.ts +2 -17
- package/src/vite/rango.ts +257 -289
- package/src/vite/router-discovery.ts +930 -140
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +27 -29
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
package/src/outlet-context.ts
CHANGED
package/src/outlet-provider.tsx
CHANGED
|
@@ -5,11 +5,7 @@ import { OutletContext, type OutletContextValue } from "./outlet-context.js";
|
|
|
5
5
|
import type { ResolvedSegment } from "./types.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Stores a reference to parent context so useLoader can walk up the chain
|
|
11
|
-
* to find loader data from parent layouts. If this segment defines a loading
|
|
12
|
-
* component, Outlet will wrap content with Suspense using that as fallback.
|
|
8
|
+
* Outlet content provider — stores parent context for useLoader chain walking.
|
|
13
9
|
*/
|
|
14
10
|
export function OutletProvider({
|
|
15
11
|
content,
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Deterministic param hashing for prerender storage keys.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* runtime (worker) to look up pre-rendered data. Both environments
|
|
6
|
-
* must produce identical hashes for the same params.
|
|
7
|
-
*
|
|
8
|
-
* Uses a simple DJB2-based hash that works in all JS environments
|
|
9
|
-
* (Node.js, Cloudflare Workers, browsers) without crypto imports.
|
|
3
|
+
* Used at build time and runtime; both must produce identical hashes.
|
|
4
|
+
* DJB2-based; works in all JS environments without crypto imports.
|
|
10
5
|
*/
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
* Compute a deterministic hash string from route params.
|
|
14
|
-
* For static routes (no params), returns "_".
|
|
15
|
-
*/
|
|
7
|
+
// For static routes (no params), returns "_".
|
|
16
8
|
export function hashParams(params: Record<string, string>): string {
|
|
17
9
|
const entries = Object.entries(params);
|
|
18
10
|
if (entries.length === 0) return "_";
|
|
@@ -27,6 +19,13 @@ export function hashParams(params: Record<string, string>): string {
|
|
|
27
19
|
/**
|
|
28
20
|
* DJB2 hash returning an 8-char hex string.
|
|
29
21
|
* Deterministic across all JS runtimes.
|
|
22
|
+
*
|
|
23
|
+
* 32-bit output: per-route collision probability hits ~50% near ~77k distinct
|
|
24
|
+
* param sets (birthday bound). The production store keys solely on
|
|
25
|
+
* routeName/paramHash and does not verify the canonical param string, so a
|
|
26
|
+
* collision serves the surviving entry for both param sets. Benign for typical
|
|
27
|
+
* catalogs; revisit (wider hash or stored-param verification) before
|
|
28
|
+
* pre-rendering hundreds of thousands of pages per route.
|
|
30
29
|
*/
|
|
31
30
|
function djb2Hex(str: string): string {
|
|
32
31
|
let hash = 5381;
|
package/src/prerender/store.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prerender Store
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* The manifest module is lazily loaded via globalThis.__loadPrerenderManifestModule,
|
|
6
|
-
* a function injected into the RSC entry that returns the manifest module
|
|
7
|
-
* containing a key-to-specifier map and a `loadPrerenderAsset` function
|
|
8
|
-
* that anchors import() resolution relative to the manifest file.
|
|
2
|
+
* Prerender Store — reads pre-rendered segment data from the worker bundle.
|
|
3
|
+
* Manifest module (injected via globalThis.__loadPrerenderManifestModule)
|
|
4
|
+
* contains key-to-specifier map and loadPrerenderAsset for import() resolution.
|
|
9
5
|
*/
|
|
10
6
|
|
|
11
|
-
import type {
|
|
12
|
-
SerializedSegmentData,
|
|
13
|
-
SegmentHandleData,
|
|
14
|
-
} from "../cache/types.js";
|
|
7
|
+
import type { SerializedSegmentData } from "../cache/types.js";
|
|
15
8
|
|
|
16
9
|
export interface PrerenderEntry {
|
|
17
10
|
segments: SerializedSegmentData[];
|
|
18
|
-
|
|
11
|
+
/** RSC-encoded handle map (see handle-snapshot.ts encodeHandles); "" when the
|
|
12
|
+
* route pushed no handles. Encoded so Promise/ReactNode handle values survive
|
|
13
|
+
* the JSON-serialized build artifact / dev wire, identical to the runtime cache. */
|
|
14
|
+
handles: string;
|
|
19
15
|
}
|
|
20
16
|
|
|
21
17
|
export interface PrerenderStore {
|
|
@@ -28,7 +24,9 @@ export interface PrerenderStore {
|
|
|
28
24
|
|
|
29
25
|
export interface StaticEntry {
|
|
30
26
|
encoded: string;
|
|
31
|
-
|
|
27
|
+
/** RSC-encoded single-segment handle data (see encodeHandleValue); "" when the
|
|
28
|
+
* Static handler pushed no handles. */
|
|
29
|
+
handles: string;
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
export interface StaticStore {
|
|
@@ -99,13 +97,20 @@ export function createPrerenderStore(): PrerenderStore | null {
|
|
|
99
97
|
if (!globalThis.__loadPrerenderManifestModule) return null;
|
|
100
98
|
|
|
101
99
|
const cache = new Map<string, Promise<PrerenderEntry | null>>();
|
|
102
|
-
let manifestModulePromise: Promise<PrerenderManifestModule | null
|
|
103
|
-
null;
|
|
100
|
+
let manifestModulePromise: Promise<PrerenderManifestModule> | null = null;
|
|
104
101
|
|
|
105
|
-
function loadManifestModule(): Promise<PrerenderManifestModule
|
|
102
|
+
function loadManifestModule(): Promise<PrerenderManifestModule> {
|
|
106
103
|
if (!manifestModulePromise) {
|
|
104
|
+
// Do not cache a failed manifest-module load: clear the memoized promise
|
|
105
|
+
// on rejection so the next get() retries, and let the error propagate
|
|
106
|
+
// (consistent with the per-asset load policy below) instead of caching a
|
|
107
|
+
// null for the isolate lifetime, which would silently degrade every
|
|
108
|
+
// prerendered route to a miss after one transient failure.
|
|
107
109
|
manifestModulePromise = globalThis.__loadPrerenderManifestModule!().catch(
|
|
108
|
-
() =>
|
|
110
|
+
(err) => {
|
|
111
|
+
manifestModulePromise = null;
|
|
112
|
+
throw err;
|
|
113
|
+
},
|
|
109
114
|
);
|
|
110
115
|
}
|
|
111
116
|
return manifestModulePromise;
|
|
@@ -118,37 +123,28 @@ export function createPrerenderStore(): PrerenderStore | null {
|
|
|
118
123
|
if (cached) return cached;
|
|
119
124
|
|
|
120
125
|
const promise = loadManifestModule().then((mod) => {
|
|
121
|
-
if (!mod) return null;
|
|
122
126
|
const specifier = mod.default[key];
|
|
123
127
|
if (!specifier) return null;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
// Let asset load errors propagate — a missing/corrupted artifact
|
|
129
|
+
// for a key that exists in the manifest is a build/deploy error
|
|
130
|
+
// and should surface as a 500, not be silently swallowed as null
|
|
131
|
+
// (which the handler stub would misreport as a 404).
|
|
132
|
+
return mod.loadPrerenderAsset(specifier).then((asset) => asset.default);
|
|
128
133
|
});
|
|
129
|
-
|
|
134
|
+
// Only memoize once the manifest module resolved: a manifest-load
|
|
135
|
+
// rejection must not poison the per-key cache, or the retry above is moot.
|
|
136
|
+
cache.set(
|
|
137
|
+
key,
|
|
138
|
+
promise.catch((err) => {
|
|
139
|
+
cache.delete(key);
|
|
140
|
+
throw err;
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
130
143
|
return promise;
|
|
131
144
|
},
|
|
132
145
|
};
|
|
133
146
|
}
|
|
134
147
|
|
|
135
|
-
/**
|
|
136
|
-
* Load the prerender manifest index for test introspection.
|
|
137
|
-
* Returns the key→specifier map or null if unavailable.
|
|
138
|
-
*/
|
|
139
|
-
export async function loadPrerenderManifestIndex(): Promise<Record<
|
|
140
|
-
string,
|
|
141
|
-
string
|
|
142
|
-
> | null> {
|
|
143
|
-
if (!globalThis.__loadPrerenderManifestModule) return null;
|
|
144
|
-
try {
|
|
145
|
-
const mod = await globalThis.__loadPrerenderManifestModule();
|
|
146
|
-
return mod.default;
|
|
147
|
-
} catch {
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
148
|
/**
|
|
153
149
|
* Create a static segment store.
|
|
154
150
|
* Production only: backed by globalThis.__STATIC_MANIFEST injected at build time.
|
|
@@ -173,7 +169,7 @@ export function createStaticStore(): StaticStore | null {
|
|
|
173
169
|
const val = mod.default;
|
|
174
170
|
// Normalize: string-only (no handles) or { encoded, handles }
|
|
175
171
|
if (typeof val === "string") {
|
|
176
|
-
return { encoded: val, handles:
|
|
172
|
+
return { encoded: val, handles: "" } as StaticEntry;
|
|
177
173
|
}
|
|
178
174
|
return val as StaticEntry;
|
|
179
175
|
})
|
package/src/prerender.ts
CHANGED
|
@@ -36,7 +36,9 @@ import type { Handle } from "./handle.js";
|
|
|
36
36
|
import type { ContextVar } from "./context-var.js";
|
|
37
37
|
import type { ReverseFunction } from "./reverse.js";
|
|
38
38
|
import type { DefaultReverseRouteMap } from "./types/global-namespace.js";
|
|
39
|
+
import type { UseItems, HandlerUseItem } from "./route-types.js";
|
|
39
40
|
import { isCachedFunction } from "./cache/taint.js";
|
|
41
|
+
import { isUnderTestRunner } from "./runtime-env.js";
|
|
40
42
|
|
|
41
43
|
// -- Named route resolution types -------------------------------------------
|
|
42
44
|
|
|
@@ -68,9 +70,9 @@ type BuildReverseFunction = [DefaultReverseRouteMap] extends [
|
|
|
68
70
|
* Default route map for Prerender named route resolution.
|
|
69
71
|
* Uses GeneratedRouteMap (from gen file) to avoid circular dependencies.
|
|
70
72
|
*/
|
|
71
|
-
type DefaultPrerenderRouteMap = keyof
|
|
73
|
+
type DefaultPrerenderRouteMap = keyof Rango.GeneratedRouteMap extends never
|
|
72
74
|
? {}
|
|
73
|
-
:
|
|
75
|
+
: Rango.GeneratedRouteMap;
|
|
74
76
|
|
|
75
77
|
/** Extract params from a route map entry (string pattern or { path } object). */
|
|
76
78
|
type ExtractParamsFromEntry<TEntry> = TEntry extends string
|
|
@@ -105,13 +107,6 @@ type ResolvePrerenderParams<
|
|
|
105
107
|
// -- Types ------------------------------------------------------------------
|
|
106
108
|
|
|
107
109
|
export interface PrerenderOptions {
|
|
108
|
-
/**
|
|
109
|
-
* Keep handler in server bundle for live fallback (default: false).
|
|
110
|
-
* false: handler replaced with stub, source-only APIs excluded from bundle.
|
|
111
|
-
* true: handler stays in bundle, unknown params render live at request time.
|
|
112
|
-
*/
|
|
113
|
-
passthrough?: boolean;
|
|
114
|
-
|
|
115
110
|
/**
|
|
116
111
|
* Maximum number of param sets to render in parallel (default: 1).
|
|
117
112
|
* Only applies to dynamic Prerender handlers with getParams().
|
|
@@ -131,8 +126,8 @@ export interface PrerenderOptions {
|
|
|
131
126
|
|
|
132
127
|
/**
|
|
133
128
|
* Context passed to Prerender() handlers at build time.
|
|
134
|
-
* Has a synthetic URL from getParams, params, and
|
|
135
|
-
* No request,
|
|
129
|
+
* Has a synthetic URL from getParams, params, pathname, and optionally env.
|
|
130
|
+
* No request, headers, cookies.
|
|
136
131
|
*/
|
|
137
132
|
export interface BuildContext<TParams> {
|
|
138
133
|
/** Params extracted from the route pattern (populated from getParams). */
|
|
@@ -141,6 +136,23 @@ export interface BuildContext<TParams> {
|
|
|
141
136
|
/** True during build-time pre-rendering, false during passthrough live render. */
|
|
142
137
|
build: true;
|
|
143
138
|
|
|
139
|
+
/**
|
|
140
|
+
* True when running in Vite dev mode (on-demand prerender), false during
|
|
141
|
+
* production `vite build`. Use this to branch on runtime mode without
|
|
142
|
+
* changing build semantics.
|
|
143
|
+
*/
|
|
144
|
+
dev: boolean;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Build-time environment bindings (KV, D1, etc.) supplied by the Vite plugin.
|
|
148
|
+
* Only available when `buildEnv` is configured in rango() options.
|
|
149
|
+
* Throws with a clear error if not configured.
|
|
150
|
+
*
|
|
151
|
+
* This is NOT the live request env — it is shared across all prerender
|
|
152
|
+
* invocations for the build.
|
|
153
|
+
*/
|
|
154
|
+
env: DefaultEnv;
|
|
155
|
+
|
|
144
156
|
/** Read a variable set by getParams or a parent handler. */
|
|
145
157
|
get: {
|
|
146
158
|
<T>(contextVar: ContextVar<T>): T | undefined;
|
|
@@ -173,8 +185,8 @@ export interface BuildContext<TParams> {
|
|
|
173
185
|
|
|
174
186
|
/**
|
|
175
187
|
* Signal that this param set should not produce a local prerender artifact.
|
|
176
|
-
* At runtime the handler runs
|
|
177
|
-
* with `
|
|
188
|
+
* At runtime the live handler runs instead. Only valid on routes wrapped
|
|
189
|
+
* with `Passthrough()`.
|
|
178
190
|
*/
|
|
179
191
|
passthrough: () => PrerenderPassthroughResult;
|
|
180
192
|
}
|
|
@@ -187,6 +199,17 @@ export interface StaticBuildContext {
|
|
|
187
199
|
/** Always true for Static handlers at build time. */
|
|
188
200
|
build: true;
|
|
189
201
|
|
|
202
|
+
/**
|
|
203
|
+
* True when running in Vite dev mode, false during production build.
|
|
204
|
+
*/
|
|
205
|
+
dev: boolean;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Build-time environment bindings supplied by the Vite plugin.
|
|
209
|
+
* Only available when `buildEnv` is configured in rango() options.
|
|
210
|
+
*/
|
|
211
|
+
env: DefaultEnv;
|
|
212
|
+
|
|
190
213
|
/** Read a variable (available for type consistency with BuildContext). */
|
|
191
214
|
get: {
|
|
192
215
|
<T>(contextVar: ContextVar<T>): T | undefined;
|
|
@@ -214,6 +237,17 @@ export interface GetParamsContext {
|
|
|
214
237
|
/** Always true during build-time getParams execution. */
|
|
215
238
|
build: true;
|
|
216
239
|
|
|
240
|
+
/**
|
|
241
|
+
* True when running in Vite dev mode, false during production build.
|
|
242
|
+
*/
|
|
243
|
+
dev: boolean;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Build-time environment bindings supplied by the Vite plugin.
|
|
247
|
+
* Only available when `buildEnv` is configured in rango() options.
|
|
248
|
+
*/
|
|
249
|
+
env: DefaultEnv;
|
|
250
|
+
|
|
217
251
|
/** Set a variable that will be available to each handler invocation via ctx.get(). */
|
|
218
252
|
set: {
|
|
219
253
|
<T>(contextVar: ContextVar<T>, value: T): void;
|
|
@@ -224,23 +258,6 @@ export interface GetParamsContext {
|
|
|
224
258
|
reverse: BuildReverseFunction;
|
|
225
259
|
}
|
|
226
260
|
|
|
227
|
-
/**
|
|
228
|
-
* Context type for passthrough Prerender handlers.
|
|
229
|
-
*
|
|
230
|
-
* When `passthrough: true`, the handler runs both at build time and at request
|
|
231
|
-
* time. The context is a full `HandlerContext` with `build: boolean`:
|
|
232
|
-
* - `ctx.build === true`: build-time, env/request/res throw at runtime
|
|
233
|
-
* - `ctx.build === false`: live request, full context available
|
|
234
|
-
*
|
|
235
|
-
* For `passthrough: false` (default), handlers receive `BuildContext` only.
|
|
236
|
-
*/
|
|
237
|
-
export type PrerenderPassthroughContext<
|
|
238
|
-
TParams = {},
|
|
239
|
-
TEnv = DefaultEnv,
|
|
240
|
-
> = HandlerContext<TParams, TEnv> & {
|
|
241
|
-
passthrough: () => PrerenderPassthroughResult;
|
|
242
|
-
};
|
|
243
|
-
|
|
244
261
|
export interface PrerenderHandlerDefinition<
|
|
245
262
|
TParams extends Record<string, any> = any,
|
|
246
263
|
> {
|
|
@@ -253,8 +270,15 @@ export interface PrerenderHandlerDefinition<
|
|
|
253
270
|
getParams?: (ctx: GetParamsContext) => Promise<TParams[]> | TParams[];
|
|
254
271
|
/** Pre-render options. */
|
|
255
272
|
options?: PrerenderOptions;
|
|
273
|
+
/** Composable default DSL items merged when the handler is mounted. */
|
|
274
|
+
use?: () => UseItems<HandlerUseItem>;
|
|
256
275
|
}
|
|
257
276
|
|
|
277
|
+
// Process-stable fallback id counter (mirrors createHandle / createLoader). Only
|
|
278
|
+
// assigned in a bare unit test where the Vite plugin did not inject an id; never
|
|
279
|
+
// fires in a real build (the plugin always injects).
|
|
280
|
+
let runtimePrerenderIdCounter = 0;
|
|
281
|
+
|
|
258
282
|
// -- Overloads --------------------------------------------------------------
|
|
259
283
|
//
|
|
260
284
|
// T accepts: named route string (global or .local) OR explicit param object.
|
|
@@ -263,7 +287,7 @@ export interface PrerenderHandlerDefinition<
|
|
|
263
287
|
// Explicit params work as before:
|
|
264
288
|
// Prerender<{ slug: string }> → params = { slug: string }
|
|
265
289
|
|
|
266
|
-
// Overload 1: Static handler
|
|
290
|
+
// Overload 1: Static handler (build-time only)
|
|
267
291
|
export function Prerender<
|
|
268
292
|
T extends
|
|
269
293
|
| keyof DefaultPrerenderRouteMap
|
|
@@ -273,34 +297,15 @@ export function Prerender<
|
|
|
273
297
|
>(
|
|
274
298
|
handler: (
|
|
275
299
|
ctx: BuildContext<ResolvePrerenderParams<T, TRouteMap>>,
|
|
276
|
-
) => ReactNode | Promise<ReactNode>,
|
|
277
|
-
options?: PrerenderOptions & { passthrough?: false },
|
|
278
|
-
__injectedId?: string,
|
|
279
|
-
): PrerenderHandlerDefinition<ResolvePrerenderParams<T, TRouteMap>>;
|
|
280
|
-
|
|
281
|
-
// Overload 2: Static handler, passthrough (build + live — full HandlerContext)
|
|
282
|
-
export function Prerender<
|
|
283
|
-
T extends
|
|
284
|
-
| keyof DefaultPrerenderRouteMap
|
|
285
|
-
| `.${keyof TRouteMap & string}`
|
|
286
|
-
| Record<string, any> = {},
|
|
287
|
-
TRouteMap extends {} = DefaultPrerenderRouteMap,
|
|
288
|
-
TEnv = DefaultEnv,
|
|
289
|
-
>(
|
|
290
|
-
handler: (
|
|
291
|
-
ctx: PrerenderPassthroughContext<
|
|
292
|
-
ResolvePrerenderParams<T, TRouteMap>,
|
|
293
|
-
TEnv
|
|
294
|
-
>,
|
|
295
300
|
) =>
|
|
296
301
|
| ReactNode
|
|
297
302
|
| PrerenderPassthroughResult
|
|
298
303
|
| Promise<ReactNode | PrerenderPassthroughResult>,
|
|
299
|
-
options
|
|
304
|
+
options?: PrerenderOptions,
|
|
300
305
|
__injectedId?: string,
|
|
301
306
|
): PrerenderHandlerDefinition<ResolvePrerenderParams<T, TRouteMap>>;
|
|
302
307
|
|
|
303
|
-
// Overload
|
|
308
|
+
// Overload 2: Dynamic handler (build-time only)
|
|
304
309
|
export function Prerender<
|
|
305
310
|
T extends
|
|
306
311
|
| keyof DefaultPrerenderRouteMap
|
|
@@ -315,35 +320,11 @@ export function Prerender<
|
|
|
315
320
|
| ResolvePrerenderParams<T, TRouteMap>[],
|
|
316
321
|
handler: (
|
|
317
322
|
ctx: BuildContext<ResolvePrerenderParams<T, TRouteMap>>,
|
|
318
|
-
) => ReactNode | Promise<ReactNode>,
|
|
319
|
-
options?: PrerenderOptions & { passthrough?: false },
|
|
320
|
-
__injectedId?: string,
|
|
321
|
-
): PrerenderHandlerDefinition<ResolvePrerenderParams<T, TRouteMap>>;
|
|
322
|
-
|
|
323
|
-
// Overload 4: Dynamic handler, passthrough (build + live — full HandlerContext)
|
|
324
|
-
export function Prerender<
|
|
325
|
-
T extends
|
|
326
|
-
| keyof DefaultPrerenderRouteMap
|
|
327
|
-
| `.${keyof TRouteMap & string}`
|
|
328
|
-
| Record<string, any>,
|
|
329
|
-
TRouteMap extends {} = DefaultPrerenderRouteMap,
|
|
330
|
-
TEnv = DefaultEnv,
|
|
331
|
-
>(
|
|
332
|
-
getParams: (
|
|
333
|
-
ctx: GetParamsContext,
|
|
334
|
-
) =>
|
|
335
|
-
| Promise<ResolvePrerenderParams<T, TRouteMap>[]>
|
|
336
|
-
| ResolvePrerenderParams<T, TRouteMap>[],
|
|
337
|
-
handler: (
|
|
338
|
-
ctx: PrerenderPassthroughContext<
|
|
339
|
-
ResolvePrerenderParams<T, TRouteMap>,
|
|
340
|
-
TEnv
|
|
341
|
-
>,
|
|
342
323
|
) =>
|
|
343
324
|
| ReactNode
|
|
344
325
|
| PrerenderPassthroughResult
|
|
345
326
|
| Promise<ReactNode | PrerenderPassthroughResult>,
|
|
346
|
-
options
|
|
327
|
+
options?: PrerenderOptions,
|
|
347
328
|
__injectedId?: string,
|
|
348
329
|
): PrerenderHandlerDefinition<ResolvePrerenderParams<T, TRouteMap>>;
|
|
349
330
|
|
|
@@ -401,12 +382,27 @@ export function Prerender<TParams extends Record<string, any>>(
|
|
|
401
382
|
);
|
|
402
383
|
}
|
|
403
384
|
|
|
404
|
-
|
|
385
|
+
// Throw unless under a test runner. The plugin always injects $$id for a
|
|
386
|
+
// supported `export const` Prerender on every build, so a missing id means
|
|
387
|
+
// either no plugin (a bare test — fall back below) or an UNSUPPORTED shape the
|
|
388
|
+
// plugin silently skipped (dev OR a real build — fail loud; a synthetic id
|
|
389
|
+
// would degrade to a silent prerender miss). The message is already small (no
|
|
390
|
+
// stack-parsing diagnostic), so it ships as-is. isUnderTestRunner() is
|
|
391
|
+
// runtime-safe — never a bare `process.env` access.
|
|
392
|
+
if (!id && !isUnderTestRunner()) {
|
|
405
393
|
throw new Error(
|
|
406
|
-
"[
|
|
407
|
-
"
|
|
394
|
+
"[rango] Prerender: missing $$id. Use `export const X = Prerender(...)` " +
|
|
395
|
+
"and ensure the exposeInternalIds Vite plugin is configured.",
|
|
408
396
|
);
|
|
409
397
|
}
|
|
398
|
+
// Under vitest with no plugin id: assign a process-stable runtime id so a
|
|
399
|
+
// whole-app router with Prerender routes constructs in a bare test (for
|
|
400
|
+
// dispatch / assertGeneratedRoutesMatch). Never reached in a real build (the
|
|
401
|
+
// throw above fires there); prerender storage/lookup keys on routeName +
|
|
402
|
+
// paramHash, never $$id (mirrors createHandle / createLoader).
|
|
403
|
+
if (!id) {
|
|
404
|
+
id = `__rango_runtime_prerender_${runtimePrerenderIdCounter++}`;
|
|
405
|
+
}
|
|
410
406
|
|
|
411
407
|
return {
|
|
412
408
|
__brand: "prerenderHandler" as const,
|
|
@@ -422,7 +418,7 @@ export function Prerender<TParams extends Record<string, any>>(
|
|
|
422
418
|
/**
|
|
423
419
|
* Sentinel returned by `ctx.passthrough()` to signal that a specific param set
|
|
424
420
|
* should not produce a local prerender artifact. The build skips writing the
|
|
425
|
-
* entry; at runtime the
|
|
421
|
+
* entry; at runtime the Passthrough live handler runs instead.
|
|
426
422
|
*/
|
|
427
423
|
export const PRERENDER_PASSTHROUGH: Readonly<{
|
|
428
424
|
__brand: "prerenderPassthrough";
|
|
@@ -446,7 +442,41 @@ export function isPrerenderPassthrough(
|
|
|
446
442
|
);
|
|
447
443
|
}
|
|
448
444
|
|
|
449
|
-
|
|
445
|
+
/**
|
|
446
|
+
* Detect whether any resolved segment carries the passthrough sentinel.
|
|
447
|
+
*
|
|
448
|
+
* A build handler signals passthrough by returning `ctx.passthrough()` (the
|
|
449
|
+
* PRERENDER_PASSTHROUGH sentinel), which lands on the segment's `component`.
|
|
450
|
+
* But when the route declares `loading()`, the handler result is deferred
|
|
451
|
+
* upstream (segment-resolution/fresh.ts), so `component` is a thenable resolving
|
|
452
|
+
* to the sentinel rather than the sentinel itself — a synchronous
|
|
453
|
+
* `isPrerenderPassthrough(component)` on the Promise returns false and the build
|
|
454
|
+
* bakes a corrupt artifact instead of deferring. Resolve thenables first.
|
|
455
|
+
*
|
|
456
|
+
* Rejections are swallowed here: a throwing build handler resurfaces during
|
|
457
|
+
* segment serialization, preserving the prior error-handling behavior.
|
|
458
|
+
*/
|
|
459
|
+
export async function detectPrerenderPassthrough(
|
|
460
|
+
segments: ReadonlyArray<{ component: unknown }>,
|
|
461
|
+
): Promise<boolean> {
|
|
462
|
+
for (const seg of segments) {
|
|
463
|
+
let component: unknown = seg.component;
|
|
464
|
+
if (
|
|
465
|
+
component &&
|
|
466
|
+
typeof (component as { then?: unknown }).then === "function"
|
|
467
|
+
) {
|
|
468
|
+
try {
|
|
469
|
+
component = await component;
|
|
470
|
+
} catch {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (isPrerenderPassthrough(component)) return true;
|
|
475
|
+
}
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// -- Type guards ------------------------------------------------------------
|
|
450
480
|
|
|
451
481
|
/**
|
|
452
482
|
* Type guard to check if a value is a PrerenderHandlerDefinition.
|
|
@@ -461,3 +491,89 @@ export function isPrerenderHandler(
|
|
|
461
491
|
(value as { __brand: unknown }).__brand === "prerenderHandler"
|
|
462
492
|
);
|
|
463
493
|
}
|
|
494
|
+
|
|
495
|
+
// -- Passthrough wrapper ----------------------------------------------------
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* A prerender route with a live fallback handler for unknown params at runtime.
|
|
499
|
+
*
|
|
500
|
+
* Wraps a `Prerender(...)` definition with a separate handler that runs at
|
|
501
|
+
* request time for params not covered by `getParams()`.
|
|
502
|
+
*
|
|
503
|
+
* - Build time: `prerenderDef` provides getParams + build handler.
|
|
504
|
+
* - Runtime: `liveHandler` runs for unknown params with full HandlerContext.
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```ts
|
|
508
|
+
* const BlogPrerender = Prerender(
|
|
509
|
+
* async () => [{ slug: "getting-started" }, { slug: "api-reference" }],
|
|
510
|
+
* async (ctx) => <BlogPost slug={ctx.params.slug} />,
|
|
511
|
+
* );
|
|
512
|
+
*
|
|
513
|
+
* // In route definition:
|
|
514
|
+
* path("/blog/:slug", Passthrough(BlogPrerender, async (ctx) => {
|
|
515
|
+
* const post = await ctx.env.DB.get(ctx.params.slug);
|
|
516
|
+
* return <BlogPost slug={ctx.params.slug} post={post} />;
|
|
517
|
+
* }))
|
|
518
|
+
* ```
|
|
519
|
+
*/
|
|
520
|
+
export interface PassthroughHandlerDefinition<
|
|
521
|
+
TParams extends Record<string, any> = any,
|
|
522
|
+
TEnv = DefaultEnv,
|
|
523
|
+
> {
|
|
524
|
+
readonly __brand: "passthroughHandler";
|
|
525
|
+
/** The underlying prerender definition (build-time rendering). */
|
|
526
|
+
prerenderDef: PrerenderHandlerDefinition<TParams>;
|
|
527
|
+
/** Live handler for runtime fallback on unknown params. */
|
|
528
|
+
liveHandler: (
|
|
529
|
+
ctx: HandlerContext<TParams, TEnv>,
|
|
530
|
+
) => ReactNode | Promise<ReactNode> | Response | Promise<Response>;
|
|
531
|
+
/** Composable default DSL items merged when the handler is mounted. */
|
|
532
|
+
use?: () => UseItems<HandlerUseItem>;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
export function Passthrough<
|
|
536
|
+
TParams extends Record<string, any>,
|
|
537
|
+
TEnv = DefaultEnv,
|
|
538
|
+
>(
|
|
539
|
+
prerenderDef: PrerenderHandlerDefinition<TParams>,
|
|
540
|
+
liveHandler: (
|
|
541
|
+
ctx: HandlerContext<TParams, TEnv>,
|
|
542
|
+
) => ReactNode | Promise<ReactNode> | Response | Promise<Response>,
|
|
543
|
+
): PassthroughHandlerDefinition<TParams, TEnv>;
|
|
544
|
+
|
|
545
|
+
// Implementation
|
|
546
|
+
export function Passthrough<
|
|
547
|
+
TParams extends Record<string, any>,
|
|
548
|
+
TEnv = DefaultEnv,
|
|
549
|
+
>(
|
|
550
|
+
prerenderDef: PrerenderHandlerDefinition<TParams>,
|
|
551
|
+
liveHandler: (
|
|
552
|
+
ctx: HandlerContext<TParams, TEnv>,
|
|
553
|
+
) => ReactNode | Promise<ReactNode> | Response | Promise<Response>,
|
|
554
|
+
): PassthroughHandlerDefinition<TParams, TEnv> {
|
|
555
|
+
if (!isPrerenderHandler(prerenderDef)) {
|
|
556
|
+
throw new Error(
|
|
557
|
+
"[rango] Passthrough: first argument must be a Prerender() definition.",
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
__brand: "passthroughHandler" as const,
|
|
562
|
+
prerenderDef,
|
|
563
|
+
liveHandler,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Type guard to check if a value is a PassthroughHandlerDefinition.
|
|
569
|
+
*/
|
|
570
|
+
export function isPassthroughHandler(
|
|
571
|
+
value: unknown,
|
|
572
|
+
): value is PassthroughHandlerDefinition {
|
|
573
|
+
return (
|
|
574
|
+
typeof value === "object" &&
|
|
575
|
+
value !== null &&
|
|
576
|
+
"__brand" in value &&
|
|
577
|
+
(value as { __brand: unknown }).__brand === "passthroughHandler"
|
|
578
|
+
);
|
|
579
|
+
}
|