@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/rsc/types.ts
CHANGED
|
@@ -7,21 +7,33 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ResolvedSegment, SlotState } from "../types.js";
|
|
9
9
|
import type { HandleData } from "../server/handle-store.js";
|
|
10
|
-
import type {
|
|
10
|
+
import type { RangoInternal } from "../router/router-interfaces.js";
|
|
11
11
|
import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* RSC payload sent to the client
|
|
14
|
+
* RSC payload sent to the client.
|
|
15
|
+
* The tree is always reconstructed from metadata.segments by consumers
|
|
16
|
+
* (SSR via renderSegments, browser via renderSegments in bridges).
|
|
15
17
|
*/
|
|
16
18
|
export interface RscPayload {
|
|
17
|
-
root: React.ReactNode | Promise<React.ReactNode>;
|
|
18
19
|
metadata?: {
|
|
19
20
|
pathname: string;
|
|
20
21
|
segments: ResolvedSegment[];
|
|
22
|
+
/** Router instance ID. When this changes between navigations, the client
|
|
23
|
+
* discards cached segments and does a full tree replacement (app switch). */
|
|
24
|
+
routerId?: string;
|
|
21
25
|
isPartial?: boolean;
|
|
22
26
|
isError?: boolean;
|
|
23
27
|
matched?: string[];
|
|
24
28
|
diff?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* All segment ids re-resolved on the server, including null-component
|
|
31
|
+
* ones excluded from `segments`/`diff`. Drives client-side handle-bucket
|
|
32
|
+
* cleanup. Superset of `diff`. See MatchResult.resolvedIds.
|
|
33
|
+
*/
|
|
34
|
+
resolvedIds?: string[];
|
|
35
|
+
/** Merged route params from the matched route */
|
|
36
|
+
params?: Record<string, string>;
|
|
25
37
|
slots?: Record<string, SlotState>;
|
|
26
38
|
/** Root layout component for browser-side re-renders (client component reference) */
|
|
27
39
|
rootLayout?: React.ComponentType<{ children: React.ReactNode }>;
|
|
@@ -29,15 +41,39 @@ export interface RscPayload {
|
|
|
29
41
|
handles?: AsyncGenerator<HandleData, void, unknown>;
|
|
30
42
|
/** RSC version string for cache invalidation */
|
|
31
43
|
version?: string;
|
|
44
|
+
/** TTL in milliseconds for the client-side in-memory prefetch cache */
|
|
45
|
+
prefetchCacheTTL?: number;
|
|
46
|
+
/** Max entries in the client-side in-memory prefetch cache (FIFO eviction) */
|
|
47
|
+
prefetchCacheSize?: number;
|
|
48
|
+
/** Max concurrent speculative prefetch requests on the client */
|
|
49
|
+
prefetchConcurrency?: number;
|
|
50
|
+
/** Server-resolved rango state cookie name; the client reads it verbatim. */
|
|
51
|
+
stateCookieName?: string;
|
|
32
52
|
/** Theme configuration for FOUC prevention */
|
|
33
53
|
themeConfig?: ResolvedThemeConfig | null;
|
|
34
54
|
/** Initial theme from cookie (for SSR hydration) */
|
|
35
55
|
initialTheme?: Theme;
|
|
56
|
+
/** URL prefix for all routes (from createRouter({ basename })). */
|
|
57
|
+
basename?: string;
|
|
36
58
|
/** Whether connection warmup is enabled */
|
|
37
59
|
warmupEnabled?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Whether the client should hydrate inside React.StrictMode. Carried on
|
|
62
|
+
* the initial full-render payload only; the browser entry reads it once at
|
|
63
|
+
* hydration. Absent on partial (navigation) payloads. Defaults to true on
|
|
64
|
+
* the client when omitted.
|
|
65
|
+
*/
|
|
66
|
+
strictMode?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Server-side redirect with optional state (for partial requests).
|
|
69
|
+
* `external: true` (from redirect(url, { external: true })) tells the client
|
|
70
|
+
* to hard-navigate to an off-host target instead of validating same-origin.
|
|
71
|
+
*/
|
|
72
|
+
redirect?: { url: string; external?: boolean };
|
|
73
|
+
/** Server-set location state to include in history.pushState */
|
|
74
|
+
locationState?: Record<string, unknown>;
|
|
38
75
|
};
|
|
39
76
|
returnValue?: { ok: boolean; data: unknown };
|
|
40
|
-
formState?: unknown;
|
|
41
77
|
}
|
|
42
78
|
|
|
43
79
|
/**
|
|
@@ -54,7 +90,10 @@ export interface RSCDependencies {
|
|
|
54
90
|
*/
|
|
55
91
|
renderToReadableStream: <T>(
|
|
56
92
|
payload: T,
|
|
57
|
-
options?: {
|
|
93
|
+
options?: {
|
|
94
|
+
temporaryReferences?: unknown;
|
|
95
|
+
onError?: (error: unknown) => string | void;
|
|
96
|
+
},
|
|
58
97
|
) => ReadableStream<Uint8Array>;
|
|
59
98
|
|
|
60
99
|
/**
|
|
@@ -62,7 +101,7 @@ export interface RSCDependencies {
|
|
|
62
101
|
*/
|
|
63
102
|
decodeReply: (
|
|
64
103
|
body: FormData | string,
|
|
65
|
-
options?: { temporaryReferences?: unknown }
|
|
104
|
+
options?: { temporaryReferences?: unknown },
|
|
66
105
|
) => Promise<unknown[]>;
|
|
67
106
|
|
|
68
107
|
/**
|
|
@@ -87,7 +126,7 @@ export interface RSCDependencies {
|
|
|
87
126
|
*/
|
|
88
127
|
decodeFormState: (
|
|
89
128
|
actionResult: unknown,
|
|
90
|
-
body: FormData
|
|
129
|
+
body: FormData,
|
|
91
130
|
) => Promise<ReactFormState | null>;
|
|
92
131
|
}
|
|
93
132
|
|
|
@@ -107,6 +146,14 @@ export interface SSRRenderOptions {
|
|
|
107
146
|
* Nonce for Content Security Policy (CSP)
|
|
108
147
|
*/
|
|
109
148
|
nonce?: string;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* SSR stream mode.
|
|
152
|
+
*
|
|
153
|
+
* - `"stream"` (default) — start flushing HTML immediately.
|
|
154
|
+
* - `"allReady"` — await `stream.allReady` before returning.
|
|
155
|
+
*/
|
|
156
|
+
streamMode?: import("../router/router-options.js").SSRStreamMode;
|
|
110
157
|
}
|
|
111
158
|
|
|
112
159
|
/**
|
|
@@ -115,7 +162,33 @@ export interface SSRRenderOptions {
|
|
|
115
162
|
export interface SSRModule {
|
|
116
163
|
renderHTML: (
|
|
117
164
|
rscStream: ReadableStream<Uint8Array>,
|
|
118
|
-
options?: SSRRenderOptions
|
|
165
|
+
options?: SSRRenderOptions,
|
|
166
|
+
) => Promise<ReadableStream<Uint8Array>>;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* PPR shell CAPTURE strategy (Axis 2). Prerenders the loader-masked shell over
|
|
170
|
+
* the Flight stream, aborts once quiescent, and returns the prelude bytes plus
|
|
171
|
+
* the postponed resume state — or null when the prelude degraded and must not
|
|
172
|
+
* be stored. Present only when the SSR virtual entry wires
|
|
173
|
+
* createShellCaptureHandler; the render layer feature-detects it. See
|
|
174
|
+
* docs/design/ppr-shell-resume.md.
|
|
175
|
+
*/
|
|
176
|
+
captureShellHTML?: (
|
|
177
|
+
rscStream: ReadableStream<Uint8Array>,
|
|
178
|
+
options: { quiesce: Promise<void>; maxWaitMs?: number },
|
|
179
|
+
) => Promise<{ prelude: Uint8Array; postponed: string | null } | null>;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* PPR shell RESUME strategy (Axis 2). Produces the per-request live portion of
|
|
183
|
+
* the document: resumes fizz over a fresh SsrRoot to emit only the postponed
|
|
184
|
+
* holes (or, for the DATA variant with postponed === null, just the fresh Flight
|
|
185
|
+
* payload scripts). The caller prepends the stored prelude bytes to form the
|
|
186
|
+
* composite response. Present only when the SSR virtual entry wires
|
|
187
|
+
* createShellResumeHandler; the render layer feature-detects it.
|
|
188
|
+
*/
|
|
189
|
+
resumeShellHTML?: (
|
|
190
|
+
rscStream: ReadableStream<Uint8Array>,
|
|
191
|
+
options: { postponed: string | null; nonce?: string },
|
|
119
192
|
) => Promise<ReadableStream<Uint8Array>>;
|
|
120
193
|
}
|
|
121
194
|
|
|
@@ -141,7 +214,7 @@ export interface HandlerCacheConfig {
|
|
|
141
214
|
*/
|
|
142
215
|
export type NonceProvider<TEnv = unknown> = (
|
|
143
216
|
request: Request,
|
|
144
|
-
env: TEnv
|
|
217
|
+
env: TEnv,
|
|
145
218
|
) => string | true | Promise<string | true>;
|
|
146
219
|
|
|
147
220
|
/**
|
|
@@ -154,7 +227,7 @@ export interface CreateRSCHandlerOptions<
|
|
|
154
227
|
/**
|
|
155
228
|
* The RSC router instance
|
|
156
229
|
*/
|
|
157
|
-
router:
|
|
230
|
+
router: RangoInternal<TEnv, TRoutes>;
|
|
158
231
|
|
|
159
232
|
/**
|
|
160
233
|
* RSC dependencies from @vitejs/plugin-rsc/rsc.
|
|
@@ -213,7 +286,8 @@ export interface CreateRSCHandlerOptions<
|
|
|
213
286
|
* - Undefined to disable nonce (default)
|
|
214
287
|
*
|
|
215
288
|
* The nonce will be applied to inline scripts injected by the RSC payload.
|
|
216
|
-
* It's also available to middleware via `
|
|
289
|
+
* It's also available to middleware via the typed `nonce` token:
|
|
290
|
+
* `import { nonce } from "@rangojs/router"; ctx.get(nonce)`
|
|
217
291
|
*
|
|
218
292
|
* @example Auto-generate nonce
|
|
219
293
|
* ```tsx
|
|
@@ -230,7 +304,16 @@ export interface CreateRSCHandlerOptions<
|
|
|
230
304
|
* nonce: (request, env) => env.nonce,
|
|
231
305
|
* });
|
|
232
306
|
* ```
|
|
307
|
+
*
|
|
308
|
+
* @example Access nonce in middleware
|
|
309
|
+
* ```tsx
|
|
310
|
+
* import { nonce } from "@rangojs/router";
|
|
311
|
+
*
|
|
312
|
+
* const cspMiddleware: Middleware = async (ctx, next) => {
|
|
313
|
+
* const value = ctx.get(nonce); // string | undefined
|
|
314
|
+
* await next();
|
|
315
|
+
* };
|
|
316
|
+
* ```
|
|
233
317
|
*/
|
|
234
318
|
nonce?: NonceProvider<TEnv>;
|
|
235
|
-
|
|
236
319
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Runtime-safe detection of a test runner (Vitest), used to decide whether a
|
|
2
|
+
// create*() call with no plugin-injected $$id may fall back to a synthetic id (a
|
|
3
|
+
// bare test) or must fail loud (dev / a real build).
|
|
4
|
+
//
|
|
5
|
+
// `process` is absent in some target runtimes (the browser, certain edge/worker
|
|
6
|
+
// RSC environments), so probe it through `globalThis` with optional chaining —
|
|
7
|
+
// NEVER a bare `process.env.VITEST`, which would ReferenceError before the
|
|
8
|
+
// intended error is thrown. Unlike `process.env.NODE_ENV` (folded by the app's
|
|
9
|
+
// build `define`), `VITEST` is not folded, so this stays a small runtime check;
|
|
10
|
+
// it lives only on the create*() error path (id missing), which never runs in a
|
|
11
|
+
// correct production build.
|
|
12
|
+
//
|
|
13
|
+
// Vitest sets `VITEST` in every test process — the node project and the
|
|
14
|
+
// react-server forks alike (the RSC project forces NODE_ENV=production, so NODE_ENV
|
|
15
|
+
// cannot distinguish it from a real build; `VITEST` can). A real build never sets it.
|
|
16
|
+
export function isUnderTestRunner(): boolean {
|
|
17
|
+
return !!globalThis.process?.env?.VITEST;
|
|
18
|
+
}
|
package/src/search-params.ts
CHANGED
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
* Search parameter schema types and runtime utilities.
|
|
3
3
|
*
|
|
4
4
|
* Provides a lightweight schema system for typed query parameters.
|
|
5
|
-
* When a route defines a `search` schema, ctx.
|
|
6
|
-
*
|
|
5
|
+
* When a route defines a `search` schema, ctx.search provides a typed
|
|
6
|
+
* object with parsed values. ctx.searchParams always remains a standard
|
|
7
|
+
* URLSearchParams instance.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
import { encodeKV } from "./encode-kv.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Decimal-number grammar for `"number"` search params: optional sign, digits
|
|
14
|
+
* with optional fraction, optional exponent. Deliberately excludes hex (`0x`),
|
|
15
|
+
* `Infinity`, and empty/whitespace so `Number()`'s lenient coercions
|
|
16
|
+
* (`Number("")===0`, `Number("0x10")===16`, `Number("Infinity")===Infinity`)
|
|
17
|
+
* do not slip non-decimal values into typed search.
|
|
18
|
+
*/
|
|
19
|
+
const DECIMAL_NUMBER_RE = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
12
20
|
|
|
13
21
|
/** Supported scalar types for search params (append ? for optional). */
|
|
14
22
|
export type SearchSchemaValue =
|
|
@@ -22,10 +30,6 @@ export type SearchSchemaValue =
|
|
|
22
30
|
/** A search schema maps param names to their type descriptors. */
|
|
23
31
|
export type SearchSchema = Record<string, SearchSchemaValue>;
|
|
24
32
|
|
|
25
|
-
// ============================================================================
|
|
26
|
-
// Type-Level Schema Resolution
|
|
27
|
-
// ============================================================================
|
|
28
|
-
|
|
29
33
|
/** Strip trailing `?` from a schema value to get the base type. */
|
|
30
34
|
type BaseType<T extends string> = T extends `${infer B}?` ? B : T;
|
|
31
35
|
|
|
@@ -54,27 +58,37 @@ type Simplify<T> = { [K in keyof T]: T[K] };
|
|
|
54
58
|
/**
|
|
55
59
|
* Resolve a SearchSchema to its typed object.
|
|
56
60
|
*
|
|
61
|
+
* Both required and optional params resolve to `T | undefined` at the handler
|
|
62
|
+
* level. The required/optional distinction is a consumer-facing contract
|
|
63
|
+
* (e.g., for href() and reverse() autocomplete) — it tells callers which
|
|
64
|
+
* params the route expects, but the handler must still check for undefined
|
|
65
|
+
* since the framework cannot trust the client to send all required params.
|
|
66
|
+
*
|
|
57
67
|
* @example
|
|
58
68
|
* type S = { q: "string"; page: "number?"; sort: "string?" };
|
|
59
69
|
* type R = ResolveSearchSchema<S>;
|
|
60
|
-
* // { q: string; page?: number; sort?: string }
|
|
70
|
+
* // { q: string | undefined; page?: number; sort?: string }
|
|
61
71
|
*/
|
|
62
|
-
export type ResolveSearchSchema<T extends SearchSchema> = Simplify<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
export type ResolveSearchSchema<T extends SearchSchema> = Simplify<
|
|
73
|
+
{
|
|
74
|
+
[K in RequiredKeys<T> & string]:
|
|
75
|
+
| ResolveBaseType<BaseType<T[K]>>
|
|
76
|
+
| undefined;
|
|
77
|
+
} & {
|
|
78
|
+
[K in OptionalKeys<T> & string]?: ResolveBaseType<BaseType<T[K]>>;
|
|
79
|
+
}
|
|
80
|
+
>;
|
|
67
81
|
|
|
68
82
|
// ============================================================================
|
|
69
83
|
// Route-Level Type Extraction
|
|
70
84
|
// ============================================================================
|
|
71
85
|
|
|
72
86
|
/** Resolve the global route map from RegisteredRoutes or GeneratedRouteMap. */
|
|
73
|
-
type GlobalRouteMap = keyof
|
|
74
|
-
? keyof
|
|
87
|
+
type GlobalRouteMap = keyof Rango.RegisteredRoutes extends never
|
|
88
|
+
? keyof Rango.GeneratedRouteMap extends never
|
|
75
89
|
? Record<string, string>
|
|
76
|
-
:
|
|
77
|
-
:
|
|
90
|
+
: Rango.GeneratedRouteMap
|
|
91
|
+
: Rango.RegisteredRoutes;
|
|
78
92
|
|
|
79
93
|
/**
|
|
80
94
|
* Extract the resolved search params type for a named route.
|
|
@@ -87,19 +101,17 @@ type GlobalRouteMap = keyof RSCRouter.RegisteredRoutes extends never
|
|
|
87
101
|
* // { q: string; page?: number }
|
|
88
102
|
* ```
|
|
89
103
|
*/
|
|
90
|
-
export type RouteSearchParams<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
> = [TRouteMap] extends [never]
|
|
104
|
+
export type RouteSearchParams<TName extends string, TRouteMap = never> = [
|
|
105
|
+
TRouteMap,
|
|
106
|
+
] extends [never]
|
|
94
107
|
? ExtractAndResolveSearch<GlobalRouteMap, TName>
|
|
95
108
|
: ExtractAndResolveSearch<TRouteMap, TName>;
|
|
96
109
|
|
|
97
|
-
type ExtractAndResolveSearch<TRouteMap, TName> =
|
|
98
|
-
TName extends
|
|
99
|
-
?
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
: {};
|
|
110
|
+
type ExtractAndResolveSearch<TRouteMap, TName> = TName extends keyof TRouteMap
|
|
111
|
+
? TRouteMap[TName] extends { readonly search: infer S extends SearchSchema }
|
|
112
|
+
? ResolveSearchSchema<S>
|
|
113
|
+
: {}
|
|
114
|
+
: {};
|
|
103
115
|
|
|
104
116
|
/**
|
|
105
117
|
* Extract the route params type for a named route.
|
|
@@ -112,50 +124,62 @@ type ExtractAndResolveSearch<TRouteMap, TName> =
|
|
|
112
124
|
* // { slug: string }
|
|
113
125
|
* ```
|
|
114
126
|
*/
|
|
115
|
-
export type RouteParams<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
> = [TRouteMap] extends [never]
|
|
127
|
+
export type RouteParams<TName extends string, TRouteMap = never> = [
|
|
128
|
+
TRouteMap,
|
|
129
|
+
] extends [never]
|
|
119
130
|
? ExtractRouteParamsFromMap<GlobalRouteMap, TName>
|
|
120
131
|
: ExtractRouteParamsFromMap<TRouteMap, TName>;
|
|
121
132
|
|
|
122
|
-
type ExtractRouteParamsFromMap<TRouteMap, TName> =
|
|
123
|
-
TName extends
|
|
124
|
-
? TRouteMap[TName]
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
133
|
+
type ExtractRouteParamsFromMap<TRouteMap, TName> = TName extends keyof TRouteMap
|
|
134
|
+
? TRouteMap[TName] extends string
|
|
135
|
+
? ExtractParamsFromPattern<TRouteMap[TName]>
|
|
136
|
+
: TRouteMap[TName] extends { readonly path: infer P extends string }
|
|
137
|
+
? ExtractParamsFromPattern<P>
|
|
138
|
+
: {}
|
|
139
|
+
: {};
|
|
140
|
+
|
|
141
|
+
/** Parse "a|b|c" into "a" | "b" | "c" */
|
|
142
|
+
type ParseConstraint<T extends string> =
|
|
143
|
+
T extends `${infer First}|${infer Rest}` ? First | ParseConstraint<Rest> : T;
|
|
130
144
|
|
|
131
145
|
/** Minimal inline param extraction (avoids importing from types.ts to prevent circular deps). */
|
|
132
146
|
type ExtractParamsFromPattern<T extends string> =
|
|
133
147
|
T extends `${string}:${infer Param}/${infer Rest}`
|
|
134
|
-
? Param extends `${infer Name}?`
|
|
135
|
-
? {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
148
|
+
? Param extends `${infer Name}(${infer C})?`
|
|
149
|
+
? {
|
|
150
|
+
[K in Name]?: ParseConstraint<C>;
|
|
151
|
+
} & ExtractParamsFromPattern<`/${Rest}`>
|
|
152
|
+
: Param extends `${infer Name}(${infer C})`
|
|
153
|
+
? {
|
|
154
|
+
[K in Name]: ParseConstraint<C>;
|
|
155
|
+
} & ExtractParamsFromPattern<`/${Rest}`>
|
|
156
|
+
: Param extends `${infer Name}?`
|
|
157
|
+
? { [K in Name]?: string } & ExtractParamsFromPattern<`/${Rest}`>
|
|
158
|
+
: { [K in Param]: string } & ExtractParamsFromPattern<`/${Rest}`>
|
|
139
159
|
: T extends `${string}:${infer Param}`
|
|
140
|
-
? Param extends `${infer Name}?`
|
|
141
|
-
? { [K in Name]?:
|
|
142
|
-
: Param extends `${infer Name}(${
|
|
143
|
-
? { [K in Name]:
|
|
144
|
-
:
|
|
160
|
+
? Param extends `${infer Name}(${infer C})?`
|
|
161
|
+
? { [K in Name]?: ParseConstraint<C> }
|
|
162
|
+
: Param extends `${infer Name}(${infer C})`
|
|
163
|
+
? { [K in Name]: ParseConstraint<C> }
|
|
164
|
+
: Param extends `${infer Name}?`
|
|
165
|
+
? { [K in Name]?: string }
|
|
166
|
+
: { [K in Param]: string }
|
|
145
167
|
: {};
|
|
146
168
|
|
|
147
|
-
// ============================================================================
|
|
148
|
-
// Runtime Parser
|
|
149
|
-
// ============================================================================
|
|
150
|
-
|
|
151
169
|
/**
|
|
152
170
|
* Parse URLSearchParams into a typed object using the given schema.
|
|
153
171
|
*
|
|
154
172
|
* - `"string"` / `"string?"` - kept as-is
|
|
155
|
-
* - `"number"` / `"number?"` -
|
|
173
|
+
* - `"number"` / `"number?"` - parsed as a finite decimal number. Accepts an
|
|
174
|
+
* optional sign, digits with optional fraction, and optional exponent
|
|
175
|
+
* (e.g. `42`, `-3.5`, `1e3`). Empty/whitespace-only, non-decimal forms
|
|
176
|
+
* (`0x10`), and non-finite (`Infinity`) are treated as missing (omitted),
|
|
177
|
+
* NOT coerced to `0`/`16`/`Infinity`.
|
|
156
178
|
* - `"boolean"` / `"boolean?"` - `"true"` / `"1"` -> true, `"false"` / `"0"` / `""` -> false
|
|
157
179
|
*
|
|
158
|
-
* Missing
|
|
180
|
+
* Missing params (both required and optional) are omitted from the result
|
|
181
|
+
* (undefined). The required/optional distinction is a consumer-facing contract
|
|
182
|
+
* only — the handler must check for undefined.
|
|
159
183
|
*/
|
|
160
184
|
export function parseSearchParams<T extends SearchSchema>(
|
|
161
185
|
searchParams: URLSearchParams,
|
|
@@ -169,25 +193,24 @@ export function parseSearchParams<T extends SearchSchema>(
|
|
|
169
193
|
const raw = searchParams.get(key);
|
|
170
194
|
|
|
171
195
|
if (raw === null) {
|
|
172
|
-
|
|
173
|
-
// Required param missing: use zero value
|
|
174
|
-
if (baseType === "string") result[key] = "";
|
|
175
|
-
else if (baseType === "number") result[key] = 0;
|
|
176
|
-
else if (baseType === "boolean") result[key] = false;
|
|
177
|
-
}
|
|
178
|
-
// Optional params are omitted (undefined)
|
|
196
|
+
// Missing params are omitted (undefined) regardless of required/optional
|
|
179
197
|
continue;
|
|
180
198
|
}
|
|
181
199
|
|
|
182
200
|
if (baseType === "string") {
|
|
183
201
|
result[key] = raw;
|
|
184
202
|
} else if (baseType === "number") {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
203
|
+
// Trim, then require a valid decimal numeral and a finite result.
|
|
204
|
+
// Empty/whitespace, hex (0x10), and Infinity are treated as missing
|
|
205
|
+
// (omitted) — not coerced to 0/16/Infinity by Number()'s lenient rules.
|
|
206
|
+
const trimmed = raw.trim();
|
|
207
|
+
if (trimmed !== "" && DECIMAL_NUMBER_RE.test(trimmed)) {
|
|
208
|
+
const num = Number(trimmed);
|
|
209
|
+
if (Number.isFinite(num)) {
|
|
210
|
+
result[key] = num;
|
|
211
|
+
}
|
|
190
212
|
}
|
|
213
|
+
// Anything else treated as missing (undefined)
|
|
191
214
|
} else if (baseType === "boolean") {
|
|
192
215
|
result[key] = raw === "true" || raw === "1";
|
|
193
216
|
}
|
|
@@ -196,23 +219,17 @@ export function parseSearchParams<T extends SearchSchema>(
|
|
|
196
219
|
return result as ResolveSearchSchema<T>;
|
|
197
220
|
}
|
|
198
221
|
|
|
199
|
-
// ============================================================================
|
|
200
|
-
// Runtime Serializer
|
|
201
|
-
// ============================================================================
|
|
202
|
-
|
|
203
222
|
/**
|
|
204
223
|
* Serialize a typed search params object to a query string (without leading `?`).
|
|
205
|
-
* Skips `undefined` and `null` values.
|
|
224
|
+
* Skips `undefined` and `null` values. Preserves insertion order (no sort).
|
|
206
225
|
*/
|
|
207
|
-
export function serializeSearchParams(
|
|
208
|
-
|
|
209
|
-
)
|
|
210
|
-
const
|
|
226
|
+
export function serializeSearchParams(params: Record<string, unknown>): string {
|
|
227
|
+
// Pre-filter null/undefined and coerce values to strings here so encodeKV
|
|
228
|
+
// (which never inspects values) reproduces this call site's exact output.
|
|
229
|
+
const pairs: [string, string][] = [];
|
|
211
230
|
for (const [key, value] of Object.entries(params)) {
|
|
212
231
|
if (value === undefined || value === null) continue;
|
|
213
|
-
|
|
214
|
-
`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`,
|
|
215
|
-
);
|
|
232
|
+
pairs.push([key, String(value)]);
|
|
216
233
|
}
|
|
217
|
-
return
|
|
234
|
+
return encodeKV(pairs);
|
|
218
235
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stable Promise wrappers keyed on the component itself. Objects (React
|
|
5
|
+
* elements, functions, lazy payloads) land in a WeakMap so entries GC when
|
|
6
|
+
* the underlying component is released; primitives (string, number, boolean,
|
|
7
|
+
* null) land in a Map so memoization still applies to text-/null-backed
|
|
8
|
+
* segments like those in partial-update flows. Keeping this cache outside
|
|
9
|
+
* the segment eliminates preservation fields on ResolvedSegment — it survives
|
|
10
|
+
* reconciliation naturally because the component ref is what's stable.
|
|
11
|
+
*
|
|
12
|
+
* Browser-only. On the server each SSR render needs a fresh pending promise
|
|
13
|
+
* so Suspense can emit the loading fallback HTML before content streams. A
|
|
14
|
+
* shared already-resolved promise has `.status === "fulfilled"` attached by
|
|
15
|
+
* React on its first observation — subsequent `use()` calls return
|
|
16
|
+
* synchronously without suspending, so the Suspense fallback never makes it
|
|
17
|
+
* into the initial HTML. Route-definition components share refs across
|
|
18
|
+
* requests, so a global cache would leak tracked state between renders.
|
|
19
|
+
*/
|
|
20
|
+
const IS_BROWSER = typeof window !== "undefined";
|
|
21
|
+
const objectContentCache = IS_BROWSER
|
|
22
|
+
? new WeakMap<object, Promise<ReactNode>>()
|
|
23
|
+
: null;
|
|
24
|
+
const primitiveContentCache = IS_BROWSER
|
|
25
|
+
? new Map<unknown, Promise<ReactNode>>()
|
|
26
|
+
: null;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Return a stable Promise wrapping `component`, memoized on the component ref.
|
|
30
|
+
*
|
|
31
|
+
* A fresh `Promise.resolve(component)` each render would suspend for one
|
|
32
|
+
* microtask and briefly commit the loading fallback inside Suspender — the
|
|
33
|
+
* intercept / parallel-slot flicker this indirection prevents. Reusing the
|
|
34
|
+
* same Promise ref keeps React's `use()` in "known fulfilled" state after
|
|
35
|
+
* the first observation.
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
export function getMemoizedContentPromise(
|
|
40
|
+
component: ReactNode,
|
|
41
|
+
): Promise<ReactNode> {
|
|
42
|
+
if (component instanceof Promise) {
|
|
43
|
+
return component as Promise<ReactNode>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!objectContentCache || !primitiveContentCache) {
|
|
47
|
+
return Promise.resolve(component);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (component !== null && typeof component === "object") {
|
|
51
|
+
const cached = objectContentCache.get(component);
|
|
52
|
+
if (cached) {
|
|
53
|
+
return cached;
|
|
54
|
+
}
|
|
55
|
+
const promise = Promise.resolve(component);
|
|
56
|
+
objectContentCache.set(component, promise);
|
|
57
|
+
return promise;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const cached = primitiveContentCache.get(component);
|
|
61
|
+
if (cached) {
|
|
62
|
+
return cached;
|
|
63
|
+
}
|
|
64
|
+
const promise = Promise.resolve(component);
|
|
65
|
+
primitiveContentCache.set(component, promise);
|
|
66
|
+
return promise;
|
|
67
|
+
}
|