@cosmneo/onion-lasagna 0.2.0 → 0.2.1

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.
Files changed (45) hide show
  1. package/dist/{chunk-GGSAAZPM.js → chunk-AUMHMWDD.js} +19 -20
  2. package/dist/chunk-AUMHMWDD.js.map +1 -0
  3. package/dist/chunk-H5TNDC5U.js +138 -0
  4. package/dist/chunk-H5TNDC5U.js.map +1 -0
  5. package/dist/chunk-MF2JDREK.js +168 -0
  6. package/dist/chunk-MF2JDREK.js.map +1 -0
  7. package/dist/{chunk-PUVAB3JX.js → chunk-XIRJ73IO.js} +38 -36
  8. package/dist/chunk-XIRJ73IO.js.map +1 -0
  9. package/dist/{chunk-DS7TE6KZ.js → chunk-XP6PLTV2.js} +11 -3
  10. package/dist/chunk-XP6PLTV2.js.map +1 -0
  11. package/dist/global.js +3 -3
  12. package/dist/http/index.cjs +563 -93
  13. package/dist/http/index.cjs.map +1 -1
  14. package/dist/http/index.d.cts +4 -3
  15. package/dist/http/index.d.ts +4 -3
  16. package/dist/http/index.js +30 -12
  17. package/dist/http/openapi/index.cjs +43 -35
  18. package/dist/http/openapi/index.cjs.map +1 -1
  19. package/dist/http/openapi/index.d.cts +8 -34
  20. package/dist/http/openapi/index.d.ts +8 -34
  21. package/dist/http/openapi/index.js +2 -2
  22. package/dist/http/route/index.cjs +106 -9
  23. package/dist/http/route/index.cjs.map +1 -1
  24. package/dist/http/route/index.d.cts +133 -227
  25. package/dist/http/route/index.d.ts +133 -227
  26. package/dist/http/route/index.js +5 -2
  27. package/dist/http/server/index.cjs +24 -19
  28. package/dist/http/server/index.cjs.map +1 -1
  29. package/dist/http/server/index.d.cts +1 -1
  30. package/dist/http/server/index.d.ts +1 -1
  31. package/dist/http/server/index.js +2 -2
  32. package/dist/http/shared/index.cjs.map +1 -1
  33. package/dist/http/shared/index.d.cts +10 -14
  34. package/dist/http/shared/index.d.ts +10 -14
  35. package/dist/http/shared/index.js +11 -127
  36. package/dist/http/shared/index.js.map +1 -1
  37. package/dist/index.js +6 -6
  38. package/dist/{router-definition.type-ynBhT16T.d.cts → router-definition.type-BElX-Pl4.d.cts} +169 -256
  39. package/dist/{router-definition.type-DORVlLNk.d.ts → router-definition.type-DxG8ncJZ.d.ts} +169 -256
  40. package/package.json +1 -1
  41. package/dist/chunk-BZULBF4N.js +0 -82
  42. package/dist/chunk-BZULBF4N.js.map +0 -1
  43. package/dist/chunk-DS7TE6KZ.js.map +0 -1
  44. package/dist/chunk-GGSAAZPM.js.map +0 -1
  45. package/dist/chunk-PUVAB3JX.js.map +0 -1
@@ -1,77 +1,96 @@
1
1
  import { SchemaAdapter, InferOutput } from '../schema/types.js';
