@minisylar/express-typed-router 1.6.2 → 1.7.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/README.md +269 -774
- package/dist/schema-router.cjs +12 -1
- package/dist/schema-router.d.cts +276 -145
- package/dist/{schema-router.d.ts → schema-router.d.mts} +276 -145
- package/dist/schema-router.mjs +12 -0
- package/package.json +18 -13
- package/dist/schema-router.js +0 -1
|
@@ -2,7 +2,6 @@ import express, { NextFunction, Request, Response } from "express";
|
|
|
2
2
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
3
|
|
|
4
4
|
//#region src/schema-router.d.ts
|
|
5
|
-
|
|
6
5
|
type AnyStandardSchema = StandardSchemaV1<any, any>;
|
|
7
6
|
type InferOutput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : unknown;
|
|
8
7
|
type InferInput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferInput<T> : unknown;
|
|
@@ -120,221 +119,315 @@ interface RouteOptions<BodySchema extends AnyStandardSchema | unknown = unknown,
|
|
|
120
119
|
bodySchema?: BodySchema;
|
|
121
120
|
querySchema?: QuerySchema;
|
|
122
121
|
middleware?: TypedMiddleware<any, any>[];
|
|
122
|
+
tags?: string[];
|
|
123
|
+
description?: string;
|
|
124
|
+
summary?: string;
|
|
125
|
+
deprecated?: boolean;
|
|
126
|
+
responseSchema?: AnyStandardSchema;
|
|
127
|
+
/** Exclude this route from the generated OpenAPI spec entirely. */
|
|
128
|
+
hidden?: boolean;
|
|
123
129
|
}
|
|
130
|
+
type DocMeta = Pick<RouteOptions<unknown, unknown>, "tags" | "summary" | "description" | "deprecated" | "responseSchema" | "hidden">;
|
|
124
131
|
type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "options" | "head" | "all";
|
|
125
|
-
|
|
132
|
+
interface DocsOptions {
|
|
133
|
+
title?: string;
|
|
134
|
+
version?: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
servers?: Array<{
|
|
137
|
+
url: string;
|
|
138
|
+
description?: string;
|
|
139
|
+
}>;
|
|
140
|
+
/**
|
|
141
|
+
* Override the Scalar CDN URL. Use this to pin a specific version or
|
|
142
|
+
* self-host the Scalar bundle to avoid the external CDN dependency.
|
|
143
|
+
* @default "https://cdn.jsdelivr.net/npm/@scalar/api-reference"
|
|
144
|
+
*/
|
|
145
|
+
cdnUrl?: string;
|
|
146
|
+
/**
|
|
147
|
+
* File path to write the OpenAPI spec to whenever it is generated.
|
|
148
|
+
* Enables `openapi-typescript --watch` in development — the tool watches
|
|
149
|
+
* the file and regenerates your client types automatically as routes change.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* // docs options
|
|
153
|
+
* { specOutputPath: './openapi.json' }
|
|
154
|
+
*
|
|
155
|
+
* // then in a separate terminal (or via concurrently in package.json):
|
|
156
|
+
* // npx openapi-typescript ./openapi.json -o ./src/client.d.ts --watch
|
|
157
|
+
*/
|
|
158
|
+
specOutputPath?: string;
|
|
159
|
+
}
|
|
160
|
+
interface RouteMetadata {
|
|
161
|
+
method: HttpMethod;
|
|
162
|
+
path: string;
|
|
163
|
+
bodySchema?: AnyStandardSchema;
|
|
164
|
+
querySchema?: AnyStandardSchema;
|
|
165
|
+
tags?: string[];
|
|
166
|
+
description?: string;
|
|
167
|
+
summary?: string;
|
|
168
|
+
deprecated?: boolean;
|
|
169
|
+
responseSchema?: AnyStandardSchema;
|
|
170
|
+
hidden?: boolean;
|
|
171
|
+
responseSamples: Map<number, unknown>;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Extra properties that middleware has added to the Express `req` object.
|
|
175
|
+
*
|
|
176
|
+
* Starts as `{}` (nothing added yet) and widens automatically with every
|
|
177
|
+
* `.useMiddleware()` call. After `router.useMiddleware(authMiddleware)` where
|
|
178
|
+
* `authMiddleware` contributes `{ userId: string }`, this becomes `{ userId: string }`.
|
|
179
|
+
*
|
|
180
|
+
* You will see this type in router hover text — it is the accumulating "req additions" slot.
|
|
181
|
+
*/
|
|
182
|
+
type AdditionalReqProps = {};
|
|
183
|
+
/**
|
|
184
|
+
* Extra properties that middleware has added to `res.locals`.
|
|
185
|
+
*
|
|
186
|
+
* Starts as `{}` and widens automatically with every `.useMiddleware()` call,
|
|
187
|
+
* mirroring the `TLocals` parameter of each `TypedMiddleware` you attach.
|
|
188
|
+
*/
|
|
189
|
+
type AdditionalLocals = {};
|
|
190
|
+
/**
|
|
191
|
+
* A strongly-typed Express router. The two generic params accumulate as
|
|
192
|
+
* middleware is added via `.useMiddleware()`.
|
|
193
|
+
*
|
|
194
|
+
* @typeParam Req - Extra properties on `req` contributed by middleware. Starts as {@link AdditionalReqProps}.
|
|
195
|
+
* @typeParam Locals - Extra properties on `res.locals` contributed by middleware. Starts as {@link AdditionalLocals}.
|
|
196
|
+
*/
|
|
197
|
+
declare class TypedRouter<Req extends Record<string, any> = AdditionalReqProps, Locals extends Record<string, any> = AdditionalLocals> {
|
|
126
198
|
private router;
|
|
199
|
+
private routes;
|
|
200
|
+
private mountedRouters;
|
|
127
201
|
constructor();
|
|
128
202
|
/**
|
|
129
203
|
* Add typed middleware that extends the request with additional properties
|
|
130
204
|
* and/or adds properties to response.locals
|
|
131
|
-
*/
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
205
|
+
*/
|
|
206
|
+
/**
|
|
207
|
+
* Add typed middleware to the router.
|
|
208
|
+
* This middleware will apply to all routes defined after this call.
|
|
209
|
+
*
|
|
210
|
+
* @template TReq - Type extensions for the request object
|
|
211
|
+
* @template TLocals - Type extensions for response.locals
|
|
212
|
+
* @param middleware - The typed middleware function
|
|
213
|
+
* @returns A new router instance with updated types
|
|
214
|
+
*/
|
|
215
|
+
useMiddleware<TReq extends Record<string, any> = {}, TLocals extends Record<string, any> = {}>(middleware: TypedMiddleware<TReq, TLocals>): TypedRouter<Req & TReq, Locals & TLocals>;
|
|
216
|
+
/**
|
|
217
|
+
* Get the underlying Express router, typed as a RequestHandler so it can be
|
|
218
|
+
* passed directly to app.use() without a cast in Express 5.
|
|
219
|
+
*/
|
|
220
|
+
getRouter(): express.Router & express.RequestHandler;
|
|
221
|
+
/**
|
|
222
|
+
* Mount middleware or a sub-router at an optional path prefix.
|
|
223
|
+
*
|
|
224
|
+
* When passed the result of another TypedRouter's .getRouter(), it is
|
|
225
|
+
* automatically recognised and tracked for .docs() — no extra wiring needed.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* // v1.routes.ts — pass TypedRouter instances directly, no .getRouter() needed
|
|
229
|
+
* export const v1Routes = createTypedRouter()
|
|
230
|
+
*
|
|
231
|
+
* v1Routes.use('/products', productRoutes) // tracked ✓
|
|
232
|
+
* v1Routes.use('/profile', profileRoutes) // tracked ✓
|
|
233
|
+
* v1Routes.use('/', callbackRouter) // plain Express, also works
|
|
234
|
+
*
|
|
235
|
+
* app.use('/v1', v1Routes.getRouter())
|
|
236
|
+
* app.use('/docs', v1Routes.docs({ title: 'My API' })) // just works
|
|
237
|
+
*/
|
|
238
|
+
use(path: string, ...handlers: Array<express.RequestHandler | express.Router | TypedRouter<any, any>>): TypedRouter<Req, Locals>;
|
|
239
|
+
use(...handlers: Array<express.RequestHandler | express.Router | TypedRouter<any, any>>): TypedRouter<Req, Locals>;
|
|
141
240
|
/**
|
|
142
|
-
*
|
|
241
|
+
* Mount a TypedRouter at a path prefix, registering it both on the Express
|
|
242
|
+
* router and in the docs registry so .docs() picks it up automatically.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* const v1 = createTypedRouter()
|
|
246
|
+
* .mount('/products', productRoutes)
|
|
247
|
+
* .mount('/profile', profileRoutes)
|
|
248
|
+
* .mount('/supplier', supplierRoutes)
|
|
249
|
+
*
|
|
250
|
+
* app.use('/v1', v1.getRouter())
|
|
251
|
+
* app.use('/docs', v1.docs({ title: 'My API' }))
|
|
143
252
|
*/
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
253
|
+
mount(prefix: string, router: TypedRouter<any, any>): TypedRouter<Req, Locals>;
|
|
254
|
+
mount(router: TypedRouter<any, any>): TypedRouter<Req, Locals>;
|
|
255
|
+
/**
|
|
256
|
+
* Returns the collected route metadata for this router, including all
|
|
257
|
+
* sub-routers registered via .mount() with their prefixes applied.
|
|
258
|
+
* Used internally by .docs() and by createDocs() for multi-router merging.
|
|
259
|
+
*/
|
|
260
|
+
getRouteMetadata(): RouteMetadata[];
|
|
261
|
+
/**
|
|
262
|
+
* Returns an Express router that serves OpenAPI docs.
|
|
263
|
+
* Mount it anywhere on your app — routes are auto-discovered.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* app.use('/docs', router.docs({ title: 'My API', version: '1.0.0' }))
|
|
267
|
+
* // GET /docs → Scalar UI
|
|
268
|
+
* // GET /docs/openapi.json → raw OpenAPI 3.1 spec
|
|
269
|
+
*/
|
|
270
|
+
docs(options?: DocsOptions): express.Router & express.RequestHandler;
|
|
271
|
+
get<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
272
|
+
get<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
147
273
|
get<Path extends string, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
148
274
|
middleware: Middleware;
|
|
149
|
-
}, handler: SchemaRouteHandler<Path, unknown, unknown,
|
|
275
|
+
}, handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<Middleware>, Locals & InferMiddlewareLocals<Middleware>>): TypedRouter<Req, Locals>;
|
|
150
276
|
get<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: RouteOptions<BodySchema, QuerySchema> & {
|
|
151
277
|
middleware: [...M];
|
|
152
|
-
},
|
|
153
|
-
//
|
|
154
|
-
handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
278
|
+
}, // Using tuple spread pattern
|
|
279
|
+
handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
155
280
|
// Make it readonly for type inference
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
post<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
281
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
282
|
+
post<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
159
283
|
bodySchema: BodySchema;
|
|
160
284
|
querySchema?: QuerySchema;
|
|
161
285
|
middleware: [...M];
|
|
162
|
-
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema,
|
|
163
|
-
// Make it readonly for type inference
|
|
286
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
164
287
|
// Make it readonly for type inference
|
|
165
|
-
|
|
166
|
-
post<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
288
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
289
|
+
post<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
167
290
|
bodySchema: BodySchema;
|
|
168
291
|
middleware: [...M];
|
|
169
|
-
},
|
|
170
|
-
//
|
|
171
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
172
|
-
// Make it readonly for type inference
|
|
292
|
+
}, // Using tuple spread pattern
|
|
293
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
173
294
|
// Make it readonly for type inference
|
|
174
|
-
|
|
175
|
-
post<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
295
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
296
|
+
post<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
176
297
|
middleware: [...M];
|
|
177
|
-
},
|
|
178
|
-
//
|
|
179
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
298
|
+
}, // Using tuple spread pattern
|
|
299
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
180
300
|
// Make it readonly for type inference
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
post<Path extends string
|
|
184
|
-
|
|
185
|
-
put<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
301
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
302
|
+
post<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
303
|
+
post<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
304
|
+
put<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
186
305
|
bodySchema: BodySchema;
|
|
187
306
|
querySchema?: QuerySchema;
|
|
188
307
|
middleware: [...M];
|
|
189
|
-
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema,
|
|
190
|
-
// Make it readonly for type inference
|
|
308
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
191
309
|
// Make it readonly for type inference
|
|
192
|
-
|
|
193
|
-
put<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
310
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
311
|
+
put<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
194
312
|
bodySchema: BodySchema;
|
|
195
313
|
middleware: [...M];
|
|
196
|
-
},
|
|
197
|
-
//
|
|
198
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
314
|
+
}, // Using tuple spread pattern
|
|
315
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
199
316
|
// Make it readonly for type inference
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
put<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
317
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
318
|
+
put<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
203
319
|
middleware: [...M];
|
|
204
|
-
},
|
|
205
|
-
//
|
|
206
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
207
|
-
// Make it readonly for type inference
|
|
320
|
+
}, // Using tuple spread pattern
|
|
321
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
208
322
|
// Make it readonly for type inference
|
|
209
|
-
|
|
210
|
-
put<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema,
|
|
211
|
-
put<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown,
|
|
212
|
-
patch<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
323
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
324
|
+
put<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
325
|
+
put<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
326
|
+
patch<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
213
327
|
bodySchema: BodySchema;
|
|
214
328
|
querySchema?: QuerySchema;
|
|
215
329
|
middleware: [...M];
|
|
216
|
-
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema,
|
|
330
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
217
331
|
// Make it readonly for type inference
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
patch<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
332
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
333
|
+
patch<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
221
334
|
bodySchema: BodySchema;
|
|
222
335
|
middleware: [...M];
|
|
223
|
-
},
|
|
224
|
-
//
|
|
225
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
226
|
-
// Make it readonly for type inference
|
|
336
|
+
}, // Using tuple spread pattern
|
|
337
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
227
338
|
// Make it readonly for type inference
|
|
228
|
-
|
|
229
|
-
patch<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
339
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
340
|
+
patch<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
230
341
|
middleware: [...M];
|
|
231
|
-
},
|
|
232
|
-
//
|
|
233
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
342
|
+
}, // Using tuple spread pattern
|
|
343
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
234
344
|
// Make it readonly for type inference
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
patch<Path extends string
|
|
238
|
-
|
|
239
|
-
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
345
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
346
|
+
patch<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
347
|
+
patch<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
348
|
+
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
240
349
|
querySchema: QuerySchema;
|
|
241
350
|
middleware: [...M];
|
|
242
|
-
},
|
|
243
|
-
//
|
|
244
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
245
|
-
// Make it readonly for type inference
|
|
351
|
+
}, // Using tuple spread pattern
|
|
352
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
246
353
|
// Make it readonly for type inference
|
|
247
|
-
|
|
354
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
248
355
|
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
249
356
|
querySchema: QuerySchema;
|
|
250
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema,
|
|
251
|
-
delete<Path extends string,
|
|
357
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
358
|
+
delete<Path extends string>(path: Path, options: DocMeta, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
359
|
+
delete<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
252
360
|
middleware: [...M];
|
|
253
|
-
},
|
|
254
|
-
//
|
|
255
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
361
|
+
}, // Using tuple spread pattern
|
|
362
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
256
363
|
// Make it readonly for type inference
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
364
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
365
|
+
delete<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
366
|
+
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
261
367
|
querySchema: QuerySchema;
|
|
262
368
|
middleware: [...M];
|
|
263
|
-
},
|
|
264
|
-
//
|
|
265
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
266
|
-
// Make it readonly for type inference
|
|
369
|
+
}, // Using tuple spread pattern
|
|
370
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
267
371
|
// Make it readonly for type inference
|
|
268
|
-
|
|
372
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
269
373
|
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
270
374
|
querySchema: QuerySchema;
|
|
271
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema,
|
|
272
|
-
options<Path extends string,
|
|
375
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
376
|
+
options<Path extends string>(path: Path, options: DocMeta, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
377
|
+
options<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
273
378
|
middleware: [...M];
|
|
274
|
-
},
|
|
275
|
-
//
|
|
276
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
379
|
+
}, // Using tuple spread pattern
|
|
380
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
277
381
|
// Make it readonly for type inference
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
382
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
383
|
+
options<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
384
|
+
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
282
385
|
querySchema: QuerySchema;
|
|
283
386
|
middleware: [...M];
|
|
284
|
-
},
|
|
285
|
-
//
|
|
286
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
287
|
-
// Make it readonly for type inference
|
|
387
|
+
}, // Using tuple spread pattern
|
|
388
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
288
389
|
// Make it readonly for type inference
|
|
289
|
-
|
|
390
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
290
391
|
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
291
392
|
querySchema: QuerySchema;
|
|
292
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema,
|
|
293
|
-
head<Path extends string,
|
|
393
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
394
|
+
head<Path extends string>(path: Path, options: DocMeta, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
395
|
+
head<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
294
396
|
middleware: [...M];
|
|
295
|
-
},
|
|
296
|
-
//
|
|
297
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
397
|
+
}, // Using tuple spread pattern
|
|
398
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
298
399
|
// Make it readonly for type inference
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
all<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
400
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
401
|
+
head<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
402
|
+
all<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
303
403
|
bodySchema: BodySchema;
|
|
304
404
|
querySchema?: QuerySchema;
|
|
305
405
|
middleware: [...M];
|
|
306
|
-
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema,
|
|
307
|
-
// Make it readonly for type inference
|
|
406
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
308
407
|
// Make it readonly for type inference
|
|
309
|
-
|
|
310
|
-
all<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
408
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
409
|
+
all<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
311
410
|
bodySchema: BodySchema;
|
|
312
411
|
middleware: [...M];
|
|
313
|
-
},
|
|
314
|
-
//
|
|
315
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
316
|
-
// Make it readonly for type inference
|
|
412
|
+
}, // Using tuple spread pattern
|
|
413
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
317
414
|
// Make it readonly for type inference
|
|
318
|
-
|
|
319
|
-
all<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
415
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
416
|
+
all<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
320
417
|
querySchema: QuerySchema;
|
|
321
418
|
middleware: [...M];
|
|
322
|
-
},
|
|
323
|
-
//
|
|
324
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
419
|
+
}, // Using tuple spread pattern
|
|
420
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
325
421
|
// Make it readonly for type inference
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
all<Path extends string,
|
|
329
|
-
all<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
422
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
423
|
+
all<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
424
|
+
all<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
330
425
|
middleware: [...M];
|
|
331
|
-
},
|
|
332
|
-
//
|
|
333
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
334
|
-
// Make it readonly for type inference
|
|
426
|
+
}, // Using tuple spread pattern
|
|
427
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
335
428
|
// Make it readonly for type inference
|
|
336
|
-
|
|
337
|
-
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown,
|
|
429
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
430
|
+
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
338
431
|
private registerRoute;
|
|
339
432
|
private createBodyValidationMiddleware;
|
|
340
433
|
private createQueryValidationMiddleware;
|
|
@@ -359,7 +452,7 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
359
452
|
* const app = express();
|
|
360
453
|
* app.use('/api', router.getRouter());
|
|
361
454
|
*/
|
|
362
|
-
declare function createTypedRouter<
|
|
455
|
+
declare function createTypedRouter<Req extends Record<string, any> = AdditionalReqProps, Locals extends Record<string, any> = AdditionalLocals>(): TypedRouter<Req, Locals>;
|
|
363
456
|
/**
|
|
364
457
|
* Configuration options for createTypedRouterWithConfig.
|
|
365
458
|
*
|
|
@@ -387,7 +480,7 @@ interface RouterConfig {
|
|
|
387
480
|
* }
|
|
388
481
|
* });
|
|
389
482
|
*/
|
|
390
|
-
declare function createTypedRouterWithConfig<
|
|
483
|
+
declare function createTypedRouterWithConfig<Req extends Record<string, any> = AdditionalReqProps, Locals extends Record<string, any> = AdditionalLocals>(config?: RouterConfig): TypedRouter<Req, Locals>;
|
|
391
484
|
/**
|
|
392
485
|
* Create a new typed router with pre-configured middleware.
|
|
393
486
|
*
|
|
@@ -402,5 +495,43 @@ declare function createTypedRouterWithConfig<RouterMiddlewareProps extends Recor
|
|
|
402
495
|
* const router = createTypedRouterWithMiddleware(authMiddleware, loggingMiddleware);
|
|
403
496
|
*/
|
|
404
497
|
declare function createTypedRouterWithMiddleware<T extends Record<string, any>>(...middleware: TypedMiddleware<any, any>[]): TypedRouter<T>;
|
|
498
|
+
/**
|
|
499
|
+
* An entry for createDocs(). Either a bare TypedRouter (no prefix prepended)
|
|
500
|
+
* or an object with an explicit prefix matching the mount point in app.use().
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* // Routes defined as /users/:id — mount prefix prepends /api
|
|
504
|
+
* { prefix: '/api', router: usersRouter }
|
|
505
|
+
*
|
|
506
|
+
* // Routes already include the full path — no prefix needed
|
|
507
|
+
* authRouter
|
|
508
|
+
*/
|
|
509
|
+
type RouterDocEntry = TypedRouter<any, any> | {
|
|
510
|
+
prefix: string;
|
|
511
|
+
router: TypedRouter<any, any>;
|
|
512
|
+
};
|
|
513
|
+
/**
|
|
514
|
+
* Create a unified OpenAPI docs endpoint that merges routes from multiple
|
|
515
|
+
* TypedRouter instances. Use this when routes are split across files.
|
|
516
|
+
*
|
|
517
|
+
* @example
|
|
518
|
+
* // users.router.ts — routes like /users, /users/:id
|
|
519
|
+
* export const usersRouter = createTypedRouter();
|
|
520
|
+
*
|
|
521
|
+
* // auth.router.ts — routes like /login, /logout
|
|
522
|
+
* export const authRouter = createTypedRouter();
|
|
523
|
+
*
|
|
524
|
+
* // app.ts
|
|
525
|
+
* app.use('/api', usersRouter.getRouter());
|
|
526
|
+
* app.use('/api', authRouter.getRouter());
|
|
527
|
+
* app.use('/docs', createDocs(
|
|
528
|
+
* [
|
|
529
|
+
* { prefix: '/api', router: usersRouter },
|
|
530
|
+
* { prefix: '/api', router: authRouter },
|
|
531
|
+
* ],
|
|
532
|
+
* { title: 'My API', version: '1.0.0' }
|
|
533
|
+
* ));
|
|
534
|
+
*/
|
|
535
|
+
declare function createDocs(routers: RouterDocEntry | RouterDocEntry[], options?: DocsOptions): express.Router & express.RequestHandler;
|
|
405
536
|
//#endregion
|
|
406
|
-
export { AnyStandardSchema, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, SafeParseResult, SchemaRequest, SchemaRouteHandler, TypedMiddleware, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, isSchemaError, parseSchema, safeParseSchema };
|
|
537
|
+
export { AdditionalLocals, AdditionalReqProps, AnyStandardSchema, DocsOptions, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, RouterDocEntry, SafeParseResult, SchemaRequest, SchemaRouteHandler, TypedMiddleware, TypedRouter, createDocs, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, isSchemaError, parseSchema, safeParseSchema };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import e from"express";import{SchemaError as t}from"@standard-schema/utils";function n(e,n){let r=e;if(r&&r[`~standard`]&&typeof r[`~standard`].validate==`function`){let e=r[`~standard`].validate(n);if(e instanceof Promise)throw TypeError(`Async schema validation is not supported by parseSchema`);if(e.issues)throw new t(e.issues);return e.value}throw TypeError(`Unsupported schema shape for parseSchema`)}function r(e,t){let n=e;if(n&&n[`~standard`]&&typeof n[`~standard`].validate==`function`)return n[`~standard`].validate(t);if(n&&typeof n.safeParse==`function`)return n.safeParse(t);if(n&&typeof n.parse==`function`)try{return{value:n.parse(t)}}catch(e){return{issues:[{message:e?.message??String(e)}]}}if(n&&typeof n.validate==`function`){let e=n.validate(t);return e&&e.then&&typeof e.then==`function`?e.then(e=>e.error?{issues:[{message:e.error.message}]}:e.issues?{issues:e.issues}:{value:e.value??e}):e&&e.error?{issues:[{message:e.error.message}]}:e&&e.issues?{issues:e.issues}:{value:e.value??e}}return{issues:[{message:`Unsupported schema shape`}]}}function i(e){return typeof e==`object`&&!!e&&`issues`in e&&Array.isArray(e.issues)}const a=Function(`m`,`return import(m)`);let o,s;async function c(e){try{o??=await a(`module`),s??=await a(`url`);let t=globalThis.process?.cwd?.()??``,n=o.createRequire(s.pathToFileURL(t+`/`).href).resolve(e);return a(s.pathToFileURL(n).href)}catch{return a(e)}}const l=new WeakMap;function u(e){return e.replace(/:([^/?*.()]+)\??/g,`{$1}`)}function d(e){return[...e.matchAll(/:([^/?*.()]+)\??/g)].map(e=>e[1])}function f(e){let t=e.split(`/`).filter(Boolean)[0];return t&&!t.startsWith(`:`)?t:`default`}function p(e,t){let n=t.split(`/`).filter(e=>e&&!e.startsWith(`:`)),r=n[n.length-1]??`resource`;return`${{get:`Get`,post:`Create`,put:`Update`,patch:`Patch`,delete:`Delete`,head:`Head`,options:`Options`}[e]??e} ${r}`}async function m(e){let t=l.get(e);if(t)return t;let n=await h(e);return l.set(e,n),n}async function h(e){if(typeof e.toJsonSchema==`function`)try{return e.toJsonSchema()}catch{}let t=e[`~standard`]?.vendor;if(t===`zod`){try{let t=await c(`zod`);if(typeof t.toJSONSchema==`function`)return t.toJSONSchema(e)}catch{}try{let t=await c(`zod-to-json-schema`),n=t.zodToJsonSchema??t.default?.zodToJsonSchema;if(typeof n==`function`)return n(e)}catch{}}if(t===`valibot`)try{let t=await c(`@valibot/to-json-schema`),n=t.toJsonSchema??t.default?.toJsonSchema;if(typeof n==`function`)return n(e)}catch{}if(t===`effect`)try{let t=await c(`effect`),n=t.JSONSchema?.make??t.default?.JSONSchema?.make;if(typeof n==`function`)return n(e)}catch{}return{}}function g(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}const _=`https://cdn.jsdelivr.net/npm/@scalar/api-reference`;function v(e,t,n){return`<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>${g(e)}</title>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<script id="api-reference" data-url="${g(t)}"><\/script>
|
|
10
|
+
<script src="${g(n)}"><\/script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>`}async function y(e,t){let n=Object.create(null);for(let t of e){if(t.method===`all`||t.hidden)continue;let e=u(t.path);n[e]||(n[e]={});let r=d(t.path).map(e=>({name:e,in:`path`,required:!0,schema:{type:`string`}}));if(t.querySchema){let e=await m(t.querySchema),n=e.properties??{},i=e.required??[];for(let[e,t]of Object.entries(n))r.push({name:e,in:`query`,required:i.includes(e),schema:t})}let i={summary:t.summary??p(t.method,t.path),tags:t.tags??[f(t.path)],parameters:r};t.description&&(i.description=t.description),t.deprecated&&(i.deprecated=!0),t.bodySchema&&(i.requestBody={required:!0,content:{"application/json":{schema:await m(t.bodySchema)}}});let a={};if(t.responseSamples.size>0)for(let[e,n]of t.responseSamples)a[String(e)]={description:e<400?`Success`:`Error`,content:{"application/json":{example:n}}};else a[200]={description:`Success`};i.responses=a,n[e][t.method]=i}return{openapi:`3.1.0`,info:{title:t.title??`API`,version:t.version??`1.0.0`,...t.description?{description:t.description}:{}},...t.servers?{servers:t.servers}:{},paths:n}}const b=new WeakMap;var x=class t{router;routes=[];mountedRouters=[];constructor(){this.router=e.Router(),b.set(this.router,this)}useMiddleware(e){return this.router.use(e),this}getRouter(){return this.router}use(e,...n){let r=typeof e==`string`,i=r?e:``,a=(r?n:[e,...n]).map(e=>{if(e instanceof t)return this.mountedRouters.push({prefix:i,router:e}),e.getRouter();let n=b.get(e);return n&&this.mountedRouters.push({prefix:i,router:n}),e});return r?this.router.use(e,...a):this.router.use(...a),this}mount(e,t){if(typeof e==`string`){let n=t;this.router.use(e,n.getRouter()),this.mountedRouters.push({prefix:e,router:n})}else this.router.use(e.getRouter()),this.mountedRouters.push({prefix:``,router:e});return this}getRouteMetadata(){let e=this.mountedRouters.flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,path:e+t.path})));return[...this.routes,...e]}docs(t={}){let n=e.Router();return n.get(`/openapi.json`,async(e,n)=>{try{let e=await y(this.getRouteMetadata(),t);t.specOutputPath&&await(await a(`fs/promises`)).writeFile(t.specOutputPath,JSON.stringify(e,null,2),`utf8`).catch(()=>{}),n.json(e)}catch(e){n.status(500).json({error:`Failed to generate spec`,details:String(e)})}}),n.get(`/`,(e,n)=>{let r=`${e.baseUrl}/openapi.json`,i=t.cdnUrl??_;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(v(t.title??`API`,r,i))}),n}get(e,t,n){return this.registerRoute(`get`,e,t,n)}post(e,t,n){return this.registerRoute(`post`,e,t,n)}put(e,t,n){return this.registerRoute(`put`,e,t,n)}patch(e,t,n){return this.registerRoute(`patch`,e,t,n)}delete(e,t,n){return this.registerRoute(`delete`,e,t,n)}options(e,t,n){return this.registerRoute(`options`,e,t,n)}head(e,t,n){return this.registerRoute(`head`,e,t,n)}all(e,t,n){return this.registerRoute(`all`,e,t,n)}registerRoute(e,t,n,r){let i=[],a={method:e,path:t,responseSamples:new Map};if(this.routes.push(a),typeof n==`object`){let e=n;a.bodySchema=e.bodySchema,a.querySchema=e.querySchema,a.tags=e.tags,a.description=e.description,a.summary=e.summary,a.deprecated=e.deprecated,a.responseSchema=e.responseSchema,a.hidden=e.hidden,e.middleware&&i.push(...e.middleware),e.bodySchema&&i.push(this.createBodyValidationMiddleware(e.bodySchema)),e.querySchema&&i.push(this.createQueryValidationMiddleware(e.querySchema)),i.push(r)}else i.push(n);return this.router[e](t,(e,t,n)=>{if(a.hidden||a.responseSamples.size>=10){n();return}let r=t.json;t.json=e=>(a.responseSamples.has(t.statusCode)||a.responseSamples.set(t.statusCode,e),r.call(t,e)),n()},...i),this}createBodyValidationMiddleware(e){return async(t,n,a)=>{try{let i=r(e,t.body),o=i&&typeof i.then==`function`?await i:i;if(o&&`issues`in o&&o.issues){n.status(400).json({error:`Validation failed`,details:o.errors||o.issues});return}t.body=o&&`value`in o?o.value:o,a()}catch(e){i(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):a(e)}}}createQueryValidationMiddleware(e){return async(t,n,a)=>{try{let i=r(e,t.query),o=i&&typeof i.then==`function`?await i:i;if(o&&`issues`in o&&o.issues){n.status(400).json({error:`Validation failed`,details:o.errors||o.issues});return}let s=o&&`value`in o?o.value:o;Object.defineProperty(t,"query",{value:s,writable:!1,enumerable:!0,configurable:!0}),a()}catch(e){i(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):a(e)}}}};function S(){return new x}function C(e){let t=new x;return e?.errorHandler&&t.getRouter().use(e.errorHandler),t}function w(...e){let t=new x;for(let n of e)t=t.useMiddleware(n);return t}function T(t,n={}){let r=(Array.isArray(t)?t:[t]).map(e=>`prefix`in e?e:{prefix:``,router:e}),i=e.Router();return i.get(`/openapi.json`,async(e,t)=>{try{let e=await y(r.flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,path:e+t.path}))),n);t.json(e)}catch(e){t.status(500).json({error:`Failed to generate spec`,details:String(e)})}}),i.get(`/`,(e,t)=>{let r=`${e.baseUrl}/openapi.json`,i=n.cdnUrl??_;t.setHeader(`Content-Type`,`text/html; charset=utf-8`),t.send(v(n.title??`API`,r,i))}),i}export{x as TypedRouter,T as createDocs,S as createTypedRouter,C as createTypedRouterWithConfig,w as createTypedRouterWithMiddleware,i as isSchemaError,n as parseSchema,r as safeParseSchema};
|