@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
|
@@ -28,7 +28,6 @@ import type {
|
|
|
28
28
|
ParallelUseItem,
|
|
29
29
|
InterceptUseItem,
|
|
30
30
|
LoaderUseItem,
|
|
31
|
-
WhenItem,
|
|
32
31
|
TypedCacheItem,
|
|
33
32
|
TransitionItem,
|
|
34
33
|
TypedTransitionItem,
|
|
@@ -42,7 +41,6 @@ import type {
|
|
|
42
41
|
PassthroughHandlerDefinition,
|
|
43
42
|
} from "../prerender.js";
|
|
44
43
|
import type { StaticHandlerDefinition } from "../static-handler.js";
|
|
45
|
-
import type { InterceptWhenFn } from "../server/context";
|
|
46
44
|
import type {
|
|
47
45
|
ResponseHandler,
|
|
48
46
|
ResponseHandlerContext,
|
|
@@ -114,6 +112,12 @@ export type ResponsePathFn<TEnv> = <
|
|
|
114
112
|
* Path function for JSON response routes (path.json()).
|
|
115
113
|
* Handler can return plain JSON-serializable values or Response.
|
|
116
114
|
* TData is inferred from the handler's return type (excluding Response/Promise wrappers).
|
|
115
|
+
*
|
|
116
|
+
* Note: a nested Promise in the return (a forgotten await) is caught at runtime
|
|
117
|
+
* by response-route-handler.ts (it throws instead of silently emitting `{}`). A
|
|
118
|
+
* compile-time JsonValue constraint was evaluated and rejected — it breaks
|
|
119
|
+
* interface-typed returns (interfaces lack the index signature JsonValue
|
|
120
|
+
* requires) and preserves literal types in the inferred response shape.
|
|
117
121
|
*/
|
|
118
122
|
export type JsonResponsePathFn<TEnv> = <
|
|
119
123
|
const TPattern extends string,
|
|
@@ -148,6 +152,30 @@ export type TextResponsePathFn<TEnv> = <
|
|
|
148
152
|
use?: () => UseItems<ResponseRouteUseItem>,
|
|
149
153
|
) => TypedRouteItem<TName, TPattern, string, TSearch>;
|
|
150
154
|
|
|
155
|
+
/**
|
|
156
|
+
* What an async include() provider resolves to. Route types (`TRoutes`) are
|
|
157
|
+
* inferred from the resolved `urls()` value so `href()` and named routes stay
|
|
158
|
+
* type-safe through a code-split module (`() => import("./routes")`).
|
|
159
|
+
*/
|
|
160
|
+
type IncludeResolved<
|
|
161
|
+
TEnv,
|
|
162
|
+
TRoutes extends Record<string, any>,
|
|
163
|
+
TResponses extends Record<string, unknown>,
|
|
164
|
+
> =
|
|
165
|
+
| UrlPatterns<TEnv, TRoutes, TResponses>
|
|
166
|
+
| { default: UrlPatterns<TEnv, TRoutes, TResponses> };
|
|
167
|
+
|
|
168
|
+
/** include() argument: an eager `urls()` value or an async provider thunk. */
|
|
169
|
+
export type IncludeArg<
|
|
170
|
+
TEnv,
|
|
171
|
+
TRoutes extends Record<string, any>,
|
|
172
|
+
TResponses extends Record<string, unknown>,
|
|
173
|
+
> =
|
|
174
|
+
| UrlPatterns<TEnv, TRoutes, TResponses>
|
|
175
|
+
| (() =>
|
|
176
|
+
| IncludeResolved<TEnv, TRoutes, TResponses>
|
|
177
|
+
| Promise<IncludeResolved<TEnv, TRoutes, TResponses>>);
|
|
178
|
+
|
|
151
179
|
/**
|
|
152
180
|
* Base include function signature.
|
|
153
181
|
*/
|
|
@@ -158,7 +186,7 @@ export type IncludeFn<TEnv> = <
|
|
|
158
186
|
TResponses extends Record<string, unknown> = Record<string, unknown>,
|
|
159
187
|
>(
|
|
160
188
|
prefix: TUrlPrefix,
|
|
161
|
-
patterns:
|
|
189
|
+
patterns: IncludeArg<TEnv, TRoutes, TResponses>,
|
|
162
190
|
options?: IncludeOptions<TNamePrefix>,
|
|
163
191
|
) => TypedIncludeItem<TRoutes, TNamePrefix, TUrlPrefix, TResponses>;
|
|
164
192
|
|
|
@@ -240,9 +268,16 @@ export type PathHelpers<TEnv> = {
|
|
|
240
268
|
* `{ handler, use? }` whose `use` is scoped to that slot only. Per-slot
|
|
241
269
|
* merge order is `handler.use` → shared `use` → slot-local `use`, with
|
|
242
270
|
* narrowest scope winning for last-write-wins items like `loading()`.
|
|
271
|
+
*
|
|
272
|
+
* Not generic over the slots record: an inferred type parameter makes the
|
|
273
|
+
* object literal an inference site, which suppresses contextual typing of
|
|
274
|
+
* arrow slot handlers (`(ctx) => ...` was implicit any). Bare handlers infer
|
|
275
|
+
* now; a descriptor's `handler:` arrow still needs an explicit ctx annotation
|
|
276
|
+
* because StaticHandlerDefinition's own `.handler` joins the contextual union
|
|
277
|
+
* (two callables — see parallel-slot-handler-types.test.ts).
|
|
243
278
|
*/
|
|
244
|
-
parallel:
|
|
245
|
-
|
|
279
|
+
parallel: (
|
|
280
|
+
slots: Record<
|
|
246
281
|
`@${string}`,
|
|
247
282
|
| Handler<any, any, TEnv>
|
|
248
283
|
| ReactNode
|
|
@@ -255,8 +290,6 @@ export type PathHelpers<TEnv> = {
|
|
|
255
290
|
use?: () => ParallelUseItem[];
|
|
256
291
|
}
|
|
257
292
|
>,
|
|
258
|
-
>(
|
|
259
|
-
slots: TSlots,
|
|
260
293
|
use?: () => ParallelUseItem[],
|
|
261
294
|
) => ParallelItem;
|
|
262
295
|
|
|
@@ -269,12 +302,18 @@ export type PathHelpers<TEnv> = {
|
|
|
269
302
|
slotName: `@${string}`,
|
|
270
303
|
routeName: string,
|
|
271
304
|
handler: ReactNode | Handler<any, any, TEnv>,
|
|
305
|
+
config?:
|
|
306
|
+
| import("../server/context.js").InterceptConfig<TEnv>
|
|
307
|
+
| (() => InterceptUseItem[]),
|
|
272
308
|
use?: () => InterceptUseItem[],
|
|
273
309
|
) => InterceptItem
|
|
274
310
|
: (
|
|
275
311
|
slotName: `@${string}`,
|
|
276
312
|
routeName: (keyof Rango.GeneratedRouteMap & string) | `.${string}`,
|
|
277
313
|
handler: ReactNode | Handler<any, any, TEnv>,
|
|
314
|
+
config?:
|
|
315
|
+
| import("../server/context.js").InterceptConfig<TEnv>
|
|
316
|
+
| (() => InterceptUseItem[]),
|
|
278
317
|
use?: () => InterceptUseItem[],
|
|
279
318
|
) => InterceptItem;
|
|
280
319
|
|
|
@@ -329,11 +368,6 @@ export type PathHelpers<TEnv> = {
|
|
|
329
368
|
fallback: ReactNode | NotFoundBoundaryHandler,
|
|
330
369
|
) => NotFoundBoundaryItem;
|
|
331
370
|
|
|
332
|
-
/**
|
|
333
|
-
* Define a condition for when an intercept should activate
|
|
334
|
-
*/
|
|
335
|
-
when: (fn: InterceptWhenFn) => WhenItem;
|
|
336
|
-
|
|
337
371
|
/**
|
|
338
372
|
* Define cache configuration for segments
|
|
339
373
|
*/
|
|
@@ -342,9 +376,9 @@ export type PathHelpers<TEnv> = {
|
|
|
342
376
|
<const TChildren extends readonly (AllUseItems | readonly AllUseItems[])[]>(
|
|
343
377
|
children: () => TChildren,
|
|
344
378
|
): TypedCacheItem<ExtractRoutes<TChildren>, ExtractResponses<TChildren>>;
|
|
345
|
-
(options: PartialCacheOptions | false): TypedCacheItem<{}, {}>;
|
|
379
|
+
(options: PartialCacheOptions<TEnv> | false): TypedCacheItem<{}, {}>;
|
|
346
380
|
<const TChildren extends readonly (AllUseItems | readonly AllUseItems[])[]>(
|
|
347
|
-
options: PartialCacheOptions | false,
|
|
381
|
+
options: PartialCacheOptions<TEnv> | false,
|
|
348
382
|
use: () => TChildren,
|
|
349
383
|
): TypedCacheItem<ExtractRoutes<TChildren>, ExtractResponses<TChildren>>;
|
|
350
384
|
};
|
|
@@ -359,6 +393,10 @@ export type PathHelpers<TEnv> = {
|
|
|
359
393
|
* `{ viewTransition: false }` to keep #1 without the router boundary. A view
|
|
360
394
|
* transition cannot fire without a startTransition. See
|
|
361
395
|
* skills/view-transitions for the startTransition x ViewTransition matrix.
|
|
396
|
+
*
|
|
397
|
+
* Pass `when: (ctx) => boolean` to gate the transition per request: it runs
|
|
398
|
+
* server-side after the route handler (can read `ctx.get(...)`), and returning
|
|
399
|
+
* false drops the transition so the navigation streams its loading() skeleton.
|
|
362
400
|
*/
|
|
363
401
|
transition: {
|
|
364
402
|
(): TransitionItem;
|
package/src/urls/path-helper.ts
CHANGED
|
@@ -39,10 +39,6 @@ import {
|
|
|
39
39
|
runAndValidateUseItems,
|
|
40
40
|
} from "../route-definition/dsl-helpers.js";
|
|
41
41
|
|
|
42
|
-
/**
|
|
43
|
-
* Apply URL prefix to a pattern
|
|
44
|
-
* Handles edge cases like "/" patterns and double slashes
|
|
45
|
-
*/
|
|
46
42
|
function applyUrlPrefix(prefix: string, pattern: string): string {
|
|
47
43
|
if (!prefix) return pattern;
|
|
48
44
|
if (pattern === "/") return prefix;
|
|
@@ -52,29 +48,17 @@ function applyUrlPrefix(prefix: string, pattern: string): string {
|
|
|
52
48
|
return prefix + pattern;
|
|
53
49
|
}
|
|
54
50
|
|
|
55
|
-
/**
|
|
56
|
-
* Apply name prefix to a route name
|
|
57
|
-
*/
|
|
58
51
|
function applyNamePrefix(prefix: string | undefined, name: string): string {
|
|
59
52
|
if (!prefix) return name;
|
|
60
53
|
return `${prefix}.${name}`;
|
|
61
54
|
}
|
|
62
55
|
|
|
63
|
-
/**
|
|
64
|
-
* Resolve response type from path options (set by path.json(), path.text(), etc.)
|
|
65
|
-
*/
|
|
66
56
|
function resolveResponseType(
|
|
67
57
|
options: PathOptions | undefined,
|
|
68
58
|
): string | undefined {
|
|
69
59
|
return options?.[RESPONSE_TYPE];
|
|
70
60
|
}
|
|
71
61
|
|
|
72
|
-
/**
|
|
73
|
-
* Create path() helper
|
|
74
|
-
*
|
|
75
|
-
* The path() function is the key new feature - it combines URL pattern
|
|
76
|
-
* with handler at the definition site.
|
|
77
|
-
*/
|
|
78
62
|
export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
79
63
|
return ((
|
|
80
64
|
pattern: string,
|
|
@@ -91,8 +75,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
91
75
|
"path() cannot be used inside parallel()",
|
|
92
76
|
);
|
|
93
77
|
|
|
94
|
-
// Walk the parent chain to prevent path() nested under another path(),
|
|
95
|
-
// even when separated by intermediate layouts (e.g. path(layout(path())))
|
|
96
78
|
{
|
|
97
79
|
let ancestor = ctx.parent;
|
|
98
80
|
while (ancestor) {
|
|
@@ -104,7 +86,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
104
86
|
}
|
|
105
87
|
}
|
|
106
88
|
|
|
107
|
-
// Determine options and use based on argument types
|
|
108
89
|
let options: PathOptions | undefined;
|
|
109
90
|
let use: (() => UseItems<RouteUseItem>) | undefined;
|
|
110
91
|
|
|
@@ -117,49 +98,29 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
117
98
|
use = maybeUse;
|
|
118
99
|
}
|
|
119
100
|
|
|
120
|
-
// Merge handler.use() defaults with explicit use()
|
|
121
|
-
// Response routes (path.json, path.text, etc.) only allow middleware + cache
|
|
122
101
|
const handlerUseFn = resolveHandlerUse(handler);
|
|
123
102
|
const mountSite = resolveResponseType(options) ? "response" : "path";
|
|
124
103
|
const mergedUse = mergeHandlerUse(handlerUseFn, use, mountSite);
|
|
125
104
|
|
|
126
|
-
// Get prefixes from context (set by include())
|
|
127
105
|
const urlPrefix = getUrlPrefix();
|
|
128
106
|
const namePrefix = getNamePrefix();
|
|
129
107
|
|
|
130
|
-
// Apply URL prefix to pattern
|
|
131
108
|
const prefixedPattern = applyUrlPrefix(urlPrefix, pattern);
|
|
132
109
|
|
|
133
|
-
// Generate route name - use provided name or generate from pattern
|
|
134
110
|
const localName =
|
|
135
111
|
options?.name || `$path_${pattern.replace(/[/:*?]/g, "_")}`;
|
|
136
112
|
if (options?.name) {
|
|
137
113
|
validateUserRouteName(options.name);
|
|
138
114
|
}
|
|
139
|
-
// Apply name prefix if set (from include())
|
|
140
115
|
const routeName = applyNamePrefix(namePrefix, localName);
|
|
141
116
|
|
|
142
117
|
const namespace = `${ctx.namespace}.${store.getNextIndex("route")}.${routeName}`;
|
|
143
118
|
|
|
144
|
-
// Per-request pruning: skip registration for routes that won't be rendered.
|
|
145
|
-
// forRoute is set by loadManifest() to the matched route name. During
|
|
146
|
-
// evaluateLazyEntry() (route matching), forRoute is unset so all routes
|
|
147
|
-
// register normally. We still increment counters to keep shortCodes stable
|
|
148
|
-
// across different routes (needed for segment reconciliation on navigation).
|
|
149
|
-
//
|
|
150
|
-
// include() does not need its own forRoute pruning. include() creates lazy
|
|
151
|
-
// entries that defer handler execution until route matching. When the lazy
|
|
152
|
-
// handler eventually runs inside loadManifest(), this path() check already
|
|
153
|
-
// covers all routes defined inside the include.
|
|
154
119
|
if (ctx.forRoute && routeName !== ctx.forRoute) {
|
|
155
120
|
store.getShortCode("route");
|
|
156
121
|
return { type: "route" } as RouteItem;
|
|
157
122
|
}
|
|
158
123
|
|
|
159
|
-
// Ensure handler is always a function (wrap ReactNode or extract from prerender/static def)
|
|
160
|
-
// For prerender stubs (production builds where handler code is evicted),
|
|
161
|
-
// handler.handler is undefined — provide a notFound fallback so requests
|
|
162
|
-
// for non-prerendered params get 404 instead of "handler is not a function".
|
|
163
124
|
const wrappedHandler: Handler<any, any, TEnv> =
|
|
164
125
|
typeof handler === "function"
|
|
165
126
|
? (handler as Handler<any, any, TEnv>)
|
|
@@ -190,7 +151,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
190
151
|
type: "route" as const,
|
|
191
152
|
parent: ctx.parent,
|
|
192
153
|
handler: wrappedHandler,
|
|
193
|
-
// Store the PREFIXED pattern for route matching
|
|
194
154
|
pattern: prefixedPattern,
|
|
195
155
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
196
156
|
...(isPassthroughHandler(handler)
|
|
@@ -215,31 +175,30 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
215
175
|
...(resolveResponseType(options)
|
|
216
176
|
? { responseType: resolveResponseType(options) }
|
|
217
177
|
: {}),
|
|
178
|
+
// PPR shell-caching opt-in (document-level). Stored raw; the integrated
|
|
179
|
+
// serve path normalizes it via resolvePprConfig (rsc/shell-serve.ts).
|
|
180
|
+
...(options?.ppr !== undefined && options.ppr !== false
|
|
181
|
+
? { ppr: options.ppr }
|
|
182
|
+
: {}),
|
|
218
183
|
};
|
|
219
184
|
|
|
220
|
-
// Capture namespace prefix on static handler for build-time reverse() resolution
|
|
221
185
|
if (isStaticHandler(handler) && handler.$$id && ctx.namePrefix) {
|
|
222
186
|
(handler as any).$$routePrefix = ctx.namePrefix;
|
|
223
187
|
}
|
|
224
188
|
|
|
225
|
-
// Check for duplicate route names (TypeScript should catch this, but runtime check too)
|
|
226
189
|
invariant(
|
|
227
190
|
ctx.manifest.get(routeName) === undefined,
|
|
228
191
|
`Duplicate route name: ${routeName} at ${namespace}`,
|
|
229
192
|
);
|
|
230
193
|
|
|
231
|
-
// Register route entry with prefixed name
|
|
232
194
|
ctx.manifest.set(routeName, entry);
|
|
233
195
|
|
|
234
|
-
// Register root-scope flag for dot-local reverse resolution
|
|
235
196
|
registerRouteRootScope(routeName, getRootScoped());
|
|
236
197
|
|
|
237
|
-
// Also store pattern in a separate map for URL generation
|
|
238
198
|
if (ctx.patterns) {
|
|
239
199
|
ctx.patterns.set(routeName, prefixedPattern);
|
|
240
200
|
}
|
|
241
201
|
|
|
242
|
-
// Store pattern grouped by URL prefix for separate entry creation
|
|
243
202
|
if (ctx.patternsByPrefix) {
|
|
244
203
|
const urlPrefix = getUrlPrefix() || "";
|
|
245
204
|
if (!ctx.patternsByPrefix.has(urlPrefix)) {
|
|
@@ -248,12 +207,10 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
248
207
|
ctx.patternsByPrefix.get(urlPrefix)!.set(routeName, prefixedPattern);
|
|
249
208
|
}
|
|
250
209
|
|
|
251
|
-
// Store trailing slash config if specified
|
|
252
210
|
if (options?.trailingSlash && ctx.trailingSlash) {
|
|
253
211
|
ctx.trailingSlash.set(routeName, options.trailingSlash);
|
|
254
212
|
}
|
|
255
213
|
|
|
256
|
-
// Store search schema if specified
|
|
257
214
|
if (options?.search) {
|
|
258
215
|
if (ctx.searchSchemas) {
|
|
259
216
|
ctx.searchSchemas.set(routeName, options.search);
|
|
@@ -261,7 +218,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
261
218
|
registerSearchSchema(routeName, options.search);
|
|
262
219
|
}
|
|
263
220
|
|
|
264
|
-
// Run merged use callback (handler.use defaults + explicit use) if present
|
|
265
221
|
if (mergedUse) {
|
|
266
222
|
const result = runAndValidateUseItems(
|
|
267
223
|
store,
|
|
@@ -278,10 +234,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
278
234
|
}) as PathFn<TEnv>;
|
|
279
235
|
}
|
|
280
236
|
|
|
281
|
-
/**
|
|
282
|
-
* Attach response type tag methods (.json, .text, .html, .xml, .md, .image, .stream, .any) to a path helper.
|
|
283
|
-
* Each tag wraps the original path() call with the RESPONSE_TYPE option set.
|
|
284
|
-
*/
|
|
285
237
|
export function attachPathResponseTags<TEnv>(
|
|
286
238
|
pathFn: PathFn<TEnv>,
|
|
287
239
|
): PathFn<TEnv> & {
|
|
@@ -303,7 +255,6 @@ export function attachPathResponseTags<TEnv>(
|
|
|
303
255
|
) => {
|
|
304
256
|
let options: PathOptions;
|
|
305
257
|
let use: (() => any[]) | undefined;
|
|
306
|
-
|
|
307
258
|
if (typeof optionsOrUse === "function") {
|
|
308
259
|
options = { [RESPONSE_TYPE]: responseType };
|
|
309
260
|
use = optionsOrUse;
|
|
@@ -35,12 +35,48 @@ export type LocalOnlyInclude = string & { [LOCAL_ONLY_BRAND]: void };
|
|
|
35
35
|
/**
|
|
36
36
|
* Options for path() function
|
|
37
37
|
*/
|
|
38
|
+
/**
|
|
39
|
+
* Options for the `ppr` path option (PPR shell caching — Axis 2, see
|
|
40
|
+
* docs/design/ppr-shell-resume.md and the /ppr skill). Declaring
|
|
41
|
+
* `ppr: true | PartialPrerenderProps` on a page route opts that DOCUMENT into
|
|
42
|
+
* shell capture: the rendered HTML shell (everything that is not a live hole) is
|
|
43
|
+
* cached and, on a later GET, flushed immediately while fizz resumes only the
|
|
44
|
+
* holes. Serving is integral to the router — there is no middleware to mount;
|
|
45
|
+
* the shell store is the app-level `createRouter({ cache })` store (which must
|
|
46
|
+
* implement the `getShell`/`putShell` family).
|
|
47
|
+
*/
|
|
48
|
+
export interface PartialPrerenderProps {
|
|
49
|
+
/**
|
|
50
|
+
* Shell time-to-live in seconds. Defaults to 300 (`ppr: true` uses the same
|
|
51
|
+
* default).
|
|
52
|
+
*/
|
|
53
|
+
ttl?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Stale-while-revalidate window in seconds: a stale shell is still served
|
|
56
|
+
* while a background recapture refreshes it.
|
|
57
|
+
*/
|
|
58
|
+
swr?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Operational tags attached to the captured shell entry for
|
|
61
|
+
* `updateTag()`/`revalidateTag()`-driven eviction. UNIONED with the tags the
|
|
62
|
+
* capture render auto-collects (the shell's own non-loader request tags).
|
|
63
|
+
*/
|
|
64
|
+
tags?: string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
38
67
|
export interface PathOptions<
|
|
39
68
|
TName extends string = string,
|
|
40
69
|
TSearch extends SearchSchema = {},
|
|
41
70
|
> {
|
|
42
71
|
/** Route name for href() lookups */
|
|
43
72
|
name?: TName;
|
|
73
|
+
/**
|
|
74
|
+
* PPR shell caching opt-in for this page route (document-level). `true` uses
|
|
75
|
+
* the default policy (ttl 300); an object sets ttl/swr/tags. See
|
|
76
|
+
* {@link PartialPrerenderProps}. Routes without this option are pure axis 1 —
|
|
77
|
+
* no capture, no store reads, no logs.
|
|
78
|
+
*/
|
|
79
|
+
ppr?: boolean | PartialPrerenderProps;
|
|
44
80
|
/** Search param schema for typed query parameters */
|
|
45
81
|
search?: TSearch;
|
|
46
82
|
/** Trailing slash behavior: "never" (redirect /path/ to /path), "always" (redirect /path to /path/), "ignore" (match both) */
|
|
@@ -81,16 +81,26 @@ type ExtractRoutesFromItem<T> =
|
|
|
81
81
|
// When search schema is non-empty, value becomes { path, search } object
|
|
82
82
|
T extends TypedRouteItem<infer TName, infer TPattern, any, infer TSearch>
|
|
83
83
|
? TName extends string
|
|
84
|
-
?
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
84
|
+
? // Widened-name guard (#642): some name-less call forms — notably the
|
|
85
|
+
// 3-arg children-fn overload path(pattern, component, () => [...]) —
|
|
86
|
+
// let TName infer to the bare `string` constraint instead of the
|
|
87
|
+
// UnnamedRoute sentinel, because the children-fn argument structurally
|
|
88
|
+
// satisfies the all-optional PathOptions<TName> union member. Mapping
|
|
89
|
+
// over a bare `string` key would emit `{ [K in string]: TPattern }`, an
|
|
90
|
+
// index signature that poisons the whole sibling map (Rango.Path
|
|
91
|
+
// collapses to never). Treat an unresolved name as unnamed.
|
|
92
|
+
string extends TName
|
|
93
|
+
? {}
|
|
94
|
+
: TName extends UnnamedRoute
|
|
95
|
+
? {} // Exclude unnamed routes from type map
|
|
96
|
+
: {} extends TSearch
|
|
97
|
+
? { [K in TName]: TPattern }
|
|
98
|
+
: {
|
|
99
|
+
[K in TName]: {
|
|
100
|
+
readonly path: TPattern;
|
|
101
|
+
readonly search: TSearch;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
94
104
|
: {}
|
|
95
105
|
: // TypedIncludeItem: extract prefixed routes (both name and URL prefix)
|
|
96
106
|
T extends TypedIncludeItem<
|
|
@@ -128,9 +138,15 @@ type ExtractRoutesFromItems<T extends readonly any[]> = T extends readonly any[]
|
|
|
128
138
|
? UnionToIntersection<
|
|
129
139
|
{ [K in keyof T]: ExtractRoutesFromItem<T[K]> }[number]
|
|
130
140
|
> extends infer R
|
|
131
|
-
?
|
|
132
|
-
|
|
133
|
-
|
|
141
|
+
? // Blast-radius guard: never let a single malformed item collapse the
|
|
142
|
+
// whole map. A `never` intersection satisfies `extends Record<string,any>`
|
|
143
|
+
// (never extends everything), so check it explicitly first and fall back
|
|
144
|
+
// to `{}` rather than propagating `never`. See #642.
|
|
145
|
+
[R] extends [never]
|
|
146
|
+
? {}
|
|
147
|
+
: R extends Record<string, any>
|
|
148
|
+
? R
|
|
149
|
+
: {}
|
|
134
150
|
: {}
|
|
135
151
|
: {};
|
|
136
152
|
|
|
@@ -169,9 +185,15 @@ type PrefixKeys<
|
|
|
169
185
|
type ExtractResponsesFromItem<T> =
|
|
170
186
|
T extends TypedRouteItem<infer TName, any, infer TData>
|
|
171
187
|
? TName extends string
|
|
172
|
-
?
|
|
188
|
+
? // Widened-name guard (#642), parallels ExtractRoutesFromItem. A name-less
|
|
189
|
+
// children-fn path.json(pattern, handler, () => [...]) infers TName as
|
|
190
|
+
// bare `string`; without this the response map picks up an index
|
|
191
|
+
// signature { [K in string]: TData } that wipes named siblings.
|
|
192
|
+
string extends TName
|
|
173
193
|
? {}
|
|
174
|
-
:
|
|
194
|
+
: TName extends UnnamedRoute
|
|
195
|
+
? {}
|
|
196
|
+
: { [K in TName]: TData }
|
|
175
197
|
: {}
|
|
176
198
|
: T extends TypedIncludeItem<any, infer TNamePrefix, any, infer TResponses>
|
|
177
199
|
? TNamePrefix extends LocalOnlyInclude
|
|
@@ -206,9 +228,12 @@ type ExtractResponsesFromItems<T extends readonly any[]> =
|
|
|
206
228
|
? UnionToIntersection<
|
|
207
229
|
{ [K in keyof T]: ExtractResponsesFromItem<T[K]> }[number]
|
|
208
230
|
> extends infer R
|
|
209
|
-
?
|
|
210
|
-
|
|
211
|
-
|
|
231
|
+
? // Blast-radius guard (parallels ExtractRoutesFromItems). See #642.
|
|
232
|
+
[R] extends [never]
|
|
233
|
+
? {}
|
|
234
|
+
: R extends Record<string, unknown>
|
|
235
|
+
? R
|
|
236
|
+
: {}
|
|
212
237
|
: {}
|
|
213
238
|
: {};
|
|
214
239
|
|
|
@@ -220,44 +245,53 @@ export type ExtractResponses<T extends readonly any[]> =
|
|
|
220
245
|
ExtractResponsesFromItems<T>;
|
|
221
246
|
|
|
222
247
|
// ============================================================================
|
|
223
|
-
// Response
|
|
248
|
+
// Response Error (RFC 9457 problem+json) Type
|
|
224
249
|
// ============================================================================
|
|
225
250
|
|
|
226
251
|
/**
|
|
227
|
-
*
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
message: string;
|
|
231
|
-
code?: string;
|
|
232
|
-
type?: string;
|
|
233
|
-
stack?: string;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Discriminated union envelope for JSON response routes.
|
|
238
|
-
* Consumers check `result.error` to discriminate between success and failure.
|
|
252
|
+
* RFC 9457 (problem+json) error body returned by JSON response routes on a
|
|
253
|
+
* non-2xx status. Sent verbatim as the response body (not wrapped) with
|
|
254
|
+
* content-type `application/problem+json`.
|
|
239
255
|
*
|
|
240
256
|
* @example
|
|
241
257
|
* ```typescript
|
|
242
|
-
* const
|
|
243
|
-
* if (
|
|
244
|
-
*
|
|
258
|
+
* const res = await fetch(url);
|
|
259
|
+
* if (!res.ok) {
|
|
260
|
+
* const problem: ProblemDetails = await res.json();
|
|
261
|
+
* console.log(problem.code, problem.detail); // "NOT_FOUND", "Product not found"
|
|
245
262
|
* return;
|
|
246
263
|
* }
|
|
247
|
-
*
|
|
264
|
+
* const product = await res.json(); // bare value, no envelope
|
|
248
265
|
* ```
|
|
249
266
|
*/
|
|
250
|
-
export
|
|
251
|
-
|
|
252
|
-
|
|
267
|
+
export interface ProblemDetails {
|
|
268
|
+
/**
|
|
269
|
+
* URI reference identifying the problem type. Omitted in this phase (per RFC
|
|
270
|
+
* 9457 an absent `type` is treated as `"about:blank"` — no semantics beyond
|
|
271
|
+
* the HTTP status); per-route problem-type URIs arrive with the
|
|
272
|
+
* declared-errors map later.
|
|
273
|
+
*/
|
|
274
|
+
type?: string;
|
|
275
|
+
/** Short, human-readable summary (the HTTP status reason phrase). */
|
|
276
|
+
title: string;
|
|
277
|
+
/** The HTTP status code. */
|
|
278
|
+
status: number;
|
|
279
|
+
/** Human-readable explanation specific to this occurrence (the error message). */
|
|
280
|
+
detail: string;
|
|
281
|
+
/** Stable machine-readable error code (`RouterError.code`, else `"INTERNAL"`). */
|
|
282
|
+
code: string;
|
|
283
|
+
/** Stack trace, included in development only. */
|
|
284
|
+
stack?: string;
|
|
285
|
+
}
|
|
253
286
|
|
|
254
287
|
// ============================================================================
|
|
255
288
|
// Response Type Consumer Utilities
|
|
256
289
|
// ============================================================================
|
|
257
290
|
|
|
258
291
|
/**
|
|
259
|
-
* Extract the response
|
|
260
|
-
*
|
|
292
|
+
* Extract the JSON response payload type for a named route from a UrlPatterns
|
|
293
|
+
* instance. JSON response routes send the handler's return value verbatim
|
|
294
|
+
* (bare), so this resolves to the wire value a consumer receives — no envelope.
|
|
261
295
|
*
|
|
262
296
|
* @example
|
|
263
297
|
* ```typescript
|
|
@@ -266,7 +300,7 @@ export type ResponseEnvelope<T> =
|
|
|
266
300
|
* ]);
|
|
267
301
|
*
|
|
268
302
|
* type HealthData = RouteResponse<typeof apiPatterns, "health">;
|
|
269
|
-
* //
|
|
303
|
+
* // { status: string; timestamp: number }
|
|
270
304
|
* ```
|
|
271
305
|
*
|
|
272
306
|
* The payload is the JSON wire shape (via `Rango.JsonSerialize`), matching
|
|
@@ -277,6 +311,6 @@ export type RouteResponse<TPatterns, TName extends string> = TPatterns extends {
|
|
|
277
311
|
readonly _responses?: infer R;
|
|
278
312
|
}
|
|
279
313
|
? TName extends keyof R
|
|
280
|
-
?
|
|
314
|
+
? JsonSerialize<Exclude<R[TName], Response>>
|
|
281
315
|
: never
|
|
282
316
|
: never;
|
|
@@ -34,24 +34,18 @@ export function urls<
|
|
|
34
34
|
>(
|
|
35
35
|
builder: (helpers: PathHelpers<TEnv>) => TItems,
|
|
36
36
|
): UrlPatterns<TEnv, ExtractRoutes<TItems>, ExtractResponses<TItems>> {
|
|
37
|
-
// Create the handler function that will be called by the router
|
|
38
37
|
const handler = () => {
|
|
39
38
|
invariant(
|
|
40
39
|
typeof builder === "function",
|
|
41
40
|
"urls() expects a builder function as its argument",
|
|
42
41
|
);
|
|
43
42
|
|
|
44
|
-
// Get base helpers from the existing route-definition module
|
|
45
43
|
const baseHelpers = createRouteHelpers<any, TEnv>();
|
|
46
44
|
|
|
47
|
-
// Create the path helper (with .json, .text, .html, .xml, .image, .stream, .any tags)
|
|
48
45
|
const pathHelper = attachPathResponseTags(createPathHelper<TEnv>());
|
|
49
46
|
|
|
50
|
-
// Create the include helper
|
|
51
47
|
const includeHelper = createIncludeHelper<TEnv>();
|
|
52
48
|
|
|
53
|
-
// Combine all helpers
|
|
54
|
-
// Note: layout and cache are cast to their typed versions - phantom types don't affect runtime
|
|
55
49
|
const helpers: PathHelpers<TEnv> = {
|
|
56
50
|
path: pathHelper as any,
|
|
57
51
|
include: includeHelper as any,
|
|
@@ -64,25 +58,17 @@ export function urls<
|
|
|
64
58
|
loading: baseHelpers.loading,
|
|
65
59
|
errorBoundary: baseHelpers.errorBoundary,
|
|
66
60
|
notFoundBoundary: baseHelpers.notFoundBoundary,
|
|
67
|
-
when: baseHelpers.when,
|
|
68
61
|
cache: baseHelpers.cache as PathHelpers<TEnv>["cache"],
|
|
69
62
|
transition: baseHelpers.transition as PathHelpers<TEnv>["transition"],
|
|
70
63
|
};
|
|
71
64
|
|
|
72
|
-
// Execute builder directly - manifest.ts handles RootLayout wrapping
|
|
73
|
-
// for inline handlers (non-Promise results).
|
|
74
|
-
// For nested include() calls, routes inherit the outer RootLayout.
|
|
75
65
|
const builderResult = builder(helpers).flat(3) as AllUseItems[];
|
|
76
66
|
return processItems(builderResult);
|
|
77
67
|
};
|
|
78
68
|
|
|
79
|
-
// trailingSlash config is populated when handler() runs
|
|
80
|
-
// We expose it via a getter that reads from the context after handler execution
|
|
81
69
|
return {
|
|
82
70
|
handler,
|
|
83
71
|
get trailingSlash() {
|
|
84
|
-
// Get the trailingSlash map from the current context
|
|
85
|
-
// This will be populated after handler() is called
|
|
86
72
|
const store = getContext();
|
|
87
73
|
const ctx = store.context.getStore();
|
|
88
74
|
if (!ctx?.trailingSlash) {
|