2
- import { H as HttpMethod, R as RouteDefinition, P as PathParams, h as ResponsesConfig, j as RouterConfig, k as RouterDefinition, D as DeepMergeTwo, n as DeepMergeAll } from '../../router-definition.type-DORVlLNk.js';
3
- export { C as ContentType, L as ContextConfig, N as ExternalDocs, E as ExtractPathParamNames, F as FlattenRouter, G as GetRoute, b as HasPathParams, f as HeadersConfig, a as HttpStatusCode, I as InferRouteBody, O as InferRouteContext, r as InferRouteHeaders, u as InferRouteMethod, v as InferRoutePath, q as InferRoutePathParams, p as InferRouteQuery, s as InferRouteResponse, t as InferRouteSuccessResponse, e as PathParamsConfig, o as PrettifyDeep, Q as QueryParamsConfig, d as RequestBodyConfig, M as RequestConfig, g as ResponseConfig, c as RouteDefinitionInput, i as RouteDocumentation, l as RouterEntry, m as RouterKeys, S as SecurityRequirement, z as buildPath, y as collectRoutes, J as getPathParamNames, K as hasPathParams, w as isRouteDefinition, x as isRouterDefinition, A as normalizePath, B as pathToRegex } from '../../router-definition.type-DORVlLNk.js';
2
+ import { H as HttpMethod, y as ResponsesDefinition, S as SchemaFieldInput, R as RouteDefinition, P as PathParams, z as ExtractSuccessSchema, d as RouterConfig, A as RouterDefaults, e as RouterDefinition, D as DeepMergeTwo, h as DeepMergeAll } from '../../router-definition.type-DxG8ncJZ.js';
3
+ export { C as ContentType, M as ExternalDocs, E as ExtractPathParamNames, F as FlattenRouter, G as GetRoute, b as HasPathParams, a as HttpStatusCode, I as InferRouteBody, m as InferRouteContext, l as InferRouteHeaders, o as InferRouteMethod, p as InferRoutePath, k as InferRoutePathParams, j as InferRouteQuery, n as InferRouteResponse, i as PrettifyDeep, K as ResponseFieldConfig, c as RouteDocumentation, J as RouteFieldMeta, N as RouteRequestDefinition, f as RouterEntry, g as RouterKeys, B as SchemaFieldConfig, L as SecurityRequirement, t as buildPath, s as collectRoutes, w as getPathParamNames, x as hasPathParams, q as isRouteDefinition, r as isRouterDefinition, u as normalizePath, v as pathToRegex } from '../../router-definition.type-DxG8ncJZ.js';
4
4
 
5
5
  /**
6
- * @fileoverview Factory function for creating route definitions.
6
+ * @fileoverview Factory function for creating route definitions (v2).
7
7
  *
8
- * The `defineRoute` function is the main entry point for defining routes
9
- * in the unified system. It provides type inference and validation
10
- * for route configurations.
8
+ * The `defineRoute` function is the main entry point for defining routes.
9
+ * Request schemas are grouped under a `request` field, while responses
10
+ * are defined via the `responses` field with per-status-code config.
11
+ *
12
+ * Each schema field accepts either a bare `SchemaAdapter` or an object with
13
+ * metadata: `{ schema, description?, contentType?, required? }`.
14
+ *
15
+ * The success response type is inferred from the first 2xx status with a schema.
11
16
  *
12
17
  * @module unified/route/define-route
13
18
  */
14
19
 
15
- /**
16
- * Request body configuration input.
17
- */
18
- interface BodyInput<TSchema extends SchemaAdapter> {
19
- readonly schema: TSchema;
20
- readonly contentType?: string;
21
- readonly required?: boolean;
22
- readonly description?: string;
23
- }
24
- /**
25
- * Query parameters configuration input.
26
- */
27
- interface QueryInput<TSchema extends SchemaAdapter> {
28
- readonly schema: TSchema;
29
- readonly description?: string;
30
- }
31
- /**
32
- * Path parameters configuration input.
33
- */
34
- interface ParamsInput<TSchema extends SchemaAdapter> {
35
- readonly schema: TSchema;
36
- readonly description?: string;
37
- }
38
- /**
39
- * Headers configuration input.
40
- */
41
- interface HeadersInput<TSchema extends SchemaAdapter> {
42
- readonly schema: TSchema;
43
- readonly description?: string;
44
- }
45
- /**
46
- * Context configuration input.
47
- * Used to validate and type context data from middleware (e.g., JWT payload).
48
- */
49
- interface ContextInput<TSchema extends SchemaAdapter> {
50
- readonly schema: TSchema;
51
- readonly description?: string;
52
- }
53
- /**
54
- * Response configuration input.
55
- */
56
- interface ResponseInput<TSchema extends SchemaAdapter | undefined = undefined> {
57
- readonly description: string;
58
- readonly schema?: TSchema;
59
- readonly contentType?: string;
60
- }
61
20
  /**
62
21
  * Complete input for defineRoute.
22
+ *
23
+ * @example GET with query and response
24
+ * ```typescript
25
+ * defineRoute({
26
+ * method: 'GET',
27
+ * path: '/api/users',
28
+ * request: {
29
+ * query: zodSchema(z.object({ page: z.coerce.number().default(1) })),
30
+ * },
31
+ * responses: {
32
+ * 200: { schema: zodSchema(userListSchema) },
33
+ * },
34
+ * docs: { summary: 'List users', tags: ['Users'] },
35
+ * })
36
+ * ```
37
+ *
38
+ * @example POST with body and multiple statuses
39
+ * ```typescript
40
+ * defineRoute({
41
+ * method: 'POST',
42
+ * path: '/api/users',
43
+ * request: {
44
+ * body: zodSchema(createUserSchema),
45
+ * },
46
+ * responses: {
47
+ * 201: { schema: zodSchema(userSchema), description: 'Created' },
48
+ * 400: { description: 'Validation error' },
49
+ * 409: { description: 'Email already in use' },
50
+ * },
51
+ * })
52
+ * ```
53
+ *
54
+ * @example DELETE with 204
55
+ * ```typescript
56
+ * defineRoute({
57
+ * method: 'DELETE',
58
+ * path: '/api/users/:userId',
59
+ * responses: {
60
+ * 204: { description: 'User deleted' },
61
+ * 404: { description: 'User not found' },
62
+ * },
63
+ * })
64
+ * ```
63
65
  */
