@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.
- package/dist/{chunk-GGSAAZPM.js → chunk-AUMHMWDD.js} +19 -20
- package/dist/chunk-AUMHMWDD.js.map +1 -0
- package/dist/chunk-H5TNDC5U.js +138 -0
- package/dist/chunk-H5TNDC5U.js.map +1 -0
- package/dist/chunk-MF2JDREK.js +168 -0
- package/dist/chunk-MF2JDREK.js.map +1 -0
- package/dist/{chunk-PUVAB3JX.js → chunk-XIRJ73IO.js} +38 -36
- package/dist/chunk-XIRJ73IO.js.map +1 -0
- package/dist/{chunk-DS7TE6KZ.js → chunk-XP6PLTV2.js} +11 -3
- package/dist/chunk-XP6PLTV2.js.map +1 -0
- package/dist/global.js +3 -3
- package/dist/http/index.cjs +563 -93
- package/dist/http/index.cjs.map +1 -1
- package/dist/http/index.d.cts +4 -3
- package/dist/http/index.d.ts +4 -3
- package/dist/http/index.js +30 -12
- package/dist/http/openapi/index.cjs +43 -35
- package/dist/http/openapi/index.cjs.map +1 -1
- package/dist/http/openapi/index.d.cts +8 -34
- package/dist/http/openapi/index.d.ts +8 -34
- package/dist/http/openapi/index.js +2 -2
- package/dist/http/route/index.cjs +106 -9
- package/dist/http/route/index.cjs.map +1 -1
- package/dist/http/route/index.d.cts +133 -227
- package/dist/http/route/index.d.ts +133 -227
- package/dist/http/route/index.js +5 -2
- package/dist/http/server/index.cjs +24 -19
- package/dist/http/server/index.cjs.map +1 -1
- package/dist/http/server/index.d.cts +1 -1
- package/dist/http/server/index.d.ts +1 -1
- package/dist/http/server/index.js +2 -2
- package/dist/http/shared/index.cjs.map +1 -1
- package/dist/http/shared/index.d.cts +10 -14
- package/dist/http/shared/index.d.ts +10 -14
- package/dist/http/shared/index.js +11 -127
- package/dist/http/shared/index.js.map +1 -1
- package/dist/index.js +6 -6
- package/dist/{router-definition.type-ynBhT16T.d.cts → router-definition.type-BElX-Pl4.d.cts} +169 -256
- package/dist/{router-definition.type-DORVlLNk.d.ts → router-definition.type-DxG8ncJZ.d.ts} +169 -256
- package/package.json +1 -1
- package/dist/chunk-BZULBF4N.js +0 -82
- package/dist/chunk-BZULBF4N.js.map +0 -1
- package/dist/chunk-DS7TE6KZ.js.map +0 -1
- package/dist/chunk-GGSAAZPM.js.map +0 -1
- 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,
|
|
3
|
-
export { C as ContentType,
|
|
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
|
-
*
|
|
10
|
-
*
|
|
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
|
|
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
|
-
|
|
69
|
-
readonly
|
|
70
|
-
|
|
71
|
-
readonly
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
type
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
102
|
-
*
|
|
103
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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({
|
|
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
|
|
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
|
-
* -
|
|
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
|
|
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
|
-
*
|
|
272
|
-
*
|
|
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: '/
|
|
292
|
-
*
|
|
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
|
-
|
|
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 };
|
package/dist/http/route/index.js
CHANGED
|
@@ -2,22 +2,25 @@ import {
|
|
|
2
2
|
defineRoute,
|
|
3
3
|
defineRouter,
|
|
4
4
|
mergeRouters
|
|
5
|
-
} from "../../chunk-
|
|
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-
|
|
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 && "
|
|
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
|
|
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
|
|
500
|
-
const result = route.request.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
|
|
519
|
+
if (route.request.query) {
|
|
513
520
|
const queryObj = normalizeQuery(rawRequest.query);
|
|
514
|
-
const result = route.request.query.
|
|
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
|
|
527
|
-
const result = route.request.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
|
|
548
|
+
if (route.request.headers) {
|
|
542
549
|
const headersObj = normalizeHeaders(rawRequest.headers);
|
|
543
|
-
const result = route.request.headers.
|
|
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
|
-
|
|
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
|
|
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
|
|
587
|
+
const contextSchema = route.request.context;
|
|
583
588
|
if (!contextSchema) {
|
|
584
589
|
return { success: true, data: context };
|
|
585
590
|
}
|