@rangojs/router 0.0.0-experimental.7 → 0.0.0-experimental.70
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 +9 -0
- package/README.md +942 -4
- package/dist/bin/rango.js +1689 -0
- package/dist/vite/index.js +4951 -930
- package/package.json +70 -60
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +294 -0
- package/skills/caching/SKILL.md +93 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +334 -72
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +92 -31
- package/skills/loader/SKILL.md +404 -44
- package/skills/middleware/SKILL.md +173 -34
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +204 -1
- package/skills/prerender/SKILL.md +685 -0
- package/skills/rango/SKILL.md +85 -16
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +257 -14
- package/skills/router-setup/SKILL.md +210 -32
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +328 -89
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +92 -64
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +296 -558
- package/src/browser/navigation-client.ts +179 -69
- package/src/browser/navigation-store.ts +73 -55
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +328 -313
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +150 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +160 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +230 -74
- package/src/browser/react/NavigationProvider.tsx +87 -11
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +30 -126
- package/src/browser/react/use-href.tsx +2 -2
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +76 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +214 -58
- package/src/browser/scroll-restoration.ts +127 -52
- package/src/browser/segment-reconciler.ts +221 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +510 -603
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +141 -48
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +235 -24
- package/src/build/generate-route-types.ts +39 -0
- package/src/build/index.ts +13 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +418 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +618 -0
- package/src/build/route-types/scan-filter.ts +85 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +342 -0
- package/src/cache/cache-scope.ts +167 -309
- package/src/cache/cf/cf-cache-store.ts +571 -17
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -15
- package/src/cache/memory-segment-store.ts +191 -13
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +153 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +3 -1
- package/src/client.tsx +105 -179
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +156 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +108 -2
- package/src/handle.ts +55 -29
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +119 -29
- package/src/index.rsc.ts +155 -19
- package/src/index.ts +223 -30
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +26 -157
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +186 -0
- package/src/prerender.ts +524 -0
- package/src/reverse.ts +351 -0
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +982 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +434 -0
- package/src/route-definition/index.ts +55 -0
- package/src/route-definition/redirect.ts +101 -0
- package/src/route-definition/resolve-handler-use.ts +149 -0
- package/src/route-definition.ts +1 -1428
- package/src/route-map-builder.ts +217 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +70 -8
- package/src/router/content-negotiation.ts +215 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +435 -86
- package/src/router/intercept-resolution.ts +402 -0
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +356 -128
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +154 -35
- package/src/router/match-api.ts +555 -0
- package/src/router/match-context.ts +5 -3
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +108 -93
- package/src/router/match-middleware/cache-lookup.ts +459 -10
- package/src/router/match-middleware/cache-store.ts +98 -26
- package/src/router/match-middleware/intercept-resolution.ts +57 -17
- package/src/router/match-middleware/segment-resolution.ts +80 -6
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +55 -33
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +220 -0
- package/src/router/middleware.ts +324 -369
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +211 -43
- package/src/router/prerender-match.ts +502 -0
- package/src/router/preview-match.ts +98 -0
- package/src/router/request-classification.ts +310 -0
- package/src/router/revalidation.ts +137 -38
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +41 -21
- package/src/router/router-interfaces.ts +484 -0
- package/src/router/router-options.ts +618 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +743 -0
- package/src/router/segment-resolution/helpers.ts +268 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1373 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +239 -0
- package/src/router/types.ts +78 -3
- package/src/router.ts +740 -4252
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +907 -797
- package/src/rsc/helpers.ts +140 -6
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +229 -0
- package/src/rsc/manifest-init.ts +90 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +391 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +246 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +356 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +46 -11
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +165 -17
- package/src/server/context.ts +315 -58
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +113 -15
- package/src/server/loader-registry.ts +24 -64
- package/src/server/request-context.ts +607 -81
- package/src/server.ts +35 -130
- package/src/ssr/index.tsx +103 -30
- package/src/static-handler.ts +126 -0
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +791 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +210 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +109 -0
- package/src/types/segments.ts +150 -0
- package/src/types.ts +1 -1623
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +346 -0
- package/src/urls/path-helper.ts +364 -0
- package/src/urls/pattern-types.ts +107 -0
- package/src/urls/response-types.ts +116 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -802
- package/src/use-loader.tsx +161 -81
- package/src/vite/discovery/bundle-postprocess.ts +181 -0
- package/src/vite/discovery/discover-routers.ts +348 -0
- package/src/vite/discovery/prerender-collection.ts +439 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +117 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +15 -1129
- package/src/vite/plugin-types.ts +103 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
- package/src/vite/plugins/expose-id-utils.ts +299 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +786 -0
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +462 -0
- package/src/vite/router-discovery.ts +918 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +207 -0
- package/src/vite/utils/shared-utils.ts +170 -0
- package/CLAUDE.md +0 -43
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/href.ts +0 -255
- package/src/server/route-manifest-cache.ts +0 -173
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -426
- package/src/vite/expose-location-state-id.ts +0 -177
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import type { ExtractParams } from "../types.js";
|
|
2
|
+
import type {
|
|
3
|
+
TypedRouteItem,
|
|
4
|
+
TypedIncludeItem,
|
|
5
|
+
TypedLayoutItem,
|
|
6
|
+
TypedCacheItem,
|
|
7
|
+
TypedTransitionItem,
|
|
8
|
+
} from "../route-types.js";
|
|
9
|
+
import type {
|
|
10
|
+
LocalOnlyInclude,
|
|
11
|
+
UnnamedRoute,
|
|
12
|
+
UrlPatterns,
|
|
13
|
+
} from "./pattern-types.js";
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Route Type Extraction Utilities
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Prefix route names with a given prefix (e.g., "blog" + "post" = "blog.post")
|
|
21
|
+
*
|
|
22
|
+
* Filters out plain `string` index signatures to prevent dynamically-generated
|
|
23
|
+
* routes from poisoning the route map. When TypeScript encounters very large
|
|
24
|
+
* route sets (5000+ routes via Array.from), it may give up computing specific
|
|
25
|
+
* types and fall back to Record<string, string>. Without filtering, PrefixRoutes
|
|
26
|
+
* would map `string` to `${prefix}.${string}`, creating an index signature that
|
|
27
|
+
* accepts ANY prefixed name and defeats type-safe route checking.
|
|
28
|
+
*
|
|
29
|
+
* Uses `string extends K` (conservative filter):
|
|
30
|
+
* - Drops `string` keys (TypeScript fallback) -> prevents `[x: `site.${string}`]`
|
|
31
|
+
* - Keeps template literal patterns like `item${number}` from Array.from loops,
|
|
32
|
+
* which are imprecise but still allow writing paths like `/shop/product/1`
|
|
33
|
+
*
|
|
34
|
+
* A more aggressive alternative (`{} extends Record<K, 1>`) would also drop
|
|
35
|
+
* template literal patterns. We chose conservative because loop-generated routes
|
|
36
|
+
* with `${number}` patterns still provide some value: they don't appear in
|
|
37
|
+
* named-routes.gen.ts or IDE autocomplete, but they do let you manually write
|
|
38
|
+
* valid paths without type errors.
|
|
39
|
+
*/
|
|
40
|
+
type PrefixRoutes<
|
|
41
|
+
TRoutes extends Record<string, any>,
|
|
42
|
+
TPrefix extends string,
|
|
43
|
+
> = TPrefix extends ""
|
|
44
|
+
? TRoutes
|
|
45
|
+
: {
|
|
46
|
+
[K in keyof TRoutes as K extends string
|
|
47
|
+
? string extends K
|
|
48
|
+
? never
|
|
49
|
+
: `${TPrefix}.${K}`
|
|
50
|
+
: never]: TRoutes[K];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Prefix route patterns with a URL prefix (e.g., "/blog" + "/:slug" = "/blog/:slug")
|
|
55
|
+
*/
|
|
56
|
+
type PrefixPatterns<
|
|
57
|
+
TRoutes extends Record<string, any>,
|
|
58
|
+
TUrlPrefix extends string,
|
|
59
|
+
> = {
|
|
60
|
+
[K in keyof TRoutes]: TRoutes[K] extends string
|
|
61
|
+
? `${TUrlPrefix}${TRoutes[K]}`
|
|
62
|
+
: TRoutes[K] extends {
|
|
63
|
+
readonly path: infer P extends string;
|
|
64
|
+
readonly search: infer S;
|
|
65
|
+
}
|
|
66
|
+
? { readonly path: `${TUrlPrefix}${P}`; readonly search: S }
|
|
67
|
+
: TRoutes[K];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Depth counter for limiting recursion (max 40 levels)
|
|
72
|
+
* Supports up to 40 sibling items at any level of a urls() call
|
|
73
|
+
* Note: Higher values hit TypeScript's internal recursion limits
|
|
74
|
+
*/
|
|
75
|
+
type Depth = [
|
|
76
|
+
never,
|
|
77
|
+
0,
|
|
78
|
+
1,
|
|
79
|
+
2,
|
|
80
|
+
3,
|
|
81
|
+
4,
|
|
82
|
+
5,
|
|
83
|
+
6,
|
|
84
|
+
7,
|
|
85
|
+
8,
|
|
86
|
+
9,
|
|
87
|
+
10,
|
|
88
|
+
11,
|
|
89
|
+
12,
|
|
90
|
+
13,
|
|
91
|
+
14,
|
|
92
|
+
15,
|
|
93
|
+
16,
|
|
94
|
+
17,
|
|
95
|
+
18,
|
|
96
|
+
19,
|
|
97
|
+
20,
|
|
98
|
+
21,
|
|
99
|
+
22,
|
|
100
|
+
23,
|
|
101
|
+
24,
|
|
102
|
+
25,
|
|
103
|
+
26,
|
|
104
|
+
27,
|
|
105
|
+
28,
|
|
106
|
+
29,
|
|
107
|
+
30,
|
|
108
|
+
31,
|
|
109
|
+
32,
|
|
110
|
+
33,
|
|
111
|
+
34,
|
|
112
|
+
35,
|
|
113
|
+
36,
|
|
114
|
+
37,
|
|
115
|
+
38,
|
|
116
|
+
39,
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Force TypeScript to eagerly evaluate a type.
|
|
121
|
+
* This helps with interface extension by creating a "concrete" object type.
|
|
122
|
+
*/
|
|
123
|
+
type Simplify<T> =
|
|
124
|
+
T extends Record<string, string> ? { [K in keyof T]: T[K] } : T;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Convert a union type to an intersection type.
|
|
128
|
+
* Used to combine route maps from multiple siblings without recursive tuple processing.
|
|
129
|
+
*/
|
|
130
|
+
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
131
|
+
k: infer I,
|
|
132
|
+
) => void
|
|
133
|
+
? I
|
|
134
|
+
: never;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Extract routes from a single item (path, include, layout, cache with children)
|
|
138
|
+
* D is the current depth level for nested layouts/caches
|
|
139
|
+
*/
|
|
140
|
+
type ExtractRoutesFromItem<T, D extends number = 40> = [D] extends [never]
|
|
141
|
+
? {} // Max depth reached, stop recursion
|
|
142
|
+
: // TypedRouteItem: extract name -> pattern (exclude unnamed routes)
|
|
143
|
+
// When search schema is non-empty, value becomes { path, search } object
|
|
144
|
+
T extends TypedRouteItem<infer TName, infer TPattern, any, infer TSearch>
|
|
145
|
+
? TName extends string
|
|
146
|
+
? TName extends UnnamedRoute
|
|
147
|
+
? {} // Exclude unnamed routes from type map
|
|
148
|
+
: {} extends TSearch
|
|
149
|
+
? { [K in TName]: TPattern }
|
|
150
|
+
: {
|
|
151
|
+
[K in TName]: {
|
|
152
|
+
readonly path: TPattern;
|
|
153
|
+
readonly search: TSearch;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
: {}
|
|
157
|
+
: // TypedIncludeItem: extract prefixed routes (both name and URL prefix)
|
|
158
|
+
T extends TypedIncludeItem<
|
|
159
|
+
infer TRoutes,
|
|
160
|
+
infer TNamePrefix,
|
|
161
|
+
infer TUrlPrefix
|
|
162
|
+
>
|
|
163
|
+
? TNamePrefix extends LocalOnlyInclude
|
|
164
|
+
? {}
|
|
165
|
+
: TNamePrefix extends string
|
|
166
|
+
? TUrlPrefix extends string
|
|
167
|
+
? PrefixRoutes<PrefixPatterns<TRoutes, TUrlPrefix>, TNamePrefix>
|
|
168
|
+
: PrefixRoutes<TRoutes, TNamePrefix>
|
|
169
|
+
: TUrlPrefix extends string
|
|
170
|
+
? PrefixPatterns<TRoutes, TUrlPrefix>
|
|
171
|
+
: TRoutes
|
|
172
|
+
: // TypedLayoutItem: extract child routes from phantom type
|
|
173
|
+
T extends TypedLayoutItem<infer TChildRoutes>
|
|
174
|
+
? TChildRoutes
|
|
175
|
+
: // TypedCacheItem: extract child routes from phantom type
|
|
176
|
+
T extends TypedCacheItem<infer TChildRoutes>
|
|
177
|
+
? TChildRoutes
|
|
178
|
+
: // TypedTransitionItem: extract child routes from phantom type
|
|
179
|
+
T extends TypedTransitionItem<infer TChildRoutes>
|
|
180
|
+
? TChildRoutes
|
|
181
|
+
: // Fallback (won't extract routes)
|
|
182
|
+
{};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Extract routes from an array of items using mapped types.
|
|
186
|
+
* Uses UnionToIntersection to combine routes without recursive tuple processing,
|
|
187
|
+
* removing the sibling limit that was caused by TypeScript recursion limits.
|
|
188
|
+
* D is passed to ExtractRoutesFromItem for nested depth tracking.
|
|
189
|
+
*/
|
|
190
|
+
type ExtractRoutesFromItems<
|
|
191
|
+
T extends readonly any[],
|
|
192
|
+
D extends number = 40,
|
|
193
|
+
> = T extends readonly any[]
|
|
194
|
+
? UnionToIntersection<
|
|
195
|
+
{ [K in keyof T]: ExtractRoutesFromItem<T[K], D> }[number]
|
|
196
|
+
> extends infer R
|
|
197
|
+
? R extends Record<string, any>
|
|
198
|
+
? R
|
|
199
|
+
: {}
|
|
200
|
+
: {}
|
|
201
|
+
: {};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Main utility: extract route map from urls() callback return type
|
|
205
|
+
* Uses mapped types for sibling processing (no sibling limit).
|
|
206
|
+
* Uses Simplify to force eager evaluation for interface extension compatibility.
|
|
207
|
+
*/
|
|
208
|
+
export type ExtractRoutes<T extends readonly any[]> = ExtractRoutesFromItems<
|
|
209
|
+
T,
|
|
210
|
+
40
|
|
211
|
+
>;
|
|
212
|
+
|
|
213
|
+
// ============================================================================
|
|
214
|
+
// Response Type Extraction Utilities
|
|
215
|
+
// ============================================================================
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Prefix keys of a Record<string, unknown> with a dot-separated prefix.
|
|
219
|
+
* Used for response type maps through include().
|
|
220
|
+
* Same index signature filter as PrefixRoutes (see comment there).
|
|
221
|
+
*/
|
|
222
|
+
type PrefixKeys<
|
|
223
|
+
T extends Record<string, unknown>,
|
|
224
|
+
TPrefix extends string,
|
|
225
|
+
> = TPrefix extends ""
|
|
226
|
+
? T
|
|
227
|
+
: {
|
|
228
|
+
[K in keyof T as K extends string
|
|
229
|
+
? string extends K
|
|
230
|
+
? never
|
|
231
|
+
: `${TPrefix}.${K}`
|
|
232
|
+
: never]: T[K];
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Extract response data types from a single item.
|
|
237
|
+
* Parallel to ExtractRoutesFromItem but extracts name -> TData mapping.
|
|
238
|
+
*/
|
|
239
|
+
type ExtractResponsesFromItem<T, D extends number = 40> = [D] extends [never]
|
|
240
|
+
? {}
|
|
241
|
+
: T extends TypedRouteItem<infer TName, any, infer TData>
|
|
242
|
+
? TName extends string
|
|
243
|
+
? TName extends UnnamedRoute
|
|
244
|
+
? {}
|
|
245
|
+
: { [K in TName]: TData }
|
|
246
|
+
: {}
|
|
247
|
+
: T extends TypedIncludeItem<any, infer TNamePrefix, any, infer TResponses>
|
|
248
|
+
? TNamePrefix extends LocalOnlyInclude
|
|
249
|
+
? {}
|
|
250
|
+
: TNamePrefix extends string
|
|
251
|
+
? TResponses extends Record<string, unknown>
|
|
252
|
+
? PrefixKeys<TResponses, TNamePrefix>
|
|
253
|
+
: {}
|
|
254
|
+
: TResponses extends Record<string, unknown>
|
|
255
|
+
? TResponses
|
|
256
|
+
: {}
|
|
257
|
+
: T extends TypedLayoutItem<any, infer TChildResponses>
|
|
258
|
+
? TChildResponses extends Record<string, unknown>
|
|
259
|
+
? TChildResponses
|
|
260
|
+
: {}
|
|
261
|
+
: T extends TypedCacheItem<any, infer TChildResponses>
|
|
262
|
+
? TChildResponses extends Record<string, unknown>
|
|
263
|
+
? TChildResponses
|
|
264
|
+
: {}
|
|
265
|
+
: T extends TypedTransitionItem<any, infer TChildResponses>
|
|
266
|
+
? TChildResponses extends Record<string, unknown>
|
|
267
|
+
? TChildResponses
|
|
268
|
+
: {}
|
|
269
|
+
: {};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Extract responses from an array of items using mapped types.
|
|
273
|
+
* Parallel to ExtractRoutesFromItems.
|
|
274
|
+
*/
|
|
275
|
+
type ExtractResponsesFromItems<
|
|
276
|
+
T extends readonly any[],
|
|
277
|
+
D extends number = 40,
|
|
278
|
+
> = T extends readonly any[]
|
|
279
|
+
? UnionToIntersection<
|
|
280
|
+
{ [K in keyof T]: ExtractResponsesFromItem<T[K], D> }[number]
|
|
281
|
+
> extends infer R
|
|
282
|
+
? R extends Record<string, unknown>
|
|
283
|
+
? R
|
|
284
|
+
: {}
|
|
285
|
+
: {}
|
|
286
|
+
: {};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Main utility: extract response data type map from urls() callback return type.
|
|
290
|
+
* Parallel to ExtractRoutes.
|
|
291
|
+
*/
|
|
292
|
+
export type ExtractResponses<T extends readonly any[]> =
|
|
293
|
+
ExtractResponsesFromItems<T, 40>;
|
|
294
|
+
|
|
295
|
+
// ============================================================================
|
|
296
|
+
// Type Utilities for path()
|
|
297
|
+
// ============================================================================
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Extract route names from a UrlPatterns result
|
|
301
|
+
* Used for type-safe href() generation
|
|
302
|
+
*/
|
|
303
|
+
export type ExtractRouteNames<T extends UrlPatterns<any>> =
|
|
304
|
+
T extends UrlPatterns<infer _TEnv>
|
|
305
|
+
? string // For now, will be refined with full implementation
|
|
306
|
+
: never;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Extract params for a specific route name
|
|
310
|
+
*/
|
|
311
|
+
export type ExtractPathParams<
|
|
312
|
+
T extends UrlPatterns<any>,
|
|
313
|
+
K extends string,
|
|
314
|
+
> = ExtractParams<string>; // Will be refined with pattern tracking
|
|
315
|
+
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// Response Envelope Types
|
|
318
|
+
// ============================================================================
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Error shape returned in the `{ error }` side of a JSON response envelope.
|
|
322
|
+
*/
|
|
323
|
+
export interface ResponseError {
|
|
324
|
+
message: string;
|
|
325
|
+
code?: string;
|
|
326
|
+
type?: string;
|
|
327
|
+
stack?: string;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Discriminated union envelope for JSON response routes.
|
|
332
|
+
* Consumers check `result.error` to discriminate between success and failure.
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* ```typescript
|
|
336
|
+
* const result: ResponseEnvelope<Product> = await fetch(url).then(r => r.json());
|
|
337
|
+
* if (result.error) {
|
|
338
|
+
* console.log(result.error.message, result.error.code);
|
|
339
|
+
* return;
|
|
340
|
+
* }
|
|
341
|
+
* result.data.name // fully typed
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
export type ResponseEnvelope<T> =
|
|
345
|
+
| { data: T; error?: undefined }
|
|
346
|
+
| { data?: undefined; error: ResponseError };
|
|
347
|
+
|
|
348
|
+
// ============================================================================
|
|
349
|
+
// Response Type Consumer Utilities
|
|
350
|
+
// ============================================================================
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Extract the response data type for a named route from a UrlPatterns instance.
|
|
354
|
+
* Wraps in ResponseEnvelope since JSON response routes return enveloped data.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```typescript
|
|
358
|
+
* const apiPatterns = urls(({ path }) => [
|
|
359
|
+
* path.json("/health", (ctx) => ({ status: "ok", timestamp: Date.now() }), { name: "health" }),
|
|
360
|
+
* ]);
|
|
361
|
+
*
|
|
362
|
+
* type HealthData = RouteResponse<typeof apiPatterns, "health">;
|
|
363
|
+
* // ResponseEnvelope<{ status: string; timestamp: number }>
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
export type RouteResponse<TPatterns, TName extends string> = TPatterns extends {
|
|
367
|
+
readonly _responses?: infer R;
|
|
368
|
+
}
|
|
369
|
+
? TName extends keyof R
|
|
370
|
+
? ResponseEnvelope<Exclude<R[TName], Response>>
|
|
371
|
+
: never
|
|
372
|
+
: never;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { DefaultEnv } from "../types.js";
|
|
2
|
+
import type { AllUseItems } from "../route-types.js";
|
|
3
|
+
import { getContext } from "../server/context";
|
|
4
|
+
import { invariant } from "../errors";
|
|
5
|
+
import { createRouteHelpers } from "../route-definition.js";
|
|
6
|
+
import type { PathDefinition, UrlPatterns } from "./pattern-types.js";
|
|
7
|
+
import type { PathHelpers } from "./path-helper-types.js";
|
|
8
|
+
import type { ExtractRoutes, ExtractResponses } from "./type-extraction.js";
|
|
9
|
+
import { createPathHelper, attachPathResponseTags } from "./path-helper.js";
|
|
10
|
+
import { createIncludeHelper, processItems } from "./include-helper.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Define URL patterns with Django-inspired syntax
|
|
14
|
+
*
|
|
15
|
+
* Replaces map() as the entry point for route definitions.
|
|
16
|
+
* URL patterns are now visible at the definition site via path().
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* export const blogPatterns = urls(({ path, layout, loader }) => [
|
|
21
|
+
* layout(BlogLayout, () => [
|
|
22
|
+
* path("/", BlogIndex, { name: "index" }),
|
|
23
|
+
* path("/:slug", BlogPost, { name: "post" }, () => [
|
|
24
|
+
* loader(PostLoader),
|
|
25
|
+
* ]),
|
|
26
|
+
* ]),
|
|
27
|
+
* ]);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function urls<
|
|
31
|
+
TEnv = DefaultEnv,
|
|
32
|
+
const TItems extends readonly (AllUseItems | readonly AllUseItems[])[] =
|
|
33
|
+
readonly AllUseItems[],
|
|
34
|
+
>(
|
|
35
|
+
builder: (helpers: PathHelpers<TEnv>) => TItems,
|
|
36
|
+
): UrlPatterns<TEnv, ExtractRoutes<TItems>, ExtractResponses<TItems>> {
|
|
37
|
+
// Collect path definitions during build
|
|
38
|
+
const definitions: PathDefinition[] = [];
|
|
39
|
+
|
|
40
|
+
// Create the handler function that will be called by the router
|
|
41
|
+
const handler = () => {
|
|
42
|
+
invariant(
|
|
43
|
+
typeof builder === "function",
|
|
44
|
+
"urls() expects a builder function as its argument",
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Get base helpers from the existing route-definition module
|
|
48
|
+
const baseHelpers = createRouteHelpers<any, TEnv>();
|
|
49
|
+
|
|
50
|
+
// Create the path helper (with .json, .text, .html, .xml, .image, .stream, .any tags)
|
|
51
|
+
const pathHelper = attachPathResponseTags(createPathHelper<TEnv>());
|
|
52
|
+
|
|
53
|
+
// Create the include helper
|
|
54
|
+
const includeHelper = createIncludeHelper<TEnv>();
|
|
55
|
+
|
|
56
|
+
// Combine all helpers
|
|
57
|
+
// Note: layout and cache are cast to their typed versions - phantom types don't affect runtime
|
|
58
|
+
const helpers: PathHelpers<TEnv> = {
|
|
59
|
+
path: pathHelper as any,
|
|
60
|
+
include: includeHelper as any,
|
|
61
|
+
layout: baseHelpers.layout as PathHelpers<TEnv>["layout"],
|
|
62
|
+
parallel: baseHelpers.parallel as PathHelpers<TEnv>["parallel"],
|
|
63
|
+
intercept: baseHelpers.intercept as PathHelpers<TEnv>["intercept"],
|
|
64
|
+
middleware: baseHelpers.middleware,
|
|
65
|
+
revalidate: baseHelpers.revalidate,
|
|
66
|
+
loader: baseHelpers.loader,
|
|
67
|
+
loading: baseHelpers.loading,
|
|
68
|
+
errorBoundary: baseHelpers.errorBoundary,
|
|
69
|
+
notFoundBoundary: baseHelpers.notFoundBoundary,
|
|
70
|
+
when: baseHelpers.when,
|
|
71
|
+
cache: baseHelpers.cache as PathHelpers<TEnv>["cache"],
|
|
72
|
+
transition: baseHelpers.transition as PathHelpers<TEnv>["transition"],
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Execute builder directly - manifest.ts handles RootLayout wrapping
|
|
76
|
+
// for inline handlers (non-Promise results).
|
|
77
|
+
// For nested include() calls, routes inherit the outer RootLayout.
|
|
78
|
+
const builderResult = builder(helpers).flat(3) as AllUseItems[];
|
|
79
|
+
return processItems(builderResult);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// trailingSlash config is populated when handler() runs
|
|
83
|
+
// We expose it via a getter that reads from the context after handler execution
|
|
84
|
+
return {
|
|
85
|
+
definitions,
|
|
86
|
+
handler,
|
|
87
|
+
get trailingSlash() {
|
|
88
|
+
// Get the trailingSlash map from the current context
|
|
89
|
+
// This will be populated after handler() is called
|
|
90
|
+
const store = getContext();
|
|
91
|
+
const ctx = store.context.getStore();
|
|
92
|
+
if (!ctx?.trailingSlash) {
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
return Object.fromEntries(ctx.trailingSlash);
|
|
96
|
+
},
|
|
97
|
+
} as UrlPatterns<TEnv, ExtractRoutes<TItems>, ExtractResponses<TItems>>;
|
|
98
|
+
}
|