64
- interface DefineRouteInput<TMethod extends HttpMethod, TPath extends string, TBody extends SchemaAdapter | undefined = undefined, TQuery extends SchemaAdapter | undefined = undefined, TParams extends SchemaAdapter | undefined = undefined, THeaders extends SchemaAdapter | undefined = undefined, TContext extends SchemaAdapter | undefined = undefined, TResponses extends Record<number, ResponseInput<SchemaAdapter | undefined>> = Record<number, never>> {
66
+ interface DefineRouteInput<TMethod extends HttpMethod, TPath extends string, TBody extends SchemaAdapter | undefined = undefined, TQuery extends SchemaAdapter | undefined = undefined, TParams extends SchemaAdapter | undefined = undefined, THeaders extends SchemaAdapter | undefined = undefined, TContext extends SchemaAdapter | undefined = undefined, TResponses extends ResponsesDefinition | undefined = undefined> {
67
+ /** HTTP method for this route. */
65
68
  readonly method: TMethod;
69
+ /** URL path pattern with optional parameters (`:param` or `{param}`). */
66
70
  readonly path: TPath;
71
+ /** Request schemas (body, query, params, headers, context). */
67
72
  readonly request?: {
68
- readonly body?: TBody extends SchemaAdapter ? BodyInput<TBody> : undefined;
69
- readonly query?: TQuery extends SchemaAdapter ? QueryInput<TQuery> : undefined;
70
- readonly params?: TParams extends SchemaAdapter ? ParamsInput<TParams> : undefined;
71
- readonly headers?: THeaders extends SchemaAdapter ? HeadersInput<THeaders> : undefined;
72
- readonly context?: TContext extends SchemaAdapter ? ContextInput<TContext> : undefined;
73
+ /** Request body schema (or `{ schema, description?, contentType?, required? }`). */
74
+ readonly body?: TBody extends SchemaAdapter ? SchemaFieldInput<TBody> : TBody;
75
+ /** Query parameters schema (or `{ schema, description? }`). */
76
+ readonly query?: TQuery extends SchemaAdapter ? SchemaFieldInput<TQuery> : TQuery;
77
+ /** Path parameters schema (or `{ schema, description? }`). If omitted, inferred from path template. */
78
+ readonly params?: TParams extends SchemaAdapter ? SchemaFieldInput<TParams> : TParams;
79
+ /** Headers schema (or `{ schema, description? }`). */
80
+ readonly headers?: THeaders extends SchemaAdapter ? SchemaFieldInput<THeaders> : THeaders;
81
+ /** Context schema (or `{ schema, description? }`). */
82
+ readonly context?: TContext extends SchemaAdapter ? SchemaFieldInput<TContext> : TContext;
73
83
  };
74
- readonly responses: TResponses;
84
+ /**
85
+ * Per-status response definitions.
86
+ *
87
+ * Each key is an HTTP status code. Each value can have:
88
+ * - `schema` — response body schema (drives type inference for 2xx)
89
+ * - `description` — OpenAPI response description
90
+ * - `contentType` — response content type (default: `application/json`)
91
+ */
92
+ readonly responses?: TResponses;
93
+ /** OpenAPI documentation. */
75
94
  readonly docs?: {
76
95
  readonly summary?: string;
77
96
  readonly description?: string;
@@ -85,109 +104,29 @@ interface DefineRouteInput<TMethod extends HttpMethod, TPath extends string, TBo
85
104
  };
86
105
  };
87
106
  }
