@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -4
- package/README.md +296 -887
- package/dist/bin/rango.js +459 -91
- package/dist/testing/vitest.js +36 -2
- package/dist/vite/index.js +1708 -414
- package/package.json +35 -10
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +82 -5
- package/skills/bundle-analysis/SKILL.md +2 -2
- package/skills/cache-guide/SKILL.md +14 -9
- package/skills/caching/SKILL.md +221 -12
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +83 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +11 -3
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +9 -9
- package/skills/hooks/SKILL.md +73 -900
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +84 -7
- package/skills/i18n/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +51 -17
- package/skills/layout/SKILL.md +38 -16
- package/skills/links/SKILL.md +1 -1
- package/skills/loader/SKILL.md +48 -20
- package/skills/middleware/SKILL.md +11 -5
- package/skills/migrate-nextjs/SKILL.md +203 -20
- package/skills/migrate-react-router/SKILL.md +59 -675
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +3 -3
- package/skills/observability/SKILL.md +70 -5
- package/skills/parallel/SKILL.md +32 -8
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +59 -28
- package/skills/rango/SKILL.md +124 -50
- package/skills/response-routes/SKILL.md +78 -46
- package/skills/route/SKILL.md +85 -6
- package/skills/router-setup/SKILL.md +41 -6
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +28 -3
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +1 -1
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +68 -654
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -918
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +47 -17
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +44 -1
- package/src/__augment-tests__/augmented.check.ts +2 -3
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +14 -27
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +178 -100
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +81 -68
- package/src/browser/navigation-client.ts +115 -70
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +153 -88
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +157 -144
- package/src/browser/prefetch/cache.ts +148 -81
- package/src/browser/prefetch/fetch.ts +231 -51
- package/src/browser/prefetch/queue.ts +25 -7
- package/src/browser/rango-state.ts +157 -115
- package/src/browser/react/Link.tsx +40 -7
- package/src/browser/react/NavigationProvider.tsx +140 -99
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +17 -2
- package/src/browser/react/index.ts +0 -51
- package/src/browser/react/location-state-shared.ts +14 -15
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-router.ts +6 -4
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/response-adapter.ts +74 -8
- package/src/browser/rsc-router.tsx +97 -22
- package/src/browser/scroll-restoration.ts +15 -8
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/server-action-bridge.ts +216 -38
- package/src/browser/types.ts +94 -22
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/generate-manifest.ts +155 -131
- package/src/build/generate-route-types.ts +1 -1
- package/src/build/index.ts +11 -5
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -22
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +12 -1
- package/src/build/route-types/include-resolution.ts +455 -61
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +15 -2
- package/src/build/route-types/router-processing.ts +77 -41
- package/src/build/route-types/source-scan.ts +105 -7
- package/src/build/runtime-discovery.ts +4 -1
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +454 -101
- package/src/cache/cache-scope.ts +159 -54
- package/src/cache/cache-tag.ts +149 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2170 -377
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +194 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1132 -0
- package/src/client.rsc.tsx +39 -22
- package/src/client.tsx +28 -58
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +13 -1
- package/src/decode-loader-results.ts +18 -2
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +0 -3
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +57 -40
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +7 -0
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +7 -12
- package/src/index.rsc.ts +88 -8
- package/src/index.ts +90 -16
- package/src/internal-debug.ts +11 -10
- package/src/loader.rsc.ts +19 -9
- package/src/loader.ts +12 -4
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +75 -7
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +25 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +13 -49
- package/src/route-definition/dsl-helpers.ts +60 -53
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +46 -46
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +6 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +0 -5
- package/src/router/content-negotiation.ts +58 -23
- package/src/router/error-handling.ts +44 -17
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +6 -1
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +35 -2
- package/src/router/lazy-includes.ts +79 -56
- package/src/router/loader-resolution.ts +151 -73
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +76 -52
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +181 -178
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +115 -194
- package/src/router/match-middleware/cache-store.ts +61 -50
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +36 -67
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +231 -120
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +99 -152
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +111 -66
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +47 -42
- package/src/router/revalidation.ts +75 -81
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +70 -8
- package/src/router/router-options.ts +126 -4
- package/src/router/segment-resolution/fresh.ts +104 -80
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +155 -39
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +215 -304
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +35 -15
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +54 -35
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +9 -23
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -64
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +13 -5
- package/src/router.ts +119 -48
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +267 -152
- package/src/rsc/helpers.ts +78 -4
- package/src/rsc/index.ts +1 -4
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +11 -15
- package/src/rsc/progressive-enhancement.ts +120 -13
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +58 -141
- package/src/rsc/rsc-rendering.ts +492 -49
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +268 -82
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +45 -3
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +31 -26
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +260 -95
- package/src/server/context.ts +99 -9
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +125 -2
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +379 -138
- package/src/ssr/index.tsx +491 -182
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +14 -31
- package/src/testing/dispatch.ts +533 -160
- package/src/testing/e2e/fixture.ts +45 -11
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/e2e/parity.ts +85 -4
- package/src/testing/e2e/server.ts +12 -0
- package/src/testing/flight-matchers.ts +7 -14
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +36 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +30 -0
- package/src/testing/flight.ts +145 -70
- package/src/testing/generated-routes.ts +26 -50
- package/src/testing/index.ts +18 -19
- package/src/testing/internal/context.ts +184 -68
- 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 +134 -115
- package/src/testing/run-loader.ts +140 -51
- package/src/testing/run-middleware.ts +59 -33
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
- package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
- package/src/testing/vitest.ts +138 -16
- package/src/theme/ThemeProvider.tsx +56 -84
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +15 -15
- package/src/types/handler-context.ts +45 -15
- package/src/types/index.ts +2 -10
- package/src/types/loader-types.ts +6 -3
- package/src/types/request-scope.ts +8 -22
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +100 -13
- package/src/urls/include-helper.ts +10 -12
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -8
- package/src/urls/path-helper-types.ts +52 -14
- package/src/urls/path-helper.ts +5 -54
- package/src/urls/pattern-types.ts +36 -0
- package/src/urls/type-extraction.ts +76 -42
- package/src/urls/urls-function.ts +0 -14
- package/src/use-loader.tsx +0 -186
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +34 -43
- package/src/vite/discovery/discovery-errors.ts +61 -0
- package/src/vite/discovery/prerender-collection.ts +33 -46
- package/src/vite/discovery/state.ts +12 -1
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/index.ts +9 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +143 -10
- package/src/vite/plugins/cjs-to-esm.ts +8 -12
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +85 -56
- package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
- package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
- package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
- package/src/vite/plugins/expose-internal-ids.ts +10 -1
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +22 -27
- package/src/vite/plugins/version-plugin.ts +6 -66
- package/src/vite/plugins/virtual-entries.ts +137 -26
- package/src/vite/rango.ts +146 -135
- package/src/vite/router-discovery.ts +189 -48
- package/src/vite/utils/ast-handler-extract.ts +11 -20
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +4 -75
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +71 -44
- package/src/vite/utils/shared-utils.ts +55 -37
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -72,9 +72,12 @@ export type LoaderContext<
|
|
|
72
72
|
* **Experimental.** Wait for all non-loader segments to settle.
|
|
73
73
|
*
|
|
74
74
|
* After the returned promise resolves, handle data is available via
|
|
75
|
-
* `ctx.use(handle)`.
|
|
76
|
-
* trees
|
|
77
|
-
*
|
|
75
|
+
* `ctx.use(handle)`. Supported in DSL loaders, including on streaming
|
|
76
|
+
* trees that use `loading()` — the barrier waits for the streaming
|
|
77
|
+
* handlers to finish pushing before it resolves. Throws if called from a
|
|
78
|
+
* handler-invoked loader, or if a handler is already awaiting this loader
|
|
79
|
+
* via `ctx.use()` (that would deadlock — use a loader-to-loader
|
|
80
|
+
* dependency instead).
|
|
78
81
|
*
|
|
79
82
|
* @example
|
|
80
83
|
* ```typescript
|
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RequestScope: the fields every user-facing context shares.
|
|
3
|
-
*
|
|
4
|
-
* A handler, middleware, loader, response handler, and the ALS-bound
|
|
5
|
-
* RequestContext are all different phases of the same request, and they
|
|
6
|
-
* all carry the same set of request-scoped capabilities: the raw Request,
|
|
7
|
-
* the parsed URL pair (`url` is cleaned of internal `_rsc*` params,
|
|
8
|
-
* `originalUrl` retains them), pathname/searchParams, platform bindings
|
|
9
|
-
* (`env`), and two escape hatches for work that outlives the response
|
|
10
|
-
* (`waitUntil`) or needs the raw Cloudflare runtime object
|
|
11
|
-
* (`executionContext`).
|
|
12
|
-
*
|
|
13
|
-
* Each public context type intersects `RequestScope<TEnv>` with its own
|
|
14
|
-
* phase-specific fields (e.g. `params`/`reverse` on HandlerContext,
|
|
15
|
-
* `headers`/`header()` on MiddlewareContext). That keeps platform surface
|
|
16
|
-
* in one place and lets the next runtime escape hatch we need land in
|
|
17
|
-
* one file instead of four.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
1
|
import type { DefaultEnv } from "./global-namespace.js";
|
|
21
2
|
|
|
22
3
|
/**
|
|
@@ -44,9 +25,14 @@ export interface ExecutionContext {
|
|
|
44
25
|
* of inventing its own fallback policy.
|
|
45
26
|
*/
|
|
46
27
|
export function fireAndForgetWaitUntil(fn: () => Promise<void>): void {
|
|
47
|
-
fn()
|
|
48
|
-
|
|
49
|
-
|
|
28
|
+
// Defer fn() invocation to a microtask so a SYNCHRONOUS throw in a non-async
|
|
29
|
+
// callback (e.g. `() => { somethingThatThrows(); return p; }`) becomes a
|
|
30
|
+
// rejected promise we catch here, not an exception that escapes into the
|
|
31
|
+
// request flow. waitUntil is fire-and-forget: a background-task failure must
|
|
32
|
+
// never break the response.
|
|
33
|
+
Promise.resolve()
|
|
34
|
+
.then(fn)
|
|
35
|
+
.catch((err) => console.error("[waitUntil] Background task failed:", err));
|
|
50
36
|
}
|
|
51
37
|
|
|
52
38
|
/**
|
|
@@ -7,47 +7,33 @@ export type DocumentProps = {
|
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Parse constraint values into a union type
|
|
12
|
-
* "a|b|c" -> "a" | "b" | "c"
|
|
13
|
-
*/
|
|
14
10
|
type ParseConstraint<T extends string> =
|
|
15
11
|
T extends `${infer First}|${infer Rest}` ? First | ParseConstraint<Rest> : T;
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* - :param? -> { name: "param", optional: true, type: string }
|
|
23
|
-
* - :param(a|b) -> { name: "param", optional: false, type: "a" | "b" }
|
|
24
|
-
* - :param(a|b)? -> { name: "param", optional: true, type: "a" | "b" }
|
|
25
|
-
*/
|
|
13
|
+
// Named catch-all (`:name*` / `:name+`) is matched BEFORE the `?`/suffix
|
|
14
|
+
// branches. Its modifier is anchored to the END of the token (no trailing
|
|
15
|
+
// `${string}`) so it is a true suffix and never mis-splits a constraint body
|
|
16
|
+
// such as `id(\d+)`. Both are a required `string`: a matched catch-all always
|
|
17
|
+
// binds a value (possibly ""), so the key is always present.
|
|
26
18
|
type ExtractParamInfo<T extends string> =
|
|
27
|
-
// Optional + constrained (with optional suffix): :param(a|b)?suffix
|
|
28
19
|
T extends `${infer Name}(${infer Constraint})?${string}`
|
|
29
20
|
? { name: Name; optional: true; type: ParseConstraint<Constraint> }
|
|
30
|
-
:
|
|
31
|
-
T extends `${infer Name}(${infer Constraint})${string}`
|
|
21
|
+
: T extends `${infer Name}(${infer Constraint})${string}`
|
|
32
22
|
? { name: Name; optional: false; type: ParseConstraint<Constraint> }
|
|
33
|
-
:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
: // Param with dot-suffix: :param.html
|
|
37
|
-
T extends `${infer Name}.${string}`
|
|
23
|
+
: T extends `${infer Name}*`
|
|
24
|
+
? { name: Name; optional: false; type: string }
|
|
25
|
+
: T extends `${infer Name}+`
|
|
38
26
|
? { name: Name; optional: false; type: string }
|
|
39
|
-
:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
: // Param with tilde-suffix: :param~v2
|
|
43
|
-
T extends `${infer Name}~${string}`
|
|
27
|
+
: T extends `${infer Name}?${string}`
|
|
28
|
+
? { name: Name; optional: true; type: string }
|
|
29
|
+
: T extends `${infer Name}.${string}`
|
|
44
30
|
? { name: Name; optional: false; type: string }
|
|
45
|
-
:
|
|
46
|
-
{ name:
|
|
31
|
+
: T extends `${infer Name}-${string}`
|
|
32
|
+
? { name: Name; optional: false; type: string }
|
|
33
|
+
: T extends `${infer Name}~${string}`
|
|
34
|
+
? { name: Name; optional: false; type: string }
|
|
35
|
+
: { name: T; optional: false; type: string };
|
|
47
36
|
|
|
48
|
-
/**
|
|
49
|
-
* Build param object from info
|
|
50
|
-
*/
|
|
51
37
|
type ParamFromInfo<Info> = Info extends {
|
|
52
38
|
name: infer N extends string;
|
|
53
39
|
optional: true;
|
|
@@ -62,10 +48,6 @@ type ParamFromInfo<Info> = Info extends {
|
|
|
62
48
|
? { [K in N]: V }
|
|
63
49
|
: never;
|
|
64
50
|
|
|
65
|
-
/**
|
|
66
|
-
* Merge two param objects preserving optionality
|
|
67
|
-
* Uses Pick to preserve the modifiers from source types
|
|
68
|
-
*/
|
|
69
51
|
type MergeParams<A, B> = Pick<A, keyof A> & Pick<B, keyof B> extends infer O
|
|
70
52
|
? { [K in keyof O]: O[K] }
|
|
71
53
|
: never;
|
|
@@ -78,12 +60,15 @@ type MergeParams<A, B> = Pick<A, keyof A> & Pick<B, keyof B> extends infer O
|
|
|
78
60
|
* - Optional params: /:locale? -> { locale?: string }
|
|
79
61
|
* - Constrained params: /:locale(en|gb) -> { locale: "en" | "gb" }
|
|
80
62
|
* - Optional + constrained: /:locale(en|gb)? -> { locale?: "en" | "gb" }
|
|
63
|
+
* - Named catch-all: /:path+ (one-or-more), /:slug* (zero-or-more) -> string
|
|
81
64
|
*
|
|
82
65
|
* @example
|
|
83
66
|
* ExtractParams<"/products/:id"> // { id: string }
|
|
84
67
|
* ExtractParams<"/:locale?/blog/:slug"> // { locale?: string; slug: string }
|
|
85
68
|
* ExtractParams<"/:locale(en|gb)/blog"> // { locale: "en" | "gb" }
|
|
86
69
|
* ExtractParams<"/:locale(en|gb)?/blog/:slug"> // { locale?: "en" | "gb"; slug: string }
|
|
70
|
+
* ExtractParams<"/docs/:slug*"> // { slug: string }
|
|
71
|
+
* ExtractParams<"/shop/:path+"> // { path: string }
|
|
87
72
|
*/
|
|
88
73
|
export type ExtractParams<
|
|
89
74
|
T extends string,
|
|
@@ -109,17 +94,11 @@ export type ExtractParams<
|
|
|
109
94
|
*/
|
|
110
95
|
export type TrailingSlashMode = "never" | "always" | "ignore";
|
|
111
96
|
|
|
112
|
-
/**
|
|
113
|
-
* Route configuration object (alternative to string path)
|
|
114
|
-
*/
|
|
115
97
|
export type RouteConfig = {
|
|
116
98
|
path: string;
|
|
117
99
|
trailingSlash?: TrailingSlashMode;
|
|
118
100
|
};
|
|
119
101
|
|
|
120
|
-
/**
|
|
121
|
-
* Route definition options (global defaults)
|
|
122
|
-
*/
|
|
123
102
|
export type RouteDefinitionOptions = {
|
|
124
103
|
trailingSlash?: TrailingSlashMode;
|
|
125
104
|
};
|
|
@@ -128,11 +107,6 @@ export type RouteDefinition = {
|
|
|
128
107
|
[key: string]: string | RouteConfig | RouteDefinition;
|
|
129
108
|
};
|
|
130
109
|
|
|
131
|
-
/**
|
|
132
|
-
* Recursively flatten nested routes with depth limit to prevent infinite recursion
|
|
133
|
-
* Transforms: { products: { detail: "/product/:slug" } } => { "products.detail": "/product/:slug" }
|
|
134
|
-
* Also handles RouteConfig objects: { api: { path: "/api" } } => { "api": "/api" }
|
|
135
|
-
*/
|
|
136
110
|
type FlattenRoutes<
|
|
137
111
|
T extends RouteDefinition,
|
|
138
112
|
Prefix extends string = "",
|
|
@@ -153,18 +127,12 @@ type FlattenRoutes<
|
|
|
153
127
|
: never;
|
|
154
128
|
}[keyof T];
|
|
155
129
|
|
|
156
|
-
/**
|
|
157
|
-
* Union to intersection helper
|
|
158
|
-
*/
|
|
159
130
|
type UnionToIntersection<U> = (
|
|
160
131
|
U extends unknown ? (k: U) => void : never
|
|
161
132
|
) extends (k: infer I) => void
|
|
162
133
|
? I
|
|
163
134
|
: never;
|
|
164
135
|
|
|
165
|
-
/**
|
|
166
|
-
* Resolved route map - flattened route definitions with full paths
|
|
167
|
-
*/
|
|
168
136
|
export type ResolvedRouteMap<T extends RouteDefinition> = UnionToIntersection<
|
|
169
137
|
FlattenRoutes<T>
|
|
170
138
|
>;
|
package/src/types/route-entry.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { AllUseItems } from "../route-types.js";
|
|
2
2
|
import type { TrailingSlashMode, ResolvedRouteMap } from "./route-config.js";
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Context captured for lazy include evaluation
|
|
6
|
-
*/
|
|
7
4
|
export interface LazyIncludeContext {
|
|
8
5
|
urlPrefix: string;
|
|
9
6
|
namePrefix: string | undefined;
|
|
@@ -25,9 +22,6 @@ export interface LazyIncludeContext {
|
|
|
25
22
|
includeScope?: string;
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
/**
|
|
29
|
-
* Internal route entry stored in router
|
|
30
|
-
*/
|
|
31
25
|
export interface RouteEntry<TEnv = any> {
|
|
32
26
|
prefix: string;
|
|
33
27
|
/**
|
package/src/types/segments.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { ErrorInfo, NotFoundInfo } from "./boundaries.js";
|
|
3
|
+
import type { RevalidateParams, HandlerContext } from "./handler-context.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* CSS class(es) for a ViewTransition phase.
|
|
@@ -8,6 +9,96 @@ import type { ErrorInfo, NotFoundInfo } from "./boundaries.js";
|
|
|
8
9
|
*/
|
|
9
10
|
export type ViewTransitionClass = Record<string, string> | string;
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* The context a transition({ when }) predicate receives.
|
|
14
|
+
*
|
|
15
|
+
* It mirrors the {@link ShouldRevalidateFn} args a `revalidate()` predicate
|
|
16
|
+
* gets — the same navigation/action metadata — so the two read the same shape,
|
|
17
|
+
* plus `get`/`env` for post-handler reads. There is no full `HandlerContext`
|
|
18
|
+
* here: the gate runs at the RSC-payload layer with the request context, not a
|
|
19
|
+
* handler context, so handler-only sugar (`search`/`build`/`dev`/`headers`) is
|
|
20
|
+
* absent by design. `get` is the way to read what the handler/middleware set
|
|
21
|
+
* via `ctx.set(...)` this request.
|
|
22
|
+
*
|
|
23
|
+
* Field availability (all source fields are optional — never fabricated):
|
|
24
|
+
* - `currentUrl` / `currentParams` / `fromRouteName` (the navigation SOURCE) are
|
|
25
|
+
* populated on soft navigations and action-success revalidations. They are
|
|
26
|
+
* undefined on an initial full document load and on action-error / no-JS error
|
|
27
|
+
* paths that skip the navigation snapshot — there is no prior page to name.
|
|
28
|
+
* - `nextUrl` / `nextParams` / `get` / `env` / `method` are always present;
|
|
29
|
+
* `toRouteName` is present only when the target route is named (undefined for
|
|
30
|
+
* unnamed/auto-generated routes, like `fromRouteName`).
|
|
31
|
+
* - `actionId` / `actionUrl` / `actionResult` / `formData` are populated only
|
|
32
|
+
* when a server action triggered the render; `method` is "POST" then, "GET"
|
|
33
|
+
* otherwise. On no-JS (progressive-enhancement) action paths `actionId` may be
|
|
34
|
+
* undefined when React cannot surface the action's stable id: the success
|
|
35
|
+
* re-render still sets `actionUrl`/`formData` for a recognized action, but the
|
|
36
|
+
* error-boundary re-render exposes `actionUrl` only when `actionId` resolved.
|
|
37
|
+
* Malformed form bodies that fail before action detection expose no action
|
|
38
|
+
* fields. Treat `actionId` as "the action, if known", not as "was this an
|
|
39
|
+
* action".
|
|
40
|
+
*
|
|
41
|
+
* PREFETCH / CACHE CAVEAT (read this before gating on the source): the gate runs
|
|
42
|
+
* server-side during resolution. A PREFETCHED navigation renders at prefetch
|
|
43
|
+
* time, so `currentUrl`/`currentParams`/`fromRouteName` reflect the page the
|
|
44
|
+
* prefetch fired from, NOT necessarily the page the user actually navigates from
|
|
45
|
+
* — the decision is baked into the stored Flight payload and replayed verbatim.
|
|
46
|
+
* A `cache()`/prerender hit replays the stored transition with the predicate NOT
|
|
47
|
+
* re-run at all. So a source-sensitive predicate can be frozen to prefetch-time
|
|
48
|
+
* or store-time state. This is accepted (~99% of navigations match), but if your
|
|
49
|
+
* gate must reflect the exact click-time source, source-scope the prefetch
|
|
50
|
+
* (`<Link prefetchKey=":source">`) and do not `cache()` that segment.
|
|
51
|
+
*/
|
|
52
|
+
export type TransitionWhenContext<
|
|
53
|
+
TParams = Record<string, string>,
|
|
54
|
+
TEnv = unknown,
|
|
55
|
+
> = Partial<
|
|
56
|
+
Pick<
|
|
57
|
+
RevalidateParams<TParams, TEnv>,
|
|
58
|
+
"currentUrl" | "currentParams" | "fromRouteName"
|
|
59
|
+
>
|
|
60
|
+
> &
|
|
61
|
+
Pick<
|
|
62
|
+
RevalidateParams<TParams, TEnv>,
|
|
63
|
+
| "nextUrl"
|
|
64
|
+
| "nextParams"
|
|
65
|
+
| "toRouteName"
|
|
66
|
+
| "actionId"
|
|
67
|
+
| "actionUrl"
|
|
68
|
+
| "actionResult"
|
|
69
|
+
| "formData"
|
|
70
|
+
| "method"
|
|
71
|
+
> &
|
|
72
|
+
Pick<HandlerContext<any, TEnv>, "get" | "env">;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Predicate that gates whether a transition() applies for the current request.
|
|
76
|
+
*
|
|
77
|
+
* Evaluated server-side AFTER the route's handler runs (so `get(...)` can read
|
|
78
|
+
* handler/middleware-set state) and outside any cache scope. Return false to
|
|
79
|
+
* drop this segment's transition for the request; return true to apply it. The
|
|
80
|
+
* context ({@link TransitionWhenContext}) carries the same navigation/action
|
|
81
|
+
* metadata a `revalidate()` predicate sees plus `get`/`env`. If it throws, the
|
|
82
|
+
* error is reported to the router's onError (phase "rendering") and the
|
|
83
|
+
* transition is dropped (the navigation does not hold).
|
|
84
|
+
*
|
|
85
|
+
* Distinct from intercept()'s `when` config selector, which runs at MATCH time
|
|
86
|
+
* over `{ from, to, params, segments, … }`; a transition `when` runs
|
|
87
|
+
* post-handler over the resolved payload.
|
|
88
|
+
*
|
|
89
|
+
* Scope: dropping a transition removes only THIS segment's contribution to the
|
|
90
|
+
* navigation's hold. The startTransition hold is navigation-wide — it engages if
|
|
91
|
+
* any matched segment still has a transition — so `when: false` makes the
|
|
92
|
+
* navigation stream its loading fallback only when no other matched segment
|
|
93
|
+
* keeps a transition (the common case: a single transition on the route).
|
|
94
|
+
*
|
|
95
|
+
* Evaluated on every fresh (cache-miss) resolution; it is NOT re-run when a
|
|
96
|
+
* segment is replayed from the runtime cache or a build-time prerender, and a
|
|
97
|
+
* prefetched navigation freezes it to prefetch-time state — see the caveat on
|
|
98
|
+
* {@link TransitionWhenContext}.
|
|
99
|
+
*/
|
|
100
|
+
export type TransitionWhenFn = (ctx: TransitionWhenContext) => boolean;
|
|
101
|
+
|
|
11
102
|
/**
|
|
12
103
|
* Configuration for React's <ViewTransition> component.
|
|
13
104
|
*
|
|
@@ -36,19 +127,20 @@ export interface TransitionConfig {
|
|
|
36
127
|
* When unset, inherits the createRouter({ viewTransition }) default.
|
|
37
128
|
*/
|
|
38
129
|
viewTransition?: "auto" | false;
|
|
130
|
+
/**
|
|
131
|
+
* Optional server-side predicate that gates this transition per request. When
|
|
132
|
+
* present and it returns false (evaluated post-handler), the router drops this
|
|
133
|
+
* segment's transition for the request, so the navigation streams its loading
|
|
134
|
+
* fallback instead of holding. The predicate is server-only and never
|
|
135
|
+
* serialized to the client; only its resolved effect (transition kept or
|
|
136
|
+
* dropped) crosses. See {@link TransitionWhenFn}.
|
|
137
|
+
*/
|
|
138
|
+
when?: TransitionWhenFn;
|
|
39
139
|
}
|
|
40
140
|
|
|
41
141
|
/**
|
|
42
142
|
* Resolved segment with component
|
|
43
143
|
*
|
|
44
|
-
* Segment types:
|
|
45
|
-
* - layout: Wraps child content via <Outlet />
|
|
46
|
-
* - route: The leaf content for a URL
|
|
47
|
-
* - parallel: Named slots rendered via <ParallelOutlet name="@slot" />
|
|
48
|
-
* - loader: Data segment (no visual rendering, carries loaderData)
|
|
49
|
-
* - error: Error fallback segment (replaces failed segment with error UI)
|
|
50
|
-
* - notFound: Not found fallback segment (replaces segment when data not found)
|
|
51
|
-
*
|
|
52
144
|
* @internal This type is an implementation detail and may change without notice.
|
|
53
145
|
*/
|
|
54
146
|
export interface ResolvedSegment {
|
|
@@ -89,11 +181,6 @@ export interface ResolvedSegment {
|
|
|
89
181
|
_handlerRan?: boolean;
|
|
90
182
|
}
|
|
91
183
|
|
|
92
|
-
/**
|
|
93
|
-
* Segment metadata (without component)
|
|
94
|
-
*
|
|
95
|
-
* @internal This type is an implementation detail and may change without notice.
|
|
96
|
-
*/
|
|
97
184
|
export interface SegmentMetadata {
|
|
98
185
|
id: string;
|
|
99
186
|
type: "layout" | "route" | "parallel" | "loader" | "error" | "notFound";
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
validateUserRouteName,
|
|
10
10
|
} from "../route-name.js";
|
|
11
11
|
import type { UrlPatterns, IncludeOptions } from "./pattern-types.js";
|
|
12
|
+
import type { IncludeProvider } from "./include-provider.js";
|
|
12
13
|
import type { IncludeFn } from "./path-helper-types.js";
|
|
13
14
|
|
|
14
15
|
function hasExplicitNameOption(options: IncludeOptions | undefined): boolean {
|
|
@@ -37,10 +38,8 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
|
|
|
37
38
|
if (!item) continue;
|
|
38
39
|
|
|
39
40
|
if (item.type === "include") {
|
|
40
|
-
// All includes are lazy; the router expands them on first matching request.
|
|
41
41
|
result.push(item);
|
|
42
42
|
} else if (item.type === "layout" && (item as any).uses) {
|
|
43
|
-
// Process nested items in layout
|
|
44
43
|
const layoutItem = item as any;
|
|
45
44
|
layoutItem.uses = processItems(layoutItem.uses);
|
|
46
45
|
result.push(layoutItem);
|
|
@@ -62,7 +61,11 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
|
|
|
62
61
|
export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
63
62
|
return (
|
|
64
63
|
prefix: string,
|
|
65
|
-
|
|
64
|
+
// A `urls()` value (eager) OR an async provider thunk
|
|
65
|
+
// (`() => import("./routes")`) whose evaluation is deferred to the first
|
|
66
|
+
// request matching `prefix`. The provider is stored unevaluated and
|
|
67
|
+
// resolved by the runtime lazy-include expansion / build-time discovery.
|
|
68
|
+
patterns: UrlPatterns<TEnv> | IncludeProvider<TEnv>,
|
|
66
69
|
options?: IncludeOptions,
|
|
67
70
|
): IncludeItem => {
|
|
68
71
|
const { ctx } = requireDslContext("include() must be called inside urls()");
|
|
@@ -83,10 +86,9 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
|
83
86
|
? capturedUrlPrefix + prefix.slice(1)
|
|
84
87
|
: capturedUrlPrefix + prefix
|
|
85
88
|
: prefix;
|
|
86
|
-
const
|
|
87
|
-
?
|
|
88
|
-
:
|
|
89
|
-
const nextSegment = hasExplicitName ? explicitName : internalScope;
|
|
89
|
+
const nextSegment = hasExplicitName
|
|
90
|
+
? explicitName
|
|
91
|
+
: allocateInternalIncludeScopeId(ctx.counters);
|
|
90
92
|
const fullNamePrefix =
|
|
91
93
|
nextSegment !== undefined && nextSegment !== ""
|
|
92
94
|
? capturedNamePrefix
|
|
@@ -119,9 +121,7 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
|
119
121
|
if (capturedParent?.shortCode) {
|
|
120
122
|
const includeCounterKey = `${capturedParent.shortCode}${parentScope}_include`;
|
|
121
123
|
ctx.counters[includeCounterKey] ??= 0;
|
|
122
|
-
|
|
123
|
-
ctx.counters[includeCounterKey] = includeIdx + 1;
|
|
124
|
-
includeScope = `${parentScope}I${includeIdx}`;
|
|
124
|
+
includeScope = `${parentScope}I${ctx.counters[includeCounterKey]++}`;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// Snapshot parent's counters AFTER allocating the include scope so lazy
|
|
@@ -141,8 +141,6 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
|
141
141
|
? (parentRootScoped ?? false)
|
|
142
142
|
: parentRootScoped;
|
|
143
143
|
|
|
144
|
-
// All includes are lazy - patterns are evaluated on first matching request
|
|
145
|
-
// This improves cold start time significantly for large route sets
|
|
146
144
|
return {
|
|
147
145
|
type: "include",
|
|
148
146
|
name,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { UrlPatterns } from "./pattern-types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What an async `include()` provider may resolve to: a `urls()` value directly,
|
|
5
|
+
* or a module namespace whose `default` export is a `urls()` value (the shape
|
|
6
|
+
* produced by `() => import("./routes")` when the route module does
|
|
7
|
+
* `export default urls(...)`).
|
|
8
|
+
*/
|
|
9
|
+
export type IncludeModule<TEnv = any> =
|
|
10
|
+
| UrlPatterns<TEnv>
|
|
11
|
+
| { default: UrlPatterns<TEnv> };
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* An async/lazy include provider: a thunk returning a `urls()` value (or a
|
|
15
|
+
* Promise of one). The thunk is stored unevaluated by `include()` and called
|
|
16
|
+
* once, on the first request that matches the prefix — so the route module and
|
|
17
|
+
* its (code-split) subtree are not evaluated at startup.
|
|
18
|
+
*
|
|
19
|
+
* Forward-compatible: `() => import("./routes")` today (async, separate chunk);
|
|
20
|
+
* `() => m.routes` with native `import defer` later (sync, deferred eval).
|
|
21
|
+
*/
|
|
22
|
+
export type IncludeProvider<TEnv = any> = () =>
|
|
23
|
+
| IncludeModule<TEnv>
|
|
24
|
+
| Promise<IncludeModule<TEnv>>;
|
|
25
|
+
|
|
26
|
+
/** True when the include() argument is a provider thunk rather than a value. */
|
|
27
|
+
export function isIncludeProvider(value: unknown): value is IncludeProvider {
|
|
28
|
+
// A `urls()` value is a (branded) object; a provider is a function.
|
|
29
|
+
return typeof value === "function";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A `urls()` value is an object exposing a synchronous `handler()`. */
|
|
33
|
+
function isUrlPatterns(value: unknown): value is UrlPatterns {
|
|
34
|
+
return (
|
|
35
|
+
!!value && typeof (value as { handler?: unknown }).handler === "function"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Normalize an async provider's resolved value to a `UrlPatterns`. Accepts a
|
|
41
|
+
* `urls()` value directly or a module whose `default` export is one.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveIncludeModule<TEnv = any>(
|
|
44
|
+
mod: IncludeModule<TEnv>,
|
|
45
|
+
id?: string,
|
|
46
|
+
): UrlPatterns<TEnv> {
|
|
47
|
+
// Prefer an explicit `default` export (the `export default urls(...)`
|
|
48
|
+
// convention) BEFORE duck-typing the namespace. isUrlPatterns() keys on a
|
|
49
|
+
// `.handler` function, but a routes module can legitimately carry a NAMED
|
|
50
|
+
// `export function handler(...)` alongside its `export default urls(...)`;
|
|
51
|
+
// checking the namespace first would then misidentify the whole module as the
|
|
52
|
+
// urls() value and invoke the user's helper as the DSL handler (the group
|
|
53
|
+
// 404s with a misleading error). A bare `() => urls(...)` provider (no
|
|
54
|
+
// module) has no `default`, so it still resolves via the mod-as-value branch.
|
|
55
|
+
const def = (mod as { default?: unknown })?.default;
|
|
56
|
+
if (isUrlPatterns(def)) return def as UrlPatterns<TEnv>;
|
|
57
|
+
if (isUrlPatterns(mod)) return mod as UrlPatterns<TEnv>;
|
|
58
|
+
// The common failure is a module namespace whose `default` is missing or not a
|
|
59
|
+
// urls() value (e.g. only named exports); `typeof` alone says "object" and
|
|
60
|
+
// hides that, so name the keys present. "provider" (not "async provider") —
|
|
61
|
+
// synchronous providers are supported (see IncludeProvider).
|
|
62
|
+
const got =
|
|
63
|
+
mod && typeof mod === "object"
|
|
64
|
+
? `a module with keys [${Object.keys(mod).join(", ") || "none"}] but no valid \`default\``
|
|
65
|
+
: typeof mod;
|
|
66
|
+
throw new Error(
|
|
67
|
+
`[@rangojs/router] include() provider${id ? ` for "${id}"` : ""} must ` +
|
|
68
|
+
`resolve to a urls() value — either returned directly or as the module's ` +
|
|
69
|
+
`\`default\` export (e.g. \`export default urls(...)\`). Got ${got}.`,
|
|
70
|
+
);
|
|
71
|
+
}
|
package/src/urls/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Response types and symbols
|
|
2
1
|
export {
|
|
3
2
|
RESPONSE_TYPE,
|
|
4
3
|
type ResponseHandler,
|
|
@@ -8,25 +7,22 @@ export {
|
|
|
8
7
|
type ResponseHandlerContext,
|
|
9
8
|
} from "./response-types.js";
|
|
10
9
|
|
|
11
|
-
// Pattern types
|
|
12
10
|
export type {
|
|
13
11
|
UnnamedRoute,
|
|
14
12
|
LocalOnlyInclude,
|
|
15
13
|
PathOptions,
|
|
14
|
+
PartialPrerenderProps,
|
|
16
15
|
UrlPatterns,
|
|
17
16
|
IncludeOptions,
|
|
18
17
|
} from "./pattern-types.js";
|
|
19
18
|
|
|
20
|
-
// Type extraction utilities
|
|
21
19
|
export type {
|
|
22
20
|
ExtractRoutes,
|
|
23
21
|
ExtractResponses,
|
|
24
|
-
|
|
25
|
-
ResponseEnvelope,
|
|
22
|
+
ProblemDetails,
|
|
26
23
|
RouteResponse,
|
|
27
24
|
} from "./type-extraction.js";
|
|
28
25
|
|
|
29
|
-
// Path helper types
|
|
30
26
|
export type {
|
|
31
27
|
PathFn,
|
|
32
28
|
ResponsePathFn,
|
|
@@ -36,10 +32,8 @@ export type {
|
|
|
36
32
|
PathHelpers,
|
|
37
33
|
} from "./path-helper-types.js";
|
|
38
34
|
|
|
39
|
-
// Main entry point
|
|
40
35
|
export { urls } from "./urls-function.js";
|
|
41
36
|
|
|
42
|
-
// Re-exports from route-types
|
|
43
37
|
export type {
|
|
44
38
|
AllUseItems,
|
|
45
39
|
IncludeItem,
|