@minisylar/express-typed-router 1.6.2 → 1.7.0
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 +268 -774
- package/dist/schema-router.cjs +12 -1
- package/dist/schema-router.d.cts +273 -145
- package/dist/{schema-router.d.ts → schema-router.d.mts} +273 -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,312 @@ 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 — keep your exact existing pattern, just use TypedRouter
|
|
229
|
+
* export const v1Routes = createTypedRouter()
|
|
230
|
+
*
|
|
231
|
+
* v1Routes.use('/products', productRoutes.getRouter()) // tracked ✓
|
|
232
|
+
* v1Routes.use('/profile', profileRoutes.getRouter()) // tracked ✓
|
|
233
|
+
* v1Routes.use('/', callbackRouter) // plain Express, not tracked
|
|
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<Req, Locals>;
|
|
239
|
+
use(...handlers: Array<express.RequestHandler | express.Router>): 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, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
357
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
358
|
+
delete<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
252
359
|
middleware: [...M];
|
|
253
|
-
},
|
|
254
|
-
//
|
|
255
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
360
|
+
}, // Using tuple spread pattern
|
|
361
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
256
362
|
// 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: {
|
|
363
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
364
|
+
delete<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
365
|
+
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
261
366
|
querySchema: QuerySchema;
|
|
262
367
|
middleware: [...M];
|
|
263
|
-
},
|
|
264
|
-
//
|
|
265
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
266
|
-
// Make it readonly for type inference
|
|
368
|
+
}, // Using tuple spread pattern
|
|
369
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
267
370
|
// Make it readonly for type inference
|
|
268
|
-
|
|
371
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
269
372
|
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
270
373
|
querySchema: QuerySchema;
|
|
271
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema,
|
|
272
|
-
options<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
374
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
375
|
+
options<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
273
376
|
middleware: [...M];
|
|
274
|
-
},
|
|
275
|
-
//
|
|
276
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
377
|
+
}, // Using tuple spread pattern
|
|
378
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
277
379
|
// 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: {
|
|
380
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
381
|
+
options<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
382
|
+
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
282
383
|
querySchema: QuerySchema;
|
|
283
384
|
middleware: [...M];
|
|
284
|
-
},
|
|
285
|
-
//
|
|
286
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
287
|
-
// Make it readonly for type inference
|
|
385
|
+
}, // Using tuple spread pattern
|
|
386
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
288
387
|
// Make it readonly for type inference
|
|
289
|
-
|
|
388
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
290
389
|
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
291
390
|
querySchema: QuerySchema;
|
|
292
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema,
|
|
293
|
-
head<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
391
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
392
|
+
head<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
294
393
|
middleware: [...M];
|
|
295
|
-
},
|
|
296
|
-
//
|
|
297
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
394
|
+
}, // Using tuple spread pattern
|
|
395
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
298
396
|
// 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: {
|
|
397
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
398
|
+
head<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
399
|
+
all<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
303
400
|
bodySchema: BodySchema;
|
|
304
401
|
querySchema?: QuerySchema;
|
|
305
402
|
middleware: [...M];
|
|
306
|
-
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema,
|
|
307
|
-
// Make it readonly for type inference
|
|
403
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
308
404
|
// Make it readonly for type inference
|
|
309
|
-
|
|
310
|
-
all<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
405
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
406
|
+
all<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
311
407
|
bodySchema: BodySchema;
|
|
312
408
|
middleware: [...M];
|
|
313
|
-
},
|
|
314
|
-
//
|
|
315
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
316
|
-
// Make it readonly for type inference
|
|
409
|
+
}, // Using tuple spread pattern
|
|
410
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
317
411
|
// Make it readonly for type inference
|
|
318
|
-
|
|
319
|
-
all<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
412
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
413
|
+
all<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
320
414
|
querySchema: QuerySchema;
|
|
321
415
|
middleware: [...M];
|
|
322
|
-
},
|
|
323
|
-
//
|
|
324
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
416
|
+
}, // Using tuple spread pattern
|
|
417
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
325
418
|
// 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: {
|
|
419
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
420
|
+
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>;
|
|
421
|
+
all<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
330
422
|
middleware: [...M];
|
|
331
|
-
},
|
|
332
|
-
//
|
|
333
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
334
|
-
// Make it readonly for type inference
|
|
423
|
+
}, // Using tuple spread pattern
|
|
424
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
335
425
|
// Make it readonly for type inference
|
|
336
|
-
|
|
337
|
-
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown,
|
|
426
|
+
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
427
|
+
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
338
428
|
private registerRoute;
|
|
339
429
|
private createBodyValidationMiddleware;
|
|
340
430
|
private createQueryValidationMiddleware;
|
|
@@ -359,7 +449,7 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
359
449
|
* const app = express();
|
|
360
450
|
* app.use('/api', router.getRouter());
|
|
361
451
|
*/
|
|
362
|
-
declare function createTypedRouter<
|
|
452
|
+
declare function createTypedRouter<Req extends Record<string, any> = AdditionalReqProps, Locals extends Record<string, any> = AdditionalLocals>(): TypedRouter<Req, Locals>;
|
|
363
453
|
/**
|
|
364
454
|
* Configuration options for createTypedRouterWithConfig.
|
|
365
455
|
*
|
|
@@ -387,7 +477,7 @@ interface RouterConfig {
|
|
|
387
477
|
* }
|
|
388
478
|
* });
|
|
389
479
|
*/
|
|
390
|
-
declare function createTypedRouterWithConfig<
|
|
480
|
+
declare function createTypedRouterWithConfig<Req extends Record<string, any> = AdditionalReqProps, Locals extends Record<string, any> = AdditionalLocals>(config?: RouterConfig): TypedRouter<Req, Locals>;
|
|
391
481
|
/**
|
|
392
482
|
* Create a new typed router with pre-configured middleware.
|
|
393
483
|
*
|
|
@@ -402,5 +492,43 @@ declare function createTypedRouterWithConfig<RouterMiddlewareProps extends Recor
|
|
|
402
492
|
* const router = createTypedRouterWithMiddleware(authMiddleware, loggingMiddleware);
|
|
403
493
|
*/
|
|
404
494
|
declare function createTypedRouterWithMiddleware<T extends Record<string, any>>(...middleware: TypedMiddleware<any, any>[]): TypedRouter<T>;
|
|
495
|
+
/**
|
|
496
|
+
* An entry for createDocs(). Either a bare TypedRouter (no prefix prepended)
|
|
497
|
+
* or an object with an explicit prefix matching the mount point in app.use().
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* // Routes defined as /users/:id — mount prefix prepends /api
|
|
501
|
+
* { prefix: '/api', router: usersRouter }
|
|
502
|
+
*
|
|
503
|
+
* // Routes already include the full path — no prefix needed
|
|
504
|
+
* authRouter
|
|
505
|
+
*/
|
|
506
|
+
type RouterDocEntry = TypedRouter<any, any> | {
|
|
507
|
+
prefix: string;
|
|
508
|
+
router: TypedRouter<any, any>;
|
|
509
|
+
};
|
|
510
|
+
/**
|
|
511
|
+
* Create a unified OpenAPI docs endpoint that merges routes from multiple
|
|
512
|
+
* TypedRouter instances. Use this when routes are split across files.
|
|
513
|
+
*
|
|
514
|
+
* @example
|
|
515
|
+
* // users.router.ts — routes like /users, /users/:id
|
|
516
|
+
* export const usersRouter = createTypedRouter();
|
|
517
|
+
*
|
|
518
|
+
* // auth.router.ts — routes like /login, /logout
|
|
519
|
+
* export const authRouter = createTypedRouter();
|
|
520
|
+
*
|
|
521
|
+
* // app.ts
|
|
522
|
+
* app.use('/api', usersRouter.getRouter());
|
|
523
|
+
* app.use('/api', authRouter.getRouter());
|
|
524
|
+
* app.use('/docs', createDocs(
|
|
525
|
+
* [
|
|
526
|
+
* { prefix: '/api', router: usersRouter },
|
|
527
|
+
* { prefix: '/api', router: authRouter },
|
|
528
|
+
* ],
|
|
529
|
+
* { title: 'My API', version: '1.0.0' }
|
|
530
|
+
* ));
|
|
531
|
+
*/
|
|
532
|
+
declare function createDocs(routers: RouterDocEntry | RouterDocEntry[], options?: DocsOptions): express.Router & express.RequestHandler;
|
|
405
533
|
//#endregion
|
|
406
|
-
export { AnyStandardSchema, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, SafeParseResult, SchemaRequest, SchemaRouteHandler, TypedMiddleware, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, isSchemaError, parseSchema, safeParseSchema };
|
|
534
|
+
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{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,...t){let n=typeof e==`string`,r=n?e:``,i=n?t:[e,...t];for(let e of i){let t=b.get(e);t&&this.mountedRouters.push({prefix:r,router:t})}return n?this.router.use(e,...i):this.router.use(...i),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};
|