88
- /**
89
- * Converts response inputs to response config type.
90
- */
91
- type ToResponsesConfig<T extends Record<number, ResponseInput<SchemaAdapter | undefined>>> = {
92
- [K in keyof T]: T[K] extends ResponseInput<infer TSchema> ? {
93
- description: string;
94
- schema: TSchema extends SchemaAdapter ? TSchema : undefined;
95
- contentType?: string;
96
- } : never;
97
- };
107
+ type ResolveBody<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;
108
+ type ResolveQuery<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;
109
+ type ResolveParams<T, TPath extends string> = T extends SchemaAdapter ? InferOutput<T> : PathParams<TPath>;
110
+ type ResolveHeaders<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;
111
+ type ResolveContext<T> = T extends SchemaAdapter ? InferOutput<T> : undefined;
112
+ type ResolveResponse<T> = T extends ResponsesDefinition ? ExtractSuccessSchema<T> extends SchemaAdapter ? InferOutput<ExtractSuccessSchema<T>> : undefined : undefined;
98
113
  /**
99
114
  * Creates a route definition from the provided configuration.
100
115
  *
101
- * This is the main entry point for defining routes in the unified system.
102
- * The resulting definition can be used for:
103
- * - Type-safe client generation
104
- * - Server-side route registration
105
- * - OpenAPI specification generation
116
+ * This is the main entry point for defining routes. The resulting definition
117
+ * can be used for type-safe client generation, server-side route registration,
118
+ * and OpenAPI specification generation.
106
119
  *
107
- * @param input - Route configuration
120
+ * @param input - Route configuration with request schemas and responses
108
121
  * @returns A frozen RouteDefinition object with full type information
109
- *
110
- * @example Basic GET route
111
- * ```typescript
112
- * import { defineRoute } from '@cosmneo/onion-lasagna/http';
113
- * import { zodSchema, z } from '@cosmneo/onion-lasagna/http/schema/zod';
114
- *
115
- * const listUsers = defineRoute({
116
- * method: 'GET',
117
- * path: '/api/users',
118
- * request: {
119
- * query: {
120
- * schema: zodSchema(z.object({
121
- * page: z.coerce.number().optional().default(1),
122
- * limit: z.coerce.number().optional().default(20),
123
- * })),
124
- * },
125
- * },
126
- * responses: {
127
- * 200: {
128
- * description: 'List of users',
129
- * schema: zodSchema(z.object({
130
- * users: z.array(z.object({
131
- * id: z.string(),
132
- * name: z.string(),
133
- * })),
134
- * total: z.number(),
135
- * })),
136
- * },
137
- * },
138
- * docs: {
139
- * summary: 'List all users',
140
- * tags: ['Users'],
141
- * },
142
- * });
143
- * ```
144
- *
145
- * @example POST route with path parameters
146
- * ```typescript
147
- * const createTask = defineRoute({
148
- * method: 'POST',
149
- * path: '/api/projects/:projectId/tasks',
150
- * request: {
151
- * body: {
152
- * schema: zodSchema(z.object({
153
- * title: z.string().min(1).max(200),
154
- * description: z.string().optional(),
155
- * })),
156
- * },
157
- * },
158
- * responses: {
159
- * 201: {
160
- * description: 'Task created',
161
- * schema: zodSchema(z.object({
162
- * id: z.string().uuid(),
163
- * title: z.string(),
164
- * projectId: z.string(),
165
- * createdAt: z.string().datetime(),
166
- * })),
167
- * },
168
- * 400: {
169
- * description: 'Invalid request',
170
- * },
171
- * 404: {
172
- * description: 'Project not found',
173
- * },
174
- * },
175
- * docs: {
176
- * summary: 'Create a new task',
177
- * tags: ['Tasks'],
178
- * operationId: 'createTask',
179
- * },
180
- * });
181
- * ```
182
122
  */
