@rangojs/router 0.0.0-experimental.14 → 0.0.0-experimental.140
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 +17 -0
- package/README.md +432 -7
- package/dist/bin/rango.js +2073 -213
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +7258 -2714
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +140 -67
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +329 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +487 -0
- package/skills/caching/SKILL.md +357 -25
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +246 -0
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +16 -10
- package/skills/document-cache/SKILL.md +87 -62
- package/skills/fonts/SKILL.md +6 -4
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +557 -79
- package/skills/host-router/SKILL.md +320 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +207 -15
- package/skills/layout/SKILL.md +146 -6
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +616 -54
- package/skills/middleware/SKILL.md +217 -37
- package/skills/migrate-nextjs/SKILL.md +611 -0
- package/skills/migrate-react-router/SKILL.md +927 -0
- package/skills/mime-routes/SKILL.md +42 -11
- package/skills/observability/SKILL.md +194 -0
- package/skills/parallel/SKILL.md +284 -3
- package/skills/ppr/SKILL.md +426 -0
- package/skills/prerender/SKILL.md +437 -52
- package/skills/rango/SKILL.md +369 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +263 -121
- package/skills/route/SKILL.md +350 -21
- package/skills/router-setup/SKILL.md +246 -33
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +126 -222
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +532 -103
- package/skills/use-cache/SKILL.md +367 -0
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +77 -44
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +114 -0
- 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/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +293 -202
- package/src/browser/history-state.ts +101 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +11 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +385 -576
- package/src/browser/navigation-client.ts +245 -75
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +184 -118
- package/src/browser/navigation-transaction.ts +247 -0
- package/src/browser/network-error-handler.ts +88 -0
- package/src/browser/partial-update.ts +412 -364
- package/src/browser/prefetch/cache.ts +359 -0
- package/src/browser/prefetch/fetch.ts +452 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +209 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +194 -0
- package/src/browser/react/Link.tsx +275 -68
- package/src/browser/react/NavigationProvider.tsx +265 -109
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +70 -0
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +272 -60
- package/src/browser/react/location-state.ts +90 -20
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +35 -66
- package/src/browser/react/use-handle.ts +39 -126
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +39 -13
- package/src/browser/react/use-navigation.ts +53 -69
- package/src/browser/react/use-params.ts +75 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +98 -0
- package/src/browser/react/use-search-params.ts +51 -0
- package/src/browser/react/use-segments.ts +72 -99
- package/src/browser/response-adapter.ts +164 -0
- package/src/browser/rsc-router.tsx +300 -72
- package/src/browser/scroll-restoration.ts +138 -50
- package/src/browser/segment-reconciler.ts +243 -0
- package/src/browser/segment-structure-assert.ts +17 -1
- package/src/browser/server-action-bridge.ts +668 -613
- package/src/browser/types.ts +223 -51
- package/src/browser/validate-redirect-origin.ts +56 -0
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +252 -161
- package/src/build/generate-route-types.ts +41 -1038
- package/src/build/index.ts +12 -7
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +225 -42
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +105 -0
- package/src/build/route-types/codegen.ts +113 -0
- package/src/build/route-types/include-resolution.ts +812 -0
- package/src/build/route-types/param-extraction.ts +51 -0
- package/src/build/route-types/per-module-writer.ts +144 -0
- package/src/build/route-types/router-processing.ts +695 -0
- package/src/build/route-types/scan-filter.ts +85 -0
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +223 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +60 -0
- package/src/cache/cache-policy.ts +199 -0
- package/src/cache/cache-runtime.ts +525 -0
- package/src/cache/cache-scope.ts +298 -332
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2500 -158
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +17 -17
- package/src/cache/document-cache.ts +199 -92
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +111 -0
- package/src/cache/index.ts +29 -35
- package/src/cache/memory-segment-store.ts +363 -30
- package/src/cache/profile-registry.ts +88 -0
- package/src/cache/read-through-swr.ts +178 -0
- package/src/cache/segment-codec.ts +248 -0
- package/src/cache/shell-cache.ts +386 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +153 -0
- package/src/cache/types.ts +156 -211
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1102 -0
- package/src/client.rsc.tsx +43 -21
- package/src/client.tsx +131 -347
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +23 -4
- package/src/components/DefaultDocument.tsx +13 -3
- package/src/context-var.ts +168 -0
- package/src/debug.ts +19 -9
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +106 -10
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +110 -35
- package/src/handles/MetaTags.tsx +83 -59
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +93 -0
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +44 -53
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +20 -65
- package/src/host/errors.ts +21 -30
- package/src/host/index.ts +13 -9
- package/src/host/pattern-matcher.ts +50 -79
- package/src/host/router.ts +151 -121
- package/src/host/testing.ts +45 -32
- package/src/host/types.ts +52 -11
- package/src/host/utils.ts +2 -2
- package/src/href-client.ts +192 -57
- package/src/index.rsc.ts +177 -35
- package/src/index.ts +255 -71
- package/src/internal-debug.ts +9 -2
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +31 -99
- package/src/loader.ts +30 -12
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +41 -0
- package/src/prerender/param-hash.ts +16 -14
- package/src/prerender/store.ts +121 -21
- package/src/prerender.ts +460 -26
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -0
- package/src/reverse.ts +198 -128
- package/src/root-error-boundary.tsx +42 -48
- package/src/route-content-wrapper.tsx +22 -77
- package/src/route-definition/dsl-helpers.ts +1116 -0
- package/src/route-definition/helper-factories.ts +88 -0
- package/src/route-definition/helpers-types.ts +505 -0
- package/src/route-definition/index.ts +54 -0
- package/src/route-definition/redirect.ts +134 -0
- package/src/route-definition/resolve-handler-use.ts +160 -0
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-definition.ts +1 -1481
- package/src/route-map-builder.ts +82 -144
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +71 -45
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +263 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +54 -27
- package/src/router/find-match.ts +245 -0
- package/src/router/handler-context.ts +377 -125
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +59 -28
- package/src/router/lazy-includes.ts +254 -0
- package/src/router/loader-resolution.ts +421 -157
- package/src/router/logging.ts +106 -6
- package/src/router/manifest.ts +131 -57
- package/src/router/match-api.ts +167 -246
- package/src/router/match-context.ts +4 -24
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +117 -93
- package/src/router/match-middleware/cache-lookup.ts +297 -150
- package/src/router/match-middleware/cache-store.ts +123 -51
- package/src/router/match-middleware/intercept-resolution.ts +44 -43
- package/src/router/match-middleware/segment-resolution.ts +64 -22
- package/src/router/match-pipelines.ts +11 -87
- package/src/router/match-result.ts +121 -50
- package/src/router/metrics.ts +219 -28
- package/src/router/middleware-types.ts +93 -0
- package/src/router/middleware.ts +505 -441
- package/src/router/navigation-snapshot.ts +133 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +311 -142
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +547 -0
- package/src/router/preview-match.ts +102 -0
- package/src/router/request-classification.ts +278 -0
- package/src/router/revalidation.ts +203 -62
- package/src/router/route-snapshot.ts +246 -0
- package/src/router/router-context.ts +45 -48
- package/src/router/router-interfaces.ts +554 -0
- package/src/router/router-options.ts +779 -0
- package/src/router/router-registry.ts +21 -0
- package/src/router/segment-resolution/fresh.ts +772 -0
- package/src/router/segment-resolution/helpers.ts +348 -0
- package/src/router/segment-resolution/loader-cache.ts +250 -0
- package/src/router/segment-resolution/loader-mask.ts +44 -0
- package/src/router/segment-resolution/revalidation.ts +1331 -0
- package/src/router/segment-resolution/static-store.ts +81 -0
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +25 -1354
- package/src/router/segment-wrappers.ts +292 -0
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +75 -0
- package/src/router/telemetry-otel.ts +261 -0
- package/src/router/telemetry.ts +377 -0
- package/src/router/timeout.ts +128 -0
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +240 -61
- package/src/router/types.ts +23 -70
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +781 -2378
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +46 -0
- package/src/rsc/handler.ts +905 -1142
- package/src/rsc/helpers.ts +275 -19
- package/src/rsc/index.ts +2 -25
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +305 -0
- package/src/rsc/manifest-init.ts +77 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +155 -0
- package/src/rsc/progressive-enhancement.ts +502 -0
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +104 -0
- package/src/rsc/response-route-handler.ts +257 -0
- package/src/rsc/rsc-rendering.ts +337 -0
- package/src/rsc/runtime-warnings.ts +55 -0
- package/src/rsc/server-action.ts +522 -0
- package/src/rsc/shell-capture.ts +439 -0
- package/src/rsc/ssr-setup.ts +144 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +95 -12
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +99 -82
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +349 -134
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +452 -85
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +310 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +123 -42
- package/src/server/live.ts +130 -0
- package/src/server/loader-registry.ts +51 -100
- package/src/server/request-context.ts +842 -157
- package/src/server.ts +15 -8
- package/src/ssr/index.tsx +412 -136
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +45 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +701 -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 +257 -0
- package/src/testing/generated-routes.ts +199 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +371 -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 +357 -0
- package/src/testing/render-route.tsx +584 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -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 +40 -72
- package/src/theme/ThemeScript.tsx +12 -14
- package/src/theme/constants.ts +57 -15
- package/src/theme/index.ts +3 -20
- package/src/theme/theme-context.ts +5 -35
- package/src/theme/theme-script.ts +43 -39
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +123 -0
- package/src/types/cache-types.ts +207 -0
- package/src/types/error-types.ts +132 -0
- package/src/types/global-namespace.ts +113 -0
- package/src/types/handler-context.ts +839 -0
- package/src/types/index.ts +81 -0
- package/src/types/loader-types.ts +212 -0
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +138 -0
- package/src/types/route-entry.ts +114 -0
- package/src/types/segments.ts +271 -0
- package/src/types.ts +1 -1795
- package/src/urls/include-helper.ts +162 -0
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +43 -0
- package/src/urls/path-helper-types.ts +413 -0
- package/src/urls/path-helper.ts +275 -0
- package/src/urls/pattern-types.ts +124 -0
- package/src/urls/response-types.ts +109 -0
- package/src/urls/type-extraction.ts +316 -0
- package/src/urls/urls-function.ts +80 -0
- package/src/urls.ts +1 -1341
- package/src/use-loader.tsx +406 -141
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +182 -0
- package/src/vite/discovery/discover-routers.ts +389 -0
- package/src/vite/discovery/discovery-errors.ts +255 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +467 -0
- package/src/vite/discovery/route-types-writer.ts +214 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/state.ts +161 -0
- package/src/vite/discovery/virtual-module-codegen.ts +183 -0
- package/src/vite/index.ts +23 -2255
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +303 -0
- package/src/vite/plugins/cjs-to-esm.ts +90 -0
- package/src/vite/plugins/client-ref-dedup.ts +120 -0
- package/src/vite/plugins/client-ref-hashing.ts +118 -0
- 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/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
- package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
- package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +805 -0
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +313 -0
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +94 -0
- package/src/vite/plugins/version-plugin.ts +263 -0
- package/src/vite/plugins/virtual-entries.ts +234 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +560 -0
- package/src/vite/router-discovery.ts +1638 -0
- package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +132 -0
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +15 -0
- package/src/vite/utils/package-resolution.ts +89 -0
- package/src/vite/utils/prerender-utils.ts +249 -0
- package/src/vite/utils/shared-utils.ts +269 -0
- package/CLAUDE.md +0 -43
- package/dist/vite/index.named-routes.gen.ts +0 -103
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/react/use-client-cache.ts +0 -56
- package/src/browser/request-controller.ts +0 -164
- package/src/browser/shallow.ts +0 -35
- package/src/cache/memory-store.ts +0 -253
- package/src/handles/index.ts +0 -6
- package/src/href-context.ts +0 -33
- package/src/network-error-thrower.tsx +0 -21
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-internal-ids.ts +0 -1167
- package/src/vite/package-resolution.ts +0 -125
- package/src/vite/virtual-entries.ts +0 -114
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/src/server/context.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
import type {
|
|
4
|
-
|
|
3
|
+
import type {
|
|
4
|
+
PartialCacheOptions,
|
|
5
|
+
ErrorBoundaryHandler,
|
|
6
|
+
Handler,
|
|
7
|
+
LoaderDefinition,
|
|
8
|
+
MiddlewareFn,
|
|
9
|
+
NotFoundBoundaryHandler,
|
|
10
|
+
ShouldRevalidateFn,
|
|
11
|
+
TransitionConfig,
|
|
12
|
+
} from "../types";
|
|
13
|
+
import { invariant, DslContextError } from "../errors";
|
|
14
|
+
import type { DefaultRouteName } from "../types/global-namespace.js";
|
|
5
15
|
|
|
6
16
|
// ============================================================================
|
|
7
17
|
// Performance Metrics Types
|
|
@@ -13,9 +23,10 @@ import { invariant } from "../errors";
|
|
|
13
23
|
* @internal This type is an implementation detail and may change without notice.
|
|
14
24
|
*/
|
|
15
25
|
export interface PerformanceMetric {
|
|
16
|
-
label: string;
|
|
17
|
-
duration: number;
|
|
18
|
-
startTime: number;
|
|
26
|
+
label: string; // e.g., "route-matching", "loader:UserLoader"
|
|
27
|
+
duration: number; // milliseconds
|
|
28
|
+
startTime: number; // relative to request start
|
|
29
|
+
depth?: number; // nesting level for hierarchical display (0 = top-level)
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
/**
|
|
@@ -29,7 +40,7 @@ export interface MetricsStore {
|
|
|
29
40
|
metrics: PerformanceMetric[];
|
|
30
41
|
}
|
|
31
42
|
// ============================================================================
|
|
32
|
-
//
|
|
43
|
+
// Rango Context
|
|
33
44
|
// ============================================================================
|
|
34
45
|
|
|
35
46
|
/**
|
|
@@ -60,6 +71,10 @@ export type EntryPropCommon = {
|
|
|
60
71
|
};
|
|
61
72
|
|
|
62
73
|
/**
|
|
74
|
+
* Attachments resolved by walking the parent chain, not owned by the entry:
|
|
75
|
+
* middleware composes downward; revalidate and the error/notFound boundaries are
|
|
76
|
+
* resolved by nearest-ancestor lookup. Inherited, not a single execution chain.
|
|
77
|
+
*
|
|
63
78
|
* @internal This type is an implementation detail and may change without notice.
|
|
64
79
|
*/
|
|
65
80
|
export type EntryPropDatas = {
|
|
@@ -69,6 +84,16 @@ export type EntryPropDatas = {
|
|
|
69
84
|
notFoundBoundary: (ReactNode | NotFoundBoundaryHandler)[];
|
|
70
85
|
};
|
|
71
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Render-time presentation fields shared by every entry variant.
|
|
89
|
+
*
|
|
90
|
+
* @internal This type is an implementation detail and may change without notice.
|
|
91
|
+
*/
|
|
92
|
+
export type EntryPropRender = {
|
|
93
|
+
loading?: ReactNode | false;
|
|
94
|
+
transition?: TransitionConfig;
|
|
95
|
+
};
|
|
96
|
+
|
|
72
97
|
/**
|
|
73
98
|
* Loader entry stored in EntryData
|
|
74
99
|
* Contains the loader definition and its revalidation rules
|
|
@@ -105,12 +130,14 @@ export type InterceptSegmentsState = {
|
|
|
105
130
|
* @internal This type is an implementation detail and may change without notice.
|
|
106
131
|
*/
|
|
107
132
|
export type InterceptSelectorContext<TEnv = any> = {
|
|
108
|
-
from: URL;
|
|
109
|
-
to: URL;
|
|
110
|
-
params: Record<string, string>;
|
|
111
|
-
request: Request;
|
|
112
|
-
env: TEnv;
|
|
113
|
-
segments: InterceptSegmentsState;
|
|
133
|
+
from: URL; // Source URL (where user is coming from)
|
|
134
|
+
to: URL; // Destination URL (where user is navigating to)
|
|
135
|
+
params: Record<string, string>; // Matched route params
|
|
136
|
+
request: Request; // The HTTP request object
|
|
137
|
+
env: TEnv; // Platform bindings (Cloudflare env, etc.)
|
|
138
|
+
segments: InterceptSegmentsState; // Client's current segments (where navigating FROM)
|
|
139
|
+
fromRouteName?: DefaultRouteName; // Named route being navigated away from (undefined for unnamed routes)
|
|
140
|
+
toRouteName?: DefaultRouteName; // Named route being navigated to (undefined for unnamed routes)
|
|
114
141
|
};
|
|
115
142
|
|
|
116
143
|
/**
|
|
@@ -119,7 +146,22 @@ export type InterceptSelectorContext<TEnv = any> = {
|
|
|
119
146
|
*
|
|
120
147
|
* @internal This type is an implementation detail and may change without notice.
|
|
121
148
|
*/
|
|
122
|
-
export type InterceptWhenFn<TEnv = any> = (
|
|
149
|
+
export type InterceptWhenFn<TEnv = any> = (
|
|
150
|
+
ctx: InterceptSelectorContext<TEnv>,
|
|
151
|
+
) => boolean;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Config object passed to intercept() (its 4th argument). `when` gates whether
|
|
155
|
+
* the intercept activates on a soft navigation — a single match-time selector or
|
|
156
|
+
* an array of them (ALL must return true; omit to always activate). This is the
|
|
157
|
+
* intercept counterpart to transition({ when }); both express conditional
|
|
158
|
+
* behavior as a config field rather than a separate DSL helper.
|
|
159
|
+
*
|
|
160
|
+
* @internal This type is an implementation detail and may change without notice.
|
|
161
|
+
*/
|
|
162
|
+
export interface InterceptConfig<TEnv = any> {
|
|
163
|
+
when?: InterceptWhenFn<TEnv> | InterceptWhenFn<TEnv>[];
|
|
164
|
+
}
|
|
123
165
|
|
|
124
166
|
/**
|
|
125
167
|
* Intercept entry stored in EntryData
|
|
@@ -128,8 +170,8 @@ export type InterceptWhenFn<TEnv = any> = (ctx: InterceptSelectorContext<TEnv>)
|
|
|
128
170
|
* @internal This type is an implementation detail and may change without notice.
|
|
129
171
|
*/
|
|
130
172
|
export type InterceptEntry = {
|
|
131
|
-
slotName: `@${string}`;
|
|
132
|
-
routeName: string;
|
|
173
|
+
slotName: `@${string}`; // e.g., "@modal"
|
|
174
|
+
routeName: string; // e.g., "card"
|
|
133
175
|
handler: ReactNode | Handler<any, any, any>;
|
|
134
176
|
middleware: MiddlewareFn<any, any>[];
|
|
135
177
|
revalidate: ShouldRevalidateFn<any, any>[];
|
|
@@ -137,14 +179,34 @@ export type InterceptEntry = {
|
|
|
137
179
|
notFoundBoundary: (ReactNode | NotFoundBoundaryHandler)[];
|
|
138
180
|
loader: LoaderEntry[];
|
|
139
181
|
loading?: ReactNode | false;
|
|
140
|
-
|
|
141
|
-
|
|
182
|
+
transition?: TransitionConfig;
|
|
183
|
+
layout?: ReactNode | Handler<any, any, any>; // Wrapper layout with <Outlet /> for content
|
|
184
|
+
when: InterceptWhenFn[]; // Selector conditions - all must return true to intercept
|
|
142
185
|
};
|
|
143
186
|
|
|
187
|
+
export interface ParallelEntryData
|
|
188
|
+
extends EntryPropCommon, EntryPropDatas, EntryPropSegments, EntryPropRender {
|
|
189
|
+
type: "parallel";
|
|
190
|
+
handler: Record<`@${string}`, Handler<any, any, any> | ReactNode>;
|
|
191
|
+
/** Set when any parallel slot is a Static definition */
|
|
192
|
+
isStaticPrerender?: true;
|
|
193
|
+
/** Per-slot static handler $$ids for build-time store lookup */
|
|
194
|
+
staticHandlerIds?: Record<string, string>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export type ParallelEntries = Partial<Record<`@${string}`, ParallelEntryData>>;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* This entry's own structural children plus its owned loaders. `loader` lives
|
|
201
|
+
* here (not in EntryPropDatas) because loaders are owned by the entry, not
|
|
202
|
+
* inherited from ancestors.
|
|
203
|
+
*
|
|
204
|
+
* @internal This type is an implementation detail and may change without notice.
|
|
205
|
+
*/
|
|
144
206
|
export type EntryPropSegments = {
|
|
145
207
|
loader: LoaderEntry[];
|
|
146
208
|
layout: EntryData[];
|
|
147
|
-
parallel:
|
|
209
|
+
parallel: ParallelEntries; // slot -> parallel entry (same entry may back multiple slots)
|
|
148
210
|
intercept: InterceptEntry[]; // intercept definitions for soft navigation
|
|
149
211
|
};
|
|
150
212
|
|
|
@@ -152,46 +214,49 @@ export type EntryData =
|
|
|
152
214
|
| ({
|
|
153
215
|
type: "route";
|
|
154
216
|
handler: Handler<any, any, any>;
|
|
155
|
-
loading?: ReactNode | false;
|
|
156
217
|
/** URL pattern for this route (used by path() in urls()) */
|
|
157
218
|
pattern?: string;
|
|
158
219
|
/** Set when handler is a Prerender definition */
|
|
159
220
|
isPrerender?: true;
|
|
160
221
|
/** Original PrerenderHandlerDefinition (for build-time getParams access) */
|
|
161
|
-
prerenderDef?: {
|
|
222
|
+
prerenderDef?: {
|
|
223
|
+
getParams?: (ctx: any) => Promise<any[]> | any[];
|
|
224
|
+
options?: { concurrency?: number };
|
|
225
|
+
};
|
|
226
|
+
/** Set when route is wrapped with Passthrough() — has a separate live handler */
|
|
227
|
+
isPassthrough?: true;
|
|
228
|
+
/** Live handler for runtime fallback (only set on Passthrough routes) */
|
|
229
|
+
liveHandler?: Handler<any, any, any>;
|
|
162
230
|
/** Set when handler is a Static definition (build-time only) */
|
|
163
231
|
isStaticPrerender?: true;
|
|
232
|
+
/** Static handler $$id for build-time store lookup */
|
|
233
|
+
staticHandlerId?: string;
|
|
164
234
|
/** Response type for non-RSC routes (json, text, image, any) */
|
|
165
235
|
responseType?: string;
|
|
166
236
|
} & EntryPropCommon &
|
|
167
237
|
EntryPropDatas &
|
|
168
|
-
EntryPropSegments
|
|
238
|
+
EntryPropSegments &
|
|
239
|
+
EntryPropRender)
|
|
169
240
|
| ({
|
|
170
241
|
type: "layout";
|
|
171
242
|
handler: ReactNode | Handler<any, any, any>;
|
|
172
|
-
loading?: ReactNode | false;
|
|
173
243
|
/** Set when handler is a Static definition (build-time only) */
|
|
174
244
|
isStaticPrerender?: true;
|
|
245
|
+
/** Static handler $$id for build-time store lookup */
|
|
246
|
+
staticHandlerId?: string;
|
|
175
247
|
} & EntryPropCommon &
|
|
176
248
|
EntryPropDatas &
|
|
177
|
-
EntryPropSegments
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
handler: Record<`@${string}`, Handler<any, any, any> | ReactNode>;
|
|
181
|
-
loading?: ReactNode | false;
|
|
182
|
-
/** Set when any parallel slot is a Static definition */
|
|
183
|
-
isStaticPrerender?: true;
|
|
184
|
-
} & EntryPropCommon &
|
|
185
|
-
EntryPropDatas &
|
|
186
|
-
EntryPropSegments)
|
|
249
|
+
EntryPropSegments &
|
|
250
|
+
EntryPropRender)
|
|
251
|
+
| ParallelEntryData
|
|
187
252
|
| ({
|
|
188
253
|
type: "cache";
|
|
189
254
|
/** Cache entries create cache boundaries and render like layouts (with Outlet) */
|
|
190
255
|
handler: ReactNode | Handler<any, any, any>;
|
|
191
|
-
loading?: ReactNode | false;
|
|
192
256
|
} & EntryPropCommon &
|
|
193
257
|
EntryPropDatas &
|
|
194
|
-
EntryPropSegments
|
|
258
|
+
EntryPropSegments &
|
|
259
|
+
EntryPropRender);
|
|
195
260
|
|
|
196
261
|
/**
|
|
197
262
|
* Tracked include info for build-time manifest generation
|
|
@@ -229,13 +294,65 @@ interface HelperContext {
|
|
|
229
294
|
urlPrefix?: string;
|
|
230
295
|
/** Name prefix from include() - applied to all named routes */
|
|
231
296
|
namePrefix?: string;
|
|
297
|
+
/** True when this scope is at root level (no named include boundary above).
|
|
298
|
+
* Routes at root scope allow dot-local reverse to fall back to bare names. */
|
|
299
|
+
rootScoped?: boolean;
|
|
232
300
|
/** Run helper for cleaner middleware code */
|
|
233
301
|
run?: <T>(fn: () => T | Promise<T>) => T | Promise<T>;
|
|
234
302
|
/** Tracked includes for build-time manifest generation */
|
|
235
303
|
trackedIncludes?: TrackedInclude[];
|
|
304
|
+
/** Cache profiles for DSL-time cache("profileName") resolution */
|
|
305
|
+
cacheProfiles?: Record<
|
|
306
|
+
string,
|
|
307
|
+
import("../cache/profile-registry.js").CacheProfile
|
|
308
|
+
>;
|
|
309
|
+
/** True when resolving handlers inside a cache() DSL boundary.
|
|
310
|
+
* Read by ctx.get() to guard non-cacheable variable reads. */
|
|
311
|
+
insideCacheScope?: boolean;
|
|
312
|
+
/**
|
|
313
|
+
* Include scope string applied to direct-descendant shortCodes.
|
|
314
|
+
*
|
|
315
|
+
* Each `include(...)` call allocates a sibling-positional token like `I0`,
|
|
316
|
+
* `I1` from its parent's include counter and stores the composed scope
|
|
317
|
+
* (`${parentScope}I${idx}`) in its lazyContext. When the include's handler
|
|
318
|
+
* evaluates lazily, the store's `includeScope` is set from that context so
|
|
319
|
+
* every direct-descendant shortCode is generated as
|
|
320
|
+
* `${parent.shortCode}${includeScope}${prefix}${index}` — preventing
|
|
321
|
+
* collisions with siblings declared outside the include.
|
|
322
|
+
*
|
|
323
|
+
* The scope is NOT propagated through `store.run(...)`, so layouts /
|
|
324
|
+
* parallels / caches inside the include absorb the scope into their own
|
|
325
|
+
* shortCodes and their children start fresh.
|
|
326
|
+
*/
|
|
327
|
+
includeScope?: string;
|
|
328
|
+
}
|
|
329
|
+
// Use a global symbol key so the AsyncLocalStorage instance survives HMR
|
|
330
|
+
// module re-evaluation. Without this, Vite's RSC module runner may create
|
|
331
|
+
// a new instance when context.ts is re-evaluated, while other modules still
|
|
332
|
+
// hold references to the old instance — causing getStore() to return
|
|
333
|
+
// undefined even inside a run() callback.
|
|
334
|
+
const RSC_CONTEXT_KEY = Symbol.for("rangojs-router:rsc-context");
|
|
335
|
+
export const RangoContext: AsyncLocalStorage<HelperContext> = ((
|
|
336
|
+
globalThis as any
|
|
337
|
+
)[RSC_CONTEXT_KEY] ??= new AsyncLocalStorage<HelperContext>());
|
|
338
|
+
|
|
339
|
+
/** shortCode prefix letter per entry type (e.g. "L0", "R2", "M1C0"). */
|
|
340
|
+
const SHORT_CODE_PREFIX: Record<
|
|
341
|
+
"layout" | "parallel" | "route" | "loader" | "cache",
|
|
342
|
+
string
|
|
343
|
+
> = {
|
|
344
|
+
layout: "L",
|
|
345
|
+
parallel: "P",
|
|
346
|
+
route: "R",
|
|
347
|
+
loader: "D",
|
|
348
|
+
cache: "C",
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
/** Post-increment a named per-store counter, returning the prior value. */
|
|
352
|
+
function bumpCounter(store: HelperContext, key: string): number {
|
|
353
|
+
store.counters[key] ??= 0;
|
|
354
|
+
return store.counters[key]++;
|
|
236
355
|
}
|
|
237
|
-
export const RSCRouterContext: AsyncLocalStorage<HelperContext> =
|
|
238
|
-
new AsyncLocalStorage<HelperContext>();
|
|
239
356
|
|
|
240
357
|
export const getContext = (): {
|
|
241
358
|
context: AsyncLocalStorage<HelperContext>;
|
|
@@ -243,29 +360,29 @@ export const getContext = (): {
|
|
|
243
360
|
getParent: () => EntryData | null;
|
|
244
361
|
getOrCreateStore: (forRoute?: string) => HelperContext;
|
|
245
362
|
getNextIndex: (
|
|
246
|
-
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate"
|
|
363
|
+
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate",
|
|
247
364
|
) => string;
|
|
248
365
|
getShortCode: (
|
|
249
|
-
type: "layout" | "parallel" | "route" | "loader" | "cache"
|
|
366
|
+
type: "layout" | "parallel" | "route" | "loader" | "cache",
|
|
250
367
|
) => string;
|
|
251
368
|
run: <T>(
|
|
252
369
|
namespace: string,
|
|
253
370
|
parent: EntryData | null,
|
|
254
|
-
callback: (...args: any[]) => T
|
|
371
|
+
callback: (...args: any[]) => T,
|
|
255
372
|
) => T;
|
|
256
373
|
runWithStore: <T>(
|
|
257
374
|
store: HelperContext,
|
|
258
375
|
namespace: string,
|
|
259
376
|
parent: EntryData | null,
|
|
260
|
-
callback: (...args: any[]) => T
|
|
377
|
+
callback: (...args: any[]) => T,
|
|
261
378
|
) => T;
|
|
262
379
|
} => {
|
|
263
|
-
const context =
|
|
380
|
+
const context = RangoContext;
|
|
264
381
|
|
|
265
382
|
return {
|
|
266
383
|
context,
|
|
267
384
|
getOrCreateStore: (forRoute?: string): HelperContext => {
|
|
268
|
-
let store =
|
|
385
|
+
let store = RangoContext.getStore();
|
|
269
386
|
if (!store) {
|
|
270
387
|
store = {
|
|
271
388
|
manifest: new Map<string, EntryData>(),
|
|
@@ -285,7 +402,7 @@ export const getContext = (): {
|
|
|
285
402
|
const store = context.getStore();
|
|
286
403
|
if (!store) {
|
|
287
404
|
throw new Error(
|
|
288
|
-
"
|
|
405
|
+
"Rango context store is not available. Make sure to run within Rango context.",
|
|
289
406
|
);
|
|
290
407
|
}
|
|
291
408
|
return store;
|
|
@@ -299,44 +416,46 @@ export const getContext = (): {
|
|
|
299
416
|
return store.parent;
|
|
300
417
|
},
|
|
301
418
|
getNextIndex: (
|
|
302
|
-
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate"
|
|
419
|
+
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate",
|
|
303
420
|
) => {
|
|
304
421
|
const store = context.getStore();
|
|
305
|
-
invariant(store, "No context
|
|
306
|
-
store
|
|
307
|
-
const index = store.counters[type];
|
|
308
|
-
store.counters[type] = index + 1;
|
|
309
|
-
return `$${type}.${index}`;
|
|
422
|
+
invariant(store, "No context RangoContext available");
|
|
423
|
+
return `$${type}.${bumpCounter(store, type)}`;
|
|
310
424
|
},
|
|
311
|
-
getShortCode: (
|
|
425
|
+
getShortCode: (
|
|
426
|
+
type: "layout" | "parallel" | "route" | "loader" | "cache",
|
|
427
|
+
) => {
|
|
312
428
|
const store = context.getStore();
|
|
313
|
-
invariant(store, "No context
|
|
429
|
+
invariant(store, "No context RangoContext available");
|
|
314
430
|
|
|
315
431
|
const parent = store.parent;
|
|
316
|
-
const prefix = type
|
|
317
|
-
const mountPrefix =
|
|
432
|
+
const prefix = SHORT_CODE_PREFIX[type];
|
|
433
|
+
const mountPrefix =
|
|
434
|
+
store.mountIndex !== undefined ? `M${store.mountIndex}` : "";
|
|
435
|
+
|
|
436
|
+
const includeScope = store.includeScope ?? "";
|
|
318
437
|
|
|
319
438
|
if (!parent) {
|
|
320
439
|
// Root entry: prefix with mount index and use mount-scoped counter
|
|
321
|
-
const counterKey = mountPrefix
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
store
|
|
325
|
-
return `${mountPrefix}${prefix}${index}`;
|
|
440
|
+
const counterKey = mountPrefix
|
|
441
|
+
? `${mountPrefix}_root_${type}`
|
|
442
|
+
: `root_${type}`;
|
|
443
|
+
return `${mountPrefix}${prefix}${bumpCounter(store, counterKey)}`;
|
|
326
444
|
} else {
|
|
327
|
-
// Child entry: use parent-scoped counter
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
445
|
+
// Child entry: use parent-scoped counter with includeScope appended.
|
|
446
|
+
// When we're evaluating a lazy include's direct children, includeScope
|
|
447
|
+
// is a per-include token like "I0" / "I1I0" that partitions the
|
|
448
|
+
// parent's counter namespace so routes inside one include cannot
|
|
449
|
+
// collide with siblings declared outside it.
|
|
450
|
+
const counterKey = `${parent.shortCode}${includeScope}_${type}`;
|
|
451
|
+
return `${parent.shortCode}${includeScope}${prefix}${bumpCounter(store, counterKey)}`;
|
|
333
452
|
}
|
|
334
453
|
},
|
|
335
454
|
runWithStore: <T>(
|
|
336
455
|
store: HelperContext,
|
|
337
456
|
namespace: string,
|
|
338
457
|
parent: EntryData | null,
|
|
339
|
-
callback: (...args: any[]) => T
|
|
458
|
+
callback: (...args: any[]) => T,
|
|
340
459
|
): T => {
|
|
341
460
|
return context.run(
|
|
342
461
|
{
|
|
@@ -353,23 +472,30 @@ export const getContext = (): {
|
|
|
353
472
|
searchSchemas: store.searchSchemas,
|
|
354
473
|
urlPrefix: store.urlPrefix,
|
|
355
474
|
namePrefix: store.namePrefix,
|
|
475
|
+
rootScoped: store.rootScoped,
|
|
356
476
|
trackedIncludes: store.trackedIncludes,
|
|
477
|
+
cacheProfiles: store.cacheProfiles,
|
|
478
|
+
includeScope: store.includeScope,
|
|
357
479
|
},
|
|
358
|
-
callback
|
|
480
|
+
callback,
|
|
359
481
|
);
|
|
360
482
|
},
|
|
361
483
|
run: <T>(
|
|
362
484
|
namespace: string,
|
|
363
485
|
parent: EntryData | null,
|
|
364
|
-
callback: (...args: any[]) => T
|
|
486
|
+
callback: (...args: any[]) => T,
|
|
365
487
|
) => {
|
|
366
488
|
const store = context.getStore();
|
|
367
489
|
// Preserve parent counters to ensure globally unique shortCodes
|
|
368
490
|
const counters = store?.counters || {};
|
|
369
491
|
const manifest = store ? store.manifest : new Map<string, EntryData>();
|
|
370
492
|
const patterns = store?.patterns || new Map<string, string>();
|
|
371
|
-
const
|
|
372
|
-
const
|
|
493
|
+
const patternsByPrefix = store?.patternsByPrefix;
|
|
494
|
+
const trailingSlash =
|
|
495
|
+
store?.trailingSlash ||
|
|
496
|
+
new Map<string, "never" | "always" | "ignore">();
|
|
497
|
+
const searchSchemas =
|
|
498
|
+
store?.searchSchemas || new Map<string, Record<string, string>>();
|
|
373
499
|
return context.run(
|
|
374
500
|
{
|
|
375
501
|
manifest,
|
|
@@ -381,18 +507,46 @@ export const getContext = (): {
|
|
|
381
507
|
metrics: store?.metrics,
|
|
382
508
|
isSSR: store?.isSSR,
|
|
383
509
|
patterns,
|
|
510
|
+
patternsByPrefix,
|
|
384
511
|
trailingSlash,
|
|
385
512
|
searchSchemas,
|
|
386
513
|
urlPrefix: store?.urlPrefix,
|
|
387
514
|
namePrefix: store?.namePrefix,
|
|
515
|
+
rootScoped: store?.rootScoped,
|
|
388
516
|
trackedIncludes: store?.trackedIncludes,
|
|
517
|
+
cacheProfiles: store?.cacheProfiles,
|
|
389
518
|
},
|
|
390
|
-
callback
|
|
519
|
+
callback,
|
|
391
520
|
);
|
|
392
521
|
},
|
|
393
522
|
};
|
|
394
523
|
};
|
|
395
524
|
|
|
525
|
+
/**
|
|
526
|
+
* Acquire the active DSL build context, throwing `message` if a helper was
|
|
527
|
+
* called outside a urls()/map() builder. Returns the store API and the live
|
|
528
|
+
* HelperContext so callers avoid a second getContext() lookup.
|
|
529
|
+
*/
|
|
530
|
+
export function requireDslContext(message: string): {
|
|
531
|
+
store: ReturnType<typeof getContext>;
|
|
532
|
+
ctx: HelperContext;
|
|
533
|
+
} {
|
|
534
|
+
const store = getContext();
|
|
535
|
+
const ctx = store.context.getStore();
|
|
536
|
+
if (!ctx) {
|
|
537
|
+
// The only reason the store is absent here is that a route-definition helper
|
|
538
|
+
// ran with no active RangoContext — i.e. outside a urls()/map() builder.
|
|
539
|
+
// Record that as the cause so the throw is self-explanatory, not a bare
|
|
540
|
+
// "must be called inside urls()" with no indication of the mechanism.
|
|
541
|
+
throw new DslContextError(message, {
|
|
542
|
+
cause:
|
|
543
|
+
"RangoContext store is undefined: a route-definition helper was called " +
|
|
544
|
+
"outside an active urls()/map() builder.",
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
return { store, ctx };
|
|
548
|
+
}
|
|
549
|
+
|
|
396
550
|
/**
|
|
397
551
|
* Run a callback with specific URL and name prefixes
|
|
398
552
|
* Used by include() to apply prefixes to nested patterns
|
|
@@ -400,9 +554,9 @@ export const getContext = (): {
|
|
|
400
554
|
export function runWithPrefixes<T>(
|
|
401
555
|
urlPrefix: string,
|
|
402
556
|
namePrefix: string | undefined,
|
|
403
|
-
callback: () => T
|
|
557
|
+
callback: () => T,
|
|
404
558
|
): T {
|
|
405
|
-
const store =
|
|
559
|
+
const store = RangoContext.getStore();
|
|
406
560
|
if (!store) {
|
|
407
561
|
throw new Error("runWithPrefixes must be called within router context");
|
|
408
562
|
}
|
|
@@ -418,19 +572,43 @@ export function runWithPrefixes<T>(
|
|
|
418
572
|
} else {
|
|
419
573
|
combinedUrlPrefix = urlPrefix;
|
|
420
574
|
}
|
|
421
|
-
const combinedNamePrefix =
|
|
422
|
-
|
|
423
|
-
?
|
|
424
|
-
|
|
425
|
-
|
|
575
|
+
const combinedNamePrefix =
|
|
576
|
+
namePrefix !== undefined
|
|
577
|
+
? namePrefix === ""
|
|
578
|
+
? store.namePrefix
|
|
579
|
+
: store.namePrefix
|
|
580
|
+
? `${store.namePrefix}.${namePrefix}`
|
|
581
|
+
: namePrefix
|
|
582
|
+
: store.namePrefix;
|
|
583
|
+
|
|
584
|
+
// Track root scope for dot-local reverse resolution.
|
|
585
|
+
//
|
|
586
|
+
// The flag answers: "can this route reach bare names at root scope?"
|
|
587
|
+
// It propagates through the include chain:
|
|
588
|
+
//
|
|
589
|
+
// { name: "" } — transparent: inherit parent, default true
|
|
590
|
+
// { name: "foo" } — inherit parent if already set, else create boundary (false)
|
|
591
|
+
// no name — inherit parent unchanged
|
|
592
|
+
//
|
|
593
|
+
// This means { name: "" } + nested { name: "sub" } keeps rootScoped=true
|
|
594
|
+
// (the outer transparent include establishes root access, and the inner
|
|
595
|
+
// named include inherits it). But a direct { name: "sub" } at root gets
|
|
596
|
+
// rootScoped=false (no prior root-access grant, so it creates a boundary).
|
|
597
|
+
const combinedRootScoped =
|
|
598
|
+
namePrefix === ""
|
|
599
|
+
? (store.rootScoped ?? true)
|
|
600
|
+
: namePrefix !== undefined
|
|
601
|
+
? (store.rootScoped ?? false)
|
|
602
|
+
: store.rootScoped;
|
|
426
603
|
|
|
427
|
-
return
|
|
604
|
+
return RangoContext.run(
|
|
428
605
|
{
|
|
429
606
|
...store,
|
|
430
607
|
urlPrefix: combinedUrlPrefix,
|
|
431
608
|
namePrefix: combinedNamePrefix,
|
|
609
|
+
rootScoped: combinedRootScoped,
|
|
432
610
|
},
|
|
433
|
-
callback
|
|
611
|
+
callback,
|
|
434
612
|
);
|
|
435
613
|
}
|
|
436
614
|
|
|
@@ -438,7 +616,7 @@ export function runWithPrefixes<T>(
|
|
|
438
616
|
* Get current URL prefix from context
|
|
439
617
|
*/
|
|
440
618
|
export function getUrlPrefix(): string {
|
|
441
|
-
const store =
|
|
619
|
+
const store = RangoContext.getStore();
|
|
442
620
|
return store?.urlPrefix || "";
|
|
443
621
|
}
|
|
444
622
|
|
|
@@ -446,13 +624,96 @@ export function getUrlPrefix(): string {
|
|
|
446
624
|
* Get current name prefix from context
|
|
447
625
|
*/
|
|
448
626
|
export function getNamePrefix(): string | undefined {
|
|
449
|
-
const store =
|
|
627
|
+
const store = RangoContext.getStore();
|
|
450
628
|
return store?.namePrefix;
|
|
451
629
|
}
|
|
452
630
|
|
|
631
|
+
/**
|
|
632
|
+
* Get whether the current scope is at root level (no named include boundary above).
|
|
633
|
+
* Returns true at root or inside { name: "" } includes, false inside named includes.
|
|
634
|
+
*/
|
|
635
|
+
export function getRootScoped(): boolean {
|
|
636
|
+
const store = RangoContext.getStore();
|
|
637
|
+
return store?.rootScoped ?? true;
|
|
638
|
+
}
|
|
639
|
+
|
|
453
640
|
// Export HelperContext type for use in other modules
|
|
454
641
|
export type { HelperContext };
|
|
455
642
|
|
|
643
|
+
/**
|
|
644
|
+
* Return an isolated copy of a lazy include's captured parent entry.
|
|
645
|
+
*
|
|
646
|
+
* DSL helpers (loader(), middleware(), etc.) mutate ctx.parent in place.
|
|
647
|
+
* Multiple include() scopes capture the *same* syntheticMapRoot as their
|
|
648
|
+
* parent, so without isolation one include's loaders/middleware leak into
|
|
649
|
+
* every other route that shares that root.
|
|
650
|
+
*
|
|
651
|
+
* The clone is shallow: only the mutable arrays are copied so each
|
|
652
|
+
* include pushes to its own list. The rest of the entry (id, shortCode,
|
|
653
|
+
* parent pointer, handler) stays shared, which is correct and cheap.
|
|
654
|
+
*/
|
|
655
|
+
export function getIsolatedLazyParent(
|
|
656
|
+
captured: EntryData | null | undefined,
|
|
657
|
+
): EntryData | null {
|
|
658
|
+
if (!captured) return null;
|
|
659
|
+
return {
|
|
660
|
+
...captured,
|
|
661
|
+
loader: [...captured.loader],
|
|
662
|
+
middleware: [...captured.middleware],
|
|
663
|
+
revalidate: [...captured.revalidate],
|
|
664
|
+
errorBoundary: [...captured.errorBoundary],
|
|
665
|
+
notFoundBoundary: [...captured.notFoundBoundary],
|
|
666
|
+
layout: [...captured.layout],
|
|
667
|
+
parallel: { ...captured.parallel },
|
|
668
|
+
intercept: [...captured.intercept],
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
export function getParallelEntries(
|
|
673
|
+
parallels: ParallelEntries | EntryData[] | undefined,
|
|
674
|
+
): ParallelEntryData[] {
|
|
675
|
+
if (!parallels) return [];
|
|
676
|
+
if (Array.isArray(parallels)) {
|
|
677
|
+
return parallels.filter(
|
|
678
|
+
(entry): entry is ParallelEntryData => entry.type === "parallel",
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
return Object.values(parallels).filter(
|
|
682
|
+
(entry): entry is ParallelEntryData => !!entry,
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export function getParallelSlotEntries(
|
|
687
|
+
parallels: ParallelEntries | EntryData[] | undefined,
|
|
688
|
+
): Array<{ slot: `@${string}`; entry: ParallelEntryData }> {
|
|
689
|
+
if (!parallels) return [];
|
|
690
|
+
|
|
691
|
+
if (Array.isArray(parallels)) {
|
|
692
|
+
return getParallelEntries(parallels).flatMap((entry) =>
|
|
693
|
+
(Object.keys(entry.handler) as `@${string}`[]).map((slot) => ({
|
|
694
|
+
slot,
|
|
695
|
+
entry,
|
|
696
|
+
})),
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return Object.entries(parallels)
|
|
701
|
+
.filter(([, entry]) => !!entry)
|
|
702
|
+
.map(([slot, entry]) => ({
|
|
703
|
+
slot: slot as `@${string}`,
|
|
704
|
+
entry: entry!,
|
|
705
|
+
}));
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
export function getParallelSlotCount(
|
|
709
|
+
parallels: ParallelEntries | EntryData[] | undefined,
|
|
710
|
+
): number {
|
|
711
|
+
if (!parallels) return 0;
|
|
712
|
+
return Array.isArray(parallels)
|
|
713
|
+
? parallels.filter((entry) => entry?.type === "parallel").length
|
|
714
|
+
: Object.keys(parallels).length;
|
|
715
|
+
}
|
|
716
|
+
|
|
456
717
|
// ============================================================================
|
|
457
718
|
// Performance Metrics Helpers
|
|
458
719
|
// ============================================================================
|
|
@@ -468,8 +729,8 @@ export type { HelperContext };
|
|
|
468
729
|
* done(); // Records duration
|
|
469
730
|
* ```
|
|
470
731
|
*/
|
|
471
|
-
export function track(label: string): () => void {
|
|
472
|
-
const store =
|
|
732
|
+
export function track(label: string, depth?: number): () => void {
|
|
733
|
+
const store = RangoContext.getStore();
|
|
473
734
|
|
|
474
735
|
// No-op if context unavailable or metrics not enabled
|
|
475
736
|
if (!store?.metrics?.enabled) {
|
|
@@ -479,7 +740,113 @@ export function track(label: string): () => void {
|
|
|
479
740
|
const startTime = performance.now() - store.metrics.requestStart;
|
|
480
741
|
|
|
481
742
|
return () => {
|
|
482
|
-
const duration =
|
|
483
|
-
|
|
743
|
+
const duration =
|
|
744
|
+
performance.now() - store.metrics!.requestStart - startTime;
|
|
745
|
+
store.metrics!.metrics.push({
|
|
746
|
+
label,
|
|
747
|
+
duration,
|
|
748
|
+
startTime,
|
|
749
|
+
...(depth != null ? { depth } : {}),
|
|
750
|
+
});
|
|
484
751
|
};
|
|
485
752
|
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Separate ALS for tracking loader execution scope.
|
|
756
|
+
* Uses a dedicated ALS (not RangoContext) to avoid issues with
|
|
757
|
+
* nested RangoContext.run() calls in Vite's module runner.
|
|
758
|
+
*/
|
|
759
|
+
const LOADER_SCOPE_KEY = Symbol.for("rangojs-router:loader-scope");
|
|
760
|
+
const loaderScopeALS: AsyncLocalStorage<{ active: true }> = ((
|
|
761
|
+
globalThis as any
|
|
762
|
+
)[LOADER_SCOPE_KEY] ??= new AsyncLocalStorage<{ active: true }>());
|
|
763
|
+
|
|
764
|
+
// Purity-only scope: marks that a loader FUNCTION BODY is executing, regardless
|
|
765
|
+
// of how the loader was invoked (DSL via runInsideLoaderScope, or handler-
|
|
766
|
+
// invoked via ctx.use). Consulted ONLY by isInsideCacheScope() to exempt
|
|
767
|
+
// request-scoped reads. It deliberately does NOT affect isInsideLoaderScope(),
|
|
768
|
+
// so rendered()/barrier/deadlock gating (which must distinguish DSL from
|
|
769
|
+
// handler-invoked loaders) is unchanged.
|
|
770
|
+
const LOADER_BODY_SCOPE_KEY = Symbol.for("rangojs-router:loader-body-scope");
|
|
771
|
+
const loaderBodyScopeALS: AsyncLocalStorage<{ active: true }> = ((
|
|
772
|
+
globalThis as any
|
|
773
|
+
)[LOADER_BODY_SCOPE_KEY] ??= new AsyncLocalStorage<{ active: true }>());
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Check if the current execution is inside a cache() DSL boundary.
|
|
777
|
+
* Returns false inside loader execution — loaders are always fresh
|
|
778
|
+
* (never cached), so non-cacheable reads are safe.
|
|
779
|
+
*/
|
|
780
|
+
export function isInsideCacheScope(): boolean {
|
|
781
|
+
if (RangoContext.getStore()?.insideCacheScope !== true) return false;
|
|
782
|
+
// Loaders are always fresh — even inside a cache() boundary, the loader
|
|
783
|
+
// function re-executes on every request. Skip the guard when running
|
|
784
|
+
// inside a loader.
|
|
785
|
+
if (loaderScopeALS.getStore()?.active) return false;
|
|
786
|
+
// Also exempt handler-invoked loaders: their bodies run in a loader-body
|
|
787
|
+
// scope (not the DSL loader scope above), so request-scoped reads inside any
|
|
788
|
+
// loader — however invoked — are safe (loaders always re-run fresh).
|
|
789
|
+
if (loaderBodyScopeALS.getStore()?.active) return false;
|
|
790
|
+
return true;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Check if the current execution is inside a DSL loader scope
|
|
795
|
+
* (wrapped by runInsideLoaderScope). Used by rendered() barrier
|
|
796
|
+
* to distinguish DSL loaders from handler-invoked loaders.
|
|
797
|
+
*/
|
|
798
|
+
export function isInsideLoaderScope(): boolean {
|
|
799
|
+
return loaderScopeALS.getStore()?.active === true;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Run `fn` inside a loader scope. While active, cache-scope guards
|
|
804
|
+
* are bypassed because loaders are always fresh (never cached) and
|
|
805
|
+
* their side effects (setCookie, header, etc.) are safe.
|
|
806
|
+
*/
|
|
807
|
+
export function runInsideLoaderScope<T>(fn: () => T): T {
|
|
808
|
+
return loaderScopeALS.run({ active: true }, fn);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Run `fn` inside a loader BODY scope. Marks loader-function execution for the
|
|
813
|
+
* cache-purity guard only (isInsideCacheScope), WITHOUT affecting
|
|
814
|
+
* isInsideLoaderScope()/rendered() gating. Applied to every loader body (DSL
|
|
815
|
+
* and handler-invoked via ctx.use) so request-scoped reads inside a loader
|
|
816
|
+
* never trip the cache-scope guards — loaders always run fresh.
|
|
817
|
+
*/
|
|
818
|
+
export function runInsideLoaderBodyScope<T>(fn: () => T): T {
|
|
819
|
+
return loaderBodyScopeALS.run({ active: true }, fn);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// Scope for handle PUSH CALLBACKS (push(() => ...), including async ones).
|
|
823
|
+
// A push callback's value is stored as-is; if it is a promise it is NOT tracked
|
|
824
|
+
// by handleStore.settled and does not block segment resolution, so a
|
|
825
|
+
// ctx.use(loader) made from inside such a callback can never form a rendered()
|
|
826
|
+
// deadlock. This is an ALS (not a plain boolean) so the exemption survives the
|
|
827
|
+
// callback's own awaits — an async push callback that resumes after `await`
|
|
828
|
+
// still reads as "inside a push callback" and stays out of the deadlock guard.
|
|
829
|
+
const PUSH_CALLBACK_SCOPE_KEY = Symbol.for(
|
|
830
|
+
"rangojs-router:push-callback-scope",
|
|
831
|
+
);
|
|
832
|
+
const pushCallbackScopeALS: AsyncLocalStorage<{ active: true }> = ((
|
|
833
|
+
globalThis as any
|
|
834
|
+
)[PUSH_CALLBACK_SCOPE_KEY] ??= new AsyncLocalStorage<{ active: true }>());
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Check if the current execution is inside a handle push callback (sync or an
|
|
838
|
+
* async callback's continuation). Used by the handler-to-loader deadlock guard
|
|
839
|
+
* to exempt push-callback continuations.
|
|
840
|
+
*/
|
|
841
|
+
export function isInsidePushCallbackScope(): boolean {
|
|
842
|
+
return pushCallbackScopeALS.getStore()?.active === true;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Run `fn` inside a push-callback scope. Wraps the invocation of a handle push
|
|
847
|
+
* callback so that any ctx.use(loader) it makes — including after one of its own
|
|
848
|
+
* awaits — is exempt from the deadlock guard.
|
|
849
|
+
*/
|
|
850
|
+
export function runInsidePushCallbackScope<T>(fn: () => T): T {
|
|
851
|
+
return pushCallbackScopeALS.run({ active: true }, fn);
|
|
852
|
+
}
|