@rangojs/router 0.0.0-experimental.79 → 0.0.0-experimental.7c7e4327
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 +8 -4
- package/README.md +301 -797
- package/dist/bin/rango.js +603 -145
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3750 -1160
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +96 -24
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +85 -6
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +228 -33
- package/skills/caching/SKILL.md +336 -19
- package/skills/catalog.json +271 -0
- 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 +110 -4
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +87 -56
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +12 -10
- package/skills/hooks/SKILL.md +73 -691
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +129 -27
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +75 -19
- package/skills/layout/SKILL.md +40 -19
- package/skills/links/SKILL.md +247 -17
- package/skills/loader/SKILL.md +248 -10
- package/skills/middleware/SKILL.md +25 -13
- package/skills/migrate-nextjs/SKILL.md +205 -20
- package/skills/migrate-react-router/SKILL.md +59 -670
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +29 -2
- package/skills/observability/SKILL.md +202 -0
- package/skills/parallel/SKILL.md +40 -10
- package/skills/ppr/SKILL.md +616 -0
- package/skills/prerender/SKILL.md +72 -60
- package/skills/rango/SKILL.md +318 -26
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +138 -49
- package/skills/route/SKILL.md +117 -9
- package/skills/router-setup/SKILL.md +44 -9
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +776 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +130 -0
- 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 +1 -1
- package/skills/typesafety/SKILL.md +45 -626
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +74 -15
- 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 +0 -65
- 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/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +252 -158
- 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/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +94 -25
- package/src/browser/navigation-client.ts +144 -79
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +161 -73
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +183 -144
- package/src/browser/prefetch/cache.ts +242 -77
- package/src/browser/prefetch/fetch.ts +325 -69
- package/src/browser/prefetch/queue.ts +61 -12
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +58 -20
- package/src/browser/react/NavigationProvider.tsx +202 -120
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -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 +17 -14
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +32 -7
- 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 +25 -3
- 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 +99 -8
- package/src/browser/rsc-router.tsx +145 -28
- package/src/browser/scroll-restoration.ts +37 -22
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +236 -65
- package/src/browser/types.ts +102 -9
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +203 -154
- package/src/build/generate-route-types.ts +3 -1
- package/src/build/index.ts +11 -3
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -21
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +456 -62
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +128 -51
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +13 -21
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +421 -58
- package/src/cache/cache-scope.ts +187 -96
- package/src/cache/cache-tag.ts +149 -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 +2202 -372
- 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 +6 -16
- package/src/cache/document-cache.ts +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +180 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1127 -0
- package/src/client.rsc.tsx +41 -21
- package/src/client.tsx +33 -61
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- 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 +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +67 -37
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +9 -2
- package/src/host/pattern-matcher.ts +23 -52
- 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 +97 -12
- package/src/index.ts +98 -14
- package/src/internal-debug.ts +11 -10
- 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/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +78 -10
- package/src/redirect-origin.ts +114 -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 +65 -39
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +304 -309
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +87 -59
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +12 -1
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +19 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +73 -25
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +27 -42
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +39 -20
- package/src/router/lazy-includes.ts +82 -59
- package/src/router/loader-resolution.ts +167 -72
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +80 -55
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +211 -165
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +159 -285
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +69 -79
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -134
- package/src/router/middleware.ts +298 -172
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +181 -150
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +112 -67
- package/src/router/preview-match.ts +6 -2
- package/src/router/request-classification.ts +50 -69
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +115 -36
- package/src/router/router-options.ts +166 -5
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +131 -86
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +139 -39
- package/src/router/segment-resolution/loader-mask.ts +67 -0
- package/src/router/segment-resolution/loader-snapshot.ts +251 -0
- package/src/router/segment-resolution/revalidation.ts +272 -320
- 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 +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +75 -0
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +105 -20
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -59
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +157 -71
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +291 -217
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +124 -13
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +99 -189
- package/src/rsc/rsc-rendering.ts +421 -76
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +282 -116
- package/src/rsc/shell-capture.ts +1158 -0
- package/src/rsc/shell-serve.ts +150 -0
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +53 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +350 -149
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +208 -51
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +152 -5
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +395 -176
- package/src/ssr/index.tsx +458 -178
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +813 -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 +56 -84
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +110 -62
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +135 -14
- package/src/urls/include-helper.ts +19 -64
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -11
- package/src/urls/path-helper-types.ts +63 -17
- package/src/urls/path-helper.ts +22 -106
- package/src/urls/pattern-types.ts +72 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +98 -154
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +292 -107
- 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 +8 -7
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +127 -86
- 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 +96 -68
- 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 +45 -1
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +4 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +210 -10
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- 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 +48 -95
- package/src/vite/plugins/expose-id-utils.ts +88 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +505 -486
- package/src/vite/plugins/performance-tracks.ts +26 -25
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +138 -27
- package/src/vite/rango.ts +236 -138
- package/src/vite/router-discovery.ts +927 -136
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- 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 +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +71 -43
- package/src/vite/utils/shared-utils.ts +142 -43
- 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/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
package/src/router/find-match.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { tryTrieMatch } from "./trie-matching.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getRouterTrie,
|
|
4
|
+
isRouterTrieAuthoritative,
|
|
5
|
+
} from "../route-map-builder.js";
|
|
3
6
|
import {
|
|
4
7
|
findMatch as findRouteMatch,
|
|
5
8
|
isLazyEvaluationNeeded,
|
|
@@ -8,39 +11,55 @@ import {
|
|
|
8
11
|
import type { MetricsStore } from "../server/context";
|
|
9
12
|
import type { RouteEntry } from "../types";
|
|
10
13
|
|
|
14
|
+
// The single-entry cache is module-lifetime, keyed only on pathname, so the same
|
|
15
|
+
// result object is handed to every same-pathname request. ctx.params aliases
|
|
16
|
+
// this object, so handlers mutating it would corrupt the cache for later requests.
|
|
17
|
+
// Clone params; entry/flags are read-only and shared safely.
|
|
18
|
+
function cloneMatchResult<TEnv>(
|
|
19
|
+
r: RouteMatchResult<TEnv> | null,
|
|
20
|
+
): RouteMatchResult<TEnv> | null {
|
|
21
|
+
return r ? { ...r, params: { ...r.params } } : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
export interface FindMatchDeps<TEnv = any> {
|
|
12
25
|
routesEntries: RouteEntry<TEnv>[];
|
|
13
|
-
|
|
26
|
+
// Returns a Promise only when it had to resolve an async include provider
|
|
27
|
+
// (`() => import()`); void for eager/precomputed includes, so the per-entry
|
|
28
|
+
// match loop pays no microtask in the common case.
|
|
29
|
+
evaluateLazyEntry: (entry: RouteEntry<TEnv>) => void | Promise<void>;
|
|
14
30
|
routerId: string;
|
|
15
31
|
}
|
|
16
32
|
|
|
17
33
|
/**
|
|
18
34
|
* Create a findMatch function bound to router state.
|
|
19
35
|
* Includes single-entry cache to avoid redundant matching within the same request.
|
|
36
|
+
*
|
|
37
|
+
* Async because a lazy include may be backed by an async provider
|
|
38
|
+
* (`() => import("./routes")`) that must be resolved before its routes can be
|
|
39
|
+
* matched. The hot path is unaffected: `await` only suspends on the first
|
|
40
|
+
* request to a not-yet-loaded dynamic include; eager routes resolve in the same
|
|
41
|
+
* microtask. Every caller already runs inside an async match pipeline.
|
|
20
42
|
*/
|
|
21
43
|
export function createFindMatch<TEnv = any>(
|
|
22
44
|
deps: FindMatchDeps<TEnv>,
|
|
23
|
-
): (
|
|
45
|
+
): (
|
|
46
|
+
pathname: string,
|
|
47
|
+
ms?: MetricsStore,
|
|
48
|
+
) => Promise<RouteMatchResult<TEnv> | null> {
|
|
24
49
|
// Single-entry cache for findMatch to avoid redundant matching within the same request.
|
|
25
50
|
// previewMatch and match both call findMatch with the same pathname — this ensures
|
|
26
51
|
// the route matching work (which may check thousands of routes) only happens once.
|
|
27
52
|
let lastFindMatchPathname: string | null = null;
|
|
28
53
|
let lastFindMatchResult: RouteMatchResult<TEnv> | null = null;
|
|
29
54
|
|
|
30
|
-
|
|
31
|
-
// Handles lazy evaluation by evaluating lazy entries on first match.
|
|
32
|
-
// Phase 1: try O(path_length) trie match.
|
|
33
|
-
// Phase 2: fall back to regex iteration.
|
|
34
|
-
return function findMatch(
|
|
55
|
+
return async function findMatch(
|
|
35
56
|
pathname: string,
|
|
36
57
|
ms?: MetricsStore,
|
|
37
|
-
): RouteMatchResult<TEnv> | null {
|
|
38
|
-
// Return cached result if same pathname (avoids double-match per request)
|
|
58
|
+
): Promise<RouteMatchResult<TEnv> | null> {
|
|
39
59
|
if (lastFindMatchPathname === pathname) {
|
|
40
|
-
return lastFindMatchResult;
|
|
60
|
+
return cloneMatchResult(lastFindMatchResult);
|
|
41
61
|
}
|
|
42
62
|
|
|
43
|
-
// Helper to push sub-metrics
|
|
44
63
|
const pushMetric = ms
|
|
45
64
|
? (label: string, start: number) => {
|
|
46
65
|
ms.metrics.push({
|
|
@@ -51,17 +70,15 @@ export function createFindMatch<TEnv = any>(
|
|
|
51
70
|
}
|
|
52
71
|
: undefined;
|
|
53
72
|
|
|
54
|
-
// Phase 1: Try trie match (O(path_length))
|
|
55
|
-
// Only use the per-router trie. The global trie merges routes from ALL
|
|
56
|
-
// routers and must not be used — in multi-router setups (host routing)
|
|
57
|
-
// overlapping paths like "/" would match the wrong app's route.
|
|
58
73
|
const routeTrie = getRouterTrie(deps.routerId);
|
|
74
|
+
let trieMatched = false;
|
|
59
75
|
if (routeTrie) {
|
|
60
76
|
const trieStart = performance.now();
|
|
61
77
|
const trieResult = tryTrieMatch(routeTrie, pathname);
|
|
62
78
|
pushMetric?.("match:trie", trieStart);
|
|
63
79
|
|
|
64
80
|
if (trieResult) {
|
|
81
|
+
trieMatched = true;
|
|
65
82
|
// Find the RouteEntry that contains this route.
|
|
66
83
|
// Multiple entries can share the same staticPrefix (e.g., several
|
|
67
84
|
// include("/", patterns) calls all produce staticPrefix=""). Evaluate
|
|
@@ -69,11 +86,33 @@ export function createFindMatch<TEnv = any>(
|
|
|
69
86
|
const entryStart = performance.now();
|
|
70
87
|
let entry: RouteEntry<TEnv> | undefined;
|
|
71
88
|
let fallbackEntry: RouteEntry<TEnv> | undefined;
|
|
89
|
+
let candidateError: unknown;
|
|
90
|
+
let candidateFailed = false;
|
|
72
91
|
|
|
73
92
|
for (const e of deps.routesEntries) {
|
|
74
93
|
if (e.staticPrefix !== trieResult.sp) continue;
|
|
75
94
|
if (!fallbackEntry) fallbackEntry = e;
|
|
76
|
-
|
|
95
|
+
try {
|
|
96
|
+
const ev = deps.evaluateLazyEntry(e);
|
|
97
|
+
if (ev) await ev;
|
|
98
|
+
} catch (err) {
|
|
99
|
+
// Multiple async includes can share one collapsed static prefix, so
|
|
100
|
+
// this loop may evaluate a candidate that is NOT the route's owner.
|
|
101
|
+
// Remember the failure and keep scanning: if another candidate owns
|
|
102
|
+
// the route, this sibling's failure is correctly isolated (its
|
|
103
|
+
// _lazyInflight was cleared, so a later request retries). But if NO
|
|
104
|
+
// candidate ends up owning it, the failing one WAS the owner — a
|
|
105
|
+
// real, trie-matched route whose module failed to import — and we
|
|
106
|
+
// rethrow below as a 5xx rather than silently 404ing (see finding 3:
|
|
107
|
+
// owner failure must stay loud).
|
|
108
|
+
candidateError = err;
|
|
109
|
+
candidateFailed = true;
|
|
110
|
+
console.error(
|
|
111
|
+
`[@rangojs/router] include at "${e.staticPrefix}" failed to load: ` +
|
|
112
|
+
`${(err as Error)?.message ?? String(err)}`,
|
|
113
|
+
);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
77
116
|
if (
|
|
78
117
|
e.routes &&
|
|
79
118
|
trieResult.routeKey in (e.routes as Record<string, unknown>)
|
|
@@ -83,12 +122,16 @@ export function createFindMatch<TEnv = any>(
|
|
|
83
122
|
}
|
|
84
123
|
}
|
|
85
124
|
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
125
|
+
// The trie matched this route, so it EXISTS. If no same-prefix candidate
|
|
126
|
+
// could own it AND one failed to load, the owning module is the one that
|
|
127
|
+
// threw — surface the module failure as a 5xx, not the misleading 404 a
|
|
128
|
+
// fallthrough would give.
|
|
129
|
+
if (!entry && candidateFailed) {
|
|
130
|
+
throw candidateError;
|
|
131
|
+
}
|
|
132
|
+
|
|
89
133
|
if (!entry) entry = fallbackEntry;
|
|
90
134
|
|
|
91
|
-
// If entry not found (nested include not yet discovered), evaluate parent
|
|
92
135
|
if (!entry) {
|
|
93
136
|
const parent = deps.routesEntries.find(
|
|
94
137
|
(e) =>
|
|
@@ -97,7 +140,8 @@ export function createFindMatch<TEnv = any>(
|
|
|
97
140
|
);
|
|
98
141
|
if (parent) {
|
|
99
142
|
const lazyStart = performance.now();
|
|
100
|
-
deps.evaluateLazyEntry(parent);
|
|
143
|
+
const ev = deps.evaluateLazyEntry(parent);
|
|
144
|
+
if (ev) await ev;
|
|
101
145
|
pushMetric?.("match:lazy-eval", lazyStart);
|
|
102
146
|
}
|
|
103
147
|
entry = deps.routesEntries.find(
|
|
@@ -112,9 +156,7 @@ export function createFindMatch<TEnv = any>(
|
|
|
112
156
|
entry,
|
|
113
157
|
routeKey: trieResult.routeKey,
|
|
114
158
|
params: trieResult.params,
|
|
115
|
-
optionalParams: new Set(trieResult.optionalParams || []),
|
|
116
159
|
redirectTo: trieResult.redirectTo,
|
|
117
|
-
ancestry: trieResult.ancestry,
|
|
118
160
|
...(trieResult.pr ? { pr: true } : {}),
|
|
119
161
|
...(trieResult.pt ? { pt: true } : {}),
|
|
120
162
|
...(trieResult.responseType
|
|
@@ -125,17 +167,25 @@ export function createFindMatch<TEnv = any>(
|
|
|
125
167
|
: {}),
|
|
126
168
|
...(trieResult.rscFirst ? { rscFirst: true } : {}),
|
|
127
169
|
};
|
|
128
|
-
return lastFindMatchResult;
|
|
170
|
+
return cloneMatchResult(lastFindMatchResult);
|
|
129
171
|
}
|
|
172
|
+
} else if (isRouterTrieAuthoritative(deps.routerId)) {
|
|
173
|
+
// Authoritative miss (#664): this trie was deserialized from the
|
|
174
|
+
// COMPLETE build manifest, so trailing-slash redirects are already
|
|
175
|
+
// trie-native hits and a miss means no route exists. Skip the regex
|
|
176
|
+
// fallback — the only route-count-proportional match path — and do
|
|
177
|
+
// not evaluate lazy includes for unmatched (bot-probe) traffic.
|
|
178
|
+
// Trie hits that need lazy splicing keep the fallback loop below
|
|
179
|
+
// (trieMatched === true never reaches this branch).
|
|
180
|
+
lastFindMatchPathname = pathname;
|
|
181
|
+
lastFindMatchResult = null;
|
|
182
|
+
return null;
|
|
130
183
|
}
|
|
131
184
|
}
|
|
132
185
|
|
|
133
|
-
// Phase 2: Fall back to existing matching (regex iteration)
|
|
134
186
|
const regexStart = performance.now();
|
|
135
187
|
let result = findRouteMatch(pathname, deps.routesEntries);
|
|
136
188
|
|
|
137
|
-
// If we hit a lazy entry that needs evaluation, evaluate and retry.
|
|
138
|
-
// Cap iterations to prevent infinite loops from pathological nesting.
|
|
139
189
|
const MAX_LAZY_ITERATIONS = 100;
|
|
140
190
|
let iterations = 0;
|
|
141
191
|
while (isLazyEvaluationNeeded(result)) {
|
|
@@ -148,13 +198,62 @@ export function createFindMatch<TEnv = any>(
|
|
|
148
198
|
lastFindMatchResult = null;
|
|
149
199
|
return null;
|
|
150
200
|
}
|
|
151
|
-
|
|
201
|
+
try {
|
|
202
|
+
const ev = deps.evaluateLazyEntry(result.lazyEntry);
|
|
203
|
+
if (ev) await ev;
|
|
204
|
+
} catch (err) {
|
|
205
|
+
// If the trie matched this pathname, the route is REAL and this lazy
|
|
206
|
+
// entry owns (part of) it — a module-load failure is a genuine error, so
|
|
207
|
+
// propagate it as a 5xx. If the trie did NOT match, this is a
|
|
208
|
+
// genuinely-unmatched pathname (a typo, a bot probe): its 404 must not be
|
|
209
|
+
// upgraded to a 500 just because some lazy include — e.g. a failing root
|
|
210
|
+
// `include("/")` — happened to need evaluating while we probed. Isolate
|
|
211
|
+
// and fall through to the 404 (finding 4: the two match phases agree —
|
|
212
|
+
// owner failure is loud, unmatched stays 404).
|
|
213
|
+
if (trieMatched) throw err;
|
|
214
|
+
console.error(
|
|
215
|
+
`[@rangojs/router] include at "${result.lazyEntry.staticPrefix}" ` +
|
|
216
|
+
`failed to load while resolving unmatched path "${pathname}": ` +
|
|
217
|
+
`${(err as Error)?.message ?? String(err)}`,
|
|
218
|
+
);
|
|
219
|
+
lastFindMatchPathname = pathname;
|
|
220
|
+
lastFindMatchResult = null;
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
152
223
|
result = findRouteMatch(pathname, deps.routesEntries);
|
|
153
224
|
}
|
|
154
225
|
pushMetric?.("match:regex-fallback", regexStart);
|
|
155
226
|
|
|
227
|
+
// The trie is the single source of truth and is built before findMatch in
|
|
228
|
+
// both dev (handler rebuild) and production (ensureRouterManifest). If the
|
|
229
|
+
// trie was present yet the regex fallback resolved a real match, the trie
|
|
230
|
+
// has a gap (e.g. a route shape it cannot represent) and dev/prod could
|
|
231
|
+
// diverge if the trie were ever absent. Surface it in dev; folded out in
|
|
232
|
+
// production builds.
|
|
233
|
+
//
|
|
234
|
+
// Suppress when the trie DID match (`trieMatched`): that path falls through
|
|
235
|
+
// to the regex fallback only on the first request to a not-yet-spliced lazy
|
|
236
|
+
// entry (e.g. a 2+-level nested include whose deeper parent has not been
|
|
237
|
+
// evaluated). The trie knew the route; runtime lazy discovery simply lagged.
|
|
238
|
+
// That is the supported lazy-include flow, not a trie gap, so warning on it
|
|
239
|
+
// is a false positive (it manufactures bug reports and erodes the signal).
|
|
240
|
+
if (
|
|
241
|
+
process.env.NODE_ENV !== "production" &&
|
|
242
|
+
routeTrie &&
|
|
243
|
+
!trieMatched &&
|
|
244
|
+
result &&
|
|
245
|
+
!isLazyEvaluationNeeded(result)
|
|
246
|
+
) {
|
|
247
|
+
console.warn(
|
|
248
|
+
`[@rangojs/router] Route "${pathname}" resolved via the regex fallback ` +
|
|
249
|
+
`even though the route trie was present. The trie should be the single ` +
|
|
250
|
+
`matching source of truth; this indicates a trie gap. Please report this ` +
|
|
251
|
+
`with your route configuration.`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
156
255
|
lastFindMatchPathname = pathname;
|
|
157
256
|
lastFindMatchResult = result;
|
|
158
|
-
return result;
|
|
257
|
+
return cloneMatchResult(result);
|
|
159
258
|
};
|
|
160
259
|
}
|
|
@@ -18,6 +18,13 @@ import { isInsideCacheScope } from "../server/context.js";
|
|
|
18
18
|
import { NOCACHE_SYMBOL, assertNotInsideCacheExec } from "../cache/taint.js";
|
|
19
19
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
20
20
|
import { PRERENDER_PASSTHROUGH } from "../prerender.js";
|
|
21
|
+
import { substitutePatternParams } from "./substitute-pattern-params.js";
|
|
22
|
+
import { fireAndForgetWaitUntil } from "../types/request-scope.js";
|
|
23
|
+
|
|
24
|
+
// Mutating Headers methods guarded so they throw inside "use cache" / cache()
|
|
25
|
+
// scope. Module-level constant (read-only, .has() lookups) so it is allocated
|
|
26
|
+
// once at module load instead of per createHandlerContext (per-request) call.
|
|
27
|
+
const MUTATING_HEADERS_METHODS = new Set(["set", "append", "delete"]);
|
|
21
28
|
|
|
22
29
|
/**
|
|
23
30
|
* Strip internal _rsc* query params from a URL.
|
|
@@ -158,51 +165,14 @@ export function createReverseFunction(
|
|
|
158
165
|
);
|
|
159
166
|
}
|
|
160
167
|
|
|
161
|
-
let result = pattern;
|
|
162
|
-
|
|
163
168
|
// Merge current request params as defaults, explicit params override
|
|
164
169
|
const effectiveParams = currentParams
|
|
165
170
|
? { ...currentParams, ...hrefParams }
|
|
166
171
|
: hrefParams;
|
|
167
172
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
let hadOmittedOptional = false;
|
|
172
|
-
// First pass: optional params (trailing ?)
|
|
173
|
-
result = result.replace(
|
|
174
|
-
/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?(\?)/g,
|
|
175
|
-
(_, key) => {
|
|
176
|
-
const value = effectiveParams[key];
|
|
177
|
-
// Empty string is treated as omitted — the trie matcher fills
|
|
178
|
-
// unmatched optional params with "" (not undefined), so reverse
|
|
179
|
-
// must collapse those segments instead of leaving empty slots.
|
|
180
|
-
if (value === undefined || value === "") {
|
|
181
|
-
hadOmittedOptional = true;
|
|
182
|
-
return "";
|
|
183
|
-
}
|
|
184
|
-
return encodeURIComponent(value);
|
|
185
|
-
},
|
|
186
|
-
);
|
|
187
|
-
// Second pass: required params (no trailing ?)
|
|
188
|
-
result = result.replace(
|
|
189
|
-
/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?(?!\?)/g,
|
|
190
|
-
(_, key) => {
|
|
191
|
-
const value = effectiveParams[key];
|
|
192
|
-
if (value === undefined) {
|
|
193
|
-
throw new Error(`Missing param "${key}" for route "${name}"`);
|
|
194
|
-
}
|
|
195
|
-
return encodeURIComponent(value);
|
|
196
|
-
},
|
|
197
|
-
);
|
|
198
|
-
// Clean up slashes only when an optional param was actually omitted,
|
|
199
|
-
// so intentional trailing-slash patterns like "/blog/" are preserved.
|
|
200
|
-
if (hadOmittedOptional) {
|
|
201
|
-
const hadTrailingSlash = pattern.length > 1 && pattern.endsWith("/");
|
|
202
|
-
result = result.replace(/\/\/+/g, "/").replace(/\/+$/, "") || "/";
|
|
203
|
-
if (hadTrailingSlash && !result.endsWith("/")) result += "/";
|
|
204
|
-
}
|
|
205
|
-
}
|
|
173
|
+
let result = effectiveParams
|
|
174
|
+
? substitutePatternParams(pattern, effectiveParams, name)
|
|
175
|
+
: pattern;
|
|
206
176
|
|
|
207
177
|
// Append search params as query string
|
|
208
178
|
if (search) {
|
|
@@ -247,7 +217,7 @@ export function createHandlerContext<TEnv>(
|
|
|
247
217
|
// Guard mutating Headers methods so they throw inside "use cache" or cache() scope.
|
|
248
218
|
// Uses lazy `ctx` reference (assigned below) — only the specific handler ctx
|
|
249
219
|
// is stamped by cache-runtime, not the shared request context.
|
|
250
|
-
|
|
220
|
+
// MUTATING_HEADERS_METHODS is hoisted to module scope (constant, read-only).
|
|
251
221
|
let ctx: InternalHandlerContext<any, TEnv>;
|
|
252
222
|
const guardedHeaders = new Proxy(stubResponse.headers, {
|
|
253
223
|
get(target, prop, receiver) {
|
|
@@ -281,8 +251,12 @@ export function createHandlerContext<TEnv>(
|
|
|
281
251
|
search: searchSchema ? resolvedSearchParams : {},
|
|
282
252
|
pathname,
|
|
283
253
|
url,
|
|
284
|
-
originalUrl: new URL(request.url),
|
|
254
|
+
originalUrl: requestContext?.originalUrl ?? new URL(request.url),
|
|
285
255
|
env: bindings,
|
|
256
|
+
waitUntil: requestContext
|
|
257
|
+
? requestContext.waitUntil.bind(requestContext)
|
|
258
|
+
: fireAndForgetWaitUntil,
|
|
259
|
+
executionContext: requestContext?.executionContext,
|
|
286
260
|
_variables: variables,
|
|
287
261
|
get: ((keyOrVar: any) => {
|
|
288
262
|
// Read-time guard: non-cacheable var inside cache() → throw.
|
|
@@ -387,6 +361,12 @@ export function createPrerenderContext<TEnv>(
|
|
|
387
361
|
"Configure buildEnv in your rango() plugin options to enable build-time env access.",
|
|
388
362
|
);
|
|
389
363
|
},
|
|
364
|
+
// Build-time prerender has no live request. waitUntil is a true no-op
|
|
365
|
+
// (running fn() here would fire side effects during build, which is
|
|
366
|
+
// incorrect — these are meant to outlive the live response).
|
|
367
|
+
// executionContext is absent for the same reason.
|
|
368
|
+
waitUntil: () => {},
|
|
369
|
+
executionContext: undefined,
|
|
390
370
|
_variables: variables,
|
|
391
371
|
get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
|
|
392
372
|
set: ((keyOrVar: any, value: any) => {
|
|
@@ -476,6 +456,11 @@ export function createStaticContext<TEnv>(
|
|
|
476
456
|
"Configure buildEnv in your rango() plugin options to enable build-time env access.",
|
|
477
457
|
);
|
|
478
458
|
},
|
|
459
|
+
// Static() handlers have no live request. waitUntil is a true no-op
|
|
460
|
+
// (running fn() here would fire side effects during build, which is
|
|
461
|
+
// incorrect). executionContext is absent for the same reason.
|
|
462
|
+
waitUntil: () => {},
|
|
463
|
+
executionContext: undefined,
|
|
479
464
|
_variables: variables,
|
|
480
465
|
get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
|
|
481
466
|
set: ((keyOrVar: any, value: any) => {
|