183
- declare function defineRoute<TMethod extends HttpMethod, TPath extends string, TBody extends SchemaAdapter | undefined = undefined, TQuery extends SchemaAdapter | undefined = undefined, TParams extends SchemaAdapter | undefined = undefined, THeaders extends SchemaAdapter | undefined = undefined, TContext extends SchemaAdapter | undefined = undefined, TResponses extends Record<number, ResponseInput<SchemaAdapter | undefined>> = Record<number, never>>(input: DefineRouteInput<TMethod, TPath, TBody, TQuery, TParams, THeaders, TContext, TResponses>): RouteDefinition<TMethod, TPath, TBody extends SchemaAdapter ? InferOutput<TBody> : undefined, TQuery extends SchemaAdapter ? InferOutput<TQuery> : undefined, TParams extends SchemaAdapter ? InferOutput<TParams> : PathParams<TPath>, THeaders extends SchemaAdapter ? InferOutput<THeaders> : undefined, TContext extends SchemaAdapter ? InferOutput<TContext> : undefined, ToResponsesConfig<TResponses> & ResponsesConfig>;
123
+ declare function defineRoute<TMethod extends HttpMethod, TPath extends string, TBody extends SchemaAdapter | undefined = undefined, TQuery extends SchemaAdapter | undefined = undefined, TParams extends SchemaAdapter | undefined = undefined, THeaders extends SchemaAdapter | undefined = undefined, TContext extends SchemaAdapter | undefined = undefined, TResponses extends ResponsesDefinition | undefined = undefined>(input: DefineRouteInput<TMethod, TPath, TBody, TQuery, TParams, THeaders, TContext, TResponses>): RouteDefinition<TMethod, TPath, ResolveBody<TBody>, ResolveQuery<TQuery>, ResolveParams<TParams, TPath>, ResolveHeaders<THeaders>, ResolveContext<TContext>, ResolveResponse<TResponses>>;
184
124
 
185
125
  /**
186
126
  * @fileoverview Factory function for creating router definitions.
187
127
  *
188
128
  * The `defineRouter` function groups routes into a hierarchical structure
189
- * that enables organized API clients, OpenAPI tag grouping, and structured
190
- * server route registration.
129
+ * with optional router-level defaults for context and tags.
191
130
  *
192
131
  * @module unified/route/define-router
193
132
  */
@@ -199,18 +138,25 @@ interface DefineRouterOptions {
199
138
  /**
200
139
  * Base path prefix for all routes in this router.
201
140
  * Will be prepended to all route paths.
141
+ */
142
+ readonly basePath?: string;
143
+ /**
144
+ * Default values applied to all child routes.
202
145
  *
203
146
  * @example
204
147
  * ```typescript
205
- * defineRouter({ ... }, { basePath: '/api/v1' })
148
+ * defineRouter({
149
+ * list: listUsersRoute,
150
+ * get: getUserRoute,
151
+ * }, {
152
+ * defaults: {
153
+ * context: zodSchema(executionContextSchema),
154
+ * tags: ['Users'],
155
+ * },
156
+ * })
206
157
  * ```
207
158
  */
208
- readonly basePath?: string;
209
- /**
210
- * Default tags for all routes in this router.
211
- * Will be merged with route-specific tags.
212
- */
213
- readonly tags?: readonly string[];
159
+ readonly defaults?: RouterDefaults;
214
160
  }
