@emeryld/rrroutes-contract 2.5.9 → 2.5.11
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/core/routesV3.builder.d.ts +5 -5
- package/dist/core/routesV3.core.d.ts +8 -3
- package/dist/index.cjs +52 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +52 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ZodObject, ZodType } from 'zod';
|
|
1
|
+
import { z, ZodObject, ZodType } from 'zod';
|
|
2
2
|
import { AnyLeafLowProfile, Append, AugmentLeaves, FeedQueryField, HttpMethod, Leaf, LowProfileCfg, Merge, MergeArray, MethodCfg, NodeCfg, OutputField, Prettify } from './routesV3.core';
|
|
3
3
|
type ParamZod<Name extends string, S extends ZodType> = ZodObject<{
|
|
4
4
|
[K in Name]: S;
|
|
@@ -28,9 +28,9 @@ type EffectiveCfgWithDefaults<C extends MethodCfg, PS> = Prettify<BaseMethodCfg<
|
|
|
28
28
|
type EffectiveCfg<C extends MethodCfg, PS, I extends NodeCfg> = WithNodeDefaults<C, I> extends infer WithDefaults extends MethodCfg ? EffectiveCfgWithDefaults<WithDefaults, PS> : never;
|
|
29
29
|
type NodeWithoutSchemas<I extends NodeCfg> = Omit<I, 'queryExtensionSchema' | 'outputMetaSchema'>;
|
|
30
30
|
type BuiltLeaf<M extends HttpMethod, Base extends string, I extends NodeCfg, C extends MethodCfg, PS> = Leaf<M, Base, Merge<NodeWithoutSchemas<I>, LowProfileCfg<EffectiveCfg<C, PS, I>>>>;
|
|
31
|
-
type StripParamName<Name extends string> = Name extends `:${infer P}` ? P : Name;
|
|
32
|
-
type NormalizeBaseLiteral<Base extends string, HasParam extends boolean> = HasParam extends true ? Base extends `:${string}` ? Base : Base extends '' ? '' : `:${Base}` : Base;
|
|
33
|
-
type ParamSchemaForBase<Base extends string, Param extends ZodType | undefined> = Param extends ZodType ? ParamZod<StripParamName<Base>, Param> : undefined;
|
|
31
|
+
type StripParamName<Name extends string> = Name extends `:${infer P}` ? P : Name extends `*${infer P}` ? P : Name;
|
|
32
|
+
type NormalizeBaseLiteral<Base extends string, HasParam extends boolean> = HasParam extends true ? Base extends `:${string}` ? Base : Base extends `*${string}` ? Base : Base extends '' ? '' : `:${Base}` : Base;
|
|
33
|
+
type ParamSchemaForBase<Base extends string, Param extends ZodType | undefined> = Param extends ZodType ? Base extends `*${string}` ? ParamZod<StripParamName<Base>, z.ZodArray<Param>> : ParamZod<StripParamName<Base>, Param> : undefined;
|
|
34
34
|
type ResourceBase<Base extends string, Param extends ZodType | undefined> = NormalizeBaseLiteral<Base, Param extends ZodType ? true : false>;
|
|
35
35
|
type NodeQueryExtension<I extends NodeCfg> = I extends {
|
|
36
36
|
queryExtensionSchema?: infer QE;
|
|
@@ -100,7 +100,7 @@ export interface Branch<Base extends string, Acc extends readonly AnyLeafLowProf
|
|
|
100
100
|
* @param inherited Optional node configuration applied to all descendants.
|
|
101
101
|
* @returns Root `Branch` instance used to compose the route tree.
|
|
102
102
|
*/
|
|
103
|
-
export declare function resource<Base extends `/${string}` | `:${string}` | '', I extends NodeCfg = {}, Param extends ZodType | undefined = undefined>(base
|
|
103
|
+
export declare function resource<Base extends `/${string}` | `:${string}` | `*${string}` | '', I extends NodeCfg = {}, Param extends ZodType | undefined = undefined>(base: Base, inherited?: I, idSchema?: Param): Branch<ResourceBase<Base, Param>, readonly [], I, ParamSchemaForBase<Base, Param>>;
|
|
104
104
|
/**
|
|
105
105
|
* Merge two readonly tuples (preserves literal tuple information).
|
|
106
106
|
* @param arr1 First tuple.
|
|
@@ -168,11 +168,13 @@ type NodeOutputMeta<Node> = Node extends {
|
|
|
168
168
|
} ? OM extends ZodType ? OM : undefined : undefined;
|
|
169
169
|
type EffectiveQueryExtensionSchema<Method extends ZodType | undefined, Node extends NodeCfg | undefined> = Method extends ZodType ? Method : NodeQueryExtension<Node>;
|
|
170
170
|
type EffectiveOutputMetaSchema<Method extends ZodType | undefined, Node extends NodeCfg | undefined> = Method extends ZodType ? Method : NodeOutputMeta<Node>;
|
|
171
|
-
type SegmentParams<S extends string> = S extends `:${infer P}` ? P : never;
|
|
171
|
+
type SegmentParams<S extends string> = S extends `:${infer P}` ? P : S extends `*${infer P}` ? P : never;
|
|
172
172
|
type Split<S extends string> = S extends '' ? [] : S extends `${infer A}/${infer B}` ? [A, ...Split<B>] : [S];
|
|
173
173
|
type ExtractParamNames<Path extends string> = SegmentParams<Split<Path>[number]>;
|
|
174
|
+
type SegmentParamRecord<S extends string> = S extends `:${infer P}` ? Record<P, string | number> : S extends `*${infer P}` ? Record<P, (string | number)[]> : {};
|
|
175
|
+
type ParamsFromSegments<Segments extends readonly string[]> = Segments extends [infer Head, ...infer Rest] ? Head extends string ? Rest extends readonly string[] ? SegmentParamRecord<Head> & ParamsFromSegments<Rest> : SegmentParamRecord<Head> : {} : {};
|
|
174
176
|
/** Derive a params object type from a literal route string. */
|
|
175
|
-
export type ExtractParamsFromPath<Path extends string> = ExtractParamNames<Path> extends never ? never :
|
|
177
|
+
export type ExtractParamsFromPath<Path extends string> = ExtractParamNames<Path> extends never ? never : ParamsFromSegments<Split<Path>>;
|
|
176
178
|
/**
|
|
177
179
|
* Interpolate `:params` in a path using the given values.
|
|
178
180
|
* @param path Literal route string containing `:param` segments.
|
|
@@ -195,6 +197,9 @@ type MapKeySegments<Segments extends readonly string[], Params> = Segments exten
|
|
|
195
197
|
ParamValue<Params, Key>
|
|
196
198
|
],
|
|
197
199
|
...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>
|
|
200
|
+
] : A extends `*${infer Key}` ? [
|
|
201
|
+
ParamValue<Params, Key>,
|
|
202
|
+
...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>
|
|
198
203
|
] : [
|
|
199
204
|
A,
|
|
200
205
|
...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>
|
|
@@ -268,7 +273,7 @@ export type ReactNativeFile = {
|
|
|
268
273
|
};
|
|
269
274
|
type UploadFieldValue<F extends FileField> = F['maxCount'] extends 1 ? Blob | File | ReactNativeFile : (Blob | File | ReactNativeFile)[] | FileList;
|
|
270
275
|
type BodyFilesInputFromDefs<Defs extends readonly FileField[]> = Prettify<{
|
|
271
|
-
[F in Defs[number] as `file_${F['name']}`]
|
|
276
|
+
[F in Defs[number] as `file_${F['name']}`]?: UploadFieldValue<F>;
|
|
272
277
|
}>;
|
|
273
278
|
/** Infer request body input shape, augmented with multipart file fields when declared. */
|
|
274
279
|
export type InferBodyInput<L extends AnyLeafLowProfile, Fallback = never> = L['cfg']['bodyFiles'] extends readonly FileField[] ? Prettify<Omit<InferBody<L, Fallback> extends never ? {} : InferBody<L, Fallback>, keyof BodyFilesInputFromDefs<L['cfg']['bodyFiles']>> & BodyFilesInputFromDefs<L['cfg']['bodyFiles']>> : InferBody<L, Fallback>;
|
package/dist/index.cjs
CHANGED
|
@@ -84,11 +84,19 @@ function collectNestedFieldSuggestions(shape, prefix = []) {
|
|
|
84
84
|
}
|
|
85
85
|
function compilePath(path, params) {
|
|
86
86
|
if (!params) return path;
|
|
87
|
-
|
|
87
|
+
const withParams = path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {
|
|
88
88
|
const v = params[k];
|
|
89
89
|
if (v === void 0 || v === null) throw new Error(`Missing param :${k}`);
|
|
90
90
|
return String(v);
|
|
91
91
|
});
|
|
92
|
+
return withParams.replace(/\*([A-Za-z0-9_]+)/g, (_, k) => {
|
|
93
|
+
const v = params[k];
|
|
94
|
+
if (v === void 0 || v === null) throw new Error(`Missing param *${k}`);
|
|
95
|
+
if (!Array.isArray(v)) {
|
|
96
|
+
throw new Error(`Param *${k} must be an array`);
|
|
97
|
+
}
|
|
98
|
+
return v.map((item) => String(item)).join("/");
|
|
99
|
+
});
|
|
92
100
|
}
|
|
93
101
|
function buildCacheKey(args) {
|
|
94
102
|
const segments = args.leaf.path.split("/").filter(Boolean);
|
|
@@ -100,6 +108,15 @@ function buildCacheKey(args) {
|
|
|
100
108
|
keyParts.push([value]);
|
|
101
109
|
continue;
|
|
102
110
|
}
|
|
111
|
+
if (segment.startsWith("*")) {
|
|
112
|
+
const paramName = segment.slice(1);
|
|
113
|
+
const value = args.params && paramName in args.params ? args.params[paramName] : void 0;
|
|
114
|
+
if (value !== void 0 && value !== null && !Array.isArray(value)) {
|
|
115
|
+
throw new Error(`Param *${paramName} must be an array`);
|
|
116
|
+
}
|
|
117
|
+
keyParts.push(value);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
103
120
|
keyParts.push(segment);
|
|
104
121
|
}
|
|
105
122
|
keyParts.push(args.query ?? {});
|
|
@@ -147,7 +164,7 @@ var keyOf = (method, path, encodeSafe) => {
|
|
|
147
164
|
|
|
148
165
|
// src/core/routesV3.builder.ts
|
|
149
166
|
function resource(base, inherited, idSchema) {
|
|
150
|
-
const rawBase = base
|
|
167
|
+
const rawBase = base;
|
|
151
168
|
const normalizedBase = normalizeBaseSegment(
|
|
152
169
|
rawBase,
|
|
153
170
|
Boolean(idSchema)
|
|
@@ -156,6 +173,7 @@ function resource(base, inherited, idSchema) {
|
|
|
156
173
|
const rootParams = createParamSchema(rawBase, idSchema);
|
|
157
174
|
function makeBranch(base2, inherited2, mergedParamsSchema) {
|
|
158
175
|
const stack = [];
|
|
176
|
+
const dynamicLayerMap = /* @__PURE__ */ new Map();
|
|
159
177
|
const currentBase = base2;
|
|
160
178
|
const inheritedCfg = inherited2;
|
|
161
179
|
let currentParamsSchema = mergedParamsSchema;
|
|
@@ -168,6 +186,7 @@ function resource(base, inherited, idSchema) {
|
|
|
168
186
|
node: inheritedCfg,
|
|
169
187
|
cfg: fullCfg
|
|
170
188
|
});
|
|
189
|
+
assertDynamicLayerUniqueness(leaf.path, dynamicLayerMap);
|
|
171
190
|
stack.push(leaf);
|
|
172
191
|
return api;
|
|
173
192
|
}
|
|
@@ -207,6 +226,7 @@ function resource(base, inherited, idSchema) {
|
|
|
207
226
|
path: joinPaths(currentBase, leaf.path),
|
|
208
227
|
cfg: newCfg
|
|
209
228
|
};
|
|
229
|
+
assertDynamicLayerUniqueness(newLeaf.path, dynamicLayerMap);
|
|
210
230
|
stack.push(newLeaf);
|
|
211
231
|
}
|
|
212
232
|
}
|
|
@@ -249,20 +269,28 @@ function normalizeBaseSegment(base, hasParam) {
|
|
|
249
269
|
}
|
|
250
270
|
return base;
|
|
251
271
|
}
|
|
272
|
+
if (base.startsWith("*")) {
|
|
273
|
+
if (!hasParam) {
|
|
274
|
+
throw new Error("resource() requires a non-empty name for id schema");
|
|
275
|
+
}
|
|
276
|
+
return base;
|
|
277
|
+
}
|
|
252
278
|
if (base === "") {
|
|
253
279
|
return base;
|
|
254
280
|
}
|
|
255
|
-
throw new Error('resource() base must start with "/" or "
|
|
281
|
+
throw new Error('resource() base must start with "/", ":", or "*"');
|
|
256
282
|
}
|
|
257
283
|
function createParamSchema(nameSegment, schema) {
|
|
258
284
|
if (!schema) return void 0;
|
|
259
285
|
const segments = nameSegment.split("/").filter(Boolean);
|
|
260
286
|
const rawName = segments.length > 0 ? segments[segments.length - 1] : nameSegment;
|
|
261
|
-
const
|
|
287
|
+
const isWildcard = rawName.startsWith("*");
|
|
288
|
+
const normalized = rawName.startsWith(":") || isWildcard ? rawName.slice(1) : rawName;
|
|
262
289
|
if (!normalized) {
|
|
263
290
|
throw new Error("resource() requires a non-empty name for id schema");
|
|
264
291
|
}
|
|
265
|
-
|
|
292
|
+
const paramSchema = isWildcard ? import_zod2.z.array(schema) : schema;
|
|
293
|
+
return import_zod2.z.object({ [normalized]: paramSchema });
|
|
266
294
|
}
|
|
267
295
|
function joinPaths(parent, child) {
|
|
268
296
|
if (!parent) return child;
|
|
@@ -272,6 +300,25 @@ function joinPaths(parent, child) {
|
|
|
272
300
|
if (!trimmedChild) return trimmedParent;
|
|
273
301
|
return `${trimmedParent}/${trimmedChild}`;
|
|
274
302
|
}
|
|
303
|
+
function assertDynamicLayerUniqueness(path, dynamicLayerMap) {
|
|
304
|
+
const segments = path.split("/").filter(Boolean);
|
|
305
|
+
if (segments.length === 0) return;
|
|
306
|
+
for (let i = 0; i < segments.length; i++) {
|
|
307
|
+
const segment = segments[i];
|
|
308
|
+
if (!segment || segment[0] !== ":" && segment[0] !== "*") continue;
|
|
309
|
+
const parentKey = segments.slice(0, i).join("/");
|
|
310
|
+
const existing = dynamicLayerMap.get(parentKey);
|
|
311
|
+
if (existing && existing !== segment) {
|
|
312
|
+
const parentLabel = parentKey ? `/${parentKey}` : "/";
|
|
313
|
+
throw new Error(
|
|
314
|
+
`[routesV3.builder] Multiple dynamic segments under ${parentLabel}: ${existing} and ${segment}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
if (!existing) {
|
|
318
|
+
dynamicLayerMap.set(parentKey, segment);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
275
322
|
|
|
276
323
|
// src/core/routesV3.finalize.ts
|
|
277
324
|
function finalize(leaves) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/routesV3.builder.ts","../src/core/routesV3.core.ts","../src/core/routesV3.finalize.ts","../src/sockets/socket.index.ts"],"sourcesContent":["export * from './core/routesV3.builder'\nexport * from './core/routesV3.core'\nexport * from './core/routesV3.finalize'\nexport * from './sockets/socket.index'\n","import { z, ZodObject, ZodType } from 'zod'\nimport {\n AnyLeaf,\n AnyLeafLowProfile,\n Append,\n AugmentLeaves,\n buildLowProfileLeaf,\n FeedQueryField,\n HttpMethod,\n Leaf,\n LowProfileCfg,\n Merge,\n MergeArray,\n mergeSchemas,\n MethodCfg,\n NodeCfg,\n OutputField,\n Prettify,\n} from './routesV3.core'\n\ntype ParamZod<Name extends string, S extends ZodType> = ZodObject<{\n [K in Name]: S\n}>\ntype BaseMethodCfg<C extends MethodCfg> = Merge<\n Omit<MethodCfg, 'querySchema' | 'outputSchema' | 'feed'>,\n Omit<C, 'querySchema' | 'outputSchema' | 'feed'>\n>\n\ntype FeedField<C extends MethodCfg> = C['feed'] extends true\n ? { feed: true }\n : { feed?: boolean }\n\ntype NonFeedQueryField<C extends MethodCfg> = C['querySchema'] extends ZodType\n ? { querySchema: C['querySchema'] }\n : { querySchema?: undefined }\n\ntype ParamsField<C extends MethodCfg, PS> = C['paramsSchema'] extends ZodType\n ? { paramsSchema: C['paramsSchema'] }\n : { paramsSchema: PS }\n\ntype EffectiveFeedFields<C extends MethodCfg, PS> = C['feed'] extends true\n ? FeedField<C> & FeedQueryField<C> & OutputField<C> & ParamsField<C, PS>\n : FeedField<C> & NonFeedQueryField<C> & OutputField<C> & ParamsField<C, PS>\n\ntype WithNodeDefaults<C extends MethodCfg, I extends NodeCfg> = Merge<\n C,\n {\n queryExtensionSchema: C['queryExtensionSchema'] extends ZodType\n ? C['queryExtensionSchema']\n : NodeQueryExtension<I>\n outputMetaSchema: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : NodeOutputMeta<I>\n }\n>\n\n// Avoids the previous MethodCfg merge step so TS doesn't have to repeatedly\n// expand the entire MethodCfg intersection for every method call.\ntype EffectiveCfgWithDefaults<C extends MethodCfg, PS> = Prettify<\n BaseMethodCfg<C> & EffectiveFeedFields<C, PS>\n>\n\ntype EffectiveCfg<C extends MethodCfg, PS, I extends NodeCfg> =\n WithNodeDefaults<C, I> extends infer WithDefaults extends MethodCfg\n ? EffectiveCfgWithDefaults<WithDefaults, PS>\n : never\n\ntype NodeWithoutSchemas<I extends NodeCfg> = Omit<\n I,\n 'queryExtensionSchema' | 'outputMetaSchema'\n>\n\ntype BuiltLeaf<\n M extends HttpMethod,\n Base extends string,\n I extends NodeCfg,\n C extends MethodCfg,\n PS,\n> = Leaf<\n M,\n Base,\n Merge<NodeWithoutSchemas<I>, LowProfileCfg<EffectiveCfg<C, PS, I>>>\n>\n\ntype StripParamName<Name extends string> = Name extends `:${infer P}` ? P : Name\ntype NormalizeBaseLiteral<\n Base extends string,\n HasParam extends boolean,\n> = HasParam extends true\n ? Base extends `:${string}`\n ? Base\n : Base extends ''\n ? ''\n : `:${Base}`\n : Base\ntype ParamSchemaForBase<\n Base extends string,\n Param extends ZodType | undefined,\n> = Param extends ZodType ? ParamZod<StripParamName<Base>, Param> : undefined\ntype ResourceBase<\n Base extends string,\n Param extends ZodType | undefined,\n> = NormalizeBaseLiteral<Base, Param extends ZodType ? true : false>\ntype NodeQueryExtension<I extends NodeCfg> = I extends {\n queryExtensionSchema?: infer QE\n}\n ? QE extends ZodType\n ? QE\n : undefined\n : undefined\ntype NodeOutputMeta<I extends NodeCfg> = I extends {\n outputMetaSchema?: infer OE\n}\n ? OE extends ZodType\n ? OE\n : undefined\n : undefined\n\ntype SubResourceCollections = readonly [\n ReadonlyArray<AnyLeafLowProfile>,\n ...ReadonlyArray<AnyLeafLowProfile>[],\n]\n\n// Bundling the branch generics keeps MethodFns<State, Used> from re-instantiating\n// four independent parameters for every chained call, which speeds up inference.\ntype BranchState<\n Base extends string,\n Acc extends readonly AnyLeafLowProfile[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n> = {\n base: Base\n leaves: Acc\n node: I\n params: PS\n}\n\ntype AnyBranchState = BranchState<\n string,\n readonly AnyLeafLowProfile[],\n NodeCfg,\n ZodType | undefined\n>\n\nexport type MergeAugmentedCollections<\n Base extends string,\n Param extends ZodType | undefined,\n Collections extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>,\n Acc extends readonly AnyLeafLowProfile[] = [],\n> = Collections extends readonly [infer First, ...infer Rest]\n ? First extends ReadonlyArray<AnyLeafLowProfile>\n ? Rest extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>\n ? MergeAugmentedCollections<\n Base,\n Param,\n Rest,\n MergeArray<Acc, AugmentLeaves<Base, Param, First>>\n >\n : MergeArray<Acc, AugmentLeaves<Base, Param, First>>\n : MergeAugmentedCollections<\n Base,\n Param,\n Rest extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>\n ? Rest\n : [],\n Acc\n >\n : Acc\n\ntype MethodFns<\n State extends AnyBranchState,\n Used extends HttpMethod | 'sub',\n> = {\n /**\n * Register a GET leaf at the current path.\n */\n get: 'get' extends Used\n ? never\n : <C extends MethodCfg>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<'get', State['base'], State['node'], C, State['params']>\n >,\n State['node'],\n State['params'],\n Used | 'get'\n >\n\n /**\n * Register a POST leaf at the current path.\n */\n post: 'post' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'post',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'post'\n >\n\n /**\n * Register a PUT leaf at the current path.\n */\n put: 'put' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'put',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'put'\n >\n\n /**\n * Register a PATCH leaf at the current path.\n */\n patch: 'patch' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'patch',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'patch'\n >\n\n /**\n * Register a DELETE leaf at the current path.\n */\n delete: 'delete' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'delete',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'delete'\n >\n}\n\n/** Builder surface used by `resource(...)` to accumulate leaves. */\nexport interface Branch<\n Base extends string,\n Acc extends readonly AnyLeafLowProfile[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Used extends HttpMethod | 'sub' = never,\n> extends MethodFns<BranchState<Base, Acc, I, PS>, Used> {\n /**\n * Mount one or more sub-resources that were built via `resource(...)`.\n * Each resource carries its own base path (e.g. `users`, `:userId`, `posts`)\n * so this branch simply prefixes its current `Base` and merges param schemas.\n */\n sub: 'sub' extends Used\n ? never\n : <\n const Collections extends SubResourceCollections,\n NextAcc extends readonly AnyLeafLowProfile[] = MergeArray<\n Acc,\n MergeAugmentedCollections<Base, PS, Collections>\n >,\n >(\n ...collections: Collections\n ) => Branch<Base, NextAcc, I, PS, Used | 'sub'>\n\n /**\n * Finish the branch and return the collected leaves.\n * @returns Readonly tuple of accumulated leaves.\n */\n done(): Readonly<Acc>\n}\n\n/**\n * Start building a resource at the given base path.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Optional node configuration applied to all descendants.\n * @returns Root `Branch` instance used to compose the route tree.\n */\nexport function resource<\n Base extends `/${string}` | `:${string}` | '',\n I extends NodeCfg = {},\n Param extends ZodType | undefined = undefined,\n>(\n base?: Base,\n inherited?: I,\n idSchema?: Param,\n): Branch<\n ResourceBase<Base, Param>,\n readonly [],\n I,\n ParamSchemaForBase<Base, Param>\n> {\n const rawBase = (base ?? '') as Base\n const normalizedBase = normalizeBaseSegment(\n rawBase,\n Boolean(idSchema),\n ) as ResourceBase<Base, Param>\n const rootInherited: I =\n inherited === undefined ? ({} as I) : ({ ...inherited } as I)\n const rootParams = createParamSchema(rawBase, idSchema) as\n | ParamSchemaForBase<Base, Param>\n | undefined\n\n function makeBranch<\n Base2 extends string,\n I2 extends NodeCfg,\n PS2 extends ZodType | undefined,\n >(base2: Base2, inherited2: I2, mergedParamsSchema?: PS2) {\n const stack: AnyLeafLowProfile[] = []\n const currentBase: Base2 = base2\n const inheritedCfg: I2 = inherited2\n let currentParamsSchema: PS2 = mergedParamsSchema as PS2\n\n function add<M extends HttpMethod, C extends MethodCfg>(method: M, cfg: C) {\n const effectiveParamsSchema = (cfg.paramsSchema ??\n currentParamsSchema) as PS2 | undefined\n const fullCfg = (\n effectiveParamsSchema\n ? { ...cfg, paramsSchema: effectiveParamsSchema }\n : { ...cfg }\n ) as MethodCfg\n\n const leaf = buildLowProfileLeaf({\n method,\n path: currentBase as Base2,\n node: inheritedCfg,\n cfg: fullCfg,\n })\n\n stack.push(leaf as AnyLeafLowProfile)\n\n return api\n }\n\n const api: any = {\n /**\n * Mount one or more subtrees built elsewhere.\n * Usage:\n * resource('/api').sub(\n * resource('users').get(...).done(),\n * resource('projects').get(...).done()\n * )\n */\n sub(...collections: readonly AnyLeafLowProfile[][]) {\n if (collections.length === 0) {\n throw new Error('sub() expects at least one resource')\n }\n\n for (const leaves of collections) {\n for (const leafLow of leaves) {\n const leaf = leafLow as unknown as AnyLeaf\n const leafCfg = leaf.cfg as MethodCfg\n const leafParams = leafCfg.paramsSchema as ZodType | undefined\n\n const effectiveParams = mergeSchemas(\n currentParamsSchema as any,\n leafParams,\n )\n\n const newCfg: MethodCfg = {\n ...inheritedCfg,\n ...leafCfg,\n }\n\n if (effectiveParams) {\n newCfg.paramsSchema = effectiveParams\n } else if ('paramsSchema' in newCfg) {\n delete newCfg.paramsSchema\n }\n\n const newLeaf: AnyLeaf = {\n method: leaf.method,\n path: joinPaths(currentBase, leaf.path),\n cfg: newCfg,\n }\n\n stack.push(newLeaf as AnyLeafLowProfile)\n }\n }\n\n return api\n },\n\n // methods (inject current params schema if missing)\n get<C extends MethodCfg>(cfg: C) {\n return add('get', cfg)\n },\n post<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('post', { ...cfg, feed: false })\n },\n put<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('put', { ...cfg, feed: false })\n },\n patch<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('patch', { ...cfg, feed: false })\n },\n delete<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('delete', { ...cfg, feed: false })\n },\n\n done() {\n return stack as readonly AnyLeafLowProfile[]\n },\n }\n\n return api as Branch<Base2, readonly [], I2, PS2>\n }\n\n // Root branch starts with no params schema (PS = undefined)\n return makeBranch(normalizedBase, rootInherited, rootParams)\n}\n\n/**\n * Merge two readonly tuples (preserves literal tuple information).\n * @param arr1 First tuple.\n * @param arr2 Second tuple.\n * @returns New tuple containing values from both inputs.\n */\nexport const mergeArrays = <T extends readonly any[], S extends readonly any[]>(\n arr1: T,\n arr2: S,\n) => {\n return [...arr1, ...arr2] as [...T, ...S]\n}\n\nfunction normalizeBaseSegment(base: string, hasParam: boolean) {\n if (base.startsWith('/')) {\n return base\n }\n if (base.startsWith(':')) {\n if (!hasParam) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n return base\n }\n if (base === '') {\n return base\n }\n throw new Error('resource() base must start with \"/\" or \":\"')\n}\n\nfunction createParamSchema(nameSegment: string, schema?: ZodType) {\n if (!schema) return undefined\n const segments = nameSegment.split('/').filter(Boolean)\n const rawName =\n segments.length > 0 ? segments[segments.length - 1] : nameSegment\n const normalized = rawName.startsWith(':') ? rawName.slice(1) : rawName\n if (!normalized) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n return z.object({ [normalized]: schema } as Record<string, ZodType>)\n}\n\nfunction joinPaths(parent: string, child: string) {\n if (!parent) return child\n if (!child) return parent\n const trimmedParent = parent.endsWith('/')\n ? parent.replace(/\\/+$/, '')\n : parent\n const trimmedChild = child.startsWith('/') ? child.replace(/^\\/+/, '') : child\n if (!trimmedChild) return trimmedParent\n return `${trimmedParent}/${trimmedChild}`\n}\n","import { z, ZodObject, ZodSafeParseResult, ZodType } from 'zod'\n\n/** Supported HTTP verbs for the routes DSL. */\nexport type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'\n\n/** Declarative description of a multipart upload field. */\nexport type FileField = {\n /** Form field name used for uploads. */\n name: string\n /** Maximum number of files accepted for this field. */\n maxCount: number\n}\n\n/** Configuration that applies to an entire branch of the route tree. */\nexport interface NodeCfg {\n /**\n * Feed-specific query schema applied to all descendants unless they override it.\n */\n queryExtensionSchema?: ZodType\n /**\n * Feed meta schema applied to all descendants unless they override it.\n */\n outputMetaSchema?: ZodType\n}\n\nexport type RouteSchema<Output = unknown, Input = unknown> = {\n __out: Output\n __in?: Input\n}\n\ntype RouteSchemaOrUndefined<S extends ZodType | undefined> =\n S extends ZodType<infer Out, infer In> ? RouteSchema<Out, In> : undefined\n\ntype FeedAwareQueryRoute<\n Feed extends boolean,\n Query extends ZodType | undefined,\n Extension extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Feed extends true\n ? ToRouteSchema<\n FeedQueryField<{\n querySchema: Query\n queryExtensionSchema: EffectiveQueryExtensionSchema<Extension, Node>\n }>['querySchema']\n >\n : RouteSchemaOrUndefined<Query>\n\ntype OutputRouteSchema<\n O extends ZodType | undefined,\n Meta extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = ToRouteSchema<\n OutputField<{\n outputSchema: O\n outputMetaSchema: EffectiveOutputMetaSchema<Meta, Node>\n }>['outputSchema']\n>\n\nexport type FeedQueryField<C extends MethodCfg> = {\n querySchema: IntersectZod<\n C['querySchema'] extends ZodObject ? C['querySchema'] : undefined,\n C['queryExtensionSchema']\n >\n}\n\nexport type OutputField<C extends MethodCfg> = C['outputSchema'] extends ZodType\n ? {\n outputSchema: ZodObject<{\n out: C['outputSchema']\n meta: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : z.ZodOptional<z.ZodString>\n }>\n }\n : {\n outputSchema: ZodObject<{\n meta: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : z.ZodOptional<z.ZodString>\n }>\n }\n\nexport type RouteSchemaOutput<Schema extends RouteSchema> = Schema extends {\n __out: infer Out\n}\n ? Out\n : unknown\nexport type RouteSchemaInput<Schema extends RouteSchema> = Schema extends {\n __in?: infer In\n}\n ? In\n : unknown\nexport const lowProfileParse = <T extends RouteSchema>(\n schema: T,\n data: unknown,\n): RouteSchemaOutput<T> => {\n return (schema as any as ZodType).parse(data) as RouteSchemaOutput<T>\n}\n\nexport const lowProfileSafeParse = <T extends RouteSchema>(\n schema: T,\n data: unknown,\n): ZodSafeParseResult<RouteSchemaOutput<T>> => {\n return (schema as any as ZodType).safeParse(data) as ZodSafeParseResult<\n RouteSchemaOutput<T>\n >\n}\n\nexport const routeSchemaParse = <T>(\n routeSchema: RouteSchema<T>,\n): ZodType<T> => {\n return routeSchema as any as ZodType<T>\n}\n\n/**\n * Runtime helper that mirrors the typed merge used by the builder.\n * @param a Previously merged params schema inherited from parent segments.\n * @param b Newly introduced params schema.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nexport function mergeSchemas<A extends ZodType, B extends ZodType>(\n a: A,\n b: B,\n): ZodType\nexport function mergeSchemas<A extends ZodType>(a: A, b: undefined): A\nexport function mergeSchemas<B extends ZodType>(a: undefined, b: B): B\nexport function mergeSchemas(\n a: ZodType | undefined,\n b: ZodType | undefined,\n): ZodType | undefined\nexport function mergeSchemas(a: ZodType | undefined, b: ZodType | undefined) {\n if (a && b) {\n if (a === b) return a\n return z.intersection(a as any, b as any)\n }\n return (a ?? b) as ZodType | undefined\n}\n\nexport function getZodShape(schema: ZodObject) {\n const shapeOrGetter = (schema as any).shape\n ? (schema as any).shape\n : (schema as any)._def?.shape?.()\n if (!shapeOrGetter) return {}\n return typeof shapeOrGetter === 'function'\n ? shapeOrGetter.call(schema)\n : shapeOrGetter\n}\n\nexport function collectNestedFieldSuggestions(\n shape: Record<string, ZodType> | undefined,\n prefix: string[] = [],\n): string[] {\n if (!shape) return []\n const suggestions: string[] = []\n for (const [key, value] of Object.entries(shape)) {\n if (value instanceof z.ZodObject) {\n const nestedShape = getZodShape(value as ZodObject)\n const nestedSuggestions = collectNestedFieldSuggestions(nestedShape, [\n ...prefix,\n key,\n ])\n suggestions.push(\n ...(nestedSuggestions.length\n ? nestedSuggestions\n : [[...prefix, key].join('_')]),\n )\n } else if (prefix.length > 0) {\n suggestions.push([...prefix, key].join('_'))\n }\n }\n return suggestions\n}\n\n// Use the output generic of ZodType instead of z.output<S>\nexport type ToRouteSchema<S> =\n S extends ZodType<infer Out, infer In> ? RouteSchema<Out, In> : S\n\nexport type LowProfileCfg<Cfg extends MethodCfg = MethodCfg> = Prettify<\n Omit<\n Cfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'outputMetaSchema'\n > & {\n bodySchema: ToRouteSchema<Cfg['bodySchema']>\n querySchema: ToRouteSchema<Cfg['querySchema']>\n paramsSchema: ToRouteSchema<Cfg['paramsSchema']>\n outputSchema: ToRouteSchema<Cfg['outputSchema']>\n outputMetaSchema: ToRouteSchema<Cfg['outputMetaSchema']>\n queryExtensionSchema: ToRouteSchema<Cfg['queryExtensionSchema']>\n }\n>\n\n/** Per-method configuration merged with inherited node config. */\nexport type MethodCfg = {\n /** Zod schema describing the request body. */\n bodySchema?: ZodType\n /** Zod schema describing the query string. */\n querySchema?: ZodType\n /** Zod schema describing path params (internal only, set through resource segments). */\n paramsSchema?: ZodType\n /** Zod schema describing the response payload. */\n outputSchema?: ZodType\n /** Multipart upload definitions for the route. */\n bodyFiles?: FileField[]\n /** Marks the route as an infinite feed (enables cursor helpers). */\n feed?: boolean\n /** feed handlers */\n outputMetaSchema?: ZodType\n queryExtensionSchema?: ZodType\n\n /** Optional human-readable description for docs/debugging. */\n description?: string\n /**\n * Short one-line summary for docs list views.\n * Shown in navigation / tables instead of the full description.\n */\n summary?: string\n /**\n * Group name used for navigation sections in docs UIs.\n * e.g. \"Users\", \"Billing\", \"Auth\".\n */\n docsGroup?: string\n /**\n * Tags that can be used to filter / facet in interactive docs.\n * e.g. [\"users\", \"admin\", \"internal\"].\n */\n tags?: string[]\n /**\n * Mark the route as deprecated in docs.\n * Renderers can badge this and/or hide by default.\n */\n deprecated?: boolean\n /**\n * Optional stability information for the route.\n * Renderers can surface this prominently.\n */\n stability?: 'experimental' | 'beta' | 'stable' | 'deprecated'\n /**\n * Hide this route from public docs while keeping it usable in code.\n */\n docsHidden?: boolean\n /**\n * Arbitrary extra metadata for docs renderers.\n * Can be used for auth requirements, rate limits, feature flags, etc.\n */\n docsMeta?: Record<string, unknown>\n}\n\n/** Immutable representation of a single HTTP route in the tree. */\nexport type Leaf<\n M extends HttpMethod,\n P extends string,\n C extends MethodCfg,\n> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>\n}\n\n/** Convenience union covering all generated leaves. */\nexport type AnyLeaf = Leaf<HttpMethod, string, MethodCfg>\n\n/** Merge two object types while keeping nice IntelliSense output. */\nexport type Merge<A, B> = Prettify<Omit<A, keyof B> & B>\n\n/** Append a new element to a readonly tuple type. */\nexport type Append<T extends readonly unknown[], X> = [...T, X]\n\n/** Concatenate two readonly tuple types. */\nexport type MergeArray<\n A extends readonly unknown[],\n B extends readonly unknown[],\n> = [...A, ...B]\n\ntype TrimTrailingSlash<S extends string> = S extends `${infer R}/`\n ? TrimTrailingSlash<R>\n : S\ntype TrimLeadingSlash<S extends string> = S extends `/${infer R}`\n ? TrimLeadingSlash<R>\n : S\n\ntype NormalizedBase<Base extends string> = TrimTrailingSlash<Base>\ntype NormalizedChild<Child extends string> = TrimLeadingSlash<Child>\n\nexport type JoinPath<Base extends string, Child extends string> =\n NormalizedBase<Base> extends ''\n ? Child\n : NormalizedChild<Child> extends ''\n ? NormalizedBase<Base>\n : `${NormalizedBase<Base>}/${NormalizedChild<Child>}`\nexport type IntersectZod<\n A extends ZodType | undefined,\n B extends ZodType | undefined,\n> = B extends ZodType\n ? A extends ZodType\n ? z.ZodIntersection<A, B>\n : B\n : A extends ZodType\n ? A\n : undefined\n\ntype MergeRouteSchemas<\n Existing extends RouteSchema | undefined,\n Parent extends ZodType | undefined,\n> =\n Existing extends RouteSchema<infer ExistingOut>\n ? Parent extends ZodType<infer ParentOut>\n ? RouteSchema<ExistingOut & ParentOut>\n : Existing\n : Parent extends ZodType<infer ParentOut>\n ? RouteSchema<ParentOut>\n : undefined\n\ntype AugmentedCfg<\n Cfg extends MethodCfgLowProfile,\n Param extends ZodType | undefined,\n> = Prettify<\n Omit<Cfg, 'paramsSchema'> & {\n paramsSchema: MergeRouteSchemas<Cfg['paramsSchema'], Param>\n }\n>\n\n// Tuple-mapped helper avoids recursive instantiation while re-basing nested leaves.\ntype RebasedLeaf<\n P extends string,\n Param extends ZodType | undefined,\n L extends LeafLowProfile,\n> = LeafLowProfile<\n L['method'],\n JoinPath<P, L['path']>,\n AugmentedCfg<L['cfg'], Param>\n>\n\nexport type AugmentLeaves<\n P extends string,\n Param extends ZodType | undefined,\n R extends readonly LeafLowProfile[],\n> = {\n [K in keyof R]: R[K] extends LeafLowProfile\n ? RebasedLeaf<P, Param, R[K]>\n : never\n}\n\ntype NodeQueryExtension<Node> = Node extends {\n queryExtensionSchema?: infer QE\n}\n ? QE extends ZodType\n ? QE\n : undefined\n : undefined\n\ntype NodeOutputMeta<Node> = Node extends { outputMetaSchema?: infer OM }\n ? OM extends ZodType\n ? OM\n : undefined\n : undefined\n\ntype EffectiveQueryExtensionSchema<\n Method extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Method extends ZodType ? Method : NodeQueryExtension<Node>\n\ntype EffectiveOutputMetaSchema<\n Method extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Method extends ZodType ? Method : NodeOutputMeta<Node>\n// helpers (optional)\ntype SegmentParams<S extends string> = S extends `:${infer P}` ? P : never\ntype Split<S extends string> = S extends ''\n ? []\n : S extends `${infer A}/${infer B}`\n ? [A, ...Split<B>]\n : [S]\ntype ExtractParamNames<Path extends string> = SegmentParams<Split<Path>[number]>\n\n/** Derive a params object type from a literal route string. */\nexport type ExtractParamsFromPath<Path extends string> =\n ExtractParamNames<Path> extends never\n ? never\n : Record<ExtractParamNames<Path>, string | number>\n\n/**\n * Interpolate `:params` in a path using the given values.\n * @param path Literal route string containing `:param` segments.\n * @param params Object of parameter values to interpolate.\n * @returns Path string with parameters substituted.\n */\nexport function compilePath<Path extends string>(\n path: Path,\n params: ExtractParamsFromPath<Path>,\n) {\n if (!params) return path\n return path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k]\n if (v === undefined || v === null) throw new Error(`Missing param :${k}`)\n return String(v)\n })\n}\n\n/**\n * Build a deterministic cache key for the given leaf.\n * The key matches the shape consumed by React Query helpers.\n * @param args.leaf Leaf describing the endpoint.\n * @param args.params Optional params used to build the path.\n * @param args.query Optional query payload.\n * @returns Tuple suitable for React Query cache keys.\n */\ntype SplitPath<P extends string> = P extends ''\n ? []\n : P extends `${infer A}/${infer B}`\n ? [A, ...SplitPath<B>]\n : [P]\ntype ParamValue<Params, Key extends string> =\n Params extends Record<Key, infer V> ? V : undefined\ntype MapKeySegments<\n Segments extends readonly string[],\n Params,\n> = Segments extends [infer A, ...infer Rest]\n ? A extends string\n ? A extends `:${infer Key}`\n ? [\n [ParamValue<Params, Key>],\n ...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>,\n ]\n : [\n A,\n ...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>,\n ]\n : []\n : []\ntype CacheKeyForLeaf<L extends AnyLeafLowProfile> = readonly [\n L['method'],\n ...MapKeySegments<SplitPath<L['path']>, ExtractParamsFromPath<L['path']>>,\n InferQuery<L> extends never ? {} : InferQuery<L>,\n]\nexport function buildCacheKey<L extends AnyLeafLowProfile>(args: {\n leaf: L\n params?: ExtractParamsFromPath<L['path']>\n query?: InferQuery<L>\n}): CacheKeyForLeaf<L> {\n const segments = args.leaf.path.split('/').filter(Boolean) as SplitPath<\n L['path']\n >\n const keyParts: unknown[] = [args.leaf.method]\n for (const segment of segments) {\n if (segment.startsWith(':')) {\n const paramName = segment.slice(1)\n const value =\n args.params && paramName in args.params\n ? (args.params as Record<string, unknown>)[paramName]\n : undefined\n keyParts.push([value])\n continue\n }\n keyParts.push(segment)\n }\n keyParts.push(args.query ?? {})\n return keyParts as unknown as CacheKeyForLeaf<L>\n}\n\n/** Definition-time method config (for clarity). */\nexport type MethodCfgDef = MethodCfg\n\n/** Low-profile method config where schemas carry a phantom __out like SocketSchema. */\nexport type MethodCfgLowProfile = Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n> & {\n bodySchema?: RouteSchema\n querySchema?: RouteSchema\n paramsSchema?: RouteSchema\n outputSchema?: RouteSchema\n outputMetaSchema?: RouteSchema\n queryExtensionSchema?: RouteSchema\n bodyFiles?: FileField[]\n}\nexport type AnyLeafLowProfile = LeafLowProfile<\n HttpMethod,\n string,\n MethodCfgLowProfile\n>\n\n// keep the overload as you have it\nexport function buildLowProfileLeaf<\n const M extends HttpMethod,\n const Path extends string,\n const Node extends NodeCfg | undefined = undefined,\n const O extends ZodType | undefined = undefined,\n const P extends ZodType | undefined = undefined,\n const Q extends ZodType | undefined = undefined,\n const B extends ZodType | undefined = undefined,\n const FO extends ZodType | undefined = undefined,\n const FQ extends ZodType | undefined = undefined,\n const BF extends FileField[] = [],\n const Feed extends boolean = false,\n>(leaf: {\n method: M\n path: Path\n node?: Node\n cfg: Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'feed'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n > & {\n feed?: Feed\n bodySchema?: B\n querySchema?: Q\n paramsSchema?: P\n outputSchema?: O\n outputMetaSchema?: FO\n queryExtensionSchema?: FQ\n bodyFiles?: BF\n }\n}): LeafLowProfile<\n M,\n Path,\n Prettify<\n Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'feed'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n > & {\n feed: Feed\n bodySchema: RouteSchemaOrUndefined<B>\n querySchema: FeedAwareQueryRoute<Feed, Q, FQ, Node>\n paramsSchema: RouteSchemaOrUndefined<P>\n outputSchema: OutputRouteSchema<O, FO, Node>\n outputMetaSchema: RouteSchemaOrUndefined<\n EffectiveOutputMetaSchema<FO, Node>\n >\n queryExtensionSchema: RouteSchemaOrUndefined<\n EffectiveQueryExtensionSchema<FQ, Node>\n >\n bodyFiles: BF\n }\n >\n>\n\n// implementation: NO z.infer here\nexport function buildLowProfileLeaf(leaf: any): any {\n const nodeCfg = (leaf.node ?? {}) as NodeCfg\n const mergedCfg = { ...nodeCfg, ...leaf.cfg }\n const effectiveQuerySchema =\n mergedCfg.feed === true\n ? mergeSchemas(mergedCfg.querySchema, mergedCfg.queryExtensionSchema)\n : mergedCfg.querySchema\n const effectiveOutputSchema = mergedCfg.outputSchema\n ? z.object({\n out: mergedCfg.outputSchema,\n meta: mergedCfg.outputMetaSchema ?? z.string().optional(),\n })\n : z.object({\n meta: mergedCfg.outputMetaSchema ?? z.string().optional(),\n })\n\n // ensure query schema is not nested\n if (mergedCfg.feed === true && effectiveQuerySchema instanceof ZodObject) {\n const shape = getZodShape(effectiveQuerySchema as ZodObject)\n const nestedFieldSuggestions = collectNestedFieldSuggestions(shape)\n if (nestedFieldSuggestions.length > 0) {\n console.warn(\n `[routesV3.builder] Warning: feed query schema for ${leaf.method.toUpperCase()} ${leaf.path} contains nested objects. Consider using flat keys instead: ${nestedFieldSuggestions.join(\n ', ',\n )}`,\n )\n }\n }\n return {\n method: leaf.method,\n path: leaf.path,\n cfg: {\n ...mergedCfg,\n bodySchema: mergedCfg.bodySchema as RouteSchema<any> | undefined,\n querySchema: effectiveQuerySchema as RouteSchema<any> | undefined,\n paramsSchema: mergedCfg.paramsSchema as RouteSchema<any> | undefined,\n outputSchema: effectiveOutputSchema as any as\n | RouteSchema<any>\n | undefined,\n outputMetaSchema: mergedCfg.outputMetaSchema as\n | RouteSchema<any>\n | undefined,\n queryExtensionSchema: mergedCfg.queryExtensionSchema as\n | RouteSchema<any>\n | undefined,\n },\n }\n}\n\nexport const keyOf = (\n method: HttpMethod,\n path: string,\n encodeSafe?: boolean,\n) => {\n const key = `${method.toUpperCase()} ${path}` as const\n return encodeSafe ? encodeURIComponent(key) : key\n}\n\nexport type LeafLowProfile<\n M extends HttpMethod = HttpMethod,\n P extends string = string,\n C extends MethodCfgLowProfile = MethodCfgLowProfile,\n> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>\n}\n\n/** Infer params either from the explicit params schema or from the path literal. */\nexport type InferParams<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['paramsSchema'] extends RouteSchema<infer POut>\n ? POut\n : L['cfg']['paramsSchema'] extends ZodType<infer POut>\n ? POut\n : Fallback\n\n/** Infer query shape from a Zod schema when present. */\nexport type InferQuery<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['querySchema'] extends RouteSchema<infer QOut>\n ? QOut\n : L['cfg']['querySchema'] extends ZodType<infer QOut>\n ? QOut\n : Fallback\n\n/** Infer request body shape from a Zod schema when present. */\nexport type InferBody<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['bodySchema'] extends RouteSchema<infer BOut>\n ? BOut\n : L['cfg']['bodySchema'] extends ZodType<infer BOut>\n ? BOut\n : Fallback\n\nexport type ReactNativeFile = {\n uri: string // e.g. file:///...\n name: string // e.g. avatar.jpg\n type: string // e.g. image/jpeg\n}\ntype UploadFieldValue<F extends FileField> = F['maxCount'] extends 1\n ? Blob | File | ReactNativeFile\n : (Blob | File | ReactNativeFile)[] | FileList\n\ntype BodyFilesInputFromDefs<Defs extends readonly FileField[]> = Prettify<{\n [F in Defs[number] as `file_${F['name']}`]: UploadFieldValue<F>\n}>\n\n/** Infer request body input shape, augmented with multipart file fields when declared. */\nexport type InferBodyInput<\n L extends AnyLeafLowProfile,\n Fallback = never,\n> = L['cfg']['bodyFiles'] extends readonly FileField[]\n ? Prettify<\n Omit<\n InferBody<L, Fallback> extends never ? {} : InferBody<L, Fallback>,\n keyof BodyFilesInputFromDefs<L['cfg']['bodyFiles']>\n > &\n BodyFilesInputFromDefs<L['cfg']['bodyFiles']>\n >\n : InferBody<L, Fallback>\n\n/** Infer handler output shape from a Zod schema. Defaults to unknown. */\ntype InferMetaFromCfg<L extends AnyLeafLowProfile> =\n L['cfg']['outputMetaSchema'] extends RouteSchema<infer O>\n ? O\n : L['cfg']['outputMetaSchema'] extends ZodType<infer O>\n ? O\n : string | undefined\n\ntype ResolvedMeta<L extends AnyLeafLowProfile, M> = [M] extends\n | [never]\n | [unknown]\n ? InferMetaFromCfg<L>\n : M\n\ntype ApplyMetaFallback<L extends AnyLeafLowProfile, O> = O extends {\n meta: infer M\n}\n ? (undefined extends ResolvedMeta<L, M>\n ? { meta?: ResolvedMeta<L, M> }\n : { meta: ResolvedMeta<L, M> }) &\n Omit<O, 'meta'>\n : O\n\nexport type InferOutput<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['outputSchema'] extends RouteSchema<infer O>\n ? ApplyMetaFallback<L, O>\n : L['cfg']['outputSchema'] extends ZodType<infer O>\n ? ApplyMetaFallback<L, O>\n : Fallback\n\n/** Render a type as if it were a simple object literal. */\nexport type Prettify<T> = { [K in keyof T]: T[K] }\n","import { AnyLeafLowProfile, HttpMethod, Prettify } from './routesV3.core'\n\n/** Build the key type for a leaf — distributive so method/path stay paired. */\nexport type KeyOf<L extends AnyLeafLowProfile> = L extends AnyLeafLowProfile\n ? `${Uppercase<L['method']>} ${L['path']}`\n : never\n\n// From a tuple of leaves, get the union of keys (pairwise, no cartesian blow-up)\ntype KeysOf<Leaves extends readonly AnyLeafLowProfile[]> = KeyOf<Leaves[number]>\n\n// Parse method & path out of a key\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}`\n ? Lowercase<M>\n : never\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}`\n ? P\n : never\n\n// Given Leaves and a Key, pick the exact leaf that matches method+path\ntype LeafForKey<\n Leaves extends readonly AnyLeafLowProfile[],\n K extends string,\n> = Extract<\n Leaves[number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>\n\ntype FilterRoute<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n Acc extends readonly AnyLeafLowProfile[] = [],\n> = T extends readonly [\n infer First extends AnyLeafLowProfile,\n ...infer Rest extends AnyLeafLowProfile[],\n]\n ? First extends { path: `${string}${F}${string}` }\n ? FilterRoute<Rest, F, [...Acc, First]>\n : FilterRoute<Rest, F, Acc>\n : Acc\n\ntype UpperCase<S extends string> = S extends `${infer First}${infer Rest}`\n ? `${Uppercase<First>}${UpperCase<Rest>}`\n : S\n\ntype Routes<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n> = FilterRoute<T, F>\ntype ByKey<\n T extends readonly AnyLeafLowProfile[],\n Acc extends Record<string, AnyLeafLowProfile> = {},\n> = T extends readonly [\n infer First extends AnyLeafLowProfile,\n ...infer Rest extends AnyLeafLowProfile[],\n]\n ? ByKey<\n Rest,\n Acc & { [K in `${UpperCase<First['method']>} ${First['path']}`]: First }\n >\n : Acc\n\n/**\n * Convenience helper for extracting a subset of routes by path fragment.\n * @param T Tuple of leaves produced by the DSL.\n * @param F String fragment to match against the route path.\n */\nexport type SubsetRoutes<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n> = {\n byKey: ByKey<Routes<T, F>>\n all: Routes<T, F>\n}\n\n// In the same module as finalize\n\nexport interface FinalizedRegistry<L extends readonly AnyLeafLowProfile[]> {\n all: L\n byKey: { [K in KeysOf<L>]: Prettify<LeafForKey<L, K>> }\n log(logger: { system: (...args: unknown[]) => void }): void\n}\n\n// Optionally keep this alias if you like the name\nexport type Registry<L extends readonly AnyLeafLowProfile[]> =\n FinalizedRegistry<L>\n\nexport function finalize<const L extends readonly AnyLeafLowProfile[]>(\n leaves: L,\n): FinalizedRegistry<L> {\n type Keys = KeysOf<L>\n type MapByKey = { [K in Keys]: Prettify<LeafForKey<L, K>> }\n\n const byKey = Object.fromEntries(\n leaves.map((l) => [`${l.method.toUpperCase()} ${l.path}`, l] as const),\n ) as unknown as MapByKey\n\n const log = (logger: { system: (...args: unknown[]) => void }) => {\n logger.system('Finalized routes:')\n ;(Object.keys(byKey) as Keys[]).forEach((k) => {\n const leaf = byKey[k]\n logger.system(`- ${k}`)\n })\n }\n\n return { all: leaves, byKey, log }\n}\n","// socket.index.ts (shared client/server)\n\nimport { z } from 'zod'\n\nexport type SocketSchema<Output = unknown> = z.ZodType & {\n __out: Output\n}\n\nexport type SocketSchemaOutput<Schema extends z.ZodTypeAny> = Schema extends {\n __out: infer Out\n}\n ? Out\n : z.output<Schema>\n\nexport type SocketEvent<Out = unknown> = {\n message: z.ZodTypeAny\n /** phantom field, only for typing; not meant to be used at runtime */\n __out: Out\n}\n\nexport type EventMap = Record<string, SocketEvent<any>>\n\n/**\n * System event names – shared so server and client\n * don't drift on naming.\n */\nexport type SysEventName =\n | 'sys:connect'\n | 'sys:disconnect'\n | 'sys:reconnect'\n | 'sys:connect_error'\n | 'sys:ping'\n | 'sys:pong'\n | 'sys:room_join'\n | 'sys:room_leave'\nexport function defineSocketEvents<\n const C extends SocketConnectionConfig,\n const Schemas extends Record<\n string,\n {\n message: z.ZodTypeAny\n }\n >,\n>(\n config: C,\n events: Schemas,\n): {\n config: {\n [K in keyof C]: SocketSchema<z.output<C[K]>>\n }\n events: {\n [K in keyof Schemas]: SocketEvent<z.output<Schemas[K]['message']>>\n }\n}\n\nexport function defineSocketEvents(config: any, events: any) {\n return { config, events }\n}\n\nexport type Payload<T extends EventMap, K extends keyof T> =\n T[K] extends SocketEvent<infer Out> ? Out : never\nexport type SocketConnectionConfig = {\n joinMetaMessage: z.ZodTypeAny\n leaveMetaMessage: z.ZodTypeAny\n pingPayload: z.ZodTypeAny\n pongPayload: z.ZodTypeAny\n}\n\nexport type SocketConnectionConfigOutput = {\n joinMetaMessage: SocketSchema<any>\n leaveMetaMessage: SocketSchema<any>\n pingPayload: SocketSchema<any>\n pongPayload: SocketSchema<any>\n}\n"],"mappings":"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","names":["import_zod"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/routesV3.builder.ts","../src/core/routesV3.core.ts","../src/core/routesV3.finalize.ts","../src/sockets/socket.index.ts"],"sourcesContent":["export * from './core/routesV3.builder'\nexport * from './core/routesV3.core'\nexport * from './core/routesV3.finalize'\nexport * from './sockets/socket.index'\n","import { z, ZodObject, ZodType } from 'zod'\nimport {\n AnyLeaf,\n AnyLeafLowProfile,\n Append,\n AugmentLeaves,\n buildLowProfileLeaf,\n FeedQueryField,\n HttpMethod,\n Leaf,\n LowProfileCfg,\n Merge,\n MergeArray,\n mergeSchemas,\n MethodCfg,\n NodeCfg,\n OutputField,\n Prettify,\n} from './routesV3.core'\n\ntype ParamZod<Name extends string, S extends ZodType> = ZodObject<{\n [K in Name]: S\n}>\ntype BaseMethodCfg<C extends MethodCfg> = Merge<\n Omit<MethodCfg, 'querySchema' | 'outputSchema' | 'feed'>,\n Omit<C, 'querySchema' | 'outputSchema' | 'feed'>\n>\n\ntype FeedField<C extends MethodCfg> = C['feed'] extends true\n ? { feed: true }\n : { feed?: boolean }\n\ntype NonFeedQueryField<C extends MethodCfg> = C['querySchema'] extends ZodType\n ? { querySchema: C['querySchema'] }\n : { querySchema?: undefined }\n\ntype ParamsField<C extends MethodCfg, PS> = C['paramsSchema'] extends ZodType\n ? { paramsSchema: C['paramsSchema'] }\n : { paramsSchema: PS }\n\ntype EffectiveFeedFields<C extends MethodCfg, PS> = C['feed'] extends true\n ? FeedField<C> & FeedQueryField<C> & OutputField<C> & ParamsField<C, PS>\n : FeedField<C> & NonFeedQueryField<C> & OutputField<C> & ParamsField<C, PS>\n\ntype WithNodeDefaults<C extends MethodCfg, I extends NodeCfg> = Merge<\n C,\n {\n queryExtensionSchema: C['queryExtensionSchema'] extends ZodType\n ? C['queryExtensionSchema']\n : NodeQueryExtension<I>\n outputMetaSchema: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : NodeOutputMeta<I>\n }\n>\n\n// Avoids the previous MethodCfg merge step so TS doesn't have to repeatedly\n// expand the entire MethodCfg intersection for every method call.\ntype EffectiveCfgWithDefaults<C extends MethodCfg, PS> = Prettify<\n BaseMethodCfg<C> & EffectiveFeedFields<C, PS>\n>\n\ntype EffectiveCfg<C extends MethodCfg, PS, I extends NodeCfg> =\n WithNodeDefaults<C, I> extends infer WithDefaults extends MethodCfg\n ? EffectiveCfgWithDefaults<WithDefaults, PS>\n : never\n\ntype NodeWithoutSchemas<I extends NodeCfg> = Omit<\n I,\n 'queryExtensionSchema' | 'outputMetaSchema'\n>\n\ntype BuiltLeaf<\n M extends HttpMethod,\n Base extends string,\n I extends NodeCfg,\n C extends MethodCfg,\n PS,\n> = Leaf<\n M,\n Base,\n Merge<NodeWithoutSchemas<I>, LowProfileCfg<EffectiveCfg<C, PS, I>>>\n>\n\ntype StripParamName<Name extends string> = Name extends `:${infer P}`\n ? P\n : Name extends `*${infer P}`\n ? P\n : Name\ntype NormalizeBaseLiteral<\n Base extends string,\n HasParam extends boolean,\n> = HasParam extends true\n ? Base extends `:${string}`\n ? Base\n : Base extends `*${string}`\n ? Base\n : Base extends ''\n ? ''\n : `:${Base}`\n : Base\ntype ParamSchemaForBase<\n Base extends string,\n Param extends ZodType | undefined,\n> = Param extends ZodType\n ? Base extends `*${string}`\n ? ParamZod<StripParamName<Base>, z.ZodArray<Param>>\n : ParamZod<StripParamName<Base>, Param>\n : undefined\ntype ResourceBase<\n Base extends string,\n Param extends ZodType | undefined,\n> = NormalizeBaseLiteral<Base, Param extends ZodType ? true : false>\ntype NodeQueryExtension<I extends NodeCfg> = I extends {\n queryExtensionSchema?: infer QE\n}\n ? QE extends ZodType\n ? QE\n : undefined\n : undefined\ntype NodeOutputMeta<I extends NodeCfg> = I extends {\n outputMetaSchema?: infer OE\n}\n ? OE extends ZodType\n ? OE\n : undefined\n : undefined\n\ntype SubResourceCollections = readonly [\n ReadonlyArray<AnyLeafLowProfile>,\n ...ReadonlyArray<AnyLeafLowProfile>[],\n]\n\n// Bundling the branch generics keeps MethodFns<State, Used> from re-instantiating\n// four independent parameters for every chained call, which speeds up inference.\ntype BranchState<\n Base extends string,\n Acc extends readonly AnyLeafLowProfile[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n> = {\n base: Base\n leaves: Acc\n node: I\n params: PS\n}\n\ntype AnyBranchState = BranchState<\n string,\n readonly AnyLeafLowProfile[],\n NodeCfg,\n ZodType | undefined\n>\n\nexport type MergeAugmentedCollections<\n Base extends string,\n Param extends ZodType | undefined,\n Collections extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>,\n Acc extends readonly AnyLeafLowProfile[] = [],\n> = Collections extends readonly [infer First, ...infer Rest]\n ? First extends ReadonlyArray<AnyLeafLowProfile>\n ? Rest extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>\n ? MergeAugmentedCollections<\n Base,\n Param,\n Rest,\n MergeArray<Acc, AugmentLeaves<Base, Param, First>>\n >\n : MergeArray<Acc, AugmentLeaves<Base, Param, First>>\n : MergeAugmentedCollections<\n Base,\n Param,\n Rest extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>\n ? Rest\n : [],\n Acc\n >\n : Acc\n\ntype MethodFns<\n State extends AnyBranchState,\n Used extends HttpMethod | 'sub',\n> = {\n /**\n * Register a GET leaf at the current path.\n */\n get: 'get' extends Used\n ? never\n : <C extends MethodCfg>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<'get', State['base'], State['node'], C, State['params']>\n >,\n State['node'],\n State['params'],\n Used | 'get'\n >\n\n /**\n * Register a POST leaf at the current path.\n */\n post: 'post' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'post',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'post'\n >\n\n /**\n * Register a PUT leaf at the current path.\n */\n put: 'put' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'put',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'put'\n >\n\n /**\n * Register a PATCH leaf at the current path.\n */\n patch: 'patch' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'patch',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'patch'\n >\n\n /**\n * Register a DELETE leaf at the current path.\n */\n delete: 'delete' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'delete',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'delete'\n >\n}\n\n/** Builder surface used by `resource(...)` to accumulate leaves. */\nexport interface Branch<\n Base extends string,\n Acc extends readonly AnyLeafLowProfile[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Used extends HttpMethod | 'sub' = never,\n> extends MethodFns<BranchState<Base, Acc, I, PS>, Used> {\n /**\n * Mount one or more sub-resources that were built via `resource(...)`.\n * Each resource carries its own base path (e.g. `users`, `:userId`, `posts`)\n * so this branch simply prefixes its current `Base` and merges param schemas.\n */\n sub: 'sub' extends Used\n ? never\n : <\n const Collections extends SubResourceCollections,\n NextAcc extends readonly AnyLeafLowProfile[] = MergeArray<\n Acc,\n MergeAugmentedCollections<Base, PS, Collections>\n >,\n >(\n ...collections: Collections\n ) => Branch<Base, NextAcc, I, PS, Used | 'sub'>\n\n /**\n * Finish the branch and return the collected leaves.\n * @returns Readonly tuple of accumulated leaves.\n */\n done(): Readonly<Acc>\n}\n\n/**\n * Start building a resource at the given base path.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Optional node configuration applied to all descendants.\n * @returns Root `Branch` instance used to compose the route tree.\n */\nexport function resource<\n Base extends `/${string}` | `:${string}` | `*${string}` | '',\n I extends NodeCfg = {},\n Param extends ZodType | undefined = undefined,\n>(\n base: Base,\n inherited?: I,\n idSchema?: Param,\n): Branch<\n ResourceBase<Base, Param>,\n readonly [],\n I,\n ParamSchemaForBase<Base, Param>\n> {\n const rawBase = base as Base\n const normalizedBase = normalizeBaseSegment(\n rawBase,\n Boolean(idSchema),\n ) as ResourceBase<Base, Param>\n const rootInherited: I =\n inherited === undefined ? ({} as I) : ({ ...inherited } as I)\n const rootParams = createParamSchema(rawBase, idSchema) as\n | ParamSchemaForBase<Base, Param>\n | undefined\n\n function makeBranch<\n Base2 extends string,\n I2 extends NodeCfg,\n PS2 extends ZodType | undefined,\n >(base2: Base2, inherited2: I2, mergedParamsSchema?: PS2) {\n const stack: AnyLeafLowProfile[] = []\n const dynamicLayerMap = new Map<string, string>()\n const currentBase: Base2 = base2\n const inheritedCfg: I2 = inherited2\n let currentParamsSchema: PS2 = mergedParamsSchema as PS2\n\n function add<M extends HttpMethod, C extends MethodCfg>(method: M, cfg: C) {\n const effectiveParamsSchema = (cfg.paramsSchema ??\n currentParamsSchema) as PS2 | undefined\n const fullCfg = (\n effectiveParamsSchema\n ? { ...cfg, paramsSchema: effectiveParamsSchema }\n : { ...cfg }\n ) as MethodCfg\n\n const leaf = buildLowProfileLeaf({\n method,\n path: currentBase as Base2,\n node: inheritedCfg,\n cfg: fullCfg,\n })\n\n assertDynamicLayerUniqueness(leaf.path, dynamicLayerMap)\n stack.push(leaf as AnyLeafLowProfile)\n\n return api\n }\n\n const api: any = {\n /**\n * Mount one or more subtrees built elsewhere.\n * Usage:\n * resource('/api').sub(\n * resource('users').get(...).done(),\n * resource('projects').get(...).done()\n * )\n */\n sub(...collections: readonly AnyLeafLowProfile[][]) {\n if (collections.length === 0) {\n throw new Error('sub() expects at least one resource')\n }\n\n for (const leaves of collections) {\n for (const leafLow of leaves) {\n const leaf = leafLow as unknown as AnyLeaf\n const leafCfg = leaf.cfg as MethodCfg\n const leafParams = leafCfg.paramsSchema as ZodType | undefined\n\n const effectiveParams = mergeSchemas(\n currentParamsSchema as any,\n leafParams,\n )\n\n const newCfg: MethodCfg = {\n ...inheritedCfg,\n ...leafCfg,\n }\n\n if (effectiveParams) {\n newCfg.paramsSchema = effectiveParams\n } else if ('paramsSchema' in newCfg) {\n delete newCfg.paramsSchema\n }\n\n const newLeaf: AnyLeaf = {\n method: leaf.method,\n path: joinPaths(currentBase, leaf.path),\n cfg: newCfg,\n }\n\n assertDynamicLayerUniqueness(newLeaf.path, dynamicLayerMap)\n stack.push(newLeaf as AnyLeafLowProfile)\n }\n }\n\n return api\n },\n\n // methods (inject current params schema if missing)\n get<C extends MethodCfg>(cfg: C) {\n return add('get', cfg)\n },\n post<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('post', { ...cfg, feed: false })\n },\n put<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('put', { ...cfg, feed: false })\n },\n patch<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('patch', { ...cfg, feed: false })\n },\n delete<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('delete', { ...cfg, feed: false })\n },\n\n done() {\n return stack as readonly AnyLeafLowProfile[]\n },\n }\n\n return api as Branch<Base2, readonly [], I2, PS2>\n }\n\n // Root branch starts with no params schema (PS = undefined)\n return makeBranch(normalizedBase, rootInherited, rootParams)\n}\n\n/**\n * Merge two readonly tuples (preserves literal tuple information).\n * @param arr1 First tuple.\n * @param arr2 Second tuple.\n * @returns New tuple containing values from both inputs.\n */\nexport const mergeArrays = <T extends readonly any[], S extends readonly any[]>(\n arr1: T,\n arr2: S,\n) => {\n return [...arr1, ...arr2] as [...T, ...S]\n}\n\nfunction normalizeBaseSegment(base: string, hasParam: boolean) {\n if (base.startsWith('/')) {\n return base\n }\n if (base.startsWith(':')) {\n if (!hasParam) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n return base\n }\n if (base.startsWith('*')) {\n if (!hasParam) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n return base\n }\n if (base === '') {\n return base\n }\n throw new Error('resource() base must start with \"/\", \":\", or \"*\"')\n}\n\nfunction createParamSchema(nameSegment: string, schema?: ZodType) {\n if (!schema) return undefined\n const segments = nameSegment.split('/').filter(Boolean)\n const rawName =\n segments.length > 0 ? segments[segments.length - 1] : nameSegment\n const isWildcard = rawName.startsWith('*')\n const normalized = rawName.startsWith(':') || isWildcard ? rawName.slice(1) : rawName\n if (!normalized) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n const paramSchema = isWildcard ? z.array(schema) : schema\n return z.object({ [normalized]: paramSchema } as Record<string, ZodType>)\n}\n\nfunction joinPaths(parent: string, child: string) {\n if (!parent) return child\n if (!child) return parent\n const trimmedParent = parent.endsWith('/')\n ? parent.replace(/\\/+$/, '')\n : parent\n const trimmedChild = child.startsWith('/') ? child.replace(/^\\/+/, '') : child\n if (!trimmedChild) return trimmedParent\n return `${trimmedParent}/${trimmedChild}`\n}\n\nfunction assertDynamicLayerUniqueness(\n path: string,\n dynamicLayerMap: Map<string, string>,\n) {\n const segments = path.split('/').filter(Boolean)\n if (segments.length === 0) return\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n if (!segment || (segment[0] !== ':' && segment[0] !== '*')) continue\n\n const parentKey = segments.slice(0, i).join('/')\n const existing = dynamicLayerMap.get(parentKey)\n if (existing && existing !== segment) {\n const parentLabel = parentKey ? `/${parentKey}` : '/'\n throw new Error(\n `[routesV3.builder] Multiple dynamic segments under ${parentLabel}: ${existing} and ${segment}`,\n )\n }\n if (!existing) {\n dynamicLayerMap.set(parentKey, segment)\n }\n }\n}\n","import { z, ZodObject, ZodSafeParseResult, ZodType } from 'zod'\n\n/** Supported HTTP verbs for the routes DSL. */\nexport type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'\n\n/** Declarative description of a multipart upload field. */\nexport type FileField = {\n /** Form field name used for uploads. */\n name: string\n /** Maximum number of files accepted for this field. */\n maxCount: number\n}\n\n/** Configuration that applies to an entire branch of the route tree. */\nexport interface NodeCfg {\n /**\n * Feed-specific query schema applied to all descendants unless they override it.\n */\n queryExtensionSchema?: ZodType\n /**\n * Feed meta schema applied to all descendants unless they override it.\n */\n outputMetaSchema?: ZodType\n}\n\nexport type RouteSchema<Output = unknown, Input = unknown> = {\n __out: Output\n __in?: Input\n}\n\ntype RouteSchemaOrUndefined<S extends ZodType | undefined> =\n S extends ZodType<infer Out, infer In> ? RouteSchema<Out, In> : undefined\n\ntype FeedAwareQueryRoute<\n Feed extends boolean,\n Query extends ZodType | undefined,\n Extension extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Feed extends true\n ? ToRouteSchema<\n FeedQueryField<{\n querySchema: Query\n queryExtensionSchema: EffectiveQueryExtensionSchema<Extension, Node>\n }>['querySchema']\n >\n : RouteSchemaOrUndefined<Query>\n\ntype OutputRouteSchema<\n O extends ZodType | undefined,\n Meta extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = ToRouteSchema<\n OutputField<{\n outputSchema: O\n outputMetaSchema: EffectiveOutputMetaSchema<Meta, Node>\n }>['outputSchema']\n>\n\nexport type FeedQueryField<C extends MethodCfg> = {\n querySchema: IntersectZod<\n C['querySchema'] extends ZodObject ? C['querySchema'] : undefined,\n C['queryExtensionSchema']\n >\n}\n\nexport type OutputField<C extends MethodCfg> = C['outputSchema'] extends ZodType\n ? {\n outputSchema: ZodObject<{\n out: C['outputSchema']\n meta: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : z.ZodOptional<z.ZodString>\n }>\n }\n : {\n outputSchema: ZodObject<{\n meta: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : z.ZodOptional<z.ZodString>\n }>\n }\n\nexport type RouteSchemaOutput<Schema extends RouteSchema> = Schema extends {\n __out: infer Out\n}\n ? Out\n : unknown\nexport type RouteSchemaInput<Schema extends RouteSchema> = Schema extends {\n __in?: infer In\n}\n ? In\n : unknown\nexport const lowProfileParse = <T extends RouteSchema>(\n schema: T,\n data: unknown,\n): RouteSchemaOutput<T> => {\n return (schema as any as ZodType).parse(data) as RouteSchemaOutput<T>\n}\n\nexport const lowProfileSafeParse = <T extends RouteSchema>(\n schema: T,\n data: unknown,\n): ZodSafeParseResult<RouteSchemaOutput<T>> => {\n return (schema as any as ZodType).safeParse(data) as ZodSafeParseResult<\n RouteSchemaOutput<T>\n >\n}\n\nexport const routeSchemaParse = <T>(\n routeSchema: RouteSchema<T>,\n): ZodType<T> => {\n return routeSchema as any as ZodType<T>\n}\n\n/**\n * Runtime helper that mirrors the typed merge used by the builder.\n * @param a Previously merged params schema inherited from parent segments.\n * @param b Newly introduced params schema.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nexport function mergeSchemas<A extends ZodType, B extends ZodType>(\n a: A,\n b: B,\n): ZodType\nexport function mergeSchemas<A extends ZodType>(a: A, b: undefined): A\nexport function mergeSchemas<B extends ZodType>(a: undefined, b: B): B\nexport function mergeSchemas(\n a: ZodType | undefined,\n b: ZodType | undefined,\n): ZodType | undefined\nexport function mergeSchemas(a: ZodType | undefined, b: ZodType | undefined) {\n if (a && b) {\n if (a === b) return a\n return z.intersection(a as any, b as any)\n }\n return (a ?? b) as ZodType | undefined\n}\n\nexport function getZodShape(schema: ZodObject) {\n const shapeOrGetter = (schema as any).shape\n ? (schema as any).shape\n : (schema as any)._def?.shape?.()\n if (!shapeOrGetter) return {}\n return typeof shapeOrGetter === 'function'\n ? shapeOrGetter.call(schema)\n : shapeOrGetter\n}\n\nexport function collectNestedFieldSuggestions(\n shape: Record<string, ZodType> | undefined,\n prefix: string[] = [],\n): string[] {\n if (!shape) return []\n const suggestions: string[] = []\n for (const [key, value] of Object.entries(shape)) {\n if (value instanceof z.ZodObject) {\n const nestedShape = getZodShape(value as ZodObject)\n const nestedSuggestions = collectNestedFieldSuggestions(nestedShape, [\n ...prefix,\n key,\n ])\n suggestions.push(\n ...(nestedSuggestions.length\n ? nestedSuggestions\n : [[...prefix, key].join('_')]),\n )\n } else if (prefix.length > 0) {\n suggestions.push([...prefix, key].join('_'))\n }\n }\n return suggestions\n}\n\n// Use the output generic of ZodType instead of z.output<S>\nexport type ToRouteSchema<S> =\n S extends ZodType<infer Out, infer In> ? RouteSchema<Out, In> : S\n\nexport type LowProfileCfg<Cfg extends MethodCfg = MethodCfg> = Prettify<\n Omit<\n Cfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'outputMetaSchema'\n > & {\n bodySchema: ToRouteSchema<Cfg['bodySchema']>\n querySchema: ToRouteSchema<Cfg['querySchema']>\n paramsSchema: ToRouteSchema<Cfg['paramsSchema']>\n outputSchema: ToRouteSchema<Cfg['outputSchema']>\n outputMetaSchema: ToRouteSchema<Cfg['outputMetaSchema']>\n queryExtensionSchema: ToRouteSchema<Cfg['queryExtensionSchema']>\n }\n>\n\n/** Per-method configuration merged with inherited node config. */\nexport type MethodCfg = {\n /** Zod schema describing the request body. */\n bodySchema?: ZodType\n /** Zod schema describing the query string. */\n querySchema?: ZodType\n /** Zod schema describing path params (internal only, set through resource segments). */\n paramsSchema?: ZodType\n /** Zod schema describing the response payload. */\n outputSchema?: ZodType\n /** Multipart upload definitions for the route. */\n bodyFiles?: FileField[]\n /** Marks the route as an infinite feed (enables cursor helpers). */\n feed?: boolean\n /** feed handlers */\n outputMetaSchema?: ZodType\n queryExtensionSchema?: ZodType\n\n /** Optional human-readable description for docs/debugging. */\n description?: string\n /**\n * Short one-line summary for docs list views.\n * Shown in navigation / tables instead of the full description.\n */\n summary?: string\n /**\n * Group name used for navigation sections in docs UIs.\n * e.g. \"Users\", \"Billing\", \"Auth\".\n */\n docsGroup?: string\n /**\n * Tags that can be used to filter / facet in interactive docs.\n * e.g. [\"users\", \"admin\", \"internal\"].\n */\n tags?: string[]\n /**\n * Mark the route as deprecated in docs.\n * Renderers can badge this and/or hide by default.\n */\n deprecated?: boolean\n /**\n * Optional stability information for the route.\n * Renderers can surface this prominently.\n */\n stability?: 'experimental' | 'beta' | 'stable' | 'deprecated'\n /**\n * Hide this route from public docs while keeping it usable in code.\n */\n docsHidden?: boolean\n /**\n * Arbitrary extra metadata for docs renderers.\n * Can be used for auth requirements, rate limits, feature flags, etc.\n */\n docsMeta?: Record<string, unknown>\n}\n\n/** Immutable representation of a single HTTP route in the tree. */\nexport type Leaf<\n M extends HttpMethod,\n P extends string,\n C extends MethodCfg,\n> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>\n}\n\n/** Convenience union covering all generated leaves. */\nexport type AnyLeaf = Leaf<HttpMethod, string, MethodCfg>\n\n/** Merge two object types while keeping nice IntelliSense output. */\nexport type Merge<A, B> = Prettify<Omit<A, keyof B> & B>\n\n/** Append a new element to a readonly tuple type. */\nexport type Append<T extends readonly unknown[], X> = [...T, X]\n\n/** Concatenate two readonly tuple types. */\nexport type MergeArray<\n A extends readonly unknown[],\n B extends readonly unknown[],\n> = [...A, ...B]\n\ntype TrimTrailingSlash<S extends string> = S extends `${infer R}/`\n ? TrimTrailingSlash<R>\n : S\ntype TrimLeadingSlash<S extends string> = S extends `/${infer R}`\n ? TrimLeadingSlash<R>\n : S\n\ntype NormalizedBase<Base extends string> = TrimTrailingSlash<Base>\ntype NormalizedChild<Child extends string> = TrimLeadingSlash<Child>\n\nexport type JoinPath<Base extends string, Child extends string> =\n NormalizedBase<Base> extends ''\n ? Child\n : NormalizedChild<Child> extends ''\n ? NormalizedBase<Base>\n : `${NormalizedBase<Base>}/${NormalizedChild<Child>}`\nexport type IntersectZod<\n A extends ZodType | undefined,\n B extends ZodType | undefined,\n> = B extends ZodType\n ? A extends ZodType\n ? z.ZodIntersection<A, B>\n : B\n : A extends ZodType\n ? A\n : undefined\n\ntype MergeRouteSchemas<\n Existing extends RouteSchema | undefined,\n Parent extends ZodType | undefined,\n> =\n Existing extends RouteSchema<infer ExistingOut>\n ? Parent extends ZodType<infer ParentOut>\n ? RouteSchema<ExistingOut & ParentOut>\n : Existing\n : Parent extends ZodType<infer ParentOut>\n ? RouteSchema<ParentOut>\n : undefined\n\ntype AugmentedCfg<\n Cfg extends MethodCfgLowProfile,\n Param extends ZodType | undefined,\n> = Prettify<\n Omit<Cfg, 'paramsSchema'> & {\n paramsSchema: MergeRouteSchemas<Cfg['paramsSchema'], Param>\n }\n>\n\n// Tuple-mapped helper avoids recursive instantiation while re-basing nested leaves.\ntype RebasedLeaf<\n P extends string,\n Param extends ZodType | undefined,\n L extends LeafLowProfile,\n> = LeafLowProfile<\n L['method'],\n JoinPath<P, L['path']>,\n AugmentedCfg<L['cfg'], Param>\n>\n\nexport type AugmentLeaves<\n P extends string,\n Param extends ZodType | undefined,\n R extends readonly LeafLowProfile[],\n> = {\n [K in keyof R]: R[K] extends LeafLowProfile\n ? RebasedLeaf<P, Param, R[K]>\n : never\n}\n\ntype NodeQueryExtension<Node> = Node extends {\n queryExtensionSchema?: infer QE\n}\n ? QE extends ZodType\n ? QE\n : undefined\n : undefined\n\ntype NodeOutputMeta<Node> = Node extends { outputMetaSchema?: infer OM }\n ? OM extends ZodType\n ? OM\n : undefined\n : undefined\n\ntype EffectiveQueryExtensionSchema<\n Method extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Method extends ZodType ? Method : NodeQueryExtension<Node>\n\ntype EffectiveOutputMetaSchema<\n Method extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Method extends ZodType ? Method : NodeOutputMeta<Node>\n// helpers (optional)\ntype SegmentParams<S extends string> = S extends `:${infer P}`\n ? P\n : S extends `*${infer P}`\n ? P\n : never\ntype Split<S extends string> = S extends ''\n ? []\n : S extends `${infer A}/${infer B}`\n ? [A, ...Split<B>]\n : [S]\ntype ExtractParamNames<Path extends string> = SegmentParams<Split<Path>[number]>\n\ntype SegmentParamRecord<S extends string> = S extends `:${infer P}`\n ? Record<P, string | number>\n : S extends `*${infer P}`\n ? Record<P, (string | number)[]>\n : {}\n\ntype ParamsFromSegments<Segments extends readonly string[]> =\n Segments extends [infer Head, ...infer Rest]\n ? Head extends string\n ? Rest extends readonly string[]\n ? SegmentParamRecord<Head> & ParamsFromSegments<Rest>\n : SegmentParamRecord<Head>\n : {}\n : {}\n\n/** Derive a params object type from a literal route string. */\nexport type ExtractParamsFromPath<Path extends string> =\n ExtractParamNames<Path> extends never\n ? never\n : ParamsFromSegments<Split<Path>>\n\n/**\n * Interpolate `:params` in a path using the given values.\n * @param path Literal route string containing `:param` segments.\n * @param params Object of parameter values to interpolate.\n * @returns Path string with parameters substituted.\n */\nexport function compilePath<Path extends string>(\n path: Path,\n params: ExtractParamsFromPath<Path>,\n) {\n if (!params) return path\n const withParams = path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k]\n if (v === undefined || v === null) throw new Error(`Missing param :${k}`)\n return String(v)\n })\n return withParams.replace(/\\*([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k]\n if (v === undefined || v === null) throw new Error(`Missing param *${k}`)\n if (!Array.isArray(v)) {\n throw new Error(`Param *${k} must be an array`)\n }\n return v.map((item) => String(item)).join('/')\n })\n}\n\n/**\n * Build a deterministic cache key for the given leaf.\n * The key matches the shape consumed by React Query helpers.\n * @param args.leaf Leaf describing the endpoint.\n * @param args.params Optional params used to build the path.\n * @param args.query Optional query payload.\n * @returns Tuple suitable for React Query cache keys.\n */\ntype SplitPath<P extends string> = P extends ''\n ? []\n : P extends `${infer A}/${infer B}`\n ? [A, ...SplitPath<B>]\n : [P]\ntype ParamValue<Params, Key extends string> =\n Params extends Record<Key, infer V> ? V : undefined\ntype MapKeySegments<\n Segments extends readonly string[],\n Params,\n> = Segments extends [infer A, ...infer Rest]\n ? A extends string\n ? A extends `:${infer Key}`\n ? [\n [ParamValue<Params, Key>],\n ...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>,\n ]\n : A extends `*${infer Key}`\n ? [\n ParamValue<Params, Key>,\n ...MapKeySegments<\n Rest extends readonly string[] ? Rest : [],\n Params\n >,\n ]\n : [\n A,\n ...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>,\n ]\n : []\n : []\ntype CacheKeyForLeaf<L extends AnyLeafLowProfile> = readonly [\n L['method'],\n ...MapKeySegments<SplitPath<L['path']>, ExtractParamsFromPath<L['path']>>,\n InferQuery<L> extends never ? {} : InferQuery<L>,\n]\nexport function buildCacheKey<L extends AnyLeafLowProfile>(args: {\n leaf: L\n params?: ExtractParamsFromPath<L['path']>\n query?: InferQuery<L>\n}): CacheKeyForLeaf<L> {\n const segments = args.leaf.path.split('/').filter(Boolean) as SplitPath<\n L['path']\n >\n const keyParts: unknown[] = [args.leaf.method]\n for (const segment of segments) {\n if (segment.startsWith(':')) {\n const paramName = segment.slice(1)\n const value =\n args.params && paramName in args.params\n ? (args.params as Record<string, unknown>)[paramName]\n : undefined\n keyParts.push([value])\n continue\n }\n if (segment.startsWith('*')) {\n const paramName = segment.slice(1)\n const value =\n args.params && paramName in args.params\n ? (args.params as Record<string, unknown>)[paramName]\n : undefined\n if (value !== undefined && value !== null && !Array.isArray(value)) {\n throw new Error(`Param *${paramName} must be an array`)\n }\n keyParts.push(value)\n continue\n }\n keyParts.push(segment)\n }\n keyParts.push(args.query ?? {})\n return keyParts as unknown as CacheKeyForLeaf<L>\n}\n\n/** Definition-time method config (for clarity). */\nexport type MethodCfgDef = MethodCfg\n\n/** Low-profile method config where schemas carry a phantom __out like SocketSchema. */\nexport type MethodCfgLowProfile = Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n> & {\n bodySchema?: RouteSchema\n querySchema?: RouteSchema\n paramsSchema?: RouteSchema\n outputSchema?: RouteSchema\n outputMetaSchema?: RouteSchema\n queryExtensionSchema?: RouteSchema\n bodyFiles?: FileField[]\n}\nexport type AnyLeafLowProfile = LeafLowProfile<\n HttpMethod,\n string,\n MethodCfgLowProfile\n>\n\n// keep the overload as you have it\nexport function buildLowProfileLeaf<\n const M extends HttpMethod,\n const Path extends string,\n const Node extends NodeCfg | undefined = undefined,\n const O extends ZodType | undefined = undefined,\n const P extends ZodType | undefined = undefined,\n const Q extends ZodType | undefined = undefined,\n const B extends ZodType | undefined = undefined,\n const FO extends ZodType | undefined = undefined,\n const FQ extends ZodType | undefined = undefined,\n const BF extends FileField[] = [],\n const Feed extends boolean = false,\n>(leaf: {\n method: M\n path: Path\n node?: Node\n cfg: Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'feed'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n > & {\n feed?: Feed\n bodySchema?: B\n querySchema?: Q\n paramsSchema?: P\n outputSchema?: O\n outputMetaSchema?: FO\n queryExtensionSchema?: FQ\n bodyFiles?: BF\n }\n}): LeafLowProfile<\n M,\n Path,\n Prettify<\n Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'feed'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n > & {\n feed: Feed\n bodySchema: RouteSchemaOrUndefined<B>\n querySchema: FeedAwareQueryRoute<Feed, Q, FQ, Node>\n paramsSchema: RouteSchemaOrUndefined<P>\n outputSchema: OutputRouteSchema<O, FO, Node>\n outputMetaSchema: RouteSchemaOrUndefined<\n EffectiveOutputMetaSchema<FO, Node>\n >\n queryExtensionSchema: RouteSchemaOrUndefined<\n EffectiveQueryExtensionSchema<FQ, Node>\n >\n bodyFiles: BF\n }\n >\n>\n\n// implementation: NO z.infer here\nexport function buildLowProfileLeaf(leaf: any): any {\n const nodeCfg = (leaf.node ?? {}) as NodeCfg\n const mergedCfg = { ...nodeCfg, ...leaf.cfg }\n const effectiveQuerySchema =\n mergedCfg.feed === true\n ? mergeSchemas(mergedCfg.querySchema, mergedCfg.queryExtensionSchema)\n : mergedCfg.querySchema\n const effectiveOutputSchema = mergedCfg.outputSchema\n ? z.object({\n out: mergedCfg.outputSchema,\n meta: mergedCfg.outputMetaSchema ?? z.string().optional(),\n })\n : z.object({\n meta: mergedCfg.outputMetaSchema ?? z.string().optional(),\n })\n\n // ensure query schema is not nested\n if (mergedCfg.feed === true && effectiveQuerySchema instanceof ZodObject) {\n const shape = getZodShape(effectiveQuerySchema as ZodObject)\n const nestedFieldSuggestions = collectNestedFieldSuggestions(shape)\n if (nestedFieldSuggestions.length > 0) {\n console.warn(\n `[routesV3.builder] Warning: feed query schema for ${leaf.method.toUpperCase()} ${leaf.path} contains nested objects. Consider using flat keys instead: ${nestedFieldSuggestions.join(\n ', ',\n )}`,\n )\n }\n }\n return {\n method: leaf.method,\n path: leaf.path,\n cfg: {\n ...mergedCfg,\n bodySchema: mergedCfg.bodySchema as RouteSchema<any> | undefined,\n querySchema: effectiveQuerySchema as RouteSchema<any> | undefined,\n paramsSchema: mergedCfg.paramsSchema as RouteSchema<any> | undefined,\n outputSchema: effectiveOutputSchema as any as\n | RouteSchema<any>\n | undefined,\n outputMetaSchema: mergedCfg.outputMetaSchema as\n | RouteSchema<any>\n | undefined,\n queryExtensionSchema: mergedCfg.queryExtensionSchema as\n | RouteSchema<any>\n | undefined,\n },\n }\n}\n\nexport const keyOf = (\n method: HttpMethod,\n path: string,\n encodeSafe?: boolean,\n) => {\n const key = `${method.toUpperCase()} ${path}` as const\n return encodeSafe ? encodeURIComponent(key) : key\n}\n\nexport type LeafLowProfile<\n M extends HttpMethod = HttpMethod,\n P extends string = string,\n C extends MethodCfgLowProfile = MethodCfgLowProfile,\n> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>\n}\n\n/** Infer params either from the explicit params schema or from the path literal. */\nexport type InferParams<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['paramsSchema'] extends RouteSchema<infer POut>\n ? POut\n : L['cfg']['paramsSchema'] extends ZodType<infer POut>\n ? POut\n : Fallback\n\n/** Infer query shape from a Zod schema when present. */\nexport type InferQuery<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['querySchema'] extends RouteSchema<infer QOut>\n ? QOut\n : L['cfg']['querySchema'] extends ZodType<infer QOut>\n ? QOut\n : Fallback\n\n/** Infer request body shape from a Zod schema when present. */\nexport type InferBody<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['bodySchema'] extends RouteSchema<infer BOut>\n ? BOut\n : L['cfg']['bodySchema'] extends ZodType<infer BOut>\n ? BOut\n : Fallback\n\nexport type ReactNativeFile = {\n uri: string // e.g. file:///...\n name: string // e.g. avatar.jpg\n type: string // e.g. image/jpeg\n}\ntype UploadFieldValue<F extends FileField> = F['maxCount'] extends 1\n ? Blob | File | ReactNativeFile\n : (Blob | File | ReactNativeFile)[] | FileList\n\ntype BodyFilesInputFromDefs<Defs extends readonly FileField[]> = Prettify<{\n [F in Defs[number] as `file_${F['name']}`]?: UploadFieldValue<F>\n}>\n\n/** Infer request body input shape, augmented with multipart file fields when declared. */\nexport type InferBodyInput<\n L extends AnyLeafLowProfile,\n Fallback = never,\n> = L['cfg']['bodyFiles'] extends readonly FileField[]\n ? Prettify<\n Omit<\n InferBody<L, Fallback> extends never ? {} : InferBody<L, Fallback>,\n keyof BodyFilesInputFromDefs<L['cfg']['bodyFiles']>\n > &\n BodyFilesInputFromDefs<L['cfg']['bodyFiles']>\n >\n : InferBody<L, Fallback>\n\n/** Infer handler output shape from a Zod schema. Defaults to unknown. */\ntype InferMetaFromCfg<L extends AnyLeafLowProfile> =\n L['cfg']['outputMetaSchema'] extends RouteSchema<infer O>\n ? O\n : L['cfg']['outputMetaSchema'] extends ZodType<infer O>\n ? O\n : string | undefined\n\ntype ResolvedMeta<L extends AnyLeafLowProfile, M> = [M] extends\n | [never]\n | [unknown]\n ? InferMetaFromCfg<L>\n : M\n\ntype ApplyMetaFallback<L extends AnyLeafLowProfile, O> = O extends {\n meta: infer M\n}\n ? (undefined extends ResolvedMeta<L, M>\n ? { meta?: ResolvedMeta<L, M> }\n : { meta: ResolvedMeta<L, M> }) &\n Omit<O, 'meta'>\n : O\n\nexport type InferOutput<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['outputSchema'] extends RouteSchema<infer O>\n ? ApplyMetaFallback<L, O>\n : L['cfg']['outputSchema'] extends ZodType<infer O>\n ? ApplyMetaFallback<L, O>\n : Fallback\n\n/** Render a type as if it were a simple object literal. */\nexport type Prettify<T> = { [K in keyof T]: T[K] }\n","import { AnyLeafLowProfile, HttpMethod, Prettify } from './routesV3.core'\n\n/** Build the key type for a leaf — distributive so method/path stay paired. */\nexport type KeyOf<L extends AnyLeafLowProfile> = L extends AnyLeafLowProfile\n ? `${Uppercase<L['method']>} ${L['path']}`\n : never\n\n// From a tuple of leaves, get the union of keys (pairwise, no cartesian blow-up)\ntype KeysOf<Leaves extends readonly AnyLeafLowProfile[]> = KeyOf<Leaves[number]>\n\n// Parse method & path out of a key\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}`\n ? Lowercase<M>\n : never\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}`\n ? P\n : never\n\n// Given Leaves and a Key, pick the exact leaf that matches method+path\ntype LeafForKey<\n Leaves extends readonly AnyLeafLowProfile[],\n K extends string,\n> = Extract<\n Leaves[number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>\n\ntype FilterRoute<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n Acc extends readonly AnyLeafLowProfile[] = [],\n> = T extends readonly [\n infer First extends AnyLeafLowProfile,\n ...infer Rest extends AnyLeafLowProfile[],\n]\n ? First extends { path: `${string}${F}${string}` }\n ? FilterRoute<Rest, F, [...Acc, First]>\n : FilterRoute<Rest, F, Acc>\n : Acc\n\ntype UpperCase<S extends string> = S extends `${infer First}${infer Rest}`\n ? `${Uppercase<First>}${UpperCase<Rest>}`\n : S\n\ntype Routes<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n> = FilterRoute<T, F>\ntype ByKey<\n T extends readonly AnyLeafLowProfile[],\n Acc extends Record<string, AnyLeafLowProfile> = {},\n> = T extends readonly [\n infer First extends AnyLeafLowProfile,\n ...infer Rest extends AnyLeafLowProfile[],\n]\n ? ByKey<\n Rest,\n Acc & { [K in `${UpperCase<First['method']>} ${First['path']}`]: First }\n >\n : Acc\n\n/**\n * Convenience helper for extracting a subset of routes by path fragment.\n * @param T Tuple of leaves produced by the DSL.\n * @param F String fragment to match against the route path.\n */\nexport type SubsetRoutes<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n> = {\n byKey: ByKey<Routes<T, F>>\n all: Routes<T, F>\n}\n\n// In the same module as finalize\n\nexport interface FinalizedRegistry<L extends readonly AnyLeafLowProfile[]> {\n all: L\n byKey: { [K in KeysOf<L>]: Prettify<LeafForKey<L, K>> }\n log(logger: { system: (...args: unknown[]) => void }): void\n}\n\n// Optionally keep this alias if you like the name\nexport type Registry<L extends readonly AnyLeafLowProfile[]> =\n FinalizedRegistry<L>\n\nexport function finalize<const L extends readonly AnyLeafLowProfile[]>(\n leaves: L,\n): FinalizedRegistry<L> {\n type Keys = KeysOf<L>\n type MapByKey = { [K in Keys]: Prettify<LeafForKey<L, K>> }\n\n const byKey = Object.fromEntries(\n leaves.map((l) => [`${l.method.toUpperCase()} ${l.path}`, l] as const),\n ) as unknown as MapByKey\n\n const log = (logger: { system: (...args: unknown[]) => void }) => {\n logger.system('Finalized routes:')\n ;(Object.keys(byKey) as Keys[]).forEach((k) => {\n const leaf = byKey[k]\n logger.system(`- ${k}`)\n })\n }\n\n return { all: leaves, byKey, log }\n}\n","// socket.index.ts (shared client/server)\n\nimport { z } from 'zod'\n\nexport type SocketSchema<Output = unknown> = z.ZodType & {\n __out: Output\n}\n\nexport type SocketSchemaOutput<Schema extends z.ZodTypeAny> = Schema extends {\n __out: infer Out\n}\n ? Out\n : z.output<Schema>\n\nexport type SocketEvent<Out = unknown> = {\n message: z.ZodTypeAny\n /** phantom field, only for typing; not meant to be used at runtime */\n __out: Out\n}\n\nexport type EventMap = Record<string, SocketEvent<any>>\n\n/**\n * System event names – shared so server and client\n * don't drift on naming.\n */\nexport type SysEventName =\n | 'sys:connect'\n | 'sys:disconnect'\n | 'sys:reconnect'\n | 'sys:connect_error'\n | 'sys:ping'\n | 'sys:pong'\n | 'sys:room_join'\n | 'sys:room_leave'\nexport function defineSocketEvents<\n const C extends SocketConnectionConfig,\n const Schemas extends Record<\n string,\n {\n message: z.ZodTypeAny\n }\n >,\n>(\n config: C,\n events: Schemas,\n): {\n config: {\n [K in keyof C]: SocketSchema<z.output<C[K]>>\n }\n events: {\n [K in keyof Schemas]: SocketEvent<z.output<Schemas[K]['message']>>\n }\n}\n\nexport function defineSocketEvents(config: any, events: any) {\n return { config, events }\n}\n\nexport type Payload<T extends EventMap, K extends keyof T> =\n T[K] extends SocketEvent<infer Out> ? Out : never\nexport type SocketConnectionConfig = {\n joinMetaMessage: z.ZodTypeAny\n leaveMetaMessage: z.ZodTypeAny\n pingPayload: z.ZodTypeAny\n pongPayload: z.ZodTypeAny\n}\n\nexport type SocketConnectionConfigOutput = {\n joinMetaMessage: SocketSchema<any>\n leaveMetaMessage: SocketSchema<any>\n pingPayload: SocketSchema<any>\n pongPayload: SocketSchema<any>\n}\n"],"mappings":"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","names":["import_zod"]}
|
package/dist/index.mjs
CHANGED
|
@@ -45,11 +45,19 @@ function collectNestedFieldSuggestions(shape, prefix = []) {
|
|
|
45
45
|
}
|
|
46
46
|
function compilePath(path, params) {
|
|
47
47
|
if (!params) return path;
|
|
48
|
-
|
|
48
|
+
const withParams = path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {
|
|
49
49
|
const v = params[k];
|
|
50
50
|
if (v === void 0 || v === null) throw new Error(`Missing param :${k}`);
|
|
51
51
|
return String(v);
|
|
52
52
|
});
|
|
53
|
+
return withParams.replace(/\*([A-Za-z0-9_]+)/g, (_, k) => {
|
|
54
|
+
const v = params[k];
|
|
55
|
+
if (v === void 0 || v === null) throw new Error(`Missing param *${k}`);
|
|
56
|
+
if (!Array.isArray(v)) {
|
|
57
|
+
throw new Error(`Param *${k} must be an array`);
|
|
58
|
+
}
|
|
59
|
+
return v.map((item) => String(item)).join("/");
|
|
60
|
+
});
|
|
53
61
|
}
|
|
54
62
|
function buildCacheKey(args) {
|
|
55
63
|
const segments = args.leaf.path.split("/").filter(Boolean);
|
|
@@ -61,6 +69,15 @@ function buildCacheKey(args) {
|
|
|
61
69
|
keyParts.push([value]);
|
|
62
70
|
continue;
|
|
63
71
|
}
|
|
72
|
+
if (segment.startsWith("*")) {
|
|
73
|
+
const paramName = segment.slice(1);
|
|
74
|
+
const value = args.params && paramName in args.params ? args.params[paramName] : void 0;
|
|
75
|
+
if (value !== void 0 && value !== null && !Array.isArray(value)) {
|
|
76
|
+
throw new Error(`Param *${paramName} must be an array`);
|
|
77
|
+
}
|
|
78
|
+
keyParts.push(value);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
64
81
|
keyParts.push(segment);
|
|
65
82
|
}
|
|
66
83
|
keyParts.push(args.query ?? {});
|
|
@@ -108,7 +125,7 @@ var keyOf = (method, path, encodeSafe) => {
|
|
|
108
125
|
|
|
109
126
|
// src/core/routesV3.builder.ts
|
|
110
127
|
function resource(base, inherited, idSchema) {
|
|
111
|
-
const rawBase = base
|
|
128
|
+
const rawBase = base;
|
|
112
129
|
const normalizedBase = normalizeBaseSegment(
|
|
113
130
|
rawBase,
|
|
114
131
|
Boolean(idSchema)
|
|
@@ -117,6 +134,7 @@ function resource(base, inherited, idSchema) {
|
|
|
117
134
|
const rootParams = createParamSchema(rawBase, idSchema);
|
|
118
135
|
function makeBranch(base2, inherited2, mergedParamsSchema) {
|
|
119
136
|
const stack = [];
|
|
137
|
+
const dynamicLayerMap = /* @__PURE__ */ new Map();
|
|
120
138
|
const currentBase = base2;
|
|
121
139
|
const inheritedCfg = inherited2;
|
|
122
140
|
let currentParamsSchema = mergedParamsSchema;
|
|
@@ -129,6 +147,7 @@ function resource(base, inherited, idSchema) {
|
|
|
129
147
|
node: inheritedCfg,
|
|
130
148
|
cfg: fullCfg
|
|
131
149
|
});
|
|
150
|
+
assertDynamicLayerUniqueness(leaf.path, dynamicLayerMap);
|
|
132
151
|
stack.push(leaf);
|
|
133
152
|
return api;
|
|
134
153
|
}
|
|
@@ -168,6 +187,7 @@ function resource(base, inherited, idSchema) {
|
|
|
168
187
|
path: joinPaths(currentBase, leaf.path),
|
|
169
188
|
cfg: newCfg
|
|
170
189
|
};
|
|
190
|
+
assertDynamicLayerUniqueness(newLeaf.path, dynamicLayerMap);
|
|
171
191
|
stack.push(newLeaf);
|
|
172
192
|
}
|
|
173
193
|
}
|
|
@@ -210,20 +230,28 @@ function normalizeBaseSegment(base, hasParam) {
|
|
|
210
230
|
}
|
|
211
231
|
return base;
|
|
212
232
|
}
|
|
233
|
+
if (base.startsWith("*")) {
|
|
234
|
+
if (!hasParam) {
|
|
235
|
+
throw new Error("resource() requires a non-empty name for id schema");
|
|
236
|
+
}
|
|
237
|
+
return base;
|
|
238
|
+
}
|
|
213
239
|
if (base === "") {
|
|
214
240
|
return base;
|
|
215
241
|
}
|
|
216
|
-
throw new Error('resource() base must start with "/" or "
|
|
242
|
+
throw new Error('resource() base must start with "/", ":", or "*"');
|
|
217
243
|
}
|
|
218
244
|
function createParamSchema(nameSegment, schema) {
|
|
219
245
|
if (!schema) return void 0;
|
|
220
246
|
const segments = nameSegment.split("/").filter(Boolean);
|
|
221
247
|
const rawName = segments.length > 0 ? segments[segments.length - 1] : nameSegment;
|
|
222
|
-
const
|
|
248
|
+
const isWildcard = rawName.startsWith("*");
|
|
249
|
+
const normalized = rawName.startsWith(":") || isWildcard ? rawName.slice(1) : rawName;
|
|
223
250
|
if (!normalized) {
|
|
224
251
|
throw new Error("resource() requires a non-empty name for id schema");
|
|
225
252
|
}
|
|
226
|
-
|
|
253
|
+
const paramSchema = isWildcard ? z2.array(schema) : schema;
|
|
254
|
+
return z2.object({ [normalized]: paramSchema });
|
|
227
255
|
}
|
|
228
256
|
function joinPaths(parent, child) {
|
|
229
257
|
if (!parent) return child;
|
|
@@ -233,6 +261,25 @@ function joinPaths(parent, child) {
|
|
|
233
261
|
if (!trimmedChild) return trimmedParent;
|
|
234
262
|
return `${trimmedParent}/${trimmedChild}`;
|
|
235
263
|
}
|
|
264
|
+
function assertDynamicLayerUniqueness(path, dynamicLayerMap) {
|
|
265
|
+
const segments = path.split("/").filter(Boolean);
|
|
266
|
+
if (segments.length === 0) return;
|
|
267
|
+
for (let i = 0; i < segments.length; i++) {
|
|
268
|
+
const segment = segments[i];
|
|
269
|
+
if (!segment || segment[0] !== ":" && segment[0] !== "*") continue;
|
|
270
|
+
const parentKey = segments.slice(0, i).join("/");
|
|
271
|
+
const existing = dynamicLayerMap.get(parentKey);
|
|
272
|
+
if (existing && existing !== segment) {
|
|
273
|
+
const parentLabel = parentKey ? `/${parentKey}` : "/";
|
|
274
|
+
throw new Error(
|
|
275
|
+
`[routesV3.builder] Multiple dynamic segments under ${parentLabel}: ${existing} and ${segment}`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
if (!existing) {
|
|
279
|
+
dynamicLayerMap.set(parentKey, segment);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
236
283
|
|
|
237
284
|
// src/core/routesV3.finalize.ts
|
|
238
285
|
function finalize(leaves) {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/routesV3.builder.ts","../src/core/routesV3.core.ts","../src/core/routesV3.finalize.ts","../src/sockets/socket.index.ts"],"sourcesContent":["import { z, ZodObject, ZodType } from 'zod'\nimport {\n AnyLeaf,\n AnyLeafLowProfile,\n Append,\n AugmentLeaves,\n buildLowProfileLeaf,\n FeedQueryField,\n HttpMethod,\n Leaf,\n LowProfileCfg,\n Merge,\n MergeArray,\n mergeSchemas,\n MethodCfg,\n NodeCfg,\n OutputField,\n Prettify,\n} from './routesV3.core'\n\ntype ParamZod<Name extends string, S extends ZodType> = ZodObject<{\n [K in Name]: S\n}>\ntype BaseMethodCfg<C extends MethodCfg> = Merge<\n Omit<MethodCfg, 'querySchema' | 'outputSchema' | 'feed'>,\n Omit<C, 'querySchema' | 'outputSchema' | 'feed'>\n>\n\ntype FeedField<C extends MethodCfg> = C['feed'] extends true\n ? { feed: true }\n : { feed?: boolean }\n\ntype NonFeedQueryField<C extends MethodCfg> = C['querySchema'] extends ZodType\n ? { querySchema: C['querySchema'] }\n : { querySchema?: undefined }\n\ntype ParamsField<C extends MethodCfg, PS> = C['paramsSchema'] extends ZodType\n ? { paramsSchema: C['paramsSchema'] }\n : { paramsSchema: PS }\n\ntype EffectiveFeedFields<C extends MethodCfg, PS> = C['feed'] extends true\n ? FeedField<C> & FeedQueryField<C> & OutputField<C> & ParamsField<C, PS>\n : FeedField<C> & NonFeedQueryField<C> & OutputField<C> & ParamsField<C, PS>\n\ntype WithNodeDefaults<C extends MethodCfg, I extends NodeCfg> = Merge<\n C,\n {\n queryExtensionSchema: C['queryExtensionSchema'] extends ZodType\n ? C['queryExtensionSchema']\n : NodeQueryExtension<I>\n outputMetaSchema: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : NodeOutputMeta<I>\n }\n>\n\n// Avoids the previous MethodCfg merge step so TS doesn't have to repeatedly\n// expand the entire MethodCfg intersection for every method call.\ntype EffectiveCfgWithDefaults<C extends MethodCfg, PS> = Prettify<\n BaseMethodCfg<C> & EffectiveFeedFields<C, PS>\n>\n\ntype EffectiveCfg<C extends MethodCfg, PS, I extends NodeCfg> =\n WithNodeDefaults<C, I> extends infer WithDefaults extends MethodCfg\n ? EffectiveCfgWithDefaults<WithDefaults, PS>\n : never\n\ntype NodeWithoutSchemas<I extends NodeCfg> = Omit<\n I,\n 'queryExtensionSchema' | 'outputMetaSchema'\n>\n\ntype BuiltLeaf<\n M extends HttpMethod,\n Base extends string,\n I extends NodeCfg,\n C extends MethodCfg,\n PS,\n> = Leaf<\n M,\n Base,\n Merge<NodeWithoutSchemas<I>, LowProfileCfg<EffectiveCfg<C, PS, I>>>\n>\n\ntype StripParamName<Name extends string> = Name extends `:${infer P}` ? P : Name\ntype NormalizeBaseLiteral<\n Base extends string,\n HasParam extends boolean,\n> = HasParam extends true\n ? Base extends `:${string}`\n ? Base\n : Base extends ''\n ? ''\n : `:${Base}`\n : Base\ntype ParamSchemaForBase<\n Base extends string,\n Param extends ZodType | undefined,\n> = Param extends ZodType ? ParamZod<StripParamName<Base>, Param> : undefined\ntype ResourceBase<\n Base extends string,\n Param extends ZodType | undefined,\n> = NormalizeBaseLiteral<Base, Param extends ZodType ? true : false>\ntype NodeQueryExtension<I extends NodeCfg> = I extends {\n queryExtensionSchema?: infer QE\n}\n ? QE extends ZodType\n ? QE\n : undefined\n : undefined\ntype NodeOutputMeta<I extends NodeCfg> = I extends {\n outputMetaSchema?: infer OE\n}\n ? OE extends ZodType\n ? OE\n : undefined\n : undefined\n\ntype SubResourceCollections = readonly [\n ReadonlyArray<AnyLeafLowProfile>,\n ...ReadonlyArray<AnyLeafLowProfile>[],\n]\n\n// Bundling the branch generics keeps MethodFns<State, Used> from re-instantiating\n// four independent parameters for every chained call, which speeds up inference.\ntype BranchState<\n Base extends string,\n Acc extends readonly AnyLeafLowProfile[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n> = {\n base: Base\n leaves: Acc\n node: I\n params: PS\n}\n\ntype AnyBranchState = BranchState<\n string,\n readonly AnyLeafLowProfile[],\n NodeCfg,\n ZodType | undefined\n>\n\nexport type MergeAugmentedCollections<\n Base extends string,\n Param extends ZodType | undefined,\n Collections extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>,\n Acc extends readonly AnyLeafLowProfile[] = [],\n> = Collections extends readonly [infer First, ...infer Rest]\n ? First extends ReadonlyArray<AnyLeafLowProfile>\n ? Rest extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>\n ? MergeAugmentedCollections<\n Base,\n Param,\n Rest,\n MergeArray<Acc, AugmentLeaves<Base, Param, First>>\n >\n : MergeArray<Acc, AugmentLeaves<Base, Param, First>>\n : MergeAugmentedCollections<\n Base,\n Param,\n Rest extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>\n ? Rest\n : [],\n Acc\n >\n : Acc\n\ntype MethodFns<\n State extends AnyBranchState,\n Used extends HttpMethod | 'sub',\n> = {\n /**\n * Register a GET leaf at the current path.\n */\n get: 'get' extends Used\n ? never\n : <C extends MethodCfg>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<'get', State['base'], State['node'], C, State['params']>\n >,\n State['node'],\n State['params'],\n Used | 'get'\n >\n\n /**\n * Register a POST leaf at the current path.\n */\n post: 'post' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'post',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'post'\n >\n\n /**\n * Register a PUT leaf at the current path.\n */\n put: 'put' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'put',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'put'\n >\n\n /**\n * Register a PATCH leaf at the current path.\n */\n patch: 'patch' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'patch',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'patch'\n >\n\n /**\n * Register a DELETE leaf at the current path.\n */\n delete: 'delete' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'delete',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'delete'\n >\n}\n\n/** Builder surface used by `resource(...)` to accumulate leaves. */\nexport interface Branch<\n Base extends string,\n Acc extends readonly AnyLeafLowProfile[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Used extends HttpMethod | 'sub' = never,\n> extends MethodFns<BranchState<Base, Acc, I, PS>, Used> {\n /**\n * Mount one or more sub-resources that were built via `resource(...)`.\n * Each resource carries its own base path (e.g. `users`, `:userId`, `posts`)\n * so this branch simply prefixes its current `Base` and merges param schemas.\n */\n sub: 'sub' extends Used\n ? never\n : <\n const Collections extends SubResourceCollections,\n NextAcc extends readonly AnyLeafLowProfile[] = MergeArray<\n Acc,\n MergeAugmentedCollections<Base, PS, Collections>\n >,\n >(\n ...collections: Collections\n ) => Branch<Base, NextAcc, I, PS, Used | 'sub'>\n\n /**\n * Finish the branch and return the collected leaves.\n * @returns Readonly tuple of accumulated leaves.\n */\n done(): Readonly<Acc>\n}\n\n/**\n * Start building a resource at the given base path.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Optional node configuration applied to all descendants.\n * @returns Root `Branch` instance used to compose the route tree.\n */\nexport function resource<\n Base extends `/${string}` | `:${string}` | '',\n I extends NodeCfg = {},\n Param extends ZodType | undefined = undefined,\n>(\n base?: Base,\n inherited?: I,\n idSchema?: Param,\n): Branch<\n ResourceBase<Base, Param>,\n readonly [],\n I,\n ParamSchemaForBase<Base, Param>\n> {\n const rawBase = (base ?? '') as Base\n const normalizedBase = normalizeBaseSegment(\n rawBase,\n Boolean(idSchema),\n ) as ResourceBase<Base, Param>\n const rootInherited: I =\n inherited === undefined ? ({} as I) : ({ ...inherited } as I)\n const rootParams = createParamSchema(rawBase, idSchema) as\n | ParamSchemaForBase<Base, Param>\n | undefined\n\n function makeBranch<\n Base2 extends string,\n I2 extends NodeCfg,\n PS2 extends ZodType | undefined,\n >(base2: Base2, inherited2: I2, mergedParamsSchema?: PS2) {\n const stack: AnyLeafLowProfile[] = []\n const currentBase: Base2 = base2\n const inheritedCfg: I2 = inherited2\n let currentParamsSchema: PS2 = mergedParamsSchema as PS2\n\n function add<M extends HttpMethod, C extends MethodCfg>(method: M, cfg: C) {\n const effectiveParamsSchema = (cfg.paramsSchema ??\n currentParamsSchema) as PS2 | undefined\n const fullCfg = (\n effectiveParamsSchema\n ? { ...cfg, paramsSchema: effectiveParamsSchema }\n : { ...cfg }\n ) as MethodCfg\n\n const leaf = buildLowProfileLeaf({\n method,\n path: currentBase as Base2,\n node: inheritedCfg,\n cfg: fullCfg,\n })\n\n stack.push(leaf as AnyLeafLowProfile)\n\n return api\n }\n\n const api: any = {\n /**\n * Mount one or more subtrees built elsewhere.\n * Usage:\n * resource('/api').sub(\n * resource('users').get(...).done(),\n * resource('projects').get(...).done()\n * )\n */\n sub(...collections: readonly AnyLeafLowProfile[][]) {\n if (collections.length === 0) {\n throw new Error('sub() expects at least one resource')\n }\n\n for (const leaves of collections) {\n for (const leafLow of leaves) {\n const leaf = leafLow as unknown as AnyLeaf\n const leafCfg = leaf.cfg as MethodCfg\n const leafParams = leafCfg.paramsSchema as ZodType | undefined\n\n const effectiveParams = mergeSchemas(\n currentParamsSchema as any,\n leafParams,\n )\n\n const newCfg: MethodCfg = {\n ...inheritedCfg,\n ...leafCfg,\n }\n\n if (effectiveParams) {\n newCfg.paramsSchema = effectiveParams\n } else if ('paramsSchema' in newCfg) {\n delete newCfg.paramsSchema\n }\n\n const newLeaf: AnyLeaf = {\n method: leaf.method,\n path: joinPaths(currentBase, leaf.path),\n cfg: newCfg,\n }\n\n stack.push(newLeaf as AnyLeafLowProfile)\n }\n }\n\n return api\n },\n\n // methods (inject current params schema if missing)\n get<C extends MethodCfg>(cfg: C) {\n return add('get', cfg)\n },\n post<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('post', { ...cfg, feed: false })\n },\n put<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('put', { ...cfg, feed: false })\n },\n patch<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('patch', { ...cfg, feed: false })\n },\n delete<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('delete', { ...cfg, feed: false })\n },\n\n done() {\n return stack as readonly AnyLeafLowProfile[]\n },\n }\n\n return api as Branch<Base2, readonly [], I2, PS2>\n }\n\n // Root branch starts with no params schema (PS = undefined)\n return makeBranch(normalizedBase, rootInherited, rootParams)\n}\n\n/**\n * Merge two readonly tuples (preserves literal tuple information).\n * @param arr1 First tuple.\n * @param arr2 Second tuple.\n * @returns New tuple containing values from both inputs.\n */\nexport const mergeArrays = <T extends readonly any[], S extends readonly any[]>(\n arr1: T,\n arr2: S,\n) => {\n return [...arr1, ...arr2] as [...T, ...S]\n}\n\nfunction normalizeBaseSegment(base: string, hasParam: boolean) {\n if (base.startsWith('/')) {\n return base\n }\n if (base.startsWith(':')) {\n if (!hasParam) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n return base\n }\n if (base === '') {\n return base\n }\n throw new Error('resource() base must start with \"/\" or \":\"')\n}\n\nfunction createParamSchema(nameSegment: string, schema?: ZodType) {\n if (!schema) return undefined\n const segments = nameSegment.split('/').filter(Boolean)\n const rawName =\n segments.length > 0 ? segments[segments.length - 1] : nameSegment\n const normalized = rawName.startsWith(':') ? rawName.slice(1) : rawName\n if (!normalized) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n return z.object({ [normalized]: schema } as Record<string, ZodType>)\n}\n\nfunction joinPaths(parent: string, child: string) {\n if (!parent) return child\n if (!child) return parent\n const trimmedParent = parent.endsWith('/')\n ? parent.replace(/\\/+$/, '')\n : parent\n const trimmedChild = child.startsWith('/') ? child.replace(/^\\/+/, '') : child\n if (!trimmedChild) return trimmedParent\n return `${trimmedParent}/${trimmedChild}`\n}\n","import { z, ZodObject, ZodSafeParseResult, ZodType } from 'zod'\n\n/** Supported HTTP verbs for the routes DSL. */\nexport type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'\n\n/** Declarative description of a multipart upload field. */\nexport type FileField = {\n /** Form field name used for uploads. */\n name: string\n /** Maximum number of files accepted for this field. */\n maxCount: number\n}\n\n/** Configuration that applies to an entire branch of the route tree. */\nexport interface NodeCfg {\n /**\n * Feed-specific query schema applied to all descendants unless they override it.\n */\n queryExtensionSchema?: ZodType\n /**\n * Feed meta schema applied to all descendants unless they override it.\n */\n outputMetaSchema?: ZodType\n}\n\nexport type RouteSchema<Output = unknown, Input = unknown> = {\n __out: Output\n __in?: Input\n}\n\ntype RouteSchemaOrUndefined<S extends ZodType | undefined> =\n S extends ZodType<infer Out, infer In> ? RouteSchema<Out, In> : undefined\n\ntype FeedAwareQueryRoute<\n Feed extends boolean,\n Query extends ZodType | undefined,\n Extension extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Feed extends true\n ? ToRouteSchema<\n FeedQueryField<{\n querySchema: Query\n queryExtensionSchema: EffectiveQueryExtensionSchema<Extension, Node>\n }>['querySchema']\n >\n : RouteSchemaOrUndefined<Query>\n\ntype OutputRouteSchema<\n O extends ZodType | undefined,\n Meta extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = ToRouteSchema<\n OutputField<{\n outputSchema: O\n outputMetaSchema: EffectiveOutputMetaSchema<Meta, Node>\n }>['outputSchema']\n>\n\nexport type FeedQueryField<C extends MethodCfg> = {\n querySchema: IntersectZod<\n C['querySchema'] extends ZodObject ? C['querySchema'] : undefined,\n C['queryExtensionSchema']\n >\n}\n\nexport type OutputField<C extends MethodCfg> = C['outputSchema'] extends ZodType\n ? {\n outputSchema: ZodObject<{\n out: C['outputSchema']\n meta: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : z.ZodOptional<z.ZodString>\n }>\n }\n : {\n outputSchema: ZodObject<{\n meta: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : z.ZodOptional<z.ZodString>\n }>\n }\n\nexport type RouteSchemaOutput<Schema extends RouteSchema> = Schema extends {\n __out: infer Out\n}\n ? Out\n : unknown\nexport type RouteSchemaInput<Schema extends RouteSchema> = Schema extends {\n __in?: infer In\n}\n ? In\n : unknown\nexport const lowProfileParse = <T extends RouteSchema>(\n schema: T,\n data: unknown,\n): RouteSchemaOutput<T> => {\n return (schema as any as ZodType).parse(data) as RouteSchemaOutput<T>\n}\n\nexport const lowProfileSafeParse = <T extends RouteSchema>(\n schema: T,\n data: unknown,\n): ZodSafeParseResult<RouteSchemaOutput<T>> => {\n return (schema as any as ZodType).safeParse(data) as ZodSafeParseResult<\n RouteSchemaOutput<T>\n >\n}\n\nexport const routeSchemaParse = <T>(\n routeSchema: RouteSchema<T>,\n): ZodType<T> => {\n return routeSchema as any as ZodType<T>\n}\n\n/**\n * Runtime helper that mirrors the typed merge used by the builder.\n * @param a Previously merged params schema inherited from parent segments.\n * @param b Newly introduced params schema.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nexport function mergeSchemas<A extends ZodType, B extends ZodType>(\n a: A,\n b: B,\n): ZodType\nexport function mergeSchemas<A extends ZodType>(a: A, b: undefined): A\nexport function mergeSchemas<B extends ZodType>(a: undefined, b: B): B\nexport function mergeSchemas(\n a: ZodType | undefined,\n b: ZodType | undefined,\n): ZodType | undefined\nexport function mergeSchemas(a: ZodType | undefined, b: ZodType | undefined) {\n if (a && b) {\n if (a === b) return a\n return z.intersection(a as any, b as any)\n }\n return (a ?? b) as ZodType | undefined\n}\n\nexport function getZodShape(schema: ZodObject) {\n const shapeOrGetter = (schema as any).shape\n ? (schema as any).shape\n : (schema as any)._def?.shape?.()\n if (!shapeOrGetter) return {}\n return typeof shapeOrGetter === 'function'\n ? shapeOrGetter.call(schema)\n : shapeOrGetter\n}\n\nexport function collectNestedFieldSuggestions(\n shape: Record<string, ZodType> | undefined,\n prefix: string[] = [],\n): string[] {\n if (!shape) return []\n const suggestions: string[] = []\n for (const [key, value] of Object.entries(shape)) {\n if (value instanceof z.ZodObject) {\n const nestedShape = getZodShape(value as ZodObject)\n const nestedSuggestions = collectNestedFieldSuggestions(nestedShape, [\n ...prefix,\n key,\n ])\n suggestions.push(\n ...(nestedSuggestions.length\n ? nestedSuggestions\n : [[...prefix, key].join('_')]),\n )\n } else if (prefix.length > 0) {\n suggestions.push([...prefix, key].join('_'))\n }\n }\n return suggestions\n}\n\n// Use the output generic of ZodType instead of z.output<S>\nexport type ToRouteSchema<S> =\n S extends ZodType<infer Out, infer In> ? RouteSchema<Out, In> : S\n\nexport type LowProfileCfg<Cfg extends MethodCfg = MethodCfg> = Prettify<\n Omit<\n Cfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'outputMetaSchema'\n > & {\n bodySchema: ToRouteSchema<Cfg['bodySchema']>\n querySchema: ToRouteSchema<Cfg['querySchema']>\n paramsSchema: ToRouteSchema<Cfg['paramsSchema']>\n outputSchema: ToRouteSchema<Cfg['outputSchema']>\n outputMetaSchema: ToRouteSchema<Cfg['outputMetaSchema']>\n queryExtensionSchema: ToRouteSchema<Cfg['queryExtensionSchema']>\n }\n>\n\n/** Per-method configuration merged with inherited node config. */\nexport type MethodCfg = {\n /** Zod schema describing the request body. */\n bodySchema?: ZodType\n /** Zod schema describing the query string. */\n querySchema?: ZodType\n /** Zod schema describing path params (internal only, set through resource segments). */\n paramsSchema?: ZodType\n /** Zod schema describing the response payload. */\n outputSchema?: ZodType\n /** Multipart upload definitions for the route. */\n bodyFiles?: FileField[]\n /** Marks the route as an infinite feed (enables cursor helpers). */\n feed?: boolean\n /** feed handlers */\n outputMetaSchema?: ZodType\n queryExtensionSchema?: ZodType\n\n /** Optional human-readable description for docs/debugging. */\n description?: string\n /**\n * Short one-line summary for docs list views.\n * Shown in navigation / tables instead of the full description.\n */\n summary?: string\n /**\n * Group name used for navigation sections in docs UIs.\n * e.g. \"Users\", \"Billing\", \"Auth\".\n */\n docsGroup?: string\n /**\n * Tags that can be used to filter / facet in interactive docs.\n * e.g. [\"users\", \"admin\", \"internal\"].\n */\n tags?: string[]\n /**\n * Mark the route as deprecated in docs.\n * Renderers can badge this and/or hide by default.\n */\n deprecated?: boolean\n /**\n * Optional stability information for the route.\n * Renderers can surface this prominently.\n */\n stability?: 'experimental' | 'beta' | 'stable' | 'deprecated'\n /**\n * Hide this route from public docs while keeping it usable in code.\n */\n docsHidden?: boolean\n /**\n * Arbitrary extra metadata for docs renderers.\n * Can be used for auth requirements, rate limits, feature flags, etc.\n */\n docsMeta?: Record<string, unknown>\n}\n\n/** Immutable representation of a single HTTP route in the tree. */\nexport type Leaf<\n M extends HttpMethod,\n P extends string,\n C extends MethodCfg,\n> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>\n}\n\n/** Convenience union covering all generated leaves. */\nexport type AnyLeaf = Leaf<HttpMethod, string, MethodCfg>\n\n/** Merge two object types while keeping nice IntelliSense output. */\nexport type Merge<A, B> = Prettify<Omit<A, keyof B> & B>\n\n/** Append a new element to a readonly tuple type. */\nexport type Append<T extends readonly unknown[], X> = [...T, X]\n\n/** Concatenate two readonly tuple types. */\nexport type MergeArray<\n A extends readonly unknown[],\n B extends readonly unknown[],\n> = [...A, ...B]\n\ntype TrimTrailingSlash<S extends string> = S extends `${infer R}/`\n ? TrimTrailingSlash<R>\n : S\ntype TrimLeadingSlash<S extends string> = S extends `/${infer R}`\n ? TrimLeadingSlash<R>\n : S\n\ntype NormalizedBase<Base extends string> = TrimTrailingSlash<Base>\ntype NormalizedChild<Child extends string> = TrimLeadingSlash<Child>\n\nexport type JoinPath<Base extends string, Child extends string> =\n NormalizedBase<Base> extends ''\n ? Child\n : NormalizedChild<Child> extends ''\n ? NormalizedBase<Base>\n : `${NormalizedBase<Base>}/${NormalizedChild<Child>}`\nexport type IntersectZod<\n A extends ZodType | undefined,\n B extends ZodType | undefined,\n> = B extends ZodType\n ? A extends ZodType\n ? z.ZodIntersection<A, B>\n : B\n : A extends ZodType\n ? A\n : undefined\n\ntype MergeRouteSchemas<\n Existing extends RouteSchema | undefined,\n Parent extends ZodType | undefined,\n> =\n Existing extends RouteSchema<infer ExistingOut>\n ? Parent extends ZodType<infer ParentOut>\n ? RouteSchema<ExistingOut & ParentOut>\n : Existing\n : Parent extends ZodType<infer ParentOut>\n ? RouteSchema<ParentOut>\n : undefined\n\ntype AugmentedCfg<\n Cfg extends MethodCfgLowProfile,\n Param extends ZodType | undefined,\n> = Prettify<\n Omit<Cfg, 'paramsSchema'> & {\n paramsSchema: MergeRouteSchemas<Cfg['paramsSchema'], Param>\n }\n>\n\n// Tuple-mapped helper avoids recursive instantiation while re-basing nested leaves.\ntype RebasedLeaf<\n P extends string,\n Param extends ZodType | undefined,\n L extends LeafLowProfile,\n> = LeafLowProfile<\n L['method'],\n JoinPath<P, L['path']>,\n AugmentedCfg<L['cfg'], Param>\n>\n\nexport type AugmentLeaves<\n P extends string,\n Param extends ZodType | undefined,\n R extends readonly LeafLowProfile[],\n> = {\n [K in keyof R]: R[K] extends LeafLowProfile\n ? RebasedLeaf<P, Param, R[K]>\n : never\n}\n\ntype NodeQueryExtension<Node> = Node extends {\n queryExtensionSchema?: infer QE\n}\n ? QE extends ZodType\n ? QE\n : undefined\n : undefined\n\ntype NodeOutputMeta<Node> = Node extends { outputMetaSchema?: infer OM }\n ? OM extends ZodType\n ? OM\n : undefined\n : undefined\n\ntype EffectiveQueryExtensionSchema<\n Method extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Method extends ZodType ? Method : NodeQueryExtension<Node>\n\ntype EffectiveOutputMetaSchema<\n Method extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Method extends ZodType ? Method : NodeOutputMeta<Node>\n// helpers (optional)\ntype SegmentParams<S extends string> = S extends `:${infer P}` ? P : never\ntype Split<S extends string> = S extends ''\n ? []\n : S extends `${infer A}/${infer B}`\n ? [A, ...Split<B>]\n : [S]\ntype ExtractParamNames<Path extends string> = SegmentParams<Split<Path>[number]>\n\n/** Derive a params object type from a literal route string. */\nexport type ExtractParamsFromPath<Path extends string> =\n ExtractParamNames<Path> extends never\n ? never\n : Record<ExtractParamNames<Path>, string | number>\n\n/**\n * Interpolate `:params` in a path using the given values.\n * @param path Literal route string containing `:param` segments.\n * @param params Object of parameter values to interpolate.\n * @returns Path string with parameters substituted.\n */\nexport function compilePath<Path extends string>(\n path: Path,\n params: ExtractParamsFromPath<Path>,\n) {\n if (!params) return path\n return path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k]\n if (v === undefined || v === null) throw new Error(`Missing param :${k}`)\n return String(v)\n })\n}\n\n/**\n * Build a deterministic cache key for the given leaf.\n * The key matches the shape consumed by React Query helpers.\n * @param args.leaf Leaf describing the endpoint.\n * @param args.params Optional params used to build the path.\n * @param args.query Optional query payload.\n * @returns Tuple suitable for React Query cache keys.\n */\ntype SplitPath<P extends string> = P extends ''\n ? []\n : P extends `${infer A}/${infer B}`\n ? [A, ...SplitPath<B>]\n : [P]\ntype ParamValue<Params, Key extends string> =\n Params extends Record<Key, infer V> ? V : undefined\ntype MapKeySegments<\n Segments extends readonly string[],\n Params,\n> = Segments extends [infer A, ...infer Rest]\n ? A extends string\n ? A extends `:${infer Key}`\n ? [\n [ParamValue<Params, Key>],\n ...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>,\n ]\n : [\n A,\n ...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>,\n ]\n : []\n : []\ntype CacheKeyForLeaf<L extends AnyLeafLowProfile> = readonly [\n L['method'],\n ...MapKeySegments<SplitPath<L['path']>, ExtractParamsFromPath<L['path']>>,\n InferQuery<L> extends never ? {} : InferQuery<L>,\n]\nexport function buildCacheKey<L extends AnyLeafLowProfile>(args: {\n leaf: L\n params?: ExtractParamsFromPath<L['path']>\n query?: InferQuery<L>\n}): CacheKeyForLeaf<L> {\n const segments = args.leaf.path.split('/').filter(Boolean) as SplitPath<\n L['path']\n >\n const keyParts: unknown[] = [args.leaf.method]\n for (const segment of segments) {\n if (segment.startsWith(':')) {\n const paramName = segment.slice(1)\n const value =\n args.params && paramName in args.params\n ? (args.params as Record<string, unknown>)[paramName]\n : undefined\n keyParts.push([value])\n continue\n }\n keyParts.push(segment)\n }\n keyParts.push(args.query ?? {})\n return keyParts as unknown as CacheKeyForLeaf<L>\n}\n\n/** Definition-time method config (for clarity). */\nexport type MethodCfgDef = MethodCfg\n\n/** Low-profile method config where schemas carry a phantom __out like SocketSchema. */\nexport type MethodCfgLowProfile = Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n> & {\n bodySchema?: RouteSchema\n querySchema?: RouteSchema\n paramsSchema?: RouteSchema\n outputSchema?: RouteSchema\n outputMetaSchema?: RouteSchema\n queryExtensionSchema?: RouteSchema\n bodyFiles?: FileField[]\n}\nexport type AnyLeafLowProfile = LeafLowProfile<\n HttpMethod,\n string,\n MethodCfgLowProfile\n>\n\n// keep the overload as you have it\nexport function buildLowProfileLeaf<\n const M extends HttpMethod,\n const Path extends string,\n const Node extends NodeCfg | undefined = undefined,\n const O extends ZodType | undefined = undefined,\n const P extends ZodType | undefined = undefined,\n const Q extends ZodType | undefined = undefined,\n const B extends ZodType | undefined = undefined,\n const FO extends ZodType | undefined = undefined,\n const FQ extends ZodType | undefined = undefined,\n const BF extends FileField[] = [],\n const Feed extends boolean = false,\n>(leaf: {\n method: M\n path: Path\n node?: Node\n cfg: Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'feed'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n > & {\n feed?: Feed\n bodySchema?: B\n querySchema?: Q\n paramsSchema?: P\n outputSchema?: O\n outputMetaSchema?: FO\n queryExtensionSchema?: FQ\n bodyFiles?: BF\n }\n}): LeafLowProfile<\n M,\n Path,\n Prettify<\n Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'feed'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n > & {\n feed: Feed\n bodySchema: RouteSchemaOrUndefined<B>\n querySchema: FeedAwareQueryRoute<Feed, Q, FQ, Node>\n paramsSchema: RouteSchemaOrUndefined<P>\n outputSchema: OutputRouteSchema<O, FO, Node>\n outputMetaSchema: RouteSchemaOrUndefined<\n EffectiveOutputMetaSchema<FO, Node>\n >\n queryExtensionSchema: RouteSchemaOrUndefined<\n EffectiveQueryExtensionSchema<FQ, Node>\n >\n bodyFiles: BF\n }\n >\n>\n\n// implementation: NO z.infer here\nexport function buildLowProfileLeaf(leaf: any): any {\n const nodeCfg = (leaf.node ?? {}) as NodeCfg\n const mergedCfg = { ...nodeCfg, ...leaf.cfg }\n const effectiveQuerySchema =\n mergedCfg.feed === true\n ? mergeSchemas(mergedCfg.querySchema, mergedCfg.queryExtensionSchema)\n : mergedCfg.querySchema\n const effectiveOutputSchema = mergedCfg.outputSchema\n ? z.object({\n out: mergedCfg.outputSchema,\n meta: mergedCfg.outputMetaSchema ?? z.string().optional(),\n })\n : z.object({\n meta: mergedCfg.outputMetaSchema ?? z.string().optional(),\n })\n\n // ensure query schema is not nested\n if (mergedCfg.feed === true && effectiveQuerySchema instanceof ZodObject) {\n const shape = getZodShape(effectiveQuerySchema as ZodObject)\n const nestedFieldSuggestions = collectNestedFieldSuggestions(shape)\n if (nestedFieldSuggestions.length > 0) {\n console.warn(\n `[routesV3.builder] Warning: feed query schema for ${leaf.method.toUpperCase()} ${leaf.path} contains nested objects. Consider using flat keys instead: ${nestedFieldSuggestions.join(\n ', ',\n )}`,\n )\n }\n }\n return {\n method: leaf.method,\n path: leaf.path,\n cfg: {\n ...mergedCfg,\n bodySchema: mergedCfg.bodySchema as RouteSchema<any> | undefined,\n querySchema: effectiveQuerySchema as RouteSchema<any> | undefined,\n paramsSchema: mergedCfg.paramsSchema as RouteSchema<any> | undefined,\n outputSchema: effectiveOutputSchema as any as\n | RouteSchema<any>\n | undefined,\n outputMetaSchema: mergedCfg.outputMetaSchema as\n | RouteSchema<any>\n | undefined,\n queryExtensionSchema: mergedCfg.queryExtensionSchema as\n | RouteSchema<any>\n | undefined,\n },\n }\n}\n\nexport const keyOf = (\n method: HttpMethod,\n path: string,\n encodeSafe?: boolean,\n) => {\n const key = `${method.toUpperCase()} ${path}` as const\n return encodeSafe ? encodeURIComponent(key) : key\n}\n\nexport type LeafLowProfile<\n M extends HttpMethod = HttpMethod,\n P extends string = string,\n C extends MethodCfgLowProfile = MethodCfgLowProfile,\n> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>\n}\n\n/** Infer params either from the explicit params schema or from the path literal. */\nexport type InferParams<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['paramsSchema'] extends RouteSchema<infer POut>\n ? POut\n : L['cfg']['paramsSchema'] extends ZodType<infer POut>\n ? POut\n : Fallback\n\n/** Infer query shape from a Zod schema when present. */\nexport type InferQuery<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['querySchema'] extends RouteSchema<infer QOut>\n ? QOut\n : L['cfg']['querySchema'] extends ZodType<infer QOut>\n ? QOut\n : Fallback\n\n/** Infer request body shape from a Zod schema when present. */\nexport type InferBody<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['bodySchema'] extends RouteSchema<infer BOut>\n ? BOut\n : L['cfg']['bodySchema'] extends ZodType<infer BOut>\n ? BOut\n : Fallback\n\nexport type ReactNativeFile = {\n uri: string // e.g. file:///...\n name: string // e.g. avatar.jpg\n type: string // e.g. image/jpeg\n}\ntype UploadFieldValue<F extends FileField> = F['maxCount'] extends 1\n ? Blob | File | ReactNativeFile\n : (Blob | File | ReactNativeFile)[] | FileList\n\ntype BodyFilesInputFromDefs<Defs extends readonly FileField[]> = Prettify<{\n [F in Defs[number] as `file_${F['name']}`]: UploadFieldValue<F>\n}>\n\n/** Infer request body input shape, augmented with multipart file fields when declared. */\nexport type InferBodyInput<\n L extends AnyLeafLowProfile,\n Fallback = never,\n> = L['cfg']['bodyFiles'] extends readonly FileField[]\n ? Prettify<\n Omit<\n InferBody<L, Fallback> extends never ? {} : InferBody<L, Fallback>,\n keyof BodyFilesInputFromDefs<L['cfg']['bodyFiles']>\n > &\n BodyFilesInputFromDefs<L['cfg']['bodyFiles']>\n >\n : InferBody<L, Fallback>\n\n/** Infer handler output shape from a Zod schema. Defaults to unknown. */\ntype InferMetaFromCfg<L extends AnyLeafLowProfile> =\n L['cfg']['outputMetaSchema'] extends RouteSchema<infer O>\n ? O\n : L['cfg']['outputMetaSchema'] extends ZodType<infer O>\n ? O\n : string | undefined\n\ntype ResolvedMeta<L extends AnyLeafLowProfile, M> = [M] extends\n | [never]\n | [unknown]\n ? InferMetaFromCfg<L>\n : M\n\ntype ApplyMetaFallback<L extends AnyLeafLowProfile, O> = O extends {\n meta: infer M\n}\n ? (undefined extends ResolvedMeta<L, M>\n ? { meta?: ResolvedMeta<L, M> }\n : { meta: ResolvedMeta<L, M> }) &\n Omit<O, 'meta'>\n : O\n\nexport type InferOutput<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['outputSchema'] extends RouteSchema<infer O>\n ? ApplyMetaFallback<L, O>\n : L['cfg']['outputSchema'] extends ZodType<infer O>\n ? ApplyMetaFallback<L, O>\n : Fallback\n\n/** Render a type as if it were a simple object literal. */\nexport type Prettify<T> = { [K in keyof T]: T[K] }\n","import { AnyLeafLowProfile, HttpMethod, Prettify } from './routesV3.core'\n\n/** Build the key type for a leaf — distributive so method/path stay paired. */\nexport type KeyOf<L extends AnyLeafLowProfile> = L extends AnyLeafLowProfile\n ? `${Uppercase<L['method']>} ${L['path']}`\n : never\n\n// From a tuple of leaves, get the union of keys (pairwise, no cartesian blow-up)\ntype KeysOf<Leaves extends readonly AnyLeafLowProfile[]> = KeyOf<Leaves[number]>\n\n// Parse method & path out of a key\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}`\n ? Lowercase<M>\n : never\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}`\n ? P\n : never\n\n// Given Leaves and a Key, pick the exact leaf that matches method+path\ntype LeafForKey<\n Leaves extends readonly AnyLeafLowProfile[],\n K extends string,\n> = Extract<\n Leaves[number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>\n\ntype FilterRoute<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n Acc extends readonly AnyLeafLowProfile[] = [],\n> = T extends readonly [\n infer First extends AnyLeafLowProfile,\n ...infer Rest extends AnyLeafLowProfile[],\n]\n ? First extends { path: `${string}${F}${string}` }\n ? FilterRoute<Rest, F, [...Acc, First]>\n : FilterRoute<Rest, F, Acc>\n : Acc\n\ntype UpperCase<S extends string> = S extends `${infer First}${infer Rest}`\n ? `${Uppercase<First>}${UpperCase<Rest>}`\n : S\n\ntype Routes<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n> = FilterRoute<T, F>\ntype ByKey<\n T extends readonly AnyLeafLowProfile[],\n Acc extends Record<string, AnyLeafLowProfile> = {},\n> = T extends readonly [\n infer First extends AnyLeafLowProfile,\n ...infer Rest extends AnyLeafLowProfile[],\n]\n ? ByKey<\n Rest,\n Acc & { [K in `${UpperCase<First['method']>} ${First['path']}`]: First }\n >\n : Acc\n\n/**\n * Convenience helper for extracting a subset of routes by path fragment.\n * @param T Tuple of leaves produced by the DSL.\n * @param F String fragment to match against the route path.\n */\nexport type SubsetRoutes<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n> = {\n byKey: ByKey<Routes<T, F>>\n all: Routes<T, F>\n}\n\n// In the same module as finalize\n\nexport interface FinalizedRegistry<L extends readonly AnyLeafLowProfile[]> {\n all: L\n byKey: { [K in KeysOf<L>]: Prettify<LeafForKey<L, K>> }\n log(logger: { system: (...args: unknown[]) => void }): void\n}\n\n// Optionally keep this alias if you like the name\nexport type Registry<L extends readonly AnyLeafLowProfile[]> =\n FinalizedRegistry<L>\n\nexport function finalize<const L extends readonly AnyLeafLowProfile[]>(\n leaves: L,\n): FinalizedRegistry<L> {\n type Keys = KeysOf<L>\n type MapByKey = { [K in Keys]: Prettify<LeafForKey<L, K>> }\n\n const byKey = Object.fromEntries(\n leaves.map((l) => [`${l.method.toUpperCase()} ${l.path}`, l] as const),\n ) as unknown as MapByKey\n\n const log = (logger: { system: (...args: unknown[]) => void }) => {\n logger.system('Finalized routes:')\n ;(Object.keys(byKey) as Keys[]).forEach((k) => {\n const leaf = byKey[k]\n logger.system(`- ${k}`)\n })\n }\n\n return { all: leaves, byKey, log }\n}\n","// socket.index.ts (shared client/server)\n\nimport { z } from 'zod'\n\nexport type SocketSchema<Output = unknown> = z.ZodType & {\n __out: Output\n}\n\nexport type SocketSchemaOutput<Schema extends z.ZodTypeAny> = Schema extends {\n __out: infer Out\n}\n ? Out\n : z.output<Schema>\n\nexport type SocketEvent<Out = unknown> = {\n message: z.ZodTypeAny\n /** phantom field, only for typing; not meant to be used at runtime */\n __out: Out\n}\n\nexport type EventMap = Record<string, SocketEvent<any>>\n\n/**\n * System event names – shared so server and client\n * don't drift on naming.\n */\nexport type SysEventName =\n | 'sys:connect'\n | 'sys:disconnect'\n | 'sys:reconnect'\n | 'sys:connect_error'\n | 'sys:ping'\n | 'sys:pong'\n | 'sys:room_join'\n | 'sys:room_leave'\nexport function defineSocketEvents<\n const C extends SocketConnectionConfig,\n const Schemas extends Record<\n string,\n {\n message: z.ZodTypeAny\n }\n >,\n>(\n config: C,\n events: Schemas,\n): {\n config: {\n [K in keyof C]: SocketSchema<z.output<C[K]>>\n }\n events: {\n [K in keyof Schemas]: SocketEvent<z.output<Schemas[K]['message']>>\n }\n}\n\nexport function defineSocketEvents(config: any, events: any) {\n return { config, events }\n}\n\nexport type Payload<T extends EventMap, K extends keyof T> =\n T[K] extends SocketEvent<infer Out> ? Out : never\nexport type SocketConnectionConfig = {\n joinMetaMessage: z.ZodTypeAny\n leaveMetaMessage: z.ZodTypeAny\n pingPayload: z.ZodTypeAny\n pongPayload: z.ZodTypeAny\n}\n\nexport type SocketConnectionConfigOutput = {\n joinMetaMessage: SocketSchema<any>\n leaveMetaMessage: SocketSchema<any>\n pingPayload: SocketSchema<any>\n pongPayload: SocketSchema<any>\n}\n"],"mappings":"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","names":["z","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/routesV3.builder.ts","../src/core/routesV3.core.ts","../src/core/routesV3.finalize.ts","../src/sockets/socket.index.ts"],"sourcesContent":["import { z, ZodObject, ZodType } from 'zod'\nimport {\n AnyLeaf,\n AnyLeafLowProfile,\n Append,\n AugmentLeaves,\n buildLowProfileLeaf,\n FeedQueryField,\n HttpMethod,\n Leaf,\n LowProfileCfg,\n Merge,\n MergeArray,\n mergeSchemas,\n MethodCfg,\n NodeCfg,\n OutputField,\n Prettify,\n} from './routesV3.core'\n\ntype ParamZod<Name extends string, S extends ZodType> = ZodObject<{\n [K in Name]: S\n}>\ntype BaseMethodCfg<C extends MethodCfg> = Merge<\n Omit<MethodCfg, 'querySchema' | 'outputSchema' | 'feed'>,\n Omit<C, 'querySchema' | 'outputSchema' | 'feed'>\n>\n\ntype FeedField<C extends MethodCfg> = C['feed'] extends true\n ? { feed: true }\n : { feed?: boolean }\n\ntype NonFeedQueryField<C extends MethodCfg> = C['querySchema'] extends ZodType\n ? { querySchema: C['querySchema'] }\n : { querySchema?: undefined }\n\ntype ParamsField<C extends MethodCfg, PS> = C['paramsSchema'] extends ZodType\n ? { paramsSchema: C['paramsSchema'] }\n : { paramsSchema: PS }\n\ntype EffectiveFeedFields<C extends MethodCfg, PS> = C['feed'] extends true\n ? FeedField<C> & FeedQueryField<C> & OutputField<C> & ParamsField<C, PS>\n : FeedField<C> & NonFeedQueryField<C> & OutputField<C> & ParamsField<C, PS>\n\ntype WithNodeDefaults<C extends MethodCfg, I extends NodeCfg> = Merge<\n C,\n {\n queryExtensionSchema: C['queryExtensionSchema'] extends ZodType\n ? C['queryExtensionSchema']\n : NodeQueryExtension<I>\n outputMetaSchema: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : NodeOutputMeta<I>\n }\n>\n\n// Avoids the previous MethodCfg merge step so TS doesn't have to repeatedly\n// expand the entire MethodCfg intersection for every method call.\ntype EffectiveCfgWithDefaults<C extends MethodCfg, PS> = Prettify<\n BaseMethodCfg<C> & EffectiveFeedFields<C, PS>\n>\n\ntype EffectiveCfg<C extends MethodCfg, PS, I extends NodeCfg> =\n WithNodeDefaults<C, I> extends infer WithDefaults extends MethodCfg\n ? EffectiveCfgWithDefaults<WithDefaults, PS>\n : never\n\ntype NodeWithoutSchemas<I extends NodeCfg> = Omit<\n I,\n 'queryExtensionSchema' | 'outputMetaSchema'\n>\n\ntype BuiltLeaf<\n M extends HttpMethod,\n Base extends string,\n I extends NodeCfg,\n C extends MethodCfg,\n PS,\n> = Leaf<\n M,\n Base,\n Merge<NodeWithoutSchemas<I>, LowProfileCfg<EffectiveCfg<C, PS, I>>>\n>\n\ntype StripParamName<Name extends string> = Name extends `:${infer P}`\n ? P\n : Name extends `*${infer P}`\n ? P\n : Name\ntype NormalizeBaseLiteral<\n Base extends string,\n HasParam extends boolean,\n> = HasParam extends true\n ? Base extends `:${string}`\n ? Base\n : Base extends `*${string}`\n ? Base\n : Base extends ''\n ? ''\n : `:${Base}`\n : Base\ntype ParamSchemaForBase<\n Base extends string,\n Param extends ZodType | undefined,\n> = Param extends ZodType\n ? Base extends `*${string}`\n ? ParamZod<StripParamName<Base>, z.ZodArray<Param>>\n : ParamZod<StripParamName<Base>, Param>\n : undefined\ntype ResourceBase<\n Base extends string,\n Param extends ZodType | undefined,\n> = NormalizeBaseLiteral<Base, Param extends ZodType ? true : false>\ntype NodeQueryExtension<I extends NodeCfg> = I extends {\n queryExtensionSchema?: infer QE\n}\n ? QE extends ZodType\n ? QE\n : undefined\n : undefined\ntype NodeOutputMeta<I extends NodeCfg> = I extends {\n outputMetaSchema?: infer OE\n}\n ? OE extends ZodType\n ? OE\n : undefined\n : undefined\n\ntype SubResourceCollections = readonly [\n ReadonlyArray<AnyLeafLowProfile>,\n ...ReadonlyArray<AnyLeafLowProfile>[],\n]\n\n// Bundling the branch generics keeps MethodFns<State, Used> from re-instantiating\n// four independent parameters for every chained call, which speeds up inference.\ntype BranchState<\n Base extends string,\n Acc extends readonly AnyLeafLowProfile[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n> = {\n base: Base\n leaves: Acc\n node: I\n params: PS\n}\n\ntype AnyBranchState = BranchState<\n string,\n readonly AnyLeafLowProfile[],\n NodeCfg,\n ZodType | undefined\n>\n\nexport type MergeAugmentedCollections<\n Base extends string,\n Param extends ZodType | undefined,\n Collections extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>,\n Acc extends readonly AnyLeafLowProfile[] = [],\n> = Collections extends readonly [infer First, ...infer Rest]\n ? First extends ReadonlyArray<AnyLeafLowProfile>\n ? Rest extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>\n ? MergeAugmentedCollections<\n Base,\n Param,\n Rest,\n MergeArray<Acc, AugmentLeaves<Base, Param, First>>\n >\n : MergeArray<Acc, AugmentLeaves<Base, Param, First>>\n : MergeAugmentedCollections<\n Base,\n Param,\n Rest extends ReadonlyArray<ReadonlyArray<AnyLeafLowProfile>>\n ? Rest\n : [],\n Acc\n >\n : Acc\n\ntype MethodFns<\n State extends AnyBranchState,\n Used extends HttpMethod | 'sub',\n> = {\n /**\n * Register a GET leaf at the current path.\n */\n get: 'get' extends Used\n ? never\n : <C extends MethodCfg>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<'get', State['base'], State['node'], C, State['params']>\n >,\n State['node'],\n State['params'],\n Used | 'get'\n >\n\n /**\n * Register a POST leaf at the current path.\n */\n post: 'post' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'post',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'post'\n >\n\n /**\n * Register a PUT leaf at the current path.\n */\n put: 'put' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'put',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'put'\n >\n\n /**\n * Register a PATCH leaf at the current path.\n */\n patch: 'patch' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'patch',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'patch'\n >\n\n /**\n * Register a DELETE leaf at the current path.\n */\n delete: 'delete' extends Used\n ? never\n : <C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ) => Branch<\n State['base'],\n Append<\n State['leaves'],\n BuiltLeaf<\n 'delete',\n State['base'],\n State['node'],\n Merge<C, { feed: false }>,\n State['params']\n >\n >,\n State['node'],\n State['params'],\n Used | 'delete'\n >\n}\n\n/** Builder surface used by `resource(...)` to accumulate leaves. */\nexport interface Branch<\n Base extends string,\n Acc extends readonly AnyLeafLowProfile[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Used extends HttpMethod | 'sub' = never,\n> extends MethodFns<BranchState<Base, Acc, I, PS>, Used> {\n /**\n * Mount one or more sub-resources that were built via `resource(...)`.\n * Each resource carries its own base path (e.g. `users`, `:userId`, `posts`)\n * so this branch simply prefixes its current `Base` and merges param schemas.\n */\n sub: 'sub' extends Used\n ? never\n : <\n const Collections extends SubResourceCollections,\n NextAcc extends readonly AnyLeafLowProfile[] = MergeArray<\n Acc,\n MergeAugmentedCollections<Base, PS, Collections>\n >,\n >(\n ...collections: Collections\n ) => Branch<Base, NextAcc, I, PS, Used | 'sub'>\n\n /**\n * Finish the branch and return the collected leaves.\n * @returns Readonly tuple of accumulated leaves.\n */\n done(): Readonly<Acc>\n}\n\n/**\n * Start building a resource at the given base path.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Optional node configuration applied to all descendants.\n * @returns Root `Branch` instance used to compose the route tree.\n */\nexport function resource<\n Base extends `/${string}` | `:${string}` | `*${string}` | '',\n I extends NodeCfg = {},\n Param extends ZodType | undefined = undefined,\n>(\n base: Base,\n inherited?: I,\n idSchema?: Param,\n): Branch<\n ResourceBase<Base, Param>,\n readonly [],\n I,\n ParamSchemaForBase<Base, Param>\n> {\n const rawBase = base as Base\n const normalizedBase = normalizeBaseSegment(\n rawBase,\n Boolean(idSchema),\n ) as ResourceBase<Base, Param>\n const rootInherited: I =\n inherited === undefined ? ({} as I) : ({ ...inherited } as I)\n const rootParams = createParamSchema(rawBase, idSchema) as\n | ParamSchemaForBase<Base, Param>\n | undefined\n\n function makeBranch<\n Base2 extends string,\n I2 extends NodeCfg,\n PS2 extends ZodType | undefined,\n >(base2: Base2, inherited2: I2, mergedParamsSchema?: PS2) {\n const stack: AnyLeafLowProfile[] = []\n const dynamicLayerMap = new Map<string, string>()\n const currentBase: Base2 = base2\n const inheritedCfg: I2 = inherited2\n let currentParamsSchema: PS2 = mergedParamsSchema as PS2\n\n function add<M extends HttpMethod, C extends MethodCfg>(method: M, cfg: C) {\n const effectiveParamsSchema = (cfg.paramsSchema ??\n currentParamsSchema) as PS2 | undefined\n const fullCfg = (\n effectiveParamsSchema\n ? { ...cfg, paramsSchema: effectiveParamsSchema }\n : { ...cfg }\n ) as MethodCfg\n\n const leaf = buildLowProfileLeaf({\n method,\n path: currentBase as Base2,\n node: inheritedCfg,\n cfg: fullCfg,\n })\n\n assertDynamicLayerUniqueness(leaf.path, dynamicLayerMap)\n stack.push(leaf as AnyLeafLowProfile)\n\n return api\n }\n\n const api: any = {\n /**\n * Mount one or more subtrees built elsewhere.\n * Usage:\n * resource('/api').sub(\n * resource('users').get(...).done(),\n * resource('projects').get(...).done()\n * )\n */\n sub(...collections: readonly AnyLeafLowProfile[][]) {\n if (collections.length === 0) {\n throw new Error('sub() expects at least one resource')\n }\n\n for (const leaves of collections) {\n for (const leafLow of leaves) {\n const leaf = leafLow as unknown as AnyLeaf\n const leafCfg = leaf.cfg as MethodCfg\n const leafParams = leafCfg.paramsSchema as ZodType | undefined\n\n const effectiveParams = mergeSchemas(\n currentParamsSchema as any,\n leafParams,\n )\n\n const newCfg: MethodCfg = {\n ...inheritedCfg,\n ...leafCfg,\n }\n\n if (effectiveParams) {\n newCfg.paramsSchema = effectiveParams\n } else if ('paramsSchema' in newCfg) {\n delete newCfg.paramsSchema\n }\n\n const newLeaf: AnyLeaf = {\n method: leaf.method,\n path: joinPaths(currentBase, leaf.path),\n cfg: newCfg,\n }\n\n assertDynamicLayerUniqueness(newLeaf.path, dynamicLayerMap)\n stack.push(newLeaf as AnyLeafLowProfile)\n }\n }\n\n return api\n },\n\n // methods (inject current params schema if missing)\n get<C extends MethodCfg>(cfg: C) {\n return add('get', cfg)\n },\n post<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('post', { ...cfg, feed: false })\n },\n put<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('put', { ...cfg, feed: false })\n },\n patch<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('patch', { ...cfg, feed: false })\n },\n delete<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('delete', { ...cfg, feed: false })\n },\n\n done() {\n return stack as readonly AnyLeafLowProfile[]\n },\n }\n\n return api as Branch<Base2, readonly [], I2, PS2>\n }\n\n // Root branch starts with no params schema (PS = undefined)\n return makeBranch(normalizedBase, rootInherited, rootParams)\n}\n\n/**\n * Merge two readonly tuples (preserves literal tuple information).\n * @param arr1 First tuple.\n * @param arr2 Second tuple.\n * @returns New tuple containing values from both inputs.\n */\nexport const mergeArrays = <T extends readonly any[], S extends readonly any[]>(\n arr1: T,\n arr2: S,\n) => {\n return [...arr1, ...arr2] as [...T, ...S]\n}\n\nfunction normalizeBaseSegment(base: string, hasParam: boolean) {\n if (base.startsWith('/')) {\n return base\n }\n if (base.startsWith(':')) {\n if (!hasParam) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n return base\n }\n if (base.startsWith('*')) {\n if (!hasParam) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n return base\n }\n if (base === '') {\n return base\n }\n throw new Error('resource() base must start with \"/\", \":\", or \"*\"')\n}\n\nfunction createParamSchema(nameSegment: string, schema?: ZodType) {\n if (!schema) return undefined\n const segments = nameSegment.split('/').filter(Boolean)\n const rawName =\n segments.length > 0 ? segments[segments.length - 1] : nameSegment\n const isWildcard = rawName.startsWith('*')\n const normalized = rawName.startsWith(':') || isWildcard ? rawName.slice(1) : rawName\n if (!normalized) {\n throw new Error('resource() requires a non-empty name for id schema')\n }\n const paramSchema = isWildcard ? z.array(schema) : schema\n return z.object({ [normalized]: paramSchema } as Record<string, ZodType>)\n}\n\nfunction joinPaths(parent: string, child: string) {\n if (!parent) return child\n if (!child) return parent\n const trimmedParent = parent.endsWith('/')\n ? parent.replace(/\\/+$/, '')\n : parent\n const trimmedChild = child.startsWith('/') ? child.replace(/^\\/+/, '') : child\n if (!trimmedChild) return trimmedParent\n return `${trimmedParent}/${trimmedChild}`\n}\n\nfunction assertDynamicLayerUniqueness(\n path: string,\n dynamicLayerMap: Map<string, string>,\n) {\n const segments = path.split('/').filter(Boolean)\n if (segments.length === 0) return\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n if (!segment || (segment[0] !== ':' && segment[0] !== '*')) continue\n\n const parentKey = segments.slice(0, i).join('/')\n const existing = dynamicLayerMap.get(parentKey)\n if (existing && existing !== segment) {\n const parentLabel = parentKey ? `/${parentKey}` : '/'\n throw new Error(\n `[routesV3.builder] Multiple dynamic segments under ${parentLabel}: ${existing} and ${segment}`,\n )\n }\n if (!existing) {\n dynamicLayerMap.set(parentKey, segment)\n }\n }\n}\n","import { z, ZodObject, ZodSafeParseResult, ZodType } from 'zod'\n\n/** Supported HTTP verbs for the routes DSL. */\nexport type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'\n\n/** Declarative description of a multipart upload field. */\nexport type FileField = {\n /** Form field name used for uploads. */\n name: string\n /** Maximum number of files accepted for this field. */\n maxCount: number\n}\n\n/** Configuration that applies to an entire branch of the route tree. */\nexport interface NodeCfg {\n /**\n * Feed-specific query schema applied to all descendants unless they override it.\n */\n queryExtensionSchema?: ZodType\n /**\n * Feed meta schema applied to all descendants unless they override it.\n */\n outputMetaSchema?: ZodType\n}\n\nexport type RouteSchema<Output = unknown, Input = unknown> = {\n __out: Output\n __in?: Input\n}\n\ntype RouteSchemaOrUndefined<S extends ZodType | undefined> =\n S extends ZodType<infer Out, infer In> ? RouteSchema<Out, In> : undefined\n\ntype FeedAwareQueryRoute<\n Feed extends boolean,\n Query extends ZodType | undefined,\n Extension extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Feed extends true\n ? ToRouteSchema<\n FeedQueryField<{\n querySchema: Query\n queryExtensionSchema: EffectiveQueryExtensionSchema<Extension, Node>\n }>['querySchema']\n >\n : RouteSchemaOrUndefined<Query>\n\ntype OutputRouteSchema<\n O extends ZodType | undefined,\n Meta extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = ToRouteSchema<\n OutputField<{\n outputSchema: O\n outputMetaSchema: EffectiveOutputMetaSchema<Meta, Node>\n }>['outputSchema']\n>\n\nexport type FeedQueryField<C extends MethodCfg> = {\n querySchema: IntersectZod<\n C['querySchema'] extends ZodObject ? C['querySchema'] : undefined,\n C['queryExtensionSchema']\n >\n}\n\nexport type OutputField<C extends MethodCfg> = C['outputSchema'] extends ZodType\n ? {\n outputSchema: ZodObject<{\n out: C['outputSchema']\n meta: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : z.ZodOptional<z.ZodString>\n }>\n }\n : {\n outputSchema: ZodObject<{\n meta: C['outputMetaSchema'] extends ZodType\n ? C['outputMetaSchema']\n : z.ZodOptional<z.ZodString>\n }>\n }\n\nexport type RouteSchemaOutput<Schema extends RouteSchema> = Schema extends {\n __out: infer Out\n}\n ? Out\n : unknown\nexport type RouteSchemaInput<Schema extends RouteSchema> = Schema extends {\n __in?: infer In\n}\n ? In\n : unknown\nexport const lowProfileParse = <T extends RouteSchema>(\n schema: T,\n data: unknown,\n): RouteSchemaOutput<T> => {\n return (schema as any as ZodType).parse(data) as RouteSchemaOutput<T>\n}\n\nexport const lowProfileSafeParse = <T extends RouteSchema>(\n schema: T,\n data: unknown,\n): ZodSafeParseResult<RouteSchemaOutput<T>> => {\n return (schema as any as ZodType).safeParse(data) as ZodSafeParseResult<\n RouteSchemaOutput<T>\n >\n}\n\nexport const routeSchemaParse = <T>(\n routeSchema: RouteSchema<T>,\n): ZodType<T> => {\n return routeSchema as any as ZodType<T>\n}\n\n/**\n * Runtime helper that mirrors the typed merge used by the builder.\n * @param a Previously merged params schema inherited from parent segments.\n * @param b Newly introduced params schema.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nexport function mergeSchemas<A extends ZodType, B extends ZodType>(\n a: A,\n b: B,\n): ZodType\nexport function mergeSchemas<A extends ZodType>(a: A, b: undefined): A\nexport function mergeSchemas<B extends ZodType>(a: undefined, b: B): B\nexport function mergeSchemas(\n a: ZodType | undefined,\n b: ZodType | undefined,\n): ZodType | undefined\nexport function mergeSchemas(a: ZodType | undefined, b: ZodType | undefined) {\n if (a && b) {\n if (a === b) return a\n return z.intersection(a as any, b as any)\n }\n return (a ?? b) as ZodType | undefined\n}\n\nexport function getZodShape(schema: ZodObject) {\n const shapeOrGetter = (schema as any).shape\n ? (schema as any).shape\n : (schema as any)._def?.shape?.()\n if (!shapeOrGetter) return {}\n return typeof shapeOrGetter === 'function'\n ? shapeOrGetter.call(schema)\n : shapeOrGetter\n}\n\nexport function collectNestedFieldSuggestions(\n shape: Record<string, ZodType> | undefined,\n prefix: string[] = [],\n): string[] {\n if (!shape) return []\n const suggestions: string[] = []\n for (const [key, value] of Object.entries(shape)) {\n if (value instanceof z.ZodObject) {\n const nestedShape = getZodShape(value as ZodObject)\n const nestedSuggestions = collectNestedFieldSuggestions(nestedShape, [\n ...prefix,\n key,\n ])\n suggestions.push(\n ...(nestedSuggestions.length\n ? nestedSuggestions\n : [[...prefix, key].join('_')]),\n )\n } else if (prefix.length > 0) {\n suggestions.push([...prefix, key].join('_'))\n }\n }\n return suggestions\n}\n\n// Use the output generic of ZodType instead of z.output<S>\nexport type ToRouteSchema<S> =\n S extends ZodType<infer Out, infer In> ? RouteSchema<Out, In> : S\n\nexport type LowProfileCfg<Cfg extends MethodCfg = MethodCfg> = Prettify<\n Omit<\n Cfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'outputMetaSchema'\n > & {\n bodySchema: ToRouteSchema<Cfg['bodySchema']>\n querySchema: ToRouteSchema<Cfg['querySchema']>\n paramsSchema: ToRouteSchema<Cfg['paramsSchema']>\n outputSchema: ToRouteSchema<Cfg['outputSchema']>\n outputMetaSchema: ToRouteSchema<Cfg['outputMetaSchema']>\n queryExtensionSchema: ToRouteSchema<Cfg['queryExtensionSchema']>\n }\n>\n\n/** Per-method configuration merged with inherited node config. */\nexport type MethodCfg = {\n /** Zod schema describing the request body. */\n bodySchema?: ZodType\n /** Zod schema describing the query string. */\n querySchema?: ZodType\n /** Zod schema describing path params (internal only, set through resource segments). */\n paramsSchema?: ZodType\n /** Zod schema describing the response payload. */\n outputSchema?: ZodType\n /** Multipart upload definitions for the route. */\n bodyFiles?: FileField[]\n /** Marks the route as an infinite feed (enables cursor helpers). */\n feed?: boolean\n /** feed handlers */\n outputMetaSchema?: ZodType\n queryExtensionSchema?: ZodType\n\n /** Optional human-readable description for docs/debugging. */\n description?: string\n /**\n * Short one-line summary for docs list views.\n * Shown in navigation / tables instead of the full description.\n */\n summary?: string\n /**\n * Group name used for navigation sections in docs UIs.\n * e.g. \"Users\", \"Billing\", \"Auth\".\n */\n docsGroup?: string\n /**\n * Tags that can be used to filter / facet in interactive docs.\n * e.g. [\"users\", \"admin\", \"internal\"].\n */\n tags?: string[]\n /**\n * Mark the route as deprecated in docs.\n * Renderers can badge this and/or hide by default.\n */\n deprecated?: boolean\n /**\n * Optional stability information for the route.\n * Renderers can surface this prominently.\n */\n stability?: 'experimental' | 'beta' | 'stable' | 'deprecated'\n /**\n * Hide this route from public docs while keeping it usable in code.\n */\n docsHidden?: boolean\n /**\n * Arbitrary extra metadata for docs renderers.\n * Can be used for auth requirements, rate limits, feature flags, etc.\n */\n docsMeta?: Record<string, unknown>\n}\n\n/** Immutable representation of a single HTTP route in the tree. */\nexport type Leaf<\n M extends HttpMethod,\n P extends string,\n C extends MethodCfg,\n> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>\n}\n\n/** Convenience union covering all generated leaves. */\nexport type AnyLeaf = Leaf<HttpMethod, string, MethodCfg>\n\n/** Merge two object types while keeping nice IntelliSense output. */\nexport type Merge<A, B> = Prettify<Omit<A, keyof B> & B>\n\n/** Append a new element to a readonly tuple type. */\nexport type Append<T extends readonly unknown[], X> = [...T, X]\n\n/** Concatenate two readonly tuple types. */\nexport type MergeArray<\n A extends readonly unknown[],\n B extends readonly unknown[],\n> = [...A, ...B]\n\ntype TrimTrailingSlash<S extends string> = S extends `${infer R}/`\n ? TrimTrailingSlash<R>\n : S\ntype TrimLeadingSlash<S extends string> = S extends `/${infer R}`\n ? TrimLeadingSlash<R>\n : S\n\ntype NormalizedBase<Base extends string> = TrimTrailingSlash<Base>\ntype NormalizedChild<Child extends string> = TrimLeadingSlash<Child>\n\nexport type JoinPath<Base extends string, Child extends string> =\n NormalizedBase<Base> extends ''\n ? Child\n : NormalizedChild<Child> extends ''\n ? NormalizedBase<Base>\n : `${NormalizedBase<Base>}/${NormalizedChild<Child>}`\nexport type IntersectZod<\n A extends ZodType | undefined,\n B extends ZodType | undefined,\n> = B extends ZodType\n ? A extends ZodType\n ? z.ZodIntersection<A, B>\n : B\n : A extends ZodType\n ? A\n : undefined\n\ntype MergeRouteSchemas<\n Existing extends RouteSchema | undefined,\n Parent extends ZodType | undefined,\n> =\n Existing extends RouteSchema<infer ExistingOut>\n ? Parent extends ZodType<infer ParentOut>\n ? RouteSchema<ExistingOut & ParentOut>\n : Existing\n : Parent extends ZodType<infer ParentOut>\n ? RouteSchema<ParentOut>\n : undefined\n\ntype AugmentedCfg<\n Cfg extends MethodCfgLowProfile,\n Param extends ZodType | undefined,\n> = Prettify<\n Omit<Cfg, 'paramsSchema'> & {\n paramsSchema: MergeRouteSchemas<Cfg['paramsSchema'], Param>\n }\n>\n\n// Tuple-mapped helper avoids recursive instantiation while re-basing nested leaves.\ntype RebasedLeaf<\n P extends string,\n Param extends ZodType | undefined,\n L extends LeafLowProfile,\n> = LeafLowProfile<\n L['method'],\n JoinPath<P, L['path']>,\n AugmentedCfg<L['cfg'], Param>\n>\n\nexport type AugmentLeaves<\n P extends string,\n Param extends ZodType | undefined,\n R extends readonly LeafLowProfile[],\n> = {\n [K in keyof R]: R[K] extends LeafLowProfile\n ? RebasedLeaf<P, Param, R[K]>\n : never\n}\n\ntype NodeQueryExtension<Node> = Node extends {\n queryExtensionSchema?: infer QE\n}\n ? QE extends ZodType\n ? QE\n : undefined\n : undefined\n\ntype NodeOutputMeta<Node> = Node extends { outputMetaSchema?: infer OM }\n ? OM extends ZodType\n ? OM\n : undefined\n : undefined\n\ntype EffectiveQueryExtensionSchema<\n Method extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Method extends ZodType ? Method : NodeQueryExtension<Node>\n\ntype EffectiveOutputMetaSchema<\n Method extends ZodType | undefined,\n Node extends NodeCfg | undefined,\n> = Method extends ZodType ? Method : NodeOutputMeta<Node>\n// helpers (optional)\ntype SegmentParams<S extends string> = S extends `:${infer P}`\n ? P\n : S extends `*${infer P}`\n ? P\n : never\ntype Split<S extends string> = S extends ''\n ? []\n : S extends `${infer A}/${infer B}`\n ? [A, ...Split<B>]\n : [S]\ntype ExtractParamNames<Path extends string> = SegmentParams<Split<Path>[number]>\n\ntype SegmentParamRecord<S extends string> = S extends `:${infer P}`\n ? Record<P, string | number>\n : S extends `*${infer P}`\n ? Record<P, (string | number)[]>\n : {}\n\ntype ParamsFromSegments<Segments extends readonly string[]> =\n Segments extends [infer Head, ...infer Rest]\n ? Head extends string\n ? Rest extends readonly string[]\n ? SegmentParamRecord<Head> & ParamsFromSegments<Rest>\n : SegmentParamRecord<Head>\n : {}\n : {}\n\n/** Derive a params object type from a literal route string. */\nexport type ExtractParamsFromPath<Path extends string> =\n ExtractParamNames<Path> extends never\n ? never\n : ParamsFromSegments<Split<Path>>\n\n/**\n * Interpolate `:params` in a path using the given values.\n * @param path Literal route string containing `:param` segments.\n * @param params Object of parameter values to interpolate.\n * @returns Path string with parameters substituted.\n */\nexport function compilePath<Path extends string>(\n path: Path,\n params: ExtractParamsFromPath<Path>,\n) {\n if (!params) return path\n const withParams = path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k]\n if (v === undefined || v === null) throw new Error(`Missing param :${k}`)\n return String(v)\n })\n return withParams.replace(/\\*([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k]\n if (v === undefined || v === null) throw new Error(`Missing param *${k}`)\n if (!Array.isArray(v)) {\n throw new Error(`Param *${k} must be an array`)\n }\n return v.map((item) => String(item)).join('/')\n })\n}\n\n/**\n * Build a deterministic cache key for the given leaf.\n * The key matches the shape consumed by React Query helpers.\n * @param args.leaf Leaf describing the endpoint.\n * @param args.params Optional params used to build the path.\n * @param args.query Optional query payload.\n * @returns Tuple suitable for React Query cache keys.\n */\ntype SplitPath<P extends string> = P extends ''\n ? []\n : P extends `${infer A}/${infer B}`\n ? [A, ...SplitPath<B>]\n : [P]\ntype ParamValue<Params, Key extends string> =\n Params extends Record<Key, infer V> ? V : undefined\ntype MapKeySegments<\n Segments extends readonly string[],\n Params,\n> = Segments extends [infer A, ...infer Rest]\n ? A extends string\n ? A extends `:${infer Key}`\n ? [\n [ParamValue<Params, Key>],\n ...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>,\n ]\n : A extends `*${infer Key}`\n ? [\n ParamValue<Params, Key>,\n ...MapKeySegments<\n Rest extends readonly string[] ? Rest : [],\n Params\n >,\n ]\n : [\n A,\n ...MapKeySegments<Rest extends readonly string[] ? Rest : [], Params>,\n ]\n : []\n : []\ntype CacheKeyForLeaf<L extends AnyLeafLowProfile> = readonly [\n L['method'],\n ...MapKeySegments<SplitPath<L['path']>, ExtractParamsFromPath<L['path']>>,\n InferQuery<L> extends never ? {} : InferQuery<L>,\n]\nexport function buildCacheKey<L extends AnyLeafLowProfile>(args: {\n leaf: L\n params?: ExtractParamsFromPath<L['path']>\n query?: InferQuery<L>\n}): CacheKeyForLeaf<L> {\n const segments = args.leaf.path.split('/').filter(Boolean) as SplitPath<\n L['path']\n >\n const keyParts: unknown[] = [args.leaf.method]\n for (const segment of segments) {\n if (segment.startsWith(':')) {\n const paramName = segment.slice(1)\n const value =\n args.params && paramName in args.params\n ? (args.params as Record<string, unknown>)[paramName]\n : undefined\n keyParts.push([value])\n continue\n }\n if (segment.startsWith('*')) {\n const paramName = segment.slice(1)\n const value =\n args.params && paramName in args.params\n ? (args.params as Record<string, unknown>)[paramName]\n : undefined\n if (value !== undefined && value !== null && !Array.isArray(value)) {\n throw new Error(`Param *${paramName} must be an array`)\n }\n keyParts.push(value)\n continue\n }\n keyParts.push(segment)\n }\n keyParts.push(args.query ?? {})\n return keyParts as unknown as CacheKeyForLeaf<L>\n}\n\n/** Definition-time method config (for clarity). */\nexport type MethodCfgDef = MethodCfg\n\n/** Low-profile method config where schemas carry a phantom __out like SocketSchema. */\nexport type MethodCfgLowProfile = Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n> & {\n bodySchema?: RouteSchema\n querySchema?: RouteSchema\n paramsSchema?: RouteSchema\n outputSchema?: RouteSchema\n outputMetaSchema?: RouteSchema\n queryExtensionSchema?: RouteSchema\n bodyFiles?: FileField[]\n}\nexport type AnyLeafLowProfile = LeafLowProfile<\n HttpMethod,\n string,\n MethodCfgLowProfile\n>\n\n// keep the overload as you have it\nexport function buildLowProfileLeaf<\n const M extends HttpMethod,\n const Path extends string,\n const Node extends NodeCfg | undefined = undefined,\n const O extends ZodType | undefined = undefined,\n const P extends ZodType | undefined = undefined,\n const Q extends ZodType | undefined = undefined,\n const B extends ZodType | undefined = undefined,\n const FO extends ZodType | undefined = undefined,\n const FQ extends ZodType | undefined = undefined,\n const BF extends FileField[] = [],\n const Feed extends boolean = false,\n>(leaf: {\n method: M\n path: Path\n node?: Node\n cfg: Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'feed'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n > & {\n feed?: Feed\n bodySchema?: B\n querySchema?: Q\n paramsSchema?: P\n outputSchema?: O\n outputMetaSchema?: FO\n queryExtensionSchema?: FQ\n bodyFiles?: BF\n }\n}): LeafLowProfile<\n M,\n Path,\n Prettify<\n Omit<\n MethodCfg,\n | 'bodySchema'\n | 'querySchema'\n | 'paramsSchema'\n | 'outputSchema'\n | 'feed'\n | 'outputMetaSchema'\n | 'queryExtensionSchema'\n | 'bodyFiles'\n > & {\n feed: Feed\n bodySchema: RouteSchemaOrUndefined<B>\n querySchema: FeedAwareQueryRoute<Feed, Q, FQ, Node>\n paramsSchema: RouteSchemaOrUndefined<P>\n outputSchema: OutputRouteSchema<O, FO, Node>\n outputMetaSchema: RouteSchemaOrUndefined<\n EffectiveOutputMetaSchema<FO, Node>\n >\n queryExtensionSchema: RouteSchemaOrUndefined<\n EffectiveQueryExtensionSchema<FQ, Node>\n >\n bodyFiles: BF\n }\n >\n>\n\n// implementation: NO z.infer here\nexport function buildLowProfileLeaf(leaf: any): any {\n const nodeCfg = (leaf.node ?? {}) as NodeCfg\n const mergedCfg = { ...nodeCfg, ...leaf.cfg }\n const effectiveQuerySchema =\n mergedCfg.feed === true\n ? mergeSchemas(mergedCfg.querySchema, mergedCfg.queryExtensionSchema)\n : mergedCfg.querySchema\n const effectiveOutputSchema = mergedCfg.outputSchema\n ? z.object({\n out: mergedCfg.outputSchema,\n meta: mergedCfg.outputMetaSchema ?? z.string().optional(),\n })\n : z.object({\n meta: mergedCfg.outputMetaSchema ?? z.string().optional(),\n })\n\n // ensure query schema is not nested\n if (mergedCfg.feed === true && effectiveQuerySchema instanceof ZodObject) {\n const shape = getZodShape(effectiveQuerySchema as ZodObject)\n const nestedFieldSuggestions = collectNestedFieldSuggestions(shape)\n if (nestedFieldSuggestions.length > 0) {\n console.warn(\n `[routesV3.builder] Warning: feed query schema for ${leaf.method.toUpperCase()} ${leaf.path} contains nested objects. Consider using flat keys instead: ${nestedFieldSuggestions.join(\n ', ',\n )}`,\n )\n }\n }\n return {\n method: leaf.method,\n path: leaf.path,\n cfg: {\n ...mergedCfg,\n bodySchema: mergedCfg.bodySchema as RouteSchema<any> | undefined,\n querySchema: effectiveQuerySchema as RouteSchema<any> | undefined,\n paramsSchema: mergedCfg.paramsSchema as RouteSchema<any> | undefined,\n outputSchema: effectiveOutputSchema as any as\n | RouteSchema<any>\n | undefined,\n outputMetaSchema: mergedCfg.outputMetaSchema as\n | RouteSchema<any>\n | undefined,\n queryExtensionSchema: mergedCfg.queryExtensionSchema as\n | RouteSchema<any>\n | undefined,\n },\n }\n}\n\nexport const keyOf = (\n method: HttpMethod,\n path: string,\n encodeSafe?: boolean,\n) => {\n const key = `${method.toUpperCase()} ${path}` as const\n return encodeSafe ? encodeURIComponent(key) : key\n}\n\nexport type LeafLowProfile<\n M extends HttpMethod = HttpMethod,\n P extends string = string,\n C extends MethodCfgLowProfile = MethodCfgLowProfile,\n> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>\n}\n\n/** Infer params either from the explicit params schema or from the path literal. */\nexport type InferParams<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['paramsSchema'] extends RouteSchema<infer POut>\n ? POut\n : L['cfg']['paramsSchema'] extends ZodType<infer POut>\n ? POut\n : Fallback\n\n/** Infer query shape from a Zod schema when present. */\nexport type InferQuery<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['querySchema'] extends RouteSchema<infer QOut>\n ? QOut\n : L['cfg']['querySchema'] extends ZodType<infer QOut>\n ? QOut\n : Fallback\n\n/** Infer request body shape from a Zod schema when present. */\nexport type InferBody<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['bodySchema'] extends RouteSchema<infer BOut>\n ? BOut\n : L['cfg']['bodySchema'] extends ZodType<infer BOut>\n ? BOut\n : Fallback\n\nexport type ReactNativeFile = {\n uri: string // e.g. file:///...\n name: string // e.g. avatar.jpg\n type: string // e.g. image/jpeg\n}\ntype UploadFieldValue<F extends FileField> = F['maxCount'] extends 1\n ? Blob | File | ReactNativeFile\n : (Blob | File | ReactNativeFile)[] | FileList\n\ntype BodyFilesInputFromDefs<Defs extends readonly FileField[]> = Prettify<{\n [F in Defs[number] as `file_${F['name']}`]?: UploadFieldValue<F>\n}>\n\n/** Infer request body input shape, augmented with multipart file fields when declared. */\nexport type InferBodyInput<\n L extends AnyLeafLowProfile,\n Fallback = never,\n> = L['cfg']['bodyFiles'] extends readonly FileField[]\n ? Prettify<\n Omit<\n InferBody<L, Fallback> extends never ? {} : InferBody<L, Fallback>,\n keyof BodyFilesInputFromDefs<L['cfg']['bodyFiles']>\n > &\n BodyFilesInputFromDefs<L['cfg']['bodyFiles']>\n >\n : InferBody<L, Fallback>\n\n/** Infer handler output shape from a Zod schema. Defaults to unknown. */\ntype InferMetaFromCfg<L extends AnyLeafLowProfile> =\n L['cfg']['outputMetaSchema'] extends RouteSchema<infer O>\n ? O\n : L['cfg']['outputMetaSchema'] extends ZodType<infer O>\n ? O\n : string | undefined\n\ntype ResolvedMeta<L extends AnyLeafLowProfile, M> = [M] extends\n | [never]\n | [unknown]\n ? InferMetaFromCfg<L>\n : M\n\ntype ApplyMetaFallback<L extends AnyLeafLowProfile, O> = O extends {\n meta: infer M\n}\n ? (undefined extends ResolvedMeta<L, M>\n ? { meta?: ResolvedMeta<L, M> }\n : { meta: ResolvedMeta<L, M> }) &\n Omit<O, 'meta'>\n : O\n\nexport type InferOutput<L extends AnyLeafLowProfile, Fallback = never> =\n L['cfg']['outputSchema'] extends RouteSchema<infer O>\n ? ApplyMetaFallback<L, O>\n : L['cfg']['outputSchema'] extends ZodType<infer O>\n ? ApplyMetaFallback<L, O>\n : Fallback\n\n/** Render a type as if it were a simple object literal. */\nexport type Prettify<T> = { [K in keyof T]: T[K] }\n","import { AnyLeafLowProfile, HttpMethod, Prettify } from './routesV3.core'\n\n/** Build the key type for a leaf — distributive so method/path stay paired. */\nexport type KeyOf<L extends AnyLeafLowProfile> = L extends AnyLeafLowProfile\n ? `${Uppercase<L['method']>} ${L['path']}`\n : never\n\n// From a tuple of leaves, get the union of keys (pairwise, no cartesian blow-up)\ntype KeysOf<Leaves extends readonly AnyLeafLowProfile[]> = KeyOf<Leaves[number]>\n\n// Parse method & path out of a key\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}`\n ? Lowercase<M>\n : never\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}`\n ? P\n : never\n\n// Given Leaves and a Key, pick the exact leaf that matches method+path\ntype LeafForKey<\n Leaves extends readonly AnyLeafLowProfile[],\n K extends string,\n> = Extract<\n Leaves[number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>\n\ntype FilterRoute<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n Acc extends readonly AnyLeafLowProfile[] = [],\n> = T extends readonly [\n infer First extends AnyLeafLowProfile,\n ...infer Rest extends AnyLeafLowProfile[],\n]\n ? First extends { path: `${string}${F}${string}` }\n ? FilterRoute<Rest, F, [...Acc, First]>\n : FilterRoute<Rest, F, Acc>\n : Acc\n\ntype UpperCase<S extends string> = S extends `${infer First}${infer Rest}`\n ? `${Uppercase<First>}${UpperCase<Rest>}`\n : S\n\ntype Routes<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n> = FilterRoute<T, F>\ntype ByKey<\n T extends readonly AnyLeafLowProfile[],\n Acc extends Record<string, AnyLeafLowProfile> = {},\n> = T extends readonly [\n infer First extends AnyLeafLowProfile,\n ...infer Rest extends AnyLeafLowProfile[],\n]\n ? ByKey<\n Rest,\n Acc & { [K in `${UpperCase<First['method']>} ${First['path']}`]: First }\n >\n : Acc\n\n/**\n * Convenience helper for extracting a subset of routes by path fragment.\n * @param T Tuple of leaves produced by the DSL.\n * @param F String fragment to match against the route path.\n */\nexport type SubsetRoutes<\n T extends readonly AnyLeafLowProfile[],\n F extends string,\n> = {\n byKey: ByKey<Routes<T, F>>\n all: Routes<T, F>\n}\n\n// In the same module as finalize\n\nexport interface FinalizedRegistry<L extends readonly AnyLeafLowProfile[]> {\n all: L\n byKey: { [K in KeysOf<L>]: Prettify<LeafForKey<L, K>> }\n log(logger: { system: (...args: unknown[]) => void }): void\n}\n\n// Optionally keep this alias if you like the name\nexport type Registry<L extends readonly AnyLeafLowProfile[]> =\n FinalizedRegistry<L>\n\nexport function finalize<const L extends readonly AnyLeafLowProfile[]>(\n leaves: L,\n): FinalizedRegistry<L> {\n type Keys = KeysOf<L>\n type MapByKey = { [K in Keys]: Prettify<LeafForKey<L, K>> }\n\n const byKey = Object.fromEntries(\n leaves.map((l) => [`${l.method.toUpperCase()} ${l.path}`, l] as const),\n ) as unknown as MapByKey\n\n const log = (logger: { system: (...args: unknown[]) => void }) => {\n logger.system('Finalized routes:')\n ;(Object.keys(byKey) as Keys[]).forEach((k) => {\n const leaf = byKey[k]\n logger.system(`- ${k}`)\n })\n }\n\n return { all: leaves, byKey, log }\n}\n","// socket.index.ts (shared client/server)\n\nimport { z } from 'zod'\n\nexport type SocketSchema<Output = unknown> = z.ZodType & {\n __out: Output\n}\n\nexport type SocketSchemaOutput<Schema extends z.ZodTypeAny> = Schema extends {\n __out: infer Out\n}\n ? Out\n : z.output<Schema>\n\nexport type SocketEvent<Out = unknown> = {\n message: z.ZodTypeAny\n /** phantom field, only for typing; not meant to be used at runtime */\n __out: Out\n}\n\nexport type EventMap = Record<string, SocketEvent<any>>\n\n/**\n * System event names – shared so server and client\n * don't drift on naming.\n */\nexport type SysEventName =\n | 'sys:connect'\n | 'sys:disconnect'\n | 'sys:reconnect'\n | 'sys:connect_error'\n | 'sys:ping'\n | 'sys:pong'\n | 'sys:room_join'\n | 'sys:room_leave'\nexport function defineSocketEvents<\n const C extends SocketConnectionConfig,\n const Schemas extends Record<\n string,\n {\n message: z.ZodTypeAny\n }\n >,\n>(\n config: C,\n events: Schemas,\n): {\n config: {\n [K in keyof C]: SocketSchema<z.output<C[K]>>\n }\n events: {\n [K in keyof Schemas]: SocketEvent<z.output<Schemas[K]['message']>>\n }\n}\n\nexport function defineSocketEvents(config: any, events: any) {\n return { config, events }\n}\n\nexport type Payload<T extends EventMap, K extends keyof T> =\n T[K] extends SocketEvent<infer Out> ? Out : never\nexport type SocketConnectionConfig = {\n joinMetaMessage: z.ZodTypeAny\n leaveMetaMessage: z.ZodTypeAny\n pingPayload: z.ZodTypeAny\n pongPayload: z.ZodTypeAny\n}\n\nexport type SocketConnectionConfigOutput = {\n joinMetaMessage: SocketSchema<any>\n leaveMetaMessage: SocketSchema<any>\n pingPayload: SocketSchema<any>\n pongPayload: SocketSchema<any>\n}\n"],"mappings":"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","names":["z","z"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emeryld/rrroutes-contract",
|
|
3
3
|
"description": "TypeScript contract definitions for RRRoutes",
|
|
4
|
-
"version": "2.5.
|
|
4
|
+
"version": "2.5.11",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.cjs",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"build": "pnpm run clean && pnpm install && pnpm run build:js && pnpm run build:types",
|
|
33
33
|
"build:js": "tsup --config tsup.config.ts",
|
|
34
34
|
"build:types": "tsc -p tsconfig.build.json",
|
|
35
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
35
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
36
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --config ../../jest.base.config.js --watchman=false --runInBand"
|
|
36
37
|
}
|
|
37
38
|
}
|