@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
|
@@ -9,36 +9,10 @@ import {
|
|
|
9
9
|
useRef,
|
|
10
10
|
} from "react";
|
|
11
11
|
import { NavigationStoreContext } from "./context.js";
|
|
12
|
-
import
|
|
12
|
+
import { shallowEqual } from "./shallow-equal.js";
|
|
13
|
+
import type { PublicNavigationState } from "../types.js";
|
|
13
14
|
import type { DerivedNavigationState } from "../event-controller.js";
|
|
14
15
|
|
|
15
|
-
/**
|
|
16
|
-
* Shallow equality check for selector results
|
|
17
|
-
*/
|
|
18
|
-
function shallowEqual<T>(a: T, b: T): boolean {
|
|
19
|
-
if (Object.is(a, b)) return true;
|
|
20
|
-
if (
|
|
21
|
-
typeof a !== "object" ||
|
|
22
|
-
a === null ||
|
|
23
|
-
typeof b !== "object" ||
|
|
24
|
-
b === null
|
|
25
|
-
) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
const keysA = Object.keys(a);
|
|
29
|
-
const keysB = Object.keys(b);
|
|
30
|
-
if (keysA.length !== keysB.length) return false;
|
|
31
|
-
for (const key of keysA) {
|
|
32
|
-
if (
|
|
33
|
-
!Object.hasOwn(b, key) ||
|
|
34
|
-
!Object.is((a as any)[key], (b as any)[key])
|
|
35
|
-
) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
16
|
/**
|
|
43
17
|
* Convert derived state to public version (strips inflightActions)
|
|
44
18
|
*/
|
|
@@ -47,64 +21,61 @@ function toPublicState(state: DerivedNavigationState): PublicNavigationState {
|
|
|
47
21
|
return publicState;
|
|
48
22
|
}
|
|
49
23
|
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Navigation methods returned by useNavigation
|
|
53
|
-
*/
|
|
54
|
-
export interface NavigationMethods {
|
|
55
|
-
navigate: (url: string, options?: NavigateOptions) => Promise<void>;
|
|
56
|
-
refresh: () => Promise<void>;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
24
|
/**
|
|
60
|
-
*
|
|
61
|
-
*/
|
|
62
|
-
export type NavigationValue = PublicNavigationState & NavigationMethods;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Hook to access navigation state with optional selector for performance
|
|
25
|
+
* Hook to access reactive navigation state with optional selector for performance.
|
|
66
26
|
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
27
|
+
* Returns state only. For actions (push, replace, refresh, prefetch),
|
|
28
|
+
* use useRouter() instead.
|
|
69
29
|
*
|
|
70
30
|
* @example
|
|
71
31
|
* ```tsx
|
|
72
|
-
* const state = useNavigation(
|
|
32
|
+
* const { state, location } = useNavigation();
|
|
73
33
|
* const isLoading = useNavigation(nav => nav.state === 'loading');
|
|
74
34
|
* ```
|
|
75
35
|
*/
|
|
76
|
-
export function useNavigation():
|
|
36
|
+
export function useNavigation(): PublicNavigationState;
|
|
77
37
|
export function useNavigation<T>(
|
|
78
38
|
selector: (state: PublicNavigationState) => T,
|
|
79
39
|
): T;
|
|
80
40
|
export function useNavigation<T>(
|
|
81
41
|
selector?: (state: PublicNavigationState) => T,
|
|
82
|
-
): T |
|
|
42
|
+
): T | PublicNavigationState {
|
|
83
43
|
const ctx = useContext(NavigationStoreContext);
|
|
84
44
|
|
|
85
45
|
if (!ctx) {
|
|
86
|
-
throw new Error(
|
|
87
|
-
"useNavigation must be used within NavigationStoreContext.Provider"
|
|
88
|
-
);
|
|
46
|
+
throw new Error("useNavigation must be used within NavigationProvider");
|
|
89
47
|
}
|
|
90
48
|
|
|
91
|
-
// Base state for useOptimistic
|
|
92
49
|
const [baseValue, setBaseValue] = useState<T | PublicNavigationState>(() => {
|
|
93
50
|
const publicState = toPublicState(ctx.eventController.getState());
|
|
94
51
|
return selector ? selector(publicState) : publicState;
|
|
95
52
|
});
|
|
96
53
|
const prevState = useRef(baseValue);
|
|
97
54
|
|
|
98
|
-
//
|
|
55
|
+
// Tracks whether the most recent setOptimisticValue call pinned the value
|
|
56
|
+
// to a non-idle state. Used to decide whether to emit a release update when
|
|
57
|
+
// returning to idle, so the optimistic store doesn't stay pinned if a
|
|
58
|
+
// parent transition (e.g. <Link> click) is still pending.
|
|
59
|
+
const optimisticPinnedRef = useRef(false);
|
|
60
|
+
|
|
99
61
|
const [value, setOptimisticValue] = useOptimistic(baseValue);
|
|
100
62
|
|
|
63
|
+
// Store selector in a ref so the subscription callback always uses the
|
|
64
|
+
// latest selector without re-subscribing on every render (inline functions
|
|
65
|
+
// have a new identity each render). This is event-driven by design: the
|
|
66
|
+
// value updates when the store emits, not when the selector changes.
|
|
67
|
+
// Between events there is nothing new to select from.
|
|
68
|
+
const selectorRef = useRef(selector);
|
|
69
|
+
selectorRef.current = selector;
|
|
70
|
+
|
|
101
71
|
// Subscribe to event controller state changes (only runs on client)
|
|
102
72
|
useEffect(() => {
|
|
103
|
-
|
|
104
|
-
return ctx.eventController.subscribe(() => {
|
|
73
|
+
const update = () => {
|
|
105
74
|
const currentState = ctx.eventController.getState();
|
|
106
75
|
const publicState = toPublicState(currentState);
|
|
107
|
-
const nextSelected =
|
|
76
|
+
const nextSelected = selectorRef.current
|
|
77
|
+
? selectorRef.current(publicState)
|
|
78
|
+
: publicState;
|
|
108
79
|
|
|
109
80
|
// Check if selected value has changed
|
|
110
81
|
if (!shallowEqual(nextSelected, prevState.current)) {
|
|
@@ -114,27 +85,40 @@ export function useNavigation<T>(
|
|
|
114
85
|
const hasInflightActions =
|
|
115
86
|
ctx.eventController.getInflightActions().size > 0;
|
|
116
87
|
|
|
117
|
-
|
|
118
|
-
|
|
88
|
+
const shouldPin = hasInflightActions || publicState.state !== "idle";
|
|
89
|
+
|
|
90
|
+
if (shouldPin) {
|
|
91
|
+
// Pin the optimistic store so the loading value shows immediately
|
|
92
|
+
// even if a parent transition (e.g. <Link> click) defers the
|
|
93
|
+
// urgent setBaseValue commit.
|
|
119
94
|
startTransition(() => {
|
|
120
95
|
setOptimisticValue(nextSelected);
|
|
121
96
|
});
|
|
97
|
+
optimisticPinnedRef.current = true;
|
|
98
|
+
} else if (optimisticPinnedRef.current) {
|
|
99
|
+
// Release a previously-pinned optimistic value. Without this,
|
|
100
|
+
// useOptimistic keeps returning the stale loading value while
|
|
101
|
+
// any parent transition is still pending, even after baseValue
|
|
102
|
+
// flipped to idle.
|
|
103
|
+
startTransition(() => {
|
|
104
|
+
setOptimisticValue(nextSelected);
|
|
105
|
+
});
|
|
106
|
+
optimisticPinnedRef.current = false;
|
|
122
107
|
}
|
|
123
108
|
|
|
124
109
|
// Always update base state so UI reflects current state
|
|
125
110
|
setBaseValue(nextSelected);
|
|
126
111
|
}
|
|
127
|
-
});
|
|
128
|
-
}, [selector]);
|
|
129
|
-
|
|
130
|
-
// If no selector, include navigation methods
|
|
131
|
-
if (!selector) {
|
|
132
|
-
return {
|
|
133
|
-
...(value as PublicNavigationState),
|
|
134
|
-
navigate: ctx.navigate,
|
|
135
|
-
refresh: ctx.refresh,
|
|
136
112
|
};
|
|
137
|
-
}
|
|
138
113
|
|
|
139
|
-
|
|
114
|
+
// Catch-up: re-read state synchronously on mount before subscribing, so a
|
|
115
|
+
// state change between the seeding render and this effect commit isn't
|
|
116
|
+
// dropped until the next (debounced) notify. Mirrors usePathname /
|
|
117
|
+
// useSearchParams.
|
|
118
|
+
update();
|
|
119
|
+
|
|
120
|
+
return ctx.eventController.subscribe(update);
|
|
121
|
+
}, []);
|
|
122
|
+
|
|
123
|
+
return value as T | PublicNavigationState;
|
|
140
124
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useContext, useState, useEffect, useRef } from "react";
|
|
4
|
+
import { NavigationStoreContext } from "./context.js";
|
|
5
|
+
import { shallowEqual } from "./shallow-equal.js";
|
|
6
|
+
|
|
7
|
+
const EMPTY_PARAMS: Record<string, string> = Object.freeze({});
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook to access the current route params.
|
|
11
|
+
*
|
|
12
|
+
* Returns the merged route params from the matched route.
|
|
13
|
+
* Updates when navigation completes, not during pending navigation.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* // Route: /products/:productId
|
|
18
|
+
* const params = useParams();
|
|
19
|
+
* // { productId: "123" }
|
|
20
|
+
*
|
|
21
|
+
* // Annotate the expected shape via a generic
|
|
22
|
+
* const { productId } = useParams<{ productId: string }>();
|
|
23
|
+
*
|
|
24
|
+
* // With selector
|
|
25
|
+
* const productId = useParams(p => p.productId);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
// `T extends object` (not `Record<string, string | undefined>`) so that
|
|
29
|
+
// interface shapes pass the constraint — interfaces lack an implicit
|
|
30
|
+
// index signature and would otherwise be rejected. The generic is a
|
|
31
|
+
// shape annotation, not a runtime check; the body always returns the
|
|
32
|
+
// underlying params map unchanged. The default and selector input use
|
|
33
|
+
// `string | undefined` because absent optional params are omitted from
|
|
34
|
+
// the params record at runtime — the type must reflect that so callers
|
|
35
|
+
// don't write `p.locale.length` and crash when the segment is absent.
|
|
36
|
+
export function useParams<
|
|
37
|
+
T extends object = Record<string, string | undefined>,
|
|
38
|
+
>(): Readonly<T>;
|
|
39
|
+
export function useParams<T>(
|
|
40
|
+
selector: (params: Record<string, string | undefined>) => T,
|
|
41
|
+
): T;
|
|
42
|
+
export function useParams<T>(
|
|
43
|
+
selector?: (params: Record<string, string | undefined>) => T,
|
|
44
|
+
): T | Record<string, string | undefined> {
|
|
45
|
+
const ctx = useContext(NavigationStoreContext);
|
|
46
|
+
|
|
47
|
+
const [value, setValue] = useState<T | Record<string, string>>(() => {
|
|
48
|
+
const params = ctx ? ctx.eventController.getParams() : EMPTY_PARAMS;
|
|
49
|
+
return selector ? selector(params) : params;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const prevValue = useRef(value);
|
|
53
|
+
const selectorRef = useRef(selector);
|
|
54
|
+
selectorRef.current = selector;
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!ctx) return;
|
|
58
|
+
|
|
59
|
+
const update = () => {
|
|
60
|
+
const params = ctx.eventController.getParams();
|
|
61
|
+
const next = selectorRef.current ? selectorRef.current(params) : params;
|
|
62
|
+
|
|
63
|
+
if (!shallowEqual(next, prevValue.current)) {
|
|
64
|
+
prevValue.current = next;
|
|
65
|
+
setValue(next);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
update();
|
|
70
|
+
|
|
71
|
+
return ctx.eventController.subscribe(update);
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useContext, useState, useEffect, useRef } from "react";
|
|
4
|
+
import { NavigationStoreContext } from "./context.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook to access the current pathname.
|
|
8
|
+
*
|
|
9
|
+
* Returns the committed pathname string (excludes search params and hash).
|
|
10
|
+
* Updates when navigation completes, not during pending navigation.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const pathname = usePathname();
|
|
15
|
+
* // "/products/123"
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function usePathname(): string {
|
|
19
|
+
const ctx = useContext(NavigationStoreContext);
|
|
20
|
+
|
|
21
|
+
const [pathname, setPathname] = useState<string>(() => {
|
|
22
|
+
if (!ctx) {
|
|
23
|
+
return "/";
|
|
24
|
+
}
|
|
25
|
+
return (ctx.eventController.getState().location as URL).pathname;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const prevPathname = useRef(pathname);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!ctx) return;
|
|
32
|
+
|
|
33
|
+
const update = () => {
|
|
34
|
+
const next = (ctx.eventController.getState().location as URL).pathname;
|
|
35
|
+
if (next !== prevPathname.current) {
|
|
36
|
+
prevPathname.current = next;
|
|
37
|
+
setPathname(next);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
update();
|
|
42
|
+
|
|
43
|
+
return ctx.eventController.subscribe(update);
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
return pathname;
|
|
47
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import type { LocalReverseFunction } from "../../reverse.js";
|
|
5
|
+
import { substitutePatternParams } from "../../router/substitute-pattern-params.js";
|
|
6
|
+
import { serializeSearchParams } from "../../search-params.js";
|
|
7
|
+
import { useMount } from "./use-mount.js";
|
|
8
|
+
import { useParams } from "./use-params.js";
|
|
9
|
+
|
|
10
|
+
type RouteEntry = string | { readonly path: string };
|
|
11
|
+
type LocalRouteMap = Readonly<Record<string, RouteEntry>>;
|
|
12
|
+
|
|
13
|
+
function getPattern(entry: RouteEntry | undefined): string | undefined {
|
|
14
|
+
if (entry === undefined) return undefined;
|
|
15
|
+
return typeof entry === "string" ? entry : entry.path;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Join an include mount prefix with a mount-relative pattern.
|
|
20
|
+
*
|
|
21
|
+
* `pattern === "/"` is the index of the local module — under a non-root
|
|
22
|
+
* mount it must collapse so `/` under `/blog` becomes `/blog`, not
|
|
23
|
+
* `/blog/`. This matches `ctx.reverse(".index")` on the server.
|
|
24
|
+
*/
|
|
25
|
+
function joinMount(mount: string, pattern: string): string {
|
|
26
|
+
if (pattern === "/") {
|
|
27
|
+
if (mount === "" || mount === "/") return "/";
|
|
28
|
+
return mount.endsWith("/") ? mount.slice(0, -1) : mount;
|
|
29
|
+
}
|
|
30
|
+
const normalizedMount = mount === "/" ? "" : mount.replace(/\/+$/, "");
|
|
31
|
+
return normalizedMount + pattern;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Mount-aware reverse function for a locally-imported `routes` map.
|
|
36
|
+
*
|
|
37
|
+
* The `routes` map you pass IS the scope: `reverse("name")` looks the name up
|
|
38
|
+
* in that map (verbatim), prefixes the result with the surrounding `include()`
|
|
39
|
+
* mount path via `useMount()`, and substitutes params — auto-filling from the
|
|
40
|
+
* current matched route's params, with explicit params overriding. A module's
|
|
41
|
+
* components can therefore reverse their own routes without knowing where the
|
|
42
|
+
* module is mounted: include it under any prefix and the URLs resolve correctly.
|
|
43
|
+
*
|
|
44
|
+
* The leading dot is optional and cosmetic: `reverse("post")` and
|
|
45
|
+
* `reverse(".post")` resolve identically. The dot exists only as a readability
|
|
46
|
+
* convention and for parity with `ctx.reverse(".name")` on the server; here the
|
|
47
|
+
* passed map is the scope, so there is no separate global namespace to
|
|
48
|
+
* disambiguate and the dot carries no meaning.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* "use client";
|
|
53
|
+
* import { Link, useReverse } from "@rangojs/router/client";
|
|
54
|
+
* import { routes as blogRoutes } from "../urls/blog.gen.js";
|
|
55
|
+
*
|
|
56
|
+
* function BlogNav() {
|
|
57
|
+
* const reverse = useReverse(blogRoutes);
|
|
58
|
+
* return (
|
|
59
|
+
* <>
|
|
60
|
+
* <Link to={reverse("index")}>Blog</Link>
|
|
61
|
+
* <Link to={reverse("post", { postId: "hello" })}>Post</Link>
|
|
62
|
+
* </>
|
|
63
|
+
* );
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export function useReverse<const TRoutes extends LocalRouteMap>(
|
|
68
|
+
routes: TRoutes,
|
|
69
|
+
): LocalReverseFunction<TRoutes> {
|
|
70
|
+
const mount = useMount();
|
|
71
|
+
const currentParams = useParams();
|
|
72
|
+
|
|
73
|
+
return useCallback(
|
|
74
|
+
((
|
|
75
|
+
name: string,
|
|
76
|
+
explicitParams?: Record<string, string | undefined>,
|
|
77
|
+
search?: Record<string, unknown>,
|
|
78
|
+
): string => {
|
|
79
|
+
// The leading dot is optional. The passed map IS the scope, so a dot to
|
|
80
|
+
// signal "local" is unnecessary — "detail" and ".detail" resolve the same.
|
|
81
|
+
// A dot is accepted (and stripped) for readability / ctx.reverse parity.
|
|
82
|
+
const lookupName = name.startsWith(".") ? name.slice(1) : name;
|
|
83
|
+
const entry = (routes as LocalRouteMap)[lookupName];
|
|
84
|
+
const pattern = getPattern(entry);
|
|
85
|
+
if (pattern === undefined) {
|
|
86
|
+
throw new Error(`Unknown route: "${name}"`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const joined = joinMount(mount, pattern);
|
|
90
|
+
|
|
91
|
+
const mergedParams = explicitParams
|
|
92
|
+
? { ...currentParams, ...explicitParams }
|
|
93
|
+
: currentParams;
|
|
94
|
+
|
|
95
|
+
const substituted = substitutePatternParams(joined, mergedParams, name);
|
|
96
|
+
|
|
97
|
+
if (search) {
|
|
98
|
+
const qs = serializeSearchParams(search);
|
|
99
|
+
if (qs) return `${substituted}?${qs}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return substituted;
|
|
103
|
+
}) as LocalReverseFunction<TRoutes>,
|
|
104
|
+
[routes, mount, currentParams],
|
|
105
|
+
);
|
|
106
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useContext, useMemo } from "react";
|
|
4
|
+
import { NavigationStoreContext } from "./context.js";
|
|
5
|
+
import { prefetchDirect } from "../prefetch/fetch.js";
|
|
6
|
+
import { getAppVersion } from "../app-version.js";
|
|
7
|
+
import type { RouterInstance, RouterNavigateOptions } from "../types.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook to access router actions (push, replace, refresh, prefetch, back, forward).
|
|
11
|
+
*
|
|
12
|
+
* Returns a STABLE reference that never changes, so components using
|
|
13
|
+
* useRouter() do not re-render on navigation state changes.
|
|
14
|
+
* For reactive navigation state, use useNavigation() instead.
|
|
15
|
+
*
|
|
16
|
+
* Methods read `basename` from the context on each call. It is set once from
|
|
17
|
+
* the initial payload and is stable within a session — a cross-app navigation
|
|
18
|
+
* is a full document load (X-RSC-Reload), so the target app mounts fresh with
|
|
19
|
+
* its own basename.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* const router = useRouter();
|
|
24
|
+
* router.push("/products");
|
|
25
|
+
* router.replace("/login", { scroll: false });
|
|
26
|
+
* router.prefetch("/dashboard");
|
|
27
|
+
* router.back();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function useRouter(): RouterInstance {
|
|
31
|
+
const ctx = useContext(NavigationStoreContext);
|
|
32
|
+
|
|
33
|
+
if (!ctx) {
|
|
34
|
+
throw new Error("useRouter must be used within NavigationProvider");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Stable reference: ctx itself is stable, and reads on each method call
|
|
38
|
+
// pick up live basename values from the context (backed by a live ref
|
|
39
|
+
// in NavigationProvider), so app-switch transitions are reflected without
|
|
40
|
+
// recreating this object.
|
|
41
|
+
return useMemo<RouterInstance>(() => {
|
|
42
|
+
/** Prefix a root-relative path with basename if not already prefixed. */
|
|
43
|
+
function withBasename(url: string): string {
|
|
44
|
+
const bn = ctx!.basename;
|
|
45
|
+
if (!bn || !url.startsWith("/") || url.startsWith(bn + "/") || url === bn)
|
|
46
|
+
return url;
|
|
47
|
+
return url === "/" ? bn : bn + url;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
push(url: string, options?: RouterNavigateOptions): Promise<void> {
|
|
52
|
+
return ctx.navigate(withBasename(url), { ...options, replace: false });
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
replace(url: string, options?: RouterNavigateOptions): Promise<void> {
|
|
56
|
+
return ctx.navigate(withBasename(url), { ...options, replace: true });
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
refresh(): Promise<void> {
|
|
60
|
+
return ctx.refresh();
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
prefetch(url: string, options?: { key?: ":source" }): void {
|
|
64
|
+
const segmentState = ctx.store?.getSegmentState();
|
|
65
|
+
if (segmentState) {
|
|
66
|
+
prefetchDirect(
|
|
67
|
+
withBasename(url),
|
|
68
|
+
segmentState.currentSegmentIds,
|
|
69
|
+
getAppVersion(),
|
|
70
|
+
ctx.store?.getRouterId?.(),
|
|
71
|
+
options?.key,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
back(): void {
|
|
77
|
+
// Avoid escaping the host on the first entry of this session.
|
|
78
|
+
// Prefer the Navigation API; fall back to the router-stamped
|
|
79
|
+
// history.state.idx (set by pushHistoryWithIdx) for older browsers.
|
|
80
|
+
const nav = (window as { navigation?: { canGoBack: boolean } })
|
|
81
|
+
.navigation;
|
|
82
|
+
const canGoBack =
|
|
83
|
+
nav && typeof nav.canGoBack === "boolean"
|
|
84
|
+
? nav.canGoBack
|
|
85
|
+
: ((window.history.state as { idx?: number } | null)?.idx ?? 0) > 0;
|
|
86
|
+
if (canGoBack) {
|
|
87
|
+
window.history.back();
|
|
88
|
+
} else {
|
|
89
|
+
ctx.navigate(withBasename("/"), { replace: true });
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
forward(): void {
|
|
94
|
+
window.history.forward();
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}, []);
|
|
98
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useContext, useState, useEffect, useRef } from "react";
|
|
4
|
+
import { NavigationStoreContext } from "./context.js";
|
|
5
|
+
import type { ReadonlyURLSearchParams } from "../types.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook to access the current URL search params.
|
|
9
|
+
*
|
|
10
|
+
* Returns a read-only URLSearchParams object from the committed location.
|
|
11
|
+
* Updates when navigation completes, not during pending navigation.
|
|
12
|
+
*
|
|
13
|
+
* Note: During SSR the search params are not available (the server only sends
|
|
14
|
+
* the pathname). The hook returns empty params during SSR and syncs from
|
|
15
|
+
* the browser URL on mount.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const searchParams = useSearchParams();
|
|
20
|
+
* const query = searchParams.get("q"); // "react"
|
|
21
|
+
* const page = searchParams.get("page"); // "2"
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function useSearchParams(): ReadonlyURLSearchParams {
|
|
25
|
+
const ctx = useContext(NavigationStoreContext);
|
|
26
|
+
|
|
27
|
+
const [searchParams, setSearchParams] = useState<ReadonlyURLSearchParams>(
|
|
28
|
+
() => new URLSearchParams(),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const prevSearch = useRef("");
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!ctx) return;
|
|
35
|
+
|
|
36
|
+
const update = () => {
|
|
37
|
+
const location = ctx.eventController.getState().location as URL;
|
|
38
|
+
const nextSearch = location.searchParams.toString();
|
|
39
|
+
if (nextSearch !== prevSearch.current) {
|
|
40
|
+
prevSearch.current = nextSearch;
|
|
41
|
+
setSearchParams(new URLSearchParams(nextSearch));
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
update();
|
|
46
|
+
|
|
47
|
+
return ctx.eventController.subscribe(update);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
return searchParams;
|
|
51
|
+
}
|