215
161
  /**
216
162
  * Creates a router definition from a configuration object.
@@ -219,7 +165,7 @@ interface DefineRouterOptions {
219
165
  * - Organized API structure with nested namespaces
220
166
  * - Typed client method generation
221
167
  * - OpenAPI tag grouping
222
- * - Server route registration
168
+ * - Router-level defaults for context and tags
223
169
  *
224
170
  * @param routes - Object containing routes and nested routers
225
171
  * @param options - Optional router configuration
@@ -227,90 +173,33 @@ interface DefineRouterOptions {
227
173
  *
228
174
  * @example Basic router
229
175
  * ```typescript
230
- * import { defineRouter } from '@cosmneo/onion-lasagna/http';
231
- *
232
176
  * const api = defineRouter({
233
177
  * users: {
234
178
  * list: listUsersRoute,
235
179
  * get: getUserRoute,
236
180
  * create: createUserRoute,
237
- * update: updateUserRoute,
238
- * delete: deleteUserRoute,
239
- * },
240
- * posts: {
241
- * list: listPostsRoute,
242
- * get: getPostRoute,
243
- * create: createPostRoute,
244
181
  * },
245
182
  * });
246
- *
247
- * // Client usage:
248
- * // client.users.list({ query: { page: 1 } })
249
- * // client.users.get({ params: { id: '123' } })
250
- * // client.posts.create({ body: { title: 'Hello' } })
251
183
  * ```
252
184
  *
253
- * @example Nested routers
185
+ * @example With router-level defaults
254
186
  * ```typescript
255
- * const projectRouter = defineRouter({
256
- * list: listProjectsRoute,
257
- * get: getProjectRoute,
258
- * tasks: {
259
- * list: listTasksRoute,
260
- * create: createTaskRoute,
261
- * update: updateTaskRoute,
262
- * },
263
- * members: {
264
- * list: listMembersRoute,
265
- * add: addMemberRoute,
266
- * remove: removeMemberRoute,
267
- * },
268
- * });
269
- *
270
187
  * const api = defineRouter({
271
- * projects: projectRouter.routes,
272
- * users: userRouter.routes,
273
- * });
274
- *
275
- * // Client usage:
276
- * // client.projects.tasks.create({ params: { projectId: '123' }, body: { ... } })
277
- * ```
278
- *
279
- * @example With base path and tags
280
- * ```typescript
281
- * const adminApi = defineRouter({
282
- * users: {
283
- * list: listUsersRoute,
284
- * ban: banUserRoute,
285
- * },
286
- * analytics: {
287
- * dashboard: getDashboardRoute,
288
- * reports: getReportsRoute,
289
- * },
188
+ * list: listUsersRoute,
189
+ * get: getUserRoute,
290
190
  * }, {
291
- * basePath: '/admin',
292
- * tags: ['Admin'],
191
+ * basePath: '/api/v1',
192
+ * defaults: {
193
+ * context: zodSchema(executionContextSchema),
194
+ * tags: ['Users'],
195
+ * },
293
196
  * });
197
+ * // Context is applied to all routes that don't define their own.
198
+ * // Tags are merged with each route's existing tags.
294
199
  * ```
295
200
  */
296
201
  declare function defineRouter<T extends RouterConfig>(routes: T, options?: DefineRouterOptions): RouterDefinition<T>;
297
202
  type RouterInput<T extends RouterConfig> = T | RouterDefinition<T>;
298
- /**
299
- * Merges multiple routers into a single router with deep merge.
300
- *
301
- * Overlapping sub-router keys are recursively merged instead of overwritten.
302
- * Leaf routes (RouteDefinition) use last-one-wins semantics.
303
- *
304
- * @example
305
- * ```typescript
306
- * const api = mergeRouters(
307
- * userRouter,
308
- * organizationRouter,
309
- * feedbackRouter,
310
- * );
311
- * // If all three define `users`, their sub-routes are deep-merged.
312
- * ```
313
- */
314
203
  declare function mergeRouters<T1 extends RouterConfig, T2 extends RouterConfig>(r1: RouterInput<T1>, r2: RouterInput<T2>): RouterDefinition<DeepMergeTwo<T1, T2>>;
315
204
  declare function mergeRouters<T1 extends RouterConfig, T2 extends RouterConfig, T3 extends RouterConfig>(r1: RouterInput<T1>, r2: RouterInput<T2>, r3: RouterInput<T3>): RouterDefinition<DeepMergeAll<[T1, T2, T3]>>;
316
205
  declare function mergeRouters<T1 extends RouterConfig, T2 extends RouterConfig, T3 extends RouterConfig, T4 extends RouterConfig>(r1: RouterInput<T1>, r2: RouterInput<T2>, r3: RouterInput<T3>, r4: RouterInput<T4>): RouterDefinition<DeepMergeAll<[T1, T2, T3, T4]>>;
@@ -320,4 +209,21 @@ declare function mergeRouters<T1 extends RouterConfig, T2 extends RouterConfig,
320
209
  declare function mergeRouters<T1 extends RouterConfig, T2 extends RouterConfig, T3 extends RouterConfig, T4 extends RouterConfig, T5 extends RouterConfig, T6 extends RouterConfig, T7 extends RouterConfig, T8 extends RouterConfig>(r1: RouterInput<T1>, r2: RouterInput<T2>, r3: RouterInput<T3>, r4: RouterInput<T4>, r5: RouterInput<T5>, r6: RouterInput<T6>, r7: RouterInput<T7>, r8: RouterInput<T8>): RouterDefinition<DeepMergeAll<[T1, T2, T3, T4, T5, T6, T7, T8]>>;
