@rangojs/router 0.0.0-experimental.110 → 0.0.0-experimental.111
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/dist/vite/index.js +1 -1
- package/package.json +15 -14
- package/skills/handler-use/SKILL.md +1 -1
- package/skills/rango/SKILL.md +20 -0
- package/src/errors.ts +18 -0
- package/src/index.rsc.ts +1 -0
- package/src/index.ts +1 -0
- package/src/route-definition/dsl-helpers.ts +231 -259
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +19 -41
- package/src/server/context.ts +76 -35
- package/src/urls/include-helper.ts +10 -53
- package/src/urls/index.ts +0 -3
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +2 -19
- package/src/urls/response-types.ts +20 -19
- package/src/urls/type-extraction.ts +20 -115
- package/src/urls/urls-function.ts +1 -5
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { AllUseItems, IncludeItem } from "../route-types.js";
|
|
2
2
|
import {
|
|
3
|
-
getContext,
|
|
4
|
-
runWithPrefixes,
|
|
5
3
|
getUrlPrefix,
|
|
6
4
|
getNamePrefix,
|
|
5
|
+
requireDslContext,
|
|
7
6
|
} from "../server/context";
|
|
8
7
|
import {
|
|
9
8
|
INTERNAL_INCLUDE_SCOPE_PREFIX,
|
|
@@ -26,28 +25,10 @@ function allocateInternalIncludeScopeId(
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
|
-
*
|
|
30
|
-
* This expands the include into actual route registrations
|
|
31
|
-
*/
|
|
32
|
-
function processIncludeItem(item: IncludeItem): AllUseItems[] {
|
|
33
|
-
const { prefix, patterns } = item;
|
|
34
|
-
const namePrefix =
|
|
35
|
-
(item as IncludeItem & { _lazyContext?: { namePrefix?: string } })
|
|
36
|
-
._lazyContext?.namePrefix ?? item.options?.name;
|
|
37
|
-
|
|
38
|
-
// Execute the nested patterns' handler with URL and name prefixes
|
|
39
|
-
// The urlPrefix being set tells nested urls() to skip RootLayout wrapping
|
|
40
|
-
return runWithPrefixes(prefix, namePrefix, () => {
|
|
41
|
-
// Call the nested patterns' handler - this registers routes with prefixed patterns/names
|
|
42
|
-
return (patterns as UrlPatterns).handler();
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Recursively process items, expanding any IncludeItems
|
|
48
|
-
* Returns items with IncludeItems expanded into actual route items
|
|
28
|
+
* Recursively walk items, recursing into layout children.
|
|
49
29
|
*
|
|
50
|
-
*
|
|
30
|
+
* All includes are lazy and kept as-is; the router expands them on the first
|
|
31
|
+
* matching request.
|
|
51
32
|
*/
|
|
52
33
|
export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
|
|
53
34
|
const result: AllUseItems[] = [];
|
|
@@ -56,26 +37,8 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
|
|
|
56
37
|
if (!item) continue;
|
|
57
38
|
|
|
58
39
|
if (item.type === "include") {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
lazy?: boolean;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// Lazy includes are NOT expanded here - kept for router to handle
|
|
65
|
-
if (includeItem.lazy) {
|
|
66
|
-
result.push(item);
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Eager includes are already expanded during include() call
|
|
71
|
-
if (includeItem._expanded) {
|
|
72
|
-
// Items were expanded immediately - just process them recursively
|
|
73
|
-
result.push(...processItems(includeItem._expanded));
|
|
74
|
-
} else {
|
|
75
|
-
// Fallback for legacy include items without _expanded
|
|
76
|
-
const expanded = processIncludeItem(item as IncludeItem);
|
|
77
|
-
result.push(...processItems(expanded));
|
|
78
|
-
}
|
|
40
|
+
// All includes are lazy; the router expands them on first matching request.
|
|
41
|
+
result.push(item);
|
|
79
42
|
} else if (item.type === "layout" && (item as any).uses) {
|
|
80
43
|
// Process nested items in layout
|
|
81
44
|
const layoutItem = item as any;
|
|
@@ -92,13 +55,9 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
|
|
|
92
55
|
/**
|
|
93
56
|
* Create include() helper for composing URL patterns
|
|
94
57
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* With `lazy: true`, patterns are NOT expanded at definition time. Instead,
|
|
100
|
-
* they're evaluated on first request that matches the prefix. This improves
|
|
101
|
-
* cold start time for apps with many routes.
|
|
58
|
+
* All includes are lazy: the nested patterns are NOT expanded at definition
|
|
59
|
+
* time. Instead they are evaluated on the first request that matches the
|
|
60
|
+
* prefix, which improves cold start time for apps with many routes.
|
|
102
61
|
*/
|
|
103
62
|
export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
104
63
|
return (
|
|
@@ -106,9 +65,7 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
|
106
65
|
patterns: UrlPatterns<TEnv>,
|
|
107
66
|
options?: IncludeOptions,
|
|
108
67
|
): IncludeItem => {
|
|
109
|
-
const
|
|
110
|
-
const ctx = store.getStore();
|
|
111
|
-
if (!ctx) throw new Error("include() must be called inside urls()");
|
|
68
|
+
const { ctx } = requireDslContext("include() must be called inside urls()");
|
|
112
69
|
|
|
113
70
|
const explicitName = options?.name;
|
|
114
71
|
const hasExplicitName = hasExplicitNameOption(options);
|
package/src/urls/index.ts
CHANGED
|
@@ -13,7 +13,6 @@ export type {
|
|
|
13
13
|
UnnamedRoute,
|
|
14
14
|
LocalOnlyInclude,
|
|
15
15
|
PathOptions,
|
|
16
|
-
PathDefinition,
|
|
17
16
|
UrlPatterns,
|
|
18
17
|
IncludeOptions,
|
|
19
18
|
} from "./pattern-types.js";
|
|
@@ -22,8 +21,6 @@ export type {
|
|
|
22
21
|
export type {
|
|
23
22
|
ExtractRoutes,
|
|
24
23
|
ExtractResponses,
|
|
25
|
-
ExtractRouteNames,
|
|
26
|
-
ExtractPathParams,
|
|
27
24
|
ResponseError,
|
|
28
25
|
ResponseEnvelope,
|
|
29
26
|
RouteResponse,
|
package/src/urls/path-helper.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { Handler } from "../types.js";
|
|
3
|
-
import type {
|
|
4
|
-
AllUseItems,
|
|
5
|
-
RouteItem,
|
|
6
|
-
RouteUseItem,
|
|
7
|
-
UseItems,
|
|
8
|
-
} from "../route-types.js";
|
|
3
|
+
import type { RouteItem, RouteUseItem, UseItems } from "../route-types.js";
|
|
9
4
|
import {
|
|
10
|
-
getContext,
|
|
11
5
|
getUrlPrefix,
|
|
12
6
|
getNamePrefix,
|
|
13
7
|
getRootScoped,
|
|
8
|
+
requireDslContext,
|
|
14
9
|
} from "../server/context";
|
|
15
10
|
import { invariant, DataNotFoundError } from "../errors";
|
|
16
11
|
import { validateUserRouteName } from "../route-name.js";
|
|
@@ -39,35 +34,10 @@ import {
|
|
|
39
34
|
resolveHandlerUse,
|
|
40
35
|
mergeHandlerUse,
|
|
41
36
|
} from "../route-definition/resolve-handler-use.js";
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const isValidUseItem = (item: any): item is AllUseItems | undefined | null => {
|
|
47
|
-
return (
|
|
48
|
-
typeof item === "undefined" ||
|
|
49
|
-
item === null ||
|
|
50
|
-
(item &&
|
|
51
|
-
typeof item === "object" &&
|
|
52
|
-
"type" in item &&
|
|
53
|
-
[
|
|
54
|
-
"layout",
|
|
55
|
-
"route",
|
|
56
|
-
"middleware",
|
|
57
|
-
"revalidate",
|
|
58
|
-
"parallel",
|
|
59
|
-
"intercept",
|
|
60
|
-
"loader",
|
|
61
|
-
"loading",
|
|
62
|
-
"errorBoundary",
|
|
63
|
-
"notFoundBoundary",
|
|
64
|
-
"when",
|
|
65
|
-
"cache",
|
|
66
|
-
"transition",
|
|
67
|
-
"include",
|
|
68
|
-
].includes(item.type))
|
|
69
|
-
);
|
|
70
|
-
};
|
|
37
|
+
import {
|
|
38
|
+
emptySegmentBase,
|
|
39
|
+
runAndValidateUseItems,
|
|
40
|
+
} from "../route-definition/dsl-helpers.js";
|
|
71
41
|
|
|
72
42
|
/**
|
|
73
43
|
* Apply URL prefix to a pattern
|
|
@@ -112,9 +82,9 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
112
82
|
optionsOrUse?: PathOptions | (() => UseItems<RouteUseItem>),
|
|
113
83
|
maybeUse?: () => UseItems<RouteUseItem>,
|
|
114
84
|
): RouteItem => {
|
|
115
|
-
const store =
|
|
116
|
-
|
|
117
|
-
|
|
85
|
+
const { store, ctx } = requireDslContext(
|
|
86
|
+
"path() must be called inside urls()",
|
|
87
|
+
);
|
|
118
88
|
|
|
119
89
|
invariant(
|
|
120
90
|
!ctx.parent || ctx.parent.type !== "parallel",
|
|
@@ -214,6 +184,7 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
214
184
|
: () => handler;
|
|
215
185
|
|
|
216
186
|
const entry = {
|
|
187
|
+
...emptySegmentBase(),
|
|
217
188
|
id: namespace,
|
|
218
189
|
shortCode: store.getShortCode("route"),
|
|
219
190
|
type: "route" as const,
|
|
@@ -221,15 +192,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
221
192
|
handler: wrappedHandler,
|
|
222
193
|
// Store the PREFIXED pattern for route matching
|
|
223
194
|
pattern: prefixedPattern,
|
|
224
|
-
loading: undefined,
|
|
225
|
-
middleware: [],
|
|
226
|
-
revalidate: [],
|
|
227
|
-
errorBoundary: [],
|
|
228
|
-
notFoundBoundary: [],
|
|
229
|
-
layout: [],
|
|
230
|
-
parallel: {},
|
|
231
|
-
intercept: [],
|
|
232
|
-
loader: [],
|
|
233
195
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
234
196
|
...(isPassthroughHandler(handler)
|
|
235
197
|
? {
|
|
@@ -301,10 +263,13 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
301
263
|
|
|
302
264
|
// Run merged use callback (handler.use defaults + explicit use) if present
|
|
303
265
|
if (mergedUse) {
|
|
304
|
-
const result =
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
266
|
+
const result = runAndValidateUseItems(
|
|
267
|
+
store,
|
|
268
|
+
namespace,
|
|
269
|
+
entry,
|
|
270
|
+
mergedUse,
|
|
271
|
+
"path",
|
|
272
|
+
"use",
|
|
308
273
|
);
|
|
309
274
|
return { name: namespace, type: "route", uses: result } as RouteItem;
|
|
310
275
|
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
AllUseItems,
|
|
5
|
-
RouteUseItem,
|
|
6
|
-
UrlPatternsBrand,
|
|
7
|
-
} from "../route-types.js";
|
|
1
|
+
import type { TrailingSlashMode } from "../types.js";
|
|
2
|
+
import type { AllUseItems, UrlPatternsBrand } from "../route-types.js";
|
|
8
3
|
import type { SearchSchema } from "../search-params.js";
|
|
9
4
|
import { RESPONSE_TYPE } from "./response-types.js";
|
|
10
5
|
import type { DefaultEnv } from "../types.js";
|
|
@@ -54,16 +49,6 @@ export interface PathOptions<
|
|
|
54
49
|
[RESPONSE_TYPE]?: string;
|
|
55
50
|
}
|
|
56
51
|
|
|
57
|
-
/**
|
|
58
|
-
* Internal representation of a URL pattern definition
|
|
59
|
-
*/
|
|
60
|
-
export interface PathDefinition {
|
|
61
|
-
pattern: string;
|
|
62
|
-
name?: string;
|
|
63
|
-
handler: ReactNode | Handler<any, any, any>;
|
|
64
|
-
use?: RouteUseItem[];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
52
|
/**
|
|
68
53
|
* Result of urls() - contains the route definitions
|
|
69
54
|
*/
|
|
@@ -72,8 +57,6 @@ export interface UrlPatterns<
|
|
|
72
57
|
TRoutes extends Record<string, any> = Record<string, string>,
|
|
73
58
|
TResponses extends Record<string, unknown> = Record<string, unknown>,
|
|
74
59
|
> {
|
|
75
|
-
/** Internal: route definitions */
|
|
76
|
-
readonly definitions: PathDefinition[];
|
|
77
60
|
/** Internal: compiled handler function */
|
|
78
61
|
readonly handler: () => AllUseItems[];
|
|
79
62
|
/** Internal: trailing slash config per route name */
|
|
@@ -32,21 +32,32 @@ type ResponseReverseFunction = [DefaultReverseRouteMap] extends [
|
|
|
32
32
|
* Symbol marking a route as a response route (non-RSC).
|
|
33
33
|
* Stored on PathOptions and UrlPatterns to signal the trie to short-circuit.
|
|
34
34
|
*/
|
|
35
|
-
export const RESPONSE_TYPE: unique symbol = Symbol.for(
|
|
36
|
-
"rangojs.responseType",
|
|
37
|
-
) as any;
|
|
35
|
+
export const RESPONSE_TYPE: unique symbol = Symbol.for("rangojs.responseType");
|
|
38
36
|
|
|
39
37
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* Shared shape of a response-route handler: a function returning TReturn (or a
|
|
39
|
+
* promise of it), plus an optional composable `use` thunk merged at mount time.
|
|
42
40
|
*/
|
|
43
|
-
|
|
41
|
+
type ResponseHandlerOf<
|
|
42
|
+
TReturn,
|
|
43
|
+
TParams = Record<string, string>,
|
|
44
|
+
TEnv = any,
|
|
45
|
+
> = ((
|
|
44
46
|
ctx: ResponseHandlerContext<TParams, TEnv>,
|
|
45
|
-
) =>
|
|
47
|
+
) => TReturn | Promise<TReturn>) & {
|
|
46
48
|
/** Composable default DSL items merged when the handler is mounted. */
|
|
47
49
|
use?: () => UseItems<ResponseRouteUseItem>;
|
|
48
50
|
};
|
|
49
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Handler that must return Response (not ReactNode).
|
|
54
|
+
* Used by path.image(), path.stream(), path.any() (binary/streaming data).
|
|
55
|
+
*/
|
|
56
|
+
export type ResponseHandler<
|
|
57
|
+
TParams = Record<string, string>,
|
|
58
|
+
TEnv = any,
|
|
59
|
+
> = ResponseHandlerOf<Response, TParams, TEnv>;
|
|
60
|
+
|
|
50
61
|
/**
|
|
51
62
|
* JSON-serializable value type for auto-wrap support.
|
|
52
63
|
*/
|
|
@@ -65,12 +76,7 @@ export type JsonValue =
|
|
|
65
76
|
export type JsonResponseHandler<
|
|
66
77
|
TParams = Record<string, string>,
|
|
67
78
|
TEnv = any,
|
|
68
|
-
> =
|
|
69
|
-
ctx: ResponseHandlerContext<TParams, TEnv>,
|
|
70
|
-
) => JsonValue | Response | Promise<JsonValue | Response>) & {
|
|
71
|
-
/** Composable default DSL items merged when the handler is mounted. */
|
|
72
|
-
use?: () => UseItems<ResponseRouteUseItem>;
|
|
73
|
-
};
|
|
79
|
+
> = ResponseHandlerOf<JsonValue | Response, TParams, TEnv>;
|
|
74
80
|
|
|
75
81
|
/**
|
|
76
82
|
* Handler for text-based response routes (text, html, xml).
|
|
@@ -79,12 +85,7 @@ export type JsonResponseHandler<
|
|
|
79
85
|
export type TextResponseHandler<
|
|
80
86
|
TParams = Record<string, string>,
|
|
81
87
|
TEnv = any,
|
|
82
|
-
> =
|
|
83
|
-
ctx: ResponseHandlerContext<TParams, TEnv>,
|
|
84
|
-
) => string | Response | Promise<string | Response>) & {
|
|
85
|
-
/** Composable default DSL items merged when the handler is mounted. */
|
|
86
|
-
use?: () => UseItems<ResponseRouteUseItem>;
|
|
87
|
-
};
|
|
88
|
+
> = ResponseHandlerOf<string | Response, TParams, TEnv>;
|
|
88
89
|
|
|
89
90
|
/**
|
|
90
91
|
* Lighter handler context for response routes.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ExtractParams } from "../types.js";
|
|
2
1
|
import type { JsonSerialize } from "../serialize.js";
|
|
3
2
|
import type {
|
|
4
3
|
TypedRouteItem,
|
|
@@ -7,11 +6,7 @@ import type {
|
|
|
7
6
|
TypedCacheItem,
|
|
8
7
|
TypedTransitionItem,
|
|
9
8
|
} from "../route-types.js";
|
|
10
|
-
import type {
|
|
11
|
-
LocalOnlyInclude,
|
|
12
|
-
UnnamedRoute,
|
|
13
|
-
UrlPatterns,
|
|
14
|
-
} from "./pattern-types.js";
|
|
9
|
+
import type { LocalOnlyInclude, UnnamedRoute } from "./pattern-types.js";
|
|
15
10
|
|
|
16
11
|
// ============================================================================
|
|
17
12
|
// Route Type Extraction Utilities
|
|
@@ -68,62 +63,6 @@ type PrefixPatterns<
|
|
|
68
63
|
: TRoutes[K];
|
|
69
64
|
};
|
|
70
65
|
|
|
71
|
-
/**
|
|
72
|
-
* Depth counter for limiting recursion (max 40 levels)
|
|
73
|
-
* Supports up to 40 sibling items at any level of a urls() call
|
|
74
|
-
* Note: Higher values hit TypeScript's internal recursion limits
|
|
75
|
-
*/
|
|
76
|
-
type Depth = [
|
|
77
|
-
never,
|
|
78
|
-
0,
|
|
79
|
-
1,
|
|
80
|
-
2,
|
|
81
|
-
3,
|
|
82
|
-
4,
|
|
83
|
-
5,
|
|
84
|
-
6,
|
|
85
|
-
7,
|
|
86
|
-
8,
|
|
87
|
-
9,
|
|
88
|
-
10,
|
|
89
|
-
11,
|
|
90
|
-
12,
|
|
91
|
-
13,
|
|
92
|
-
14,
|
|
93
|
-
15,
|
|
94
|
-
16,
|
|
95
|
-
17,
|
|
96
|
-
18,
|
|
97
|
-
19,
|
|
98
|
-
20,
|
|
99
|
-
21,
|
|
100
|
-
22,
|
|
101
|
-
23,
|
|
102
|
-
24,
|
|
103
|
-
25,
|
|
104
|
-
26,
|
|
105
|
-
27,
|
|
106
|
-
28,
|
|
107
|
-
29,
|
|
108
|
-
30,
|
|
109
|
-
31,
|
|
110
|
-
32,
|
|
111
|
-
33,
|
|
112
|
-
34,
|
|
113
|
-
35,
|
|
114
|
-
36,
|
|
115
|
-
37,
|
|
116
|
-
38,
|
|
117
|
-
39,
|
|
118
|
-
];
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Force TypeScript to eagerly evaluate a type.
|
|
122
|
-
* This helps with interface extension by creating a "concrete" object type.
|
|
123
|
-
*/
|
|
124
|
-
type Simplify<T> =
|
|
125
|
-
T extends Record<string, string> ? { [K in keyof T]: T[K] } : T;
|
|
126
|
-
|
|
127
66
|
/**
|
|
128
67
|
* Convert a union type to an intersection type.
|
|
129
68
|
* Used to combine route maps from multiple siblings without recursive tuple processing.
|
|
@@ -136,13 +75,11 @@ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
|
136
75
|
|
|
137
76
|
/**
|
|
138
77
|
* Extract routes from a single item (path, include, layout, cache with children)
|
|
139
|
-
* D is the current depth level for nested layouts/caches
|
|
140
78
|
*/
|
|
141
|
-
type ExtractRoutesFromItem<T
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
T extends TypedRouteItem<infer TName, infer TPattern, any, infer TSearch>
|
|
79
|
+
type ExtractRoutesFromItem<T> =
|
|
80
|
+
// TypedRouteItem: extract name -> pattern (exclude unnamed routes)
|
|
81
|
+
// When search schema is non-empty, value becomes { path, search } object
|
|
82
|
+
T extends TypedRouteItem<infer TName, infer TPattern, any, infer TSearch>
|
|
146
83
|
? TName extends string
|
|
147
84
|
? TName extends UnnamedRoute
|
|
148
85
|
? {} // Exclude unnamed routes from type map
|
|
@@ -186,14 +123,10 @@ type ExtractRoutesFromItem<T, D extends number = 40> = [D] extends [never]
|
|
|
186
123
|
* Extract routes from an array of items using mapped types.
|
|
187
124
|
* Uses UnionToIntersection to combine routes without recursive tuple processing,
|
|
188
125
|
* removing the sibling limit that was caused by TypeScript recursion limits.
|
|
189
|
-
* D is passed to ExtractRoutesFromItem for nested depth tracking.
|
|
190
126
|
*/
|
|
191
|
-
type ExtractRoutesFromItems<
|
|
192
|
-
T extends readonly any[],
|
|
193
|
-
D extends number = 40,
|
|
194
|
-
> = T extends readonly any[]
|
|
127
|
+
type ExtractRoutesFromItems<T extends readonly any[]> = T extends readonly any[]
|
|
195
128
|
? UnionToIntersection<
|
|
196
|
-
{ [K in keyof T]: ExtractRoutesFromItem<T[K]
|
|
129
|
+
{ [K in keyof T]: ExtractRoutesFromItem<T[K]> }[number]
|
|
197
130
|
> extends infer R
|
|
198
131
|
? R extends Record<string, any>
|
|
199
132
|
? R
|
|
@@ -204,12 +137,8 @@ type ExtractRoutesFromItems<
|
|
|
204
137
|
/**
|
|
205
138
|
* Main utility: extract route map from urls() callback return type
|
|
206
139
|
* Uses mapped types for sibling processing (no sibling limit).
|
|
207
|
-
* Uses Simplify to force eager evaluation for interface extension compatibility.
|
|
208
140
|
*/
|
|
209
|
-
export type ExtractRoutes<T extends readonly any[]> = ExtractRoutesFromItems<
|
|
210
|
-
T,
|
|
211
|
-
40
|
|
212
|
-
>;
|
|
141
|
+
export type ExtractRoutes<T extends readonly any[]> = ExtractRoutesFromItems<T>;
|
|
213
142
|
|
|
214
143
|
// ============================================================================
|
|
215
144
|
// Response Type Extraction Utilities
|
|
@@ -237,9 +166,8 @@ type PrefixKeys<
|
|
|
237
166
|
* Extract response data types from a single item.
|
|
238
167
|
* Parallel to ExtractRoutesFromItem but extracts name -> TData mapping.
|
|
239
168
|
*/
|
|
240
|
-
type ExtractResponsesFromItem<T
|
|
241
|
-
|
|
242
|
-
: T extends TypedRouteItem<infer TName, any, infer TData>
|
|
169
|
+
type ExtractResponsesFromItem<T> =
|
|
170
|
+
T extends TypedRouteItem<infer TName, any, infer TData>
|
|
243
171
|
? TName extends string
|
|
244
172
|
? TName extends UnnamedRoute
|
|
245
173
|
? {}
|
|
@@ -273,46 +201,23 @@ type ExtractResponsesFromItem<T, D extends number = 40> = [D] extends [never]
|
|
|
273
201
|
* Extract responses from an array of items using mapped types.
|
|
274
202
|
* Parallel to ExtractRoutesFromItems.
|
|
275
203
|
*/
|
|
276
|
-
type ExtractResponsesFromItems<
|
|
277
|
-
T extends readonly any[]
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
? R
|
|
204
|
+
type ExtractResponsesFromItems<T extends readonly any[]> =
|
|
205
|
+
T extends readonly any[]
|
|
206
|
+
? UnionToIntersection<
|
|
207
|
+
{ [K in keyof T]: ExtractResponsesFromItem<T[K]> }[number]
|
|
208
|
+
> extends infer R
|
|
209
|
+
? R extends Record<string, unknown>
|
|
210
|
+
? R
|
|
211
|
+
: {}
|
|
285
212
|
: {}
|
|
286
|
-
: {}
|
|
287
|
-
: {};
|
|
213
|
+
: {};
|
|
288
214
|
|
|
289
215
|
/**
|
|
290
216
|
* Main utility: extract response data type map from urls() callback return type.
|
|
291
217
|
* Parallel to ExtractRoutes.
|
|
292
218
|
*/
|
|
293
219
|
export type ExtractResponses<T extends readonly any[]> =
|
|
294
|
-
ExtractResponsesFromItems<T
|
|
295
|
-
|
|
296
|
-
// ============================================================================
|
|
297
|
-
// Type Utilities for path()
|
|
298
|
-
// ============================================================================
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Extract route names from a UrlPatterns result
|
|
302
|
-
* Used for type-safe href() generation
|
|
303
|
-
*/
|
|
304
|
-
export type ExtractRouteNames<T extends UrlPatterns<any>> =
|
|
305
|
-
T extends UrlPatterns<infer _TEnv>
|
|
306
|
-
? string // For now, will be refined with full implementation
|
|
307
|
-
: never;
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Extract params for a specific route name
|
|
311
|
-
*/
|
|
312
|
-
export type ExtractPathParams<
|
|
313
|
-
T extends UrlPatterns<any>,
|
|
314
|
-
K extends string,
|
|
315
|
-
> = ExtractParams<string>; // Will be refined with pattern tracking
|
|
220
|
+
ExtractResponsesFromItems<T>;
|
|
316
221
|
|
|
317
222
|
// ============================================================================
|
|
318
223
|
// Response Envelope Types
|
|
@@ -3,7 +3,7 @@ import type { AllUseItems } from "../route-types.js";
|
|
|
3
3
|
import { getContext } from "../server/context";
|
|
4
4
|
import { invariant } from "../errors";
|
|
5
5
|
import { createRouteHelpers } from "../route-definition.js";
|
|
6
|
-
import type {
|
|
6
|
+
import type { UrlPatterns } from "./pattern-types.js";
|
|
7
7
|
import type { PathHelpers } from "./path-helper-types.js";
|
|
8
8
|
import type { ExtractRoutes, ExtractResponses } from "./type-extraction.js";
|
|
9
9
|
import { createPathHelper, attachPathResponseTags } from "./path-helper.js";
|
|
@@ -34,9 +34,6 @@ export function urls<
|
|
|
34
34
|
>(
|
|
35
35
|
builder: (helpers: PathHelpers<TEnv>) => TItems,
|
|
36
36
|
): UrlPatterns<TEnv, ExtractRoutes<TItems>, ExtractResponses<TItems>> {
|
|
37
|
-
// Collect path definitions during build
|
|
38
|
-
const definitions: PathDefinition[] = [];
|
|
39
|
-
|
|
40
37
|
// Create the handler function that will be called by the router
|
|
41
38
|
const handler = () => {
|
|
42
39
|
invariant(
|
|
@@ -82,7 +79,6 @@ export function urls<
|
|
|
82
79
|
// trailingSlash config is populated when handler() runs
|
|
83
80
|
// We expose it via a getter that reads from the context after handler execution
|
|
84
81
|
return {
|
|
85
|
-
definitions,
|
|
86
82
|
handler,
|
|
87
83
|
get trailingSlash() {
|
|
88
84
|
// Get the trailingSlash map from the current context
|