321
210
  declare function mergeRouters(...routers: RouterInput<RouterConfig>[]): RouterDefinition<RouterConfig>;
322
211
 
323
- export { DeepMergeAll, DeepMergeTwo, type DefineRouterOptions, HttpMethod, PathParams, ResponsesConfig, RouteDefinition, RouterConfig, RouterDefinition, defineRoute, defineRouter, mergeRouters };
212
+ /**
213
+ * @fileoverview Route utility functions.
214
+ *
215
+ * @module unified/route/utils
216
+ */
217
+ /**
218
+ * Generates an operationId from a router key path.
219
+ *
220
+ * Converts dotted key paths to camelCase:
221
+ * - `"users.list"` → `"usersList"`
222
+ * - `"organizations.members.get"` → `"organizationsMembersGet"`
223
+ *
224
+ * @param key - The dotted router key path
225
+ * @returns A camelCase operationId string
226
+ */
227
+ declare function generateOperationId(key: string): string;
228
+
229
+ export { DeepMergeAll, DeepMergeTwo, type DefineRouterOptions, ExtractSuccessSchema, HttpMethod, PathParams, ResponsesDefinition, RouteDefinition, RouterConfig, RouterDefaults, RouterDefinition, SchemaFieldInput, defineRoute, defineRouter, generateOperationId, mergeRouters };
@@ -2,22 +2,25 @@ import {
2
2
  defineRoute,
3
3
  defineRouter,
4
4
  mergeRouters
5
- } from "../../chunk-BZULBF4N.js";
5
+ } from "../../chunk-MF2JDREK.js";
6
6
  import {
7
7
  buildPath,
8
8
  collectRoutes,
9
+ generateOperationId,
9
10
  getPathParamNames,
10
11
  hasPathParams,
11
12
  isRouteDefinition,
12
13
  isRouterDefinition,
13
14
  normalizePath,
14
15
  pathToRegex
15
- } from "../../chunk-DS7TE6KZ.js";
16
+ } from "../../chunk-XP6PLTV2.js";
17
+ import "../../chunk-42JMR62O.js";
16
18
  export {
17
19
  buildPath,
18
20
  collectRoutes,
19
21
  defineRoute,
20
22
  defineRouter,
23
+ generateOperationId,
21
24
  getPathParamNames,
22
25
  hasPathParams,
23
26
  isRouteDefinition,
@@ -31,7 +31,7 @@ function normalizePath(path) {
31
31
 
32
32
  // src/presentation/http/route/types/router-definition.type.ts
33
33
  function isRouteDefinition(value) {
34
- return typeof value === "object" && value !== null && "method" in value && "path" in value && "responses" in value && "_types" in value;
34
+ return typeof value === "object" && value !== null && "method" in value && "path" in value && "_types" in value;
35
35
  }
36
36
  function isRouterDefinition(value) {
37
37
  return typeof value === "object" && value !== null && "_isRouter" in value && value._isRouter === true;
@@ -327,6 +327,13 @@ function wrapError(fn, errorFactory) {
327
327
  }
328
328
  }
329
329
 
330
+ // src/presentation/http/route/utils.ts
331
+ function generateOperationId(key) {
332
+ return key.split(".").map(
333
+ (segment, index) => index === 0 ? segment : segment.charAt(0).toUpperCase() + segment.slice(1)
334
+ ).join("");
335
+ }
336
+
330
337
  // src/presentation/http/server/create-server-routes.ts
331
338
  function createServerRoutesInternal(router, handlers, options) {
332
339
  const routes = isRouterDefinition(router) ? router.routes : router;
@@ -349,7 +356,7 @@ function createServerRoutesInternal(router, handlers, options) {
349
356
  `Missing handler for route "${key}". All routes must have a handler configuration.`
350
357
  );
351
358
  }
352
- result.push(createRouteHandler(route, handlerConfig, resolvedOptions));
359
+ result.push(createRouteHandler(key, route, handlerConfig, resolvedOptions));
353
360
  }
354
361
  return result;
355
362
  }
@@ -374,7 +381,7 @@ function sortRoutesBySpecificity(routes) {
374
381
  return 0;
375
382
  });
376
383
  }
377
- function createRouteHandler(route, config, options) {
384
+ function createRouteHandler(key, route, config, options) {
378
385
  const middleware = config.middleware ?? [];
379
386
  const globalMiddleware = options?.middleware ?? [];
380
387
  const allMiddleware = [...globalMiddleware, ...middleware];
@@ -384,7 +391,7 @@ function createRouteHandler(route, config, options) {
384
391
  method: route.method,
385
392
  path: normalizePath(route.path),
386
393
  metadata: {
387
- operationId: route.docs.operationId,
394
+ operationId: route.docs.operationId ?? generateOperationId(key),
388
395
  summary: route.docs.summary,
389
396
  description: route.docs.description,
390
397
  tags: route.docs.tags,
@@ -392,7 +399,7 @@ function createRouteHandler(route, config, options) {
392
399
  },
393
400
  handler: async (rawRequest, ctx) => {
394
401
  const rawContext = options?.createContext ? options.createContext(rawRequest) : ctx ?? { requestId: generateRequestId() };
395
- const validatedContext = route.request.context?.schema ? wrapError(
402
+ const validatedContext = route.request.context ? wrapError(
396
403
  () => {
397
404
  const result = validateContextData(route, rawContext);
398
405
  if (!result.success) {
@@ -496,8 +503,8 @@ function createRouteHandler(route, config, options) {
496
503
  function validateRequestData(route, rawRequest) {
497
504
  const errors = [];
498
505
  const data = {};
499
- if (route.request.body?.schema) {
500
- const result = route.request.body.schema.validate(rawRequest.body);
506
+ if (route.request.body) {
507
+ const result = route.request.body.validate(rawRequest.body);
501
508
  if (result.success) {
502
509
  data.body = result.data;
503
510
  } else {
@@ -509,9 +516,9 @@ function validateRequestData(route, rawRequest) {
509
516
  );
510
517
  }
511
518
  }
512
- if (route.request.query?.schema) {
519
+ if (route.request.query) {
513
520
  const queryObj = normalizeQuery(rawRequest.query);
514
- const result = route.request.query.schema.validate(queryObj);
521
+ const result = route.request.query.validate(queryObj);
515
522
  if (result.success) {
516
523
  data.query = result.data;
517
524
  } else {
@@ -523,8 +530,8 @@ function validateRequestData(route, rawRequest) {
523
530
  );
524
531
  }
525
532
  }
526
- if (route.request.params?.schema) {
527
- const result = route.request.params.schema.validate(rawRequest.params ?? {});
533
+ if (route.request.params) {
534
+ const result = route.request.params.validate(rawRequest.params ?? {});
528
535
  if (result.success) {
529
536
  data.pathParams = result.data;
530
537
  } else {
@@ -538,9 +545,9 @@ function validateRequestData(route, rawRequest) {
538
545
  } else {
539
546
  data.pathParams = normalizePathParams(rawRequest.params);
540
547
  }
541
- if (route.request.headers?.schema) {
548
+ if (route.request.headers) {
542
549
  const headersObj = normalizeHeaders(rawRequest.headers);
543
- const result = route.request.headers.schema.validate(headersObj);
550
+ const result = route.request.headers.validate(headersObj);
544
551
  if (result.success) {
545
552
  data.headers = result.data;
546
553
  } else {
@@ -558,13 +565,11 @@ function validateRequestData(route, rawRequest) {
558
565
  return { success: true, data };
559
566
  }
560
567
  function validateResponseData(route, response) {
561
- const statusCode = String(response.status);
562
- const responses = route.responses;
563
- const responseConfig = responses[statusCode];
564
- if (!responseConfig) {
568
+ if (!route.responses) {
565
569
  return { success: true };
566
570
  }
567
- const schema = responseConfig.schema;
571
+ const entry = route.responses[String(response.status)];
572
+ const schema = entry?.schema;
568
573
  if (!schema) {
569
574
  return { success: true };
570
575
  }
@@ -579,7 +584,7 @@ function validateResponseData(route, response) {
579
584
  return { success: false, errors };
580
585
  }
581
586
  function validateContextData(route, context) {
582
- const contextSchema = route.request.context?.schema;
587
+ const contextSchema = route.request.context;
583
588
  if (!contextSchema) {
584
589
  return { success: true, data: context };
585
590
  }