@minisylar/express-typed-router 1.9.3 → 1.9.5
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 +36 -0
- package/dist/schema-router.cjs +5 -5
- package/dist/schema-router.d.cts +174 -187
- package/dist/schema-router.d.mts +174 -187
- package/dist/schema-router.mjs +5 -5
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @minisylar/express-typed-router
|
|
2
2
|
|
|
3
|
+
[](https://github.com/sponsors/Mini-Sylar)
|
|
4
|
+
|
|
3
5
|
A strongly typed Express router with **Standard Schema** validation, automatic type inference, and ✨ OpenAPI docs.
|
|
4
6
|
|
|
5
7
|
Define routes once, infer `params` / `body` / `query`, and generate a clean API spec for docs or client generation.
|
|
@@ -491,6 +493,40 @@ app.use("/docs", api.docs({ title: "My API", version: "1.0.0" }));
|
|
|
491
493
|
|
|
492
494
|
---
|
|
493
495
|
|
|
496
|
+
## Gotchas
|
|
497
|
+
|
|
498
|
+
### `querySchema` booleans: `req.query.flag` is text, not a real boolean
|
|
499
|
+
|
|
500
|
+
Query strings have no wire format for booleans — `?flag=false` arrives at your schema as the *string* `"false"`, no matter what the client intended. This isn't specific to this library; it's true of every Express app, but it trips people up specifically when picking a schema for `querySchema`:
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
router.get(
|
|
504
|
+
"/search",
|
|
505
|
+
{ querySchema: z.object({ flag: z.boolean() }) }, // ❌ always rejects
|
|
506
|
+
handler,
|
|
507
|
+
);
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
`z.boolean()` rejects **every** request here, since `typeof "false" !== "boolean"` — it fails before it even looks at the text.
|
|
511
|
+
|
|
512
|
+
The obvious fix has its own trap:
|
|
513
|
+
|
|
514
|
+
```ts
|
|
515
|
+
{ querySchema: z.object({ flag: z.coerce.boolean() }) } // ❌ silently wrong
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
`z.coerce.boolean()` does plain JS `Boolean(value)` — and `Boolean("false")` is `true`, because any non-empty string is truthy in JavaScript. `?flag=false` stops erroring and instead silently becomes `true`.
|
|
519
|
+
|
|
520
|
+
Use a schema that actually parses the text:
|
|
521
|
+
|
|
522
|
+
```ts
|
|
523
|
+
{ querySchema: z.object({ flag: z.stringbool() }) } // ✅ zod v4+
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
`z.stringbool()` parses `"true"`/`"false"` (and a few common variants) into the correct boolean. This isn't an express-typed-router limitation to work around — the router runs whatever schema you give it exactly as written; the fix is picking the right schema primitive, not something the router could safely guess on your behalf.
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
494
530
|
## API surface
|
|
495
531
|
|
|
496
532
|
| | |
|
package/dist/schema-router.cjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));let c=require("express");c=s(c,1);let l=require("@standard-schema/utils");function u(e,t){let n=e;if(n&&n[`~standard`]&&typeof n[`~standard`].validate==`function`){let e=n[`~standard`].validate(t);if(e instanceof Promise)throw TypeError(`Async schema validation is not supported by parseSchema`);if(e.issues)throw new l.SchemaError(e.issues);return e.value}throw TypeError(`Unsupported schema shape for parseSchema`)}function d(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 f(e){return typeof e==`object`&&!!e&&`issues`in e&&Array.isArray(e.issues)}const p=Function(`m`,`return import(m)`);let m,h;async function g(e){m??=await p(`module`),h??=await p(`url`);let t=[],n=globalThis.process?.argv?.[1];n&&t.push(h.pathToFileURL(n).href);let r=globalThis.process?.cwd?.()??``;r&&t.push(h.pathToFileURL(r+`/`).href);for(let n of t)try{let t=m.createRequire(n).resolve(e);return await p(h.pathToFileURL(t).href)}catch{}return p(e)}const _=new WeakMap,v=/\(\?<[^>]+>/;function y(e){return typeof e==`string`&&v.test(e)?new RegExp(e):e}function b(e){if(typeof e.path==`string`)return e.path;if(e.pathExample)return e.pathExample;let t=0;return e.path.source.replace(/\\\//g,`/`).replace(/\((?!\?)[^()]*\)/g,()=>`:${t++}`)}function x(e){return e.replace(/\{([^{}]*)\}/g,`$1`).replace(/:([A-Za-z0-9_]+)(?:\([^)]*\))?[?+*]?/g,`{$1}`).replace(/\(\?<([A-Za-z0-9_]+)>[^)]*\)/g,`{$1}`).replace(/^\^|\$$/g,``).replace(/\/{2,}/g,`/`)}function S(e){let t=e.replace(/\{([^{}]*)\}/g,`$1`);return[...t.matchAll(/:([A-Za-z0-9_]+)/g),...t.matchAll(/\(\?<([A-Za-z0-9_]+)>/g)].map(e=>e[1])}function C(e){return e.startsWith(`:`)||e.startsWith(`*`)||e.includes(`(?<`)}function w(e){return e.replace(/^\^|\$$/g,``).replace(/[{}]/g,``).split(`/`).filter(Boolean).find(e=>!C(e))??`default`}function T(e,t){let n=t.replace(/^\^|\$$/g,``).replace(/[{}]/g,``).split(`/`).filter(e=>e&&!C(e)),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 E(e){let t=_.get(e);if(t)return t;let n=await D(e);return _.set(e,n),n}async function D(e){if(typeof e.toJsonSchema==`function`)try{return e.toJsonSchema()}catch{}let t=e[`~standard`]?.vendor;if(t===`zod`){try{let t=await g(`zod`);if(typeof t.toJSONSchema==`function`)return t.toJSONSchema(e)}catch{}try{let t=await g(`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 g(`@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 g(`effect`),n=t.JSONSchema?.make??t.default?.JSONSchema?.make;if(typeof n==`function`)return n(e)}catch{}return{}}function O(e){return k(e,0,new WeakSet)}function k(e,t,n){if(e==null)return{type:`null`};if(t>=12)return{};if(e instanceof Date)return{type:`string`,format:`date-time`};if(typeof e==`bigint`)return{type:`integer`};if(typeof e==`object`&&typeof e.toJSON==`function`)return k(e.toJSON(),t,n);if(Array.isArray(e)){if(e.length===0||n.has(e))return{type:`array`,items:{}};n.add(e);let r=Math.min(e.length,20),i=k(e[0],t+1,n);for(let a=1;a<r;a++)i=M(i,k(e[a],t+1,n));return n.delete(e),{type:`array`,items:i}}switch(typeof e){case`string`:return{type:`string`};case`boolean`:return{type:`boolean`};case`number`:return Number.isFinite(e)?{type:Number.isInteger(e)?`integer`:`number`}:{type:`null`};case`object`:{if(n.has(e))return{type:`object`};n.add(e);let r={},i=[];for(let[a,o]of Object.entries(e))typeof o!=`function`&&o!==void 0&&(r[a]=k(o,t+1,n),i.push(a));n.delete(e);let a={type:`object`,properties:r};return i.length&&(a.required=i),a}default:return{}}}function A(e){return!e||e.type===void 0?new Set:new Set(Array.isArray(e.type)?e.type:[e.type])}function j(e){e.has(`integer`)&&e.has(`number`)&&e.delete(`integer`);let t=[...e];if(t.length!==0)return t.length===1?t[0]:t}function M(e,t){if(!e||Object.keys(e).length===0)return t??{};if(!t||Object.keys(t).length===0)return e??{};let n=new Set([...A(e),...A(t)]),r={},i=j(n);if(i!==void 0&&(r.type=i),n.has(`object`)&&(e.properties||t.properties)){let n=e.properties??{},i=t.properties??{},a={};for(let e of new Set([...Object.keys(n),...Object.keys(i)]))a[e]=M(n[e],i[e]);r.properties=a;let o=e.required??[],s=t.required??[],c=o.filter(e=>s.includes(e));c.length&&(r.required=c)}if(n.has(`array`)){let n=M(e.items,t.items);n&&Object.keys(n).length&&(r.items=n)}return r}function N(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}const P=`https://cdn.jsdelivr.net/npm/@scalar/api-reference`;function F(e,t,n){return`<!doctype html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
|
-
<title>${
|
|
4
|
+
<title>${N(e)}</title>
|
|
5
5
|
<meta charset="utf-8" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
7
|
</head>
|
|
8
8
|
<body>
|
|
9
|
-
<script id="api-reference" data-url="${
|
|
10
|
-
<script src="${
|
|
9
|
+
<script id="api-reference" data-url="${N(t)}"><\/script>
|
|
10
|
+
<script src="${N(n)}"><\/script>
|
|
11
11
|
</body>
|
|
12
|
-
</html>`}async function
|
|
12
|
+
</html>`}async function I(e,t){let n=Object.create(null);for(let t of e){if(t.method===`all`||t.hidden)continue;let e=b(t),r=x(e);n[r]||(n[r]={});let i;if(t.paramsSchema){let e=await E(t.paramsSchema),n=e.properties??{},r=e.required??[];i=Object.entries(n).map(([e,t])=>({name:e,in:`path`,required:r.includes(e),schema:t}))}else i=S(e).map(e=>({name:e,in:`path`,required:!0,schema:{type:`string`}}));if(t.querySchema){let e=await E(t.querySchema),n=e.properties??{},r=e.required??[];for(let[e,t]of Object.entries(n))i.push({name:e,in:`query`,required:r.includes(e),schema:t})}let a={summary:t.summary??T(t.method,e),tags:t.tags??[w(e)],parameters:i};t.description&&(a.description=t.description),t.deprecated&&(a.deprecated=!0),t.bodySchema&&(a.requestBody={required:!0,content:{"application/json":{schema:await E(t.bodySchema)}}});let o={};for(let[e,n]of t.responseSamples){let t={schema:n.schema};n.example!==void 0&&(t.example=n.example),o[String(e)]={description:e<400?`Success`:`Error`,content:{"application/json":t}}}if(t.responseSchema){let e=await E(t.responseSchema);if(e&&Object.keys(e).length){let n=`200`;for(let e of t.responseSamples.keys())if(e>=200&&e<300){n=String(e);break}o[n]={description:`Success`,content:{"application/json":{schema:e}}}}}Object.keys(o).length===0&&(o[200]={description:`Success`}),a.responses=o,n[r][t.method]=a}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 L=new WeakMap;var R=class e{router;routes=[];mountedRouters=[];sampleMode=`off`;scheduleSpecWrite;constructor(){this.router=c.default.Router(),L.set(this.router,this)}useMiddleware(e){return this.router.use(e),this}getRouter(){return this.router}use(t,...n){let r=typeof t==`string`,i=r?t:``,a=(r?n:[t,...n]).map(t=>{if(t instanceof e)return this.trackMounted(i,t),t.getRouter();let n=L.get(t);return n&&this.trackMounted(i,n),t});return r?this.router.use(t,...a):this.router.use(...a),this}mount(e,t){if(typeof e==`string`){let n=t;this.router.use(e,n.getRouter()),this.trackMounted(e,n)}else this.router.use(e.getRouter()),this.trackMounted(``,e);return this}getRouteMetadata(){let e=this.mountedRouters.flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,...typeof t.path==`string`?{path:e+t.path}:{path:t.path,pathExample:e+b(t)}})));return[...this.routes,...e]}enableSampling(e=`redacted`,t,n=new Set){if(!n.has(this)){n.add(this),this.sampleMode=e,this.scheduleSpecWrite=t;for(let{router:r}of this.mountedRouters)r.enableSampling(e,t,n)}}trackMounted(e,t){this.mountedRouters.some(n=>n.router===t&&n.prefix===e)||(this.mountedRouters.push({prefix:e,router:t}),this.sampleMode!==`off`&&t.enableSampling(this.sampleMode,this.scheduleSpecWrite))}hydrateResponses(e,t=``,n=new Set){if(n.has(this))return;n.add(this);let r=e?.paths??{};for(let e of this.routes){let n=r[x(t+b(e))]?.[e.method]?.responses;if(n)for(let t of Object.keys(n)){let r=Number(t);if(Number.isNaN(r)||e.responseSamples.has(r))continue;let i=n[t]?.content?.[`application/json`];i?.schema&&e.responseSamples.set(r,i.example===void 0?{schema:i.schema}:{schema:i.schema,example:i.example})}}for(let{prefix:r,router:i}of this.mountedRouters)i.hydrateResponses(e,t+r,n)}docs(e={}){let t=c.default.Router(),n;if(e.specOutputPath){let t=e.specOutputPath,r=!1,i=!1,a=async()=>{if(r){i=!0;return}r=!0;try{let n=await I(this.getRouteMetadata(),e),r=await p(`fs/promises`),i=t.replace(/[/\\][^/\\]*$/,``);i&&i!==t&&await r.mkdir(i,{recursive:!0}).catch(()=>{});let a=globalThis.process?.pid??`0`,o=`${t}.${a}.tmp`;await r.writeFile(o,JSON.stringify(n,null,2),`utf8`),await r.rename(o,t)}catch{}finally{r=!1,i&&(i=!1,a())}},o;n=()=>{o&&clearTimeout(o),o=setTimeout(a,300),o.unref?.()},setImmediate(async()=>{try{let e=await(await p(`fs/promises`)).readFile(t,`utf8`).catch(()=>null);if(e)try{this.hydrateResponses(JSON.parse(e))}catch{}}catch{}await a()})}return e.sampleResponses!==!1&&this.enableSampling(e.sampleResponses===`live`?`live`:`redacted`,n),t.get(`/openapi.json`,async(t,n)=>{try{let t=await I(this.getRouteMetadata(),e);n.json(t)}catch(e){n.status(500).json({error:`Failed to generate spec`,details:String(e)})}}),t.get(`/`,(t,n)=>{let r=`${t.baseUrl}/openapi.json`,i=e.cdnUrl??P;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(F(e.title??`API`,r,i))}),t}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.paramsSchema=e.paramsSchema,a.tags=e.tags,a.description=e.description,a.summary=e.summary,a.deprecated=e.deprecated,a.responseSchema=e.responseSchema,a.hidden=e.hidden,a.pathExample=e.pathExample,e.middleware&&i.push(...e.middleware),e.bodySchema&&i.push(this.createBodyValidationMiddleware(e.bodySchema)),e.querySchema&&i.push(this.createQueryValidationMiddleware(e.querySchema)),e.paramsSchema&&i.push(this.createParamsValidationMiddleware(e.paramsSchema)),i.push(r)}else i.push(n);let o=0,s=(e,t)=>{let n=e.statusCode,r=O(t),i=a.responseSamples.get(n),o=i?M(i.schema,r):r,s=this.sampleMode===`live`?i?.example??t:void 0,c=!i||JSON.stringify(i.schema)!==JSON.stringify(o);a.responseSamples.set(n,{schema:o,example:s}),c&&this.scheduleSpecWrite?.()};return this.router[e](y(t),(e,t,n)=>{if(this.sampleMode===`off`||a.hidden||o>=50||a.responseSamples.size>=10){n();return}o++;let r=t.json;t.json=function(e){return s(t,e),r.call(this,e)};let i=t.send;t.send=function(e){return typeof e==`object`&&e&&!Buffer.isBuffer(e)&&s(t,e),i.call(this,e)},n()},...i),this}createBodyValidationMiddleware(e){return async(t,n,r)=>{try{let i=d(e,t.body),a=i&&typeof i.then==`function`?await i:i;if(a&&`issues`in a&&a.issues){n.status(400).json({error:`Validation failed`,details:a.errors||a.issues});return}t.body=a&&`value`in a?a.value:a,r()}catch(e){f(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}createParamsValidationMiddleware(e){return async(t,n,r)=>{try{let i=d(e,t.params),a=i&&typeof i.then==`function`?await i:i;if(a&&`issues`in a&&a.issues){n.status(400).json({error:`Validation failed`,details:a.errors||a.issues});return}t.params=a&&`value`in a?a.value:a,r()}catch(e){f(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}createQueryValidationMiddleware(e){return async(t,n,r)=>{try{let i=d(e,t.query),a=i&&typeof i.then==`function`?await i:i;if(a&&`issues`in a&&a.issues){n.status(400).json({error:`Validation failed`,details:a.errors||a.issues});return}let o=a&&`value`in a?a.value:a;Object.defineProperty(t,"query",{value:o,writable:!1,enumerable:!0,configurable:!0}),r()}catch(e){f(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}};function z(){return new R}function B(e){let t=new R;return e?.errorHandler&&t.getRouter().use(e.errorHandler),t}function V(...e){let t=new R;for(let n of e)t=t.useMiddleware(n);return t}function H(e,t={}){let n=(Array.isArray(e)?e:[e]).map(e=>`prefix`in e?e:{prefix:``,router:e});if(t.sampleResponses!==!1){let e=t.sampleResponses===`live`?`live`:`redacted`;for(let{router:t}of n)t.enableSampling(e)}let r=c.default.Router();return r.get(`/openapi.json`,async(e,r)=>{try{let e=await I(n.flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,...typeof t.path==`string`?{path:e+t.path}:{path:t.path,pathExample:e+b(t)}}))),t);r.json(e)}catch(e){r.status(500).json({error:`Failed to generate spec`,details:String(e)})}}),r.get(`/`,(e,n)=>{let r=`${e.baseUrl}/openapi.json`,i=t.cdnUrl??P;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(F(t.title??`API`,r,i))}),r}exports.TypedRouter=R,exports.createDocs=H,exports.createTypedRouter=z,exports.createTypedRouterWithConfig=B,exports.createTypedRouterWithMiddleware=V,exports.inferJsonSchema=O,exports.isSchemaError=f,exports.parseSchema=u,exports.safeParseSchema=d;
|
package/dist/schema-router.d.cts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import express, { NextFunction, Request, Response } from "express";
|
|
2
2
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
|
-
|
|
4
3
|
//#region src/schema-router.d.ts
|
|
5
4
|
type AnyStandardSchema = StandardSchemaV1<any, any>;
|
|
5
|
+
type SchemaLike = AnyStandardSchema | {
|
|
6
|
+
safeParse: (...args: any[]) => any;
|
|
7
|
+
} | {
|
|
8
|
+
parse: (...args: any[]) => any;
|
|
9
|
+
} | {
|
|
10
|
+
validate: (...args: any[]) => any;
|
|
11
|
+
};
|
|
6
12
|
type InferOutput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : unknown;
|
|
7
13
|
type InferInput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferInput<T> : unknown;
|
|
8
14
|
type InferFromSafeParse<T> = T extends {
|
|
@@ -43,28 +49,36 @@ declare function isSchemaError(error: unknown): error is {
|
|
|
43
49
|
* - Optional parameters: /posts/:year/:month? → { year: string; month?: string }
|
|
44
50
|
* - Wildcard parameters: /files/* → { "0": string }
|
|
45
51
|
* - Multiple wildcards: /a/star/b/star → { "0": string; "1": string }
|
|
52
|
+
*
|
|
53
|
+
* A path containing a named regex capture group — `(?<id>...)` — is treated
|
|
54
|
+
* as a raw regex pattern instead: /^\/legacy\/(?<id>\d+)$/ → { id: string }.
|
|
55
|
+
* This syntax never appears in Express's own path syntax, so detecting it is
|
|
56
|
+
* unambiguous; registerRoute converts the string to a real RegExp at
|
|
57
|
+
* runtime so Express matches it as one.
|
|
46
58
|
*/
|
|
47
|
-
type ExtractRouteParams<Path extends string> = string extends Path ? Record<string, string> : ExtractParams<Path>;
|
|
59
|
+
type ExtractRouteParams<Path extends string> = string extends Path ? Record<string, string> : Path extends `${infer _Before}(?<${infer _Name}>${infer _Rest}` ? ExtractRegexGroupParams<Path> : ExtractParams<Path>;
|
|
60
|
+
type ExtractRegexGroupParams<S extends string> = S extends `${infer _Before}(?<${infer Name}>${infer _Rest}` ? { [K in Name]: string; } & ExtractRegexGroupParams<RemoveFirstRegexGroup<S>> : {};
|
|
61
|
+
type RemoveFirstRegexGroup<S extends string> = S extends `${infer _Before}(?<${infer _Name}>${infer After}` ? After extends `${infer _Inner})${infer Rest}` ? Rest : "" : "";
|
|
48
62
|
/**
|
|
49
63
|
* Main parameter extraction logic - enhanced for Express 5 support with recursion depth limit
|
|
50
64
|
*/
|
|
51
|
-
type ExtractParams<Path extends string> = Path extends `${infer Before}{${infer OptionalContent}}${infer After}` ? ExtractOptionalSegment<OptionalContent> & ExtractParams<`${Before}${After}`> : Path extends `${infer _Before}:${infer Rest}` ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>> : Path extends `${infer _Before}*${infer Name}/${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`/${After}`> : { [K in Name]: string[] } & ExtractParams<`/${After}`> : Path extends `${infer _Before}*${infer Name}-${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`-${After}`> : { [K in Name]: string[] } & ExtractParams<`-${After}`> : Path extends `${infer _Before}*${infer Name}.${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`.${After}`> : { [K in Name]: string[] } & ExtractParams<`.${After}`> : Path extends `${infer _Before}*${infer Name}#${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`#${After}`> : { [K in Name]: string[] } & ExtractParams<`#${After}`> : Path extends `${infer _Before}*${infer Name}:${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`:${After}`> : { [K in Name]: string[] } & ExtractParams<`:${After}`> : Path extends `${infer _Before}*${infer Name}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<``> : { [K in Name]: string[] } & ExtractParams<``> : Path extends `${infer _Before}*${infer After}` ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<After> : {};
|
|
65
|
+
type ExtractParams<Path extends string> = Path extends `${infer Before}{${infer OptionalContent}}${infer After}` ? ExtractOptionalSegment<OptionalContent> & ExtractParams<`${Before}${After}`> : Path extends `${infer _Before}:${infer Rest}` ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>> : Path extends `${infer _Before}*${infer Name}/${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`/${After}`> : { [K in Name]: string[]; } & ExtractParams<`/${After}`> : Path extends `${infer _Before}*${infer Name}-${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`-${After}`> : { [K in Name]: string[]; } & ExtractParams<`-${After}`> : Path extends `${infer _Before}*${infer Name}.${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`.${After}`> : { [K in Name]: string[]; } & ExtractParams<`.${After}`> : Path extends `${infer _Before}*${infer Name}#${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`#${After}`> : { [K in Name]: string[]; } & ExtractParams<`#${After}`> : Path extends `${infer _Before}*${infer Name}:${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`:${After}`> : { [K in Name]: string[]; } & ExtractParams<`:${After}`> : Path extends `${infer _Before}*${infer Name}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<``> : { [K in Name]: string[]; } & ExtractParams<``> : Path extends `${infer _Before}*${infer After}` ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<After> : {};
|
|
52
66
|
/**
|
|
53
67
|
* Extract parameters from Express 5 optional segments in braces
|
|
54
68
|
* Handles patterns like {/:param}, {.:ext}, {/optional/:param}
|
|
55
69
|
*/
|
|
56
|
-
type ExtractOptionalSegment<Content extends string> = Content extends `*${infer Name}` ? Name extends "" ? {} : { [K in Name]?: string[] } : Content extends `/${infer Rest}` ? Rest extends `*${infer Name}` ? Name extends "" ? {} : { [K in Name]?: string[] } : Rest extends `:${infer R}` ? ExtractOptionalParam<R> : {} : Content extends `/:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `.:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `${infer _Path}:${infer Rest}` ? ExtractOptionalParam<Rest> : {};
|
|
70
|
+
type ExtractOptionalSegment<Content extends string> = Content extends `*${infer Name}` ? Name extends "" ? {} : { [K in Name]?: string[]; } : Content extends `/${infer Rest}` ? Rest extends `*${infer Name}` ? Name extends "" ? {} : { [K in Name]?: string[]; } : Rest extends `:${infer R}` ? ExtractOptionalParam<R> : {} : Content extends `/:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `.:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `${infer _Path}:${infer Rest}` ? ExtractOptionalParam<Rest> : {};
|
|
57
71
|
/**
|
|
58
72
|
* Extract a single optional parameter from brace content
|
|
59
73
|
*/
|
|
60
|
-
type ExtractOptionalParam<Rest extends string> = Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}` ? { [K in ParamName]?: string } : {};
|
|
74
|
+
type ExtractOptionalParam<Rest extends string> = Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}` ? { [K in ParamName]?: string; } : {};
|
|
61
75
|
/**
|
|
62
76
|
* Extract a single parameter name from the rest of the path
|
|
63
77
|
* Enhanced to handle Express 5 patterns and optional parameters correctly
|
|
64
78
|
* Special handling for consecutive parameters like :from-:to
|
|
65
79
|
* Order matters: regex constraints must be handled before repeating parameters
|
|
66
80
|
*/
|
|
67
|
-
type ExtractSingleParam<Rest extends string> = Rest extends `${infer ParamName}(${infer _Constraint})${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}-:${infer _NextParam}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}.:${infer _NextParam}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}?/${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?-${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?.${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?#${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?:${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}#${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}:${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}+${infer _After}` ? { [K in ParamName]: string[] } : Rest extends `${infer ParamName}*${infer _After}` ? { [K in ParamName]?: string[] } : Rest extends `${infer ParamName}?${infer _After}` ? { [K in ParamName]?: string } : Rest extends string ? Rest extends "" ? {} : Rest extends `${infer ParamName}?` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}+` ? { [K in ParamName]: string[] } : Rest extends `${infer ParamName}*` ? { [K in ParamName]?: string[] } : { [K in Rest]: string } : {};
|
|
81
|
+
type ExtractSingleParam<Rest extends string> = Rest extends `${infer ParamName}(${infer _Constraint})${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}-:${infer _NextParam}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}.:${infer _NextParam}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}?/${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}?-${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}?.${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}?#${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}?:${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}#${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}:${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}+${infer _After}` ? { [K in ParamName]: string[]; } : Rest extends `${infer ParamName}*${infer _After}` ? { [K in ParamName]?: string[]; } : Rest extends `${infer ParamName}?${infer _After}` ? { [K in ParamName]?: string; } : Rest extends string ? Rest extends "" ? {} : Rest extends `${infer ParamName}?` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}+` ? { [K in ParamName]: string[]; } : Rest extends `${infer ParamName}*` ? { [K in ParamName]?: string[]; } : { [K in Rest]: string; } : {};
|
|
68
82
|
/**
|
|
69
83
|
* Remove the first parameter from path to continue parsing
|
|
70
84
|
* Enhanced to handle Express 5 patterns and optional parameters
|
|
@@ -100,24 +114,39 @@ type RequestOnlyMiddleware<TReq extends Record<string, any>> = TypedMiddleware<T
|
|
|
100
114
|
type LocalsOnlyMiddleware<TLocals extends Record<string, any>> = TypedMiddleware<{}, TLocals>;
|
|
101
115
|
type InferMiddlewareProps<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<infer FirstReq, any> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstReq & InferMiddlewareProps<Rest> : FirstReq : {} : {};
|
|
102
116
|
type InferMiddlewareLocals<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<any, infer FirstLocals> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstLocals & InferMiddlewareLocals<Rest> : FirstLocals : {} : {};
|
|
103
|
-
type SchemaRequest<Path extends string = string, BodySchema extends
|
|
104
|
-
params: ExtractRouteParams<Path>;
|
|
105
|
-
body: BodySchema extends
|
|
106
|
-
query: QuerySchema extends
|
|
117
|
+
type SchemaRequest<Path extends string = string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, MiddlewareProps extends Record<string, any> = {}, ParamsSchema extends SchemaLike | undefined = undefined> = Omit<Request, "params" | "query" | "body"> & {
|
|
118
|
+
params: ParamsSchema extends undefined ? ExtractRouteParams<Path> : InferSchemaOutput<ParamsSchema>;
|
|
119
|
+
body: BodySchema extends undefined ? unknown : InferSchemaOutput<BodySchema>;
|
|
120
|
+
query: QuerySchema extends undefined ? unknown : InferSchemaOutput<QuerySchema>;
|
|
107
121
|
} & MiddlewareProps;
|
|
108
|
-
type SchemaRouteHandler<Path extends string = string, BodySchema extends
|
|
122
|
+
type SchemaRouteHandler<Path extends string = string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, MiddlewareProps extends Record<string, any> = {}, ResponseLocals extends Record<string, any> = {}, ParamsSchema extends SchemaLike | undefined = undefined> = (req: SchemaRequest<Path, BodySchema, QuerySchema, MiddlewareProps, ParamsSchema>, res: Response<any, ResponseLocals>, next?: NextFunction) => void | undefined | Promise<void | undefined> | Response | Promise<Response> | Promise<Response | undefined>;
|
|
109
123
|
/**
|
|
110
124
|
* Options for defining a typed route, including schemas and middleware.
|
|
111
125
|
*
|
|
112
126
|
* @template BodySchema - Schema for request body validation.
|
|
113
127
|
* @template QuerySchema - Schema for query parameter validation.
|
|
128
|
+
* @template ParamsSchema - Schema for route param validation.
|
|
114
129
|
* @property bodySchema - Optional schema for validating the request body.
|
|
115
130
|
* @property querySchema - Optional schema for validating the query string.
|
|
131
|
+
* @property paramsSchema - Optional schema for validating route params.
|
|
116
132
|
* @property middleware - Optional array of TypedMiddleware for this route.
|
|
117
133
|
*/
|
|
118
|
-
interface RouteOptions<BodySchema extends
|
|
134
|
+
interface RouteOptions<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined> {
|
|
119
135
|
bodySchema?: BodySchema;
|
|
136
|
+
/**
|
|
137
|
+
* Every value here arrives as a string, never a real boolean/number —
|
|
138
|
+
* `z.coerce.boolean()` treats `"false"` as truthy. Use a text-aware parser
|
|
139
|
+
* like `z.stringbool()` for boolean fields.
|
|
140
|
+
* @see https://github.com/Mini-Sylar/express-typed-router#gotchas
|
|
141
|
+
*/
|
|
120
142
|
querySchema?: QuerySchema;
|
|
143
|
+
/**
|
|
144
|
+
* Overrides the inferred `req.params` type with this schema's output —
|
|
145
|
+
* useful for coercing a numeric-looking param (`z.coerce.number()`) since
|
|
146
|
+
* Express never converts params from strings on its own. Same
|
|
147
|
+
* string-arrival caveat as `querySchema` applies; see its docs above.
|
|
148
|
+
*/
|
|
149
|
+
paramsSchema?: ParamsSchema;
|
|
121
150
|
middleware?: TypedMiddleware<any, any>[];
|
|
122
151
|
tags?: string[];
|
|
123
152
|
description?: string;
|
|
@@ -126,8 +155,16 @@ interface RouteOptions<BodySchema extends AnyStandardSchema | unknown = unknown,
|
|
|
126
155
|
responseSchema?: AnyStandardSchema;
|
|
127
156
|
/** Exclude this route from the generated OpenAPI spec entirely. */
|
|
128
157
|
hidden?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Only used when `path` is a `RegExp`. A readable stand-in path (e.g.
|
|
160
|
+
* `/legacy/:id`) for the OpenAPI doc, since one can't be derived from a
|
|
161
|
+
* `RegExp` value. Defaults to `regex.toString()`. Doc-only — `req.params`
|
|
162
|
+
* types as `Record<string, string>` for RegExp routes unless overridden
|
|
163
|
+
* by `paramsSchema`.
|
|
164
|
+
*/
|
|
165
|
+
pathExample?: string;
|
|
129
166
|
}
|
|
130
|
-
type DocMeta = Pick<RouteOptions
|
|
167
|
+
type DocMeta = Pick<RouteOptions, "tags" | "summary" | "description" | "deprecated" | "responseSchema" | "hidden" | "pathExample">;
|
|
131
168
|
type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "options" | "head" | "all";
|
|
132
169
|
interface DocsOptions {
|
|
133
170
|
title?: string;
|
|
@@ -184,15 +221,18 @@ interface DocsOptions {
|
|
|
184
221
|
}
|
|
185
222
|
interface RouteMetadata {
|
|
186
223
|
method: HttpMethod;
|
|
187
|
-
path: string;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
224
|
+
path: string | RegExp;
|
|
225
|
+
/** Doc-only path override for RegExp routes — see RouteOptions.pathExample. */
|
|
226
|
+
pathExample?: string | undefined;
|
|
227
|
+
bodySchema?: AnyStandardSchema | undefined;
|
|
228
|
+
querySchema?: AnyStandardSchema | undefined;
|
|
229
|
+
paramsSchema?: AnyStandardSchema | undefined;
|
|
230
|
+
tags?: string[] | undefined;
|
|
231
|
+
description?: string | undefined;
|
|
232
|
+
summary?: string | undefined;
|
|
233
|
+
deprecated?: boolean | undefined;
|
|
234
|
+
responseSchema?: AnyStandardSchema | undefined;
|
|
235
|
+
hidden?: boolean | undefined;
|
|
196
236
|
responseSamples: Map<number, {
|
|
197
237
|
schema: Record<string, any>;
|
|
198
238
|
example?: unknown;
|
|
@@ -235,14 +275,14 @@ declare class TypedRouter<Req extends Record<string, any> = AdditionalReqProps,
|
|
|
235
275
|
* and/or adds properties to response.locals
|
|
236
276
|
*/
|
|
237
277
|
/**
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
278
|
+
* Add typed middleware to the router.
|
|
279
|
+
* This middleware will apply to all routes defined after this call.
|
|
280
|
+
*
|
|
281
|
+
* @template TReq - Type extensions for the request object
|
|
282
|
+
* @template TLocals - Type extensions for response.locals
|
|
283
|
+
* @param middleware - The typed middleware function
|
|
284
|
+
* @returns A new router instance with updated types
|
|
285
|
+
*/
|
|
246
286
|
useMiddleware<TReq extends Record<string, any> = {}, TLocals extends Record<string, any> = {}>(middleware: TypedMiddleware<TReq, TLocals>): TypedRouter<Req & TReq, Locals & TLocals>;
|
|
247
287
|
/**
|
|
248
288
|
* Get the underlying Express router, typed as a RequestHandler so it can be
|
|
@@ -318,168 +358,115 @@ declare class TypedRouter<Req extends Record<string, any> = AdditionalReqProps,
|
|
|
318
358
|
* // GET /docs/openapi.json → raw OpenAPI 3.1 spec
|
|
319
359
|
*/
|
|
320
360
|
docs(options?: DocsOptions): express.Router & express.RequestHandler;
|
|
321
|
-
get<Path extends string>(path: Path, handler: SchemaRouteHandler<Path,
|
|
322
|
-
get<Path extends string, BodySchema extends
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
361
|
+
get<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
362
|
+
get<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
363
|
+
bodySchema?: BodySchema;
|
|
364
|
+
querySchema?: QuerySchema;
|
|
365
|
+
paramsSchema?: ParamsSchema;
|
|
366
|
+
middleware?: [...M];
|
|
367
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
368
|
+
get(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
369
|
+
get<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
370
|
+
bodySchema?: BodySchema;
|
|
371
|
+
querySchema?: QuerySchema;
|
|
372
|
+
paramsSchema?: ParamsSchema;
|
|
373
|
+
middleware?: [...M];
|
|
374
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
375
|
+
post<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
376
|
+
post<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
377
|
+
bodySchema?: BodySchema;
|
|
378
|
+
querySchema?: QuerySchema;
|
|
379
|
+
paramsSchema?: ParamsSchema;
|
|
380
|
+
middleware?: [...M];
|
|
381
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
382
|
+
post(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
383
|
+
post<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
384
|
+
bodySchema?: BodySchema;
|
|
385
|
+
querySchema?: QuerySchema;
|
|
386
|
+
paramsSchema?: ParamsSchema;
|
|
387
|
+
middleware?: [...M];
|
|
388
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
389
|
+
put<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
390
|
+
put<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
391
|
+
bodySchema?: BodySchema;
|
|
392
|
+
querySchema?: QuerySchema;
|
|
393
|
+
paramsSchema?: ParamsSchema;
|
|
394
|
+
middleware?: [...M];
|
|
395
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
396
|
+
put(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
397
|
+
put<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
398
|
+
bodySchema?: BodySchema;
|
|
399
|
+
querySchema?: QuerySchema;
|
|
400
|
+
paramsSchema?: ParamsSchema;
|
|
401
|
+
middleware?: [...M];
|
|
402
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
403
|
+
patch<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
404
|
+
patch<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
405
|
+
bodySchema?: BodySchema;
|
|
406
|
+
querySchema?: QuerySchema;
|
|
407
|
+
paramsSchema?: ParamsSchema;
|
|
408
|
+
middleware?: [...M];
|
|
409
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
410
|
+
patch(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
411
|
+
patch<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
412
|
+
bodySchema?: BodySchema;
|
|
413
|
+
querySchema?: QuerySchema;
|
|
414
|
+
paramsSchema?: ParamsSchema;
|
|
415
|
+
middleware?: [...M];
|
|
416
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
417
|
+
delete<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
418
|
+
delete<Path extends string, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
419
|
+
querySchema?: QuerySchema;
|
|
420
|
+
paramsSchema?: ParamsSchema;
|
|
421
|
+
middleware?: [...M];
|
|
422
|
+
}, handler: SchemaRouteHandler<Path, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
423
|
+
delete(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
424
|
+
delete<QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
425
|
+
querySchema?: QuerySchema;
|
|
426
|
+
paramsSchema?: ParamsSchema;
|
|
427
|
+
middleware?: [...M];
|
|
428
|
+
}, handler: SchemaRouteHandler<string, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
429
|
+
options<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
430
|
+
options<Path extends string, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
431
|
+
querySchema?: QuerySchema;
|
|
432
|
+
paramsSchema?: ParamsSchema;
|
|
433
|
+
middleware?: [...M];
|
|
434
|
+
}, handler: SchemaRouteHandler<Path, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
435
|
+
options(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
436
|
+
options<QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
437
|
+
querySchema?: QuerySchema;
|
|
438
|
+
paramsSchema?: ParamsSchema;
|
|
439
|
+
middleware?: [...M];
|
|
440
|
+
}, handler: SchemaRouteHandler<string, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
441
|
+
head<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
442
|
+
head<Path extends string, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
334
443
|
querySchema?: QuerySchema;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
bodySchema: BodySchema;
|
|
341
|
-
middleware: [...M];
|
|
342
|
-
}, // Using tuple spread pattern
|
|
343
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
344
|
-
// Make it readonly for type inference
|
|
345
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
346
|
-
post<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
347
|
-
middleware: [...M];
|
|
348
|
-
}, // Using tuple spread pattern
|
|
349
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
350
|
-
// Make it readonly for type inference
|
|
351
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
352
|
-
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>;
|
|
353
|
-
post<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
354
|
-
put<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
355
|
-
bodySchema: BodySchema;
|
|
444
|
+
paramsSchema?: ParamsSchema;
|
|
445
|
+
middleware?: [...M];
|
|
446
|
+
}, handler: SchemaRouteHandler<Path, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
447
|
+
head(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
448
|
+
head<QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
356
449
|
querySchema?: QuerySchema;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
bodySchema
|
|
363
|
-
middleware: [...M];
|
|
364
|
-
}, // Using tuple spread pattern
|
|
365
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
366
|
-
// Make it readonly for type inference
|
|
367
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
368
|
-
put<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
369
|
-
middleware: [...M];
|
|
370
|
-
}, // Using tuple spread pattern
|
|
371
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
372
|
-
// Make it readonly for type inference
|
|
373
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
374
|
-
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>;
|
|
375
|
-
put<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
376
|
-
patch<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
377
|
-
bodySchema: BodySchema;
|
|
450
|
+
paramsSchema?: ParamsSchema;
|
|
451
|
+
middleware?: [...M];
|
|
452
|
+
}, handler: SchemaRouteHandler<string, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
453
|
+
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
454
|
+
all<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
455
|
+
bodySchema?: BodySchema;
|
|
378
456
|
querySchema?: QuerySchema;
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
bodySchema
|
|
385
|
-
middleware: [...M];
|
|
386
|
-
}, // Using tuple spread pattern
|
|
387
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
388
|
-
// Make it readonly for type inference
|
|
389
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
390
|
-
patch<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
391
|
-
middleware: [...M];
|
|
392
|
-
}, // Using tuple spread pattern
|
|
393
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
394
|
-
// Make it readonly for type inference
|
|
395
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
396
|
-
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>;
|
|
397
|
-
patch<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
398
|
-
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
399
|
-
querySchema: QuerySchema;
|
|
400
|
-
middleware: [...M];
|
|
401
|
-
}, // Using tuple spread pattern
|
|
402
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
403
|
-
// Make it readonly for type inference
|
|
404
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
405
|
-
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
406
|
-
querySchema: QuerySchema;
|
|
407
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
408
|
-
delete<Path extends string>(path: Path, options: DocMeta, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
409
|
-
delete<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
410
|
-
middleware: [...M];
|
|
411
|
-
}, // Using tuple spread pattern
|
|
412
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
413
|
-
// Make it readonly for type inference
|
|
414
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
415
|
-
delete<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
416
|
-
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
417
|
-
querySchema: QuerySchema;
|
|
418
|
-
middleware: [...M];
|
|
419
|
-
}, // Using tuple spread pattern
|
|
420
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
421
|
-
// Make it readonly for type inference
|
|
422
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
423
|
-
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
424
|
-
querySchema: QuerySchema;
|
|
425
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
426
|
-
options<Path extends string>(path: Path, options: DocMeta, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
427
|
-
options<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
428
|
-
middleware: [...M];
|
|
429
|
-
}, // Using tuple spread pattern
|
|
430
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
431
|
-
// Make it readonly for type inference
|
|
432
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
433
|
-
options<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
434
|
-
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
435
|
-
querySchema: QuerySchema;
|
|
436
|
-
middleware: [...M];
|
|
437
|
-
}, // Using tuple spread pattern
|
|
438
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
439
|
-
// Make it readonly for type inference
|
|
440
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
441
|
-
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
442
|
-
querySchema: QuerySchema;
|
|
443
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
444
|
-
head<Path extends string>(path: Path, options: DocMeta, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
445
|
-
head<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
446
|
-
middleware: [...M];
|
|
447
|
-
}, // Using tuple spread pattern
|
|
448
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
449
|
-
// Make it readonly for type inference
|
|
450
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
451
|
-
head<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
452
|
-
all<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
453
|
-
bodySchema: BodySchema;
|
|
457
|
+
paramsSchema?: ParamsSchema;
|
|
458
|
+
middleware?: [...M];
|
|
459
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
460
|
+
all(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
461
|
+
all<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
462
|
+
bodySchema?: BodySchema;
|
|
454
463
|
querySchema?: QuerySchema;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
459
|
-
all<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
460
|
-
bodySchema: BodySchema;
|
|
461
|
-
middleware: [...M];
|
|
462
|
-
}, // Using tuple spread pattern
|
|
463
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
464
|
-
// Make it readonly for type inference
|
|
465
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
466
|
-
all<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
467
|
-
querySchema: QuerySchema;
|
|
468
|
-
middleware: [...M];
|
|
469
|
-
}, // Using tuple spread pattern
|
|
470
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
471
|
-
// Make it readonly for type inference
|
|
472
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
473
|
-
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>;
|
|
474
|
-
all<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
475
|
-
middleware: [...M];
|
|
476
|
-
}, // Using tuple spread pattern
|
|
477
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
478
|
-
// Make it readonly for type inference
|
|
479
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
480
|
-
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
464
|
+
paramsSchema?: ParamsSchema;
|
|
465
|
+
middleware?: [...M];
|
|
466
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
481
467
|
private registerRoute;
|
|
482
468
|
private createBodyValidationMiddleware;
|
|
469
|
+
private createParamsValidationMiddleware;
|
|
483
470
|
private createQueryValidationMiddleware;
|
|
484
471
|
}
|
|
485
472
|
/**
|
|
@@ -584,4 +571,4 @@ type RouterDocEntry = TypedRouter<any, any> | {
|
|
|
584
571
|
*/
|
|
585
572
|
declare function createDocs(routers: RouterDocEntry | RouterDocEntry[], options?: DocsOptions): express.Router & express.RequestHandler;
|
|
586
573
|
//#endregion
|
|
587
|
-
export { AdditionalLocals, AdditionalReqProps, AnyStandardSchema, DocsOptions, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, RouterDocEntry, SafeParseResult, SchemaRequest, SchemaRouteHandler, TypedMiddleware, TypedRouter, createDocs, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, inferJsonSchema, isSchemaError, parseSchema, safeParseSchema };
|
|
574
|
+
export { AdditionalLocals, AdditionalReqProps, AnyStandardSchema, DocsOptions, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, RouterDocEntry, SafeParseResult, SchemaLike, SchemaRequest, SchemaRouteHandler, TypedMiddleware, TypedRouter, createDocs, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, inferJsonSchema, isSchemaError, parseSchema, safeParseSchema };
|
package/dist/schema-router.d.mts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import express, { NextFunction, Request, Response } from "express";
|
|
2
2
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
|
-
|
|
4
3
|
//#region src/schema-router.d.ts
|
|
5
4
|
type AnyStandardSchema = StandardSchemaV1<any, any>;
|
|
5
|
+
type SchemaLike = AnyStandardSchema | {
|
|
6
|
+
safeParse: (...args: any[]) => any;
|
|
7
|
+
} | {
|
|
8
|
+
parse: (...args: any[]) => any;
|
|
9
|
+
} | {
|
|
10
|
+
validate: (...args: any[]) => any;
|
|
11
|
+
};
|
|
6
12
|
type InferOutput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : unknown;
|
|
7
13
|
type InferInput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferInput<T> : unknown;
|
|
8
14
|
type InferFromSafeParse<T> = T extends {
|
|
@@ -43,28 +49,36 @@ declare function isSchemaError(error: unknown): error is {
|
|
|
43
49
|
* - Optional parameters: /posts/:year/:month? → { year: string; month?: string }
|
|
44
50
|
* - Wildcard parameters: /files/* → { "0": string }
|
|
45
51
|
* - Multiple wildcards: /a/star/b/star → { "0": string; "1": string }
|
|
52
|
+
*
|
|
53
|
+
* A path containing a named regex capture group — `(?<id>...)` — is treated
|
|
54
|
+
* as a raw regex pattern instead: /^\/legacy\/(?<id>\d+)$/ → { id: string }.
|
|
55
|
+
* This syntax never appears in Express's own path syntax, so detecting it is
|
|
56
|
+
* unambiguous; registerRoute converts the string to a real RegExp at
|
|
57
|
+
* runtime so Express matches it as one.
|
|
46
58
|
*/
|
|
47
|
-
type ExtractRouteParams<Path extends string> = string extends Path ? Record<string, string> : ExtractParams<Path>;
|
|
59
|
+
type ExtractRouteParams<Path extends string> = string extends Path ? Record<string, string> : Path extends `${infer _Before}(?<${infer _Name}>${infer _Rest}` ? ExtractRegexGroupParams<Path> : ExtractParams<Path>;
|
|
60
|
+
type ExtractRegexGroupParams<S extends string> = S extends `${infer _Before}(?<${infer Name}>${infer _Rest}` ? { [K in Name]: string; } & ExtractRegexGroupParams<RemoveFirstRegexGroup<S>> : {};
|
|
61
|
+
type RemoveFirstRegexGroup<S extends string> = S extends `${infer _Before}(?<${infer _Name}>${infer After}` ? After extends `${infer _Inner})${infer Rest}` ? Rest : "" : "";
|
|
48
62
|
/**
|
|
49
63
|
* Main parameter extraction logic - enhanced for Express 5 support with recursion depth limit
|
|
50
64
|
*/
|
|
51
|
-
type ExtractParams<Path extends string> = Path extends `${infer Before}{${infer OptionalContent}}${infer After}` ? ExtractOptionalSegment<OptionalContent> & ExtractParams<`${Before}${After}`> : Path extends `${infer _Before}:${infer Rest}` ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>> : Path extends `${infer _Before}*${infer Name}/${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`/${After}`> : { [K in Name]: string[] } & ExtractParams<`/${After}`> : Path extends `${infer _Before}*${infer Name}-${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`-${After}`> : { [K in Name]: string[] } & ExtractParams<`-${After}`> : Path extends `${infer _Before}*${infer Name}.${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`.${After}`> : { [K in Name]: string[] } & ExtractParams<`.${After}`> : Path extends `${infer _Before}*${infer Name}#${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`#${After}`> : { [K in Name]: string[] } & ExtractParams<`#${After}`> : Path extends `${infer _Before}*${infer Name}:${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`:${After}`> : { [K in Name]: string[] } & ExtractParams<`:${After}`> : Path extends `${infer _Before}*${infer Name}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<``> : { [K in Name]: string[] } & ExtractParams<``> : Path extends `${infer _Before}*${infer After}` ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<After> : {};
|
|
65
|
+
type ExtractParams<Path extends string> = Path extends `${infer Before}{${infer OptionalContent}}${infer After}` ? ExtractOptionalSegment<OptionalContent> & ExtractParams<`${Before}${After}`> : Path extends `${infer _Before}:${infer Rest}` ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>> : Path extends `${infer _Before}*${infer Name}/${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`/${After}`> : { [K in Name]: string[]; } & ExtractParams<`/${After}`> : Path extends `${infer _Before}*${infer Name}-${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`-${After}`> : { [K in Name]: string[]; } & ExtractParams<`-${After}`> : Path extends `${infer _Before}*${infer Name}.${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`.${After}`> : { [K in Name]: string[]; } & ExtractParams<`.${After}`> : Path extends `${infer _Before}*${infer Name}#${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`#${After}`> : { [K in Name]: string[]; } & ExtractParams<`#${After}`> : Path extends `${infer _Before}*${infer Name}:${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<`:${After}`> : { [K in Name]: string[]; } & ExtractParams<`:${After}`> : Path extends `${infer _Before}*${infer Name}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<``> : { [K in Name]: string[]; } & ExtractParams<``> : Path extends `${infer _Before}*${infer After}` ? { [K in CountWildcards<_Before, "0">]: string; } & ExtractParams<After> : {};
|
|
52
66
|
/**
|
|
53
67
|
* Extract parameters from Express 5 optional segments in braces
|
|
54
68
|
* Handles patterns like {/:param}, {.:ext}, {/optional/:param}
|
|
55
69
|
*/
|
|
56
|
-
type ExtractOptionalSegment<Content extends string> = Content extends `*${infer Name}` ? Name extends "" ? {} : { [K in Name]?: string[] } : Content extends `/${infer Rest}` ? Rest extends `*${infer Name}` ? Name extends "" ? {} : { [K in Name]?: string[] } : Rest extends `:${infer R}` ? ExtractOptionalParam<R> : {} : Content extends `/:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `.:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `${infer _Path}:${infer Rest}` ? ExtractOptionalParam<Rest> : {};
|
|
70
|
+
type ExtractOptionalSegment<Content extends string> = Content extends `*${infer Name}` ? Name extends "" ? {} : { [K in Name]?: string[]; } : Content extends `/${infer Rest}` ? Rest extends `*${infer Name}` ? Name extends "" ? {} : { [K in Name]?: string[]; } : Rest extends `:${infer R}` ? ExtractOptionalParam<R> : {} : Content extends `/:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `.:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `${infer _Path}:${infer Rest}` ? ExtractOptionalParam<Rest> : {};
|
|
57
71
|
/**
|
|
58
72
|
* Extract a single optional parameter from brace content
|
|
59
73
|
*/
|
|
60
|
-
type ExtractOptionalParam<Rest extends string> = Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}` ? { [K in ParamName]?: string } : {};
|
|
74
|
+
type ExtractOptionalParam<Rest extends string> = Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}` ? { [K in ParamName]?: string; } : {};
|
|
61
75
|
/**
|
|
62
76
|
* Extract a single parameter name from the rest of the path
|
|
63
77
|
* Enhanced to handle Express 5 patterns and optional parameters correctly
|
|
64
78
|
* Special handling for consecutive parameters like :from-:to
|
|
65
79
|
* Order matters: regex constraints must be handled before repeating parameters
|
|
66
80
|
*/
|
|
67
|
-
type ExtractSingleParam<Rest extends string> = Rest extends `${infer ParamName}(${infer _Constraint})${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}-:${infer _NextParam}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}.:${infer _NextParam}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}?/${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?-${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?.${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?#${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?:${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}#${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}:${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}+${infer _After}` ? { [K in ParamName]: string[] } : Rest extends `${infer ParamName}*${infer _After}` ? { [K in ParamName]?: string[] } : Rest extends `${infer ParamName}?${infer _After}` ? { [K in ParamName]?: string } : Rest extends string ? Rest extends "" ? {} : Rest extends `${infer ParamName}?` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}+` ? { [K in ParamName]: string[] } : Rest extends `${infer ParamName}*` ? { [K in ParamName]?: string[] } : { [K in Rest]: string } : {};
|
|
81
|
+
type ExtractSingleParam<Rest extends string> = Rest extends `${infer ParamName}(${infer _Constraint})${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}-:${infer _NextParam}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}.:${infer _NextParam}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}?/${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}?-${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}?.${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}?#${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}?:${infer _After}` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}#${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}:${infer _After}` ? { [K in ParamName]: string; } : Rest extends `${infer ParamName}+${infer _After}` ? { [K in ParamName]: string[]; } : Rest extends `${infer ParamName}*${infer _After}` ? { [K in ParamName]?: string[]; } : Rest extends `${infer ParamName}?${infer _After}` ? { [K in ParamName]?: string; } : Rest extends string ? Rest extends "" ? {} : Rest extends `${infer ParamName}?` ? { [K in ParamName]?: string; } : Rest extends `${infer ParamName}+` ? { [K in ParamName]: string[]; } : Rest extends `${infer ParamName}*` ? { [K in ParamName]?: string[]; } : { [K in Rest]: string; } : {};
|
|
68
82
|
/**
|
|
69
83
|
* Remove the first parameter from path to continue parsing
|
|
70
84
|
* Enhanced to handle Express 5 patterns and optional parameters
|
|
@@ -100,24 +114,39 @@ type RequestOnlyMiddleware<TReq extends Record<string, any>> = TypedMiddleware<T
|
|
|
100
114
|
type LocalsOnlyMiddleware<TLocals extends Record<string, any>> = TypedMiddleware<{}, TLocals>;
|
|
101
115
|
type InferMiddlewareProps<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<infer FirstReq, any> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstReq & InferMiddlewareProps<Rest> : FirstReq : {} : {};
|
|
102
116
|
type InferMiddlewareLocals<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<any, infer FirstLocals> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstLocals & InferMiddlewareLocals<Rest> : FirstLocals : {} : {};
|
|
103
|
-
type SchemaRequest<Path extends string = string, BodySchema extends
|
|
104
|
-
params: ExtractRouteParams<Path>;
|
|
105
|
-
body: BodySchema extends
|
|
106
|
-
query: QuerySchema extends
|
|
117
|
+
type SchemaRequest<Path extends string = string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, MiddlewareProps extends Record<string, any> = {}, ParamsSchema extends SchemaLike | undefined = undefined> = Omit<Request, "params" | "query" | "body"> & {
|
|
118
|
+
params: ParamsSchema extends undefined ? ExtractRouteParams<Path> : InferSchemaOutput<ParamsSchema>;
|
|
119
|
+
body: BodySchema extends undefined ? unknown : InferSchemaOutput<BodySchema>;
|
|
120
|
+
query: QuerySchema extends undefined ? unknown : InferSchemaOutput<QuerySchema>;
|
|
107
121
|
} & MiddlewareProps;
|
|
108
|
-
type SchemaRouteHandler<Path extends string = string, BodySchema extends
|
|
122
|
+
type SchemaRouteHandler<Path extends string = string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, MiddlewareProps extends Record<string, any> = {}, ResponseLocals extends Record<string, any> = {}, ParamsSchema extends SchemaLike | undefined = undefined> = (req: SchemaRequest<Path, BodySchema, QuerySchema, MiddlewareProps, ParamsSchema>, res: Response<any, ResponseLocals>, next?: NextFunction) => void | undefined | Promise<void | undefined> | Response | Promise<Response> | Promise<Response | undefined>;
|
|
109
123
|
/**
|
|
110
124
|
* Options for defining a typed route, including schemas and middleware.
|
|
111
125
|
*
|
|
112
126
|
* @template BodySchema - Schema for request body validation.
|
|
113
127
|
* @template QuerySchema - Schema for query parameter validation.
|
|
128
|
+
* @template ParamsSchema - Schema for route param validation.
|
|
114
129
|
* @property bodySchema - Optional schema for validating the request body.
|
|
115
130
|
* @property querySchema - Optional schema for validating the query string.
|
|
131
|
+
* @property paramsSchema - Optional schema for validating route params.
|
|
116
132
|
* @property middleware - Optional array of TypedMiddleware for this route.
|
|
117
133
|
*/
|
|
118
|
-
interface RouteOptions<BodySchema extends
|
|
134
|
+
interface RouteOptions<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined> {
|
|
119
135
|
bodySchema?: BodySchema;
|
|
136
|
+
/**
|
|
137
|
+
* Every value here arrives as a string, never a real boolean/number —
|
|
138
|
+
* `z.coerce.boolean()` treats `"false"` as truthy. Use a text-aware parser
|
|
139
|
+
* like `z.stringbool()` for boolean fields.
|
|
140
|
+
* @see https://github.com/Mini-Sylar/express-typed-router#gotchas
|
|
141
|
+
*/
|
|
120
142
|
querySchema?: QuerySchema;
|
|
143
|
+
/**
|
|
144
|
+
* Overrides the inferred `req.params` type with this schema's output —
|
|
145
|
+
* useful for coercing a numeric-looking param (`z.coerce.number()`) since
|
|
146
|
+
* Express never converts params from strings on its own. Same
|
|
147
|
+
* string-arrival caveat as `querySchema` applies; see its docs above.
|
|
148
|
+
*/
|
|
149
|
+
paramsSchema?: ParamsSchema;
|
|
121
150
|
middleware?: TypedMiddleware<any, any>[];
|
|
122
151
|
tags?: string[];
|
|
123
152
|
description?: string;
|
|
@@ -126,8 +155,16 @@ interface RouteOptions<BodySchema extends AnyStandardSchema | unknown = unknown,
|
|
|
126
155
|
responseSchema?: AnyStandardSchema;
|
|
127
156
|
/** Exclude this route from the generated OpenAPI spec entirely. */
|
|
128
157
|
hidden?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Only used when `path` is a `RegExp`. A readable stand-in path (e.g.
|
|
160
|
+
* `/legacy/:id`) for the OpenAPI doc, since one can't be derived from a
|
|
161
|
+
* `RegExp` value. Defaults to `regex.toString()`. Doc-only — `req.params`
|
|
162
|
+
* types as `Record<string, string>` for RegExp routes unless overridden
|
|
163
|
+
* by `paramsSchema`.
|
|
164
|
+
*/
|
|
165
|
+
pathExample?: string;
|
|
129
166
|
}
|
|
130
|
-
type DocMeta = Pick<RouteOptions
|
|
167
|
+
type DocMeta = Pick<RouteOptions, "tags" | "summary" | "description" | "deprecated" | "responseSchema" | "hidden" | "pathExample">;
|
|
131
168
|
type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "options" | "head" | "all";
|
|
132
169
|
interface DocsOptions {
|
|
133
170
|
title?: string;
|
|
@@ -184,15 +221,18 @@ interface DocsOptions {
|
|
|
184
221
|
}
|
|
185
222
|
interface RouteMetadata {
|
|
186
223
|
method: HttpMethod;
|
|
187
|
-
path: string;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
224
|
+
path: string | RegExp;
|
|
225
|
+
/** Doc-only path override for RegExp routes — see RouteOptions.pathExample. */
|
|
226
|
+
pathExample?: string | undefined;
|
|
227
|
+
bodySchema?: AnyStandardSchema | undefined;
|
|
228
|
+
querySchema?: AnyStandardSchema | undefined;
|
|
229
|
+
paramsSchema?: AnyStandardSchema | undefined;
|
|
230
|
+
tags?: string[] | undefined;
|
|
231
|
+
description?: string | undefined;
|
|
232
|
+
summary?: string | undefined;
|
|
233
|
+
deprecated?: boolean | undefined;
|
|
234
|
+
responseSchema?: AnyStandardSchema | undefined;
|
|
235
|
+
hidden?: boolean | undefined;
|
|
196
236
|
responseSamples: Map<number, {
|
|
197
237
|
schema: Record<string, any>;
|
|
198
238
|
example?: unknown;
|
|
@@ -235,14 +275,14 @@ declare class TypedRouter<Req extends Record<string, any> = AdditionalReqProps,
|
|
|
235
275
|
* and/or adds properties to response.locals
|
|
236
276
|
*/
|
|
237
277
|
/**
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
278
|
+
* Add typed middleware to the router.
|
|
279
|
+
* This middleware will apply to all routes defined after this call.
|
|
280
|
+
*
|
|
281
|
+
* @template TReq - Type extensions for the request object
|
|
282
|
+
* @template TLocals - Type extensions for response.locals
|
|
283
|
+
* @param middleware - The typed middleware function
|
|
284
|
+
* @returns A new router instance with updated types
|
|
285
|
+
*/
|
|
246
286
|
useMiddleware<TReq extends Record<string, any> = {}, TLocals extends Record<string, any> = {}>(middleware: TypedMiddleware<TReq, TLocals>): TypedRouter<Req & TReq, Locals & TLocals>;
|
|
247
287
|
/**
|
|
248
288
|
* Get the underlying Express router, typed as a RequestHandler so it can be
|
|
@@ -318,168 +358,115 @@ declare class TypedRouter<Req extends Record<string, any> = AdditionalReqProps,
|
|
|
318
358
|
* // GET /docs/openapi.json → raw OpenAPI 3.1 spec
|
|
319
359
|
*/
|
|
320
360
|
docs(options?: DocsOptions): express.Router & express.RequestHandler;
|
|
321
|
-
get<Path extends string>(path: Path, handler: SchemaRouteHandler<Path,
|
|
322
|
-
get<Path extends string, BodySchema extends
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
361
|
+
get<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
362
|
+
get<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
363
|
+
bodySchema?: BodySchema;
|
|
364
|
+
querySchema?: QuerySchema;
|
|
365
|
+
paramsSchema?: ParamsSchema;
|
|
366
|
+
middleware?: [...M];
|
|
367
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
368
|
+
get(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
369
|
+
get<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
370
|
+
bodySchema?: BodySchema;
|
|
371
|
+
querySchema?: QuerySchema;
|
|
372
|
+
paramsSchema?: ParamsSchema;
|
|
373
|
+
middleware?: [...M];
|
|
374
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
375
|
+
post<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
376
|
+
post<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
377
|
+
bodySchema?: BodySchema;
|
|
378
|
+
querySchema?: QuerySchema;
|
|
379
|
+
paramsSchema?: ParamsSchema;
|
|
380
|
+
middleware?: [...M];
|
|
381
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
382
|
+
post(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
383
|
+
post<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
384
|
+
bodySchema?: BodySchema;
|
|
385
|
+
querySchema?: QuerySchema;
|
|
386
|
+
paramsSchema?: ParamsSchema;
|
|
387
|
+
middleware?: [...M];
|
|
388
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
389
|
+
put<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
390
|
+
put<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
391
|
+
bodySchema?: BodySchema;
|
|
392
|
+
querySchema?: QuerySchema;
|
|
393
|
+
paramsSchema?: ParamsSchema;
|
|
394
|
+
middleware?: [...M];
|
|
395
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
396
|
+
put(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
397
|
+
put<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
398
|
+
bodySchema?: BodySchema;
|
|
399
|
+
querySchema?: QuerySchema;
|
|
400
|
+
paramsSchema?: ParamsSchema;
|
|
401
|
+
middleware?: [...M];
|
|
402
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
403
|
+
patch<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
404
|
+
patch<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
405
|
+
bodySchema?: BodySchema;
|
|
406
|
+
querySchema?: QuerySchema;
|
|
407
|
+
paramsSchema?: ParamsSchema;
|
|
408
|
+
middleware?: [...M];
|
|
409
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
410
|
+
patch(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
411
|
+
patch<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
412
|
+
bodySchema?: BodySchema;
|
|
413
|
+
querySchema?: QuerySchema;
|
|
414
|
+
paramsSchema?: ParamsSchema;
|
|
415
|
+
middleware?: [...M];
|
|
416
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
417
|
+
delete<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
418
|
+
delete<Path extends string, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
419
|
+
querySchema?: QuerySchema;
|
|
420
|
+
paramsSchema?: ParamsSchema;
|
|
421
|
+
middleware?: [...M];
|
|
422
|
+
}, handler: SchemaRouteHandler<Path, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
423
|
+
delete(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
424
|
+
delete<QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
425
|
+
querySchema?: QuerySchema;
|
|
426
|
+
paramsSchema?: ParamsSchema;
|
|
427
|
+
middleware?: [...M];
|
|
428
|
+
}, handler: SchemaRouteHandler<string, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
429
|
+
options<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
430
|
+
options<Path extends string, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
431
|
+
querySchema?: QuerySchema;
|
|
432
|
+
paramsSchema?: ParamsSchema;
|
|
433
|
+
middleware?: [...M];
|
|
434
|
+
}, handler: SchemaRouteHandler<Path, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
435
|
+
options(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
436
|
+
options<QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
437
|
+
querySchema?: QuerySchema;
|
|
438
|
+
paramsSchema?: ParamsSchema;
|
|
439
|
+
middleware?: [...M];
|
|
440
|
+
}, handler: SchemaRouteHandler<string, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
441
|
+
head<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
442
|
+
head<Path extends string, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
334
443
|
querySchema?: QuerySchema;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
bodySchema: BodySchema;
|
|
341
|
-
middleware: [...M];
|
|
342
|
-
}, // Using tuple spread pattern
|
|
343
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
344
|
-
// Make it readonly for type inference
|
|
345
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
346
|
-
post<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
347
|
-
middleware: [...M];
|
|
348
|
-
}, // Using tuple spread pattern
|
|
349
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
350
|
-
// Make it readonly for type inference
|
|
351
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
352
|
-
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>;
|
|
353
|
-
post<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
354
|
-
put<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
355
|
-
bodySchema: BodySchema;
|
|
444
|
+
paramsSchema?: ParamsSchema;
|
|
445
|
+
middleware?: [...M];
|
|
446
|
+
}, handler: SchemaRouteHandler<Path, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
447
|
+
head(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
448
|
+
head<QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
356
449
|
querySchema?: QuerySchema;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
bodySchema
|
|
363
|
-
middleware: [...M];
|
|
364
|
-
}, // Using tuple spread pattern
|
|
365
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
366
|
-
// Make it readonly for type inference
|
|
367
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
368
|
-
put<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
369
|
-
middleware: [...M];
|
|
370
|
-
}, // Using tuple spread pattern
|
|
371
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
372
|
-
// Make it readonly for type inference
|
|
373
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
374
|
-
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>;
|
|
375
|
-
put<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
376
|
-
patch<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
377
|
-
bodySchema: BodySchema;
|
|
450
|
+
paramsSchema?: ParamsSchema;
|
|
451
|
+
middleware?: [...M];
|
|
452
|
+
}, handler: SchemaRouteHandler<string, undefined, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
453
|
+
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
454
|
+
all<Path extends string, BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: Path, options: DocMeta & {
|
|
455
|
+
bodySchema?: BodySchema;
|
|
378
456
|
querySchema?: QuerySchema;
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
bodySchema
|
|
385
|
-
middleware: [...M];
|
|
386
|
-
}, // Using tuple spread pattern
|
|
387
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
388
|
-
// Make it readonly for type inference
|
|
389
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
390
|
-
patch<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
391
|
-
middleware: [...M];
|
|
392
|
-
}, // Using tuple spread pattern
|
|
393
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
394
|
-
// Make it readonly for type inference
|
|
395
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
396
|
-
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>;
|
|
397
|
-
patch<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
398
|
-
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
399
|
-
querySchema: QuerySchema;
|
|
400
|
-
middleware: [...M];
|
|
401
|
-
}, // Using tuple spread pattern
|
|
402
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
403
|
-
// Make it readonly for type inference
|
|
404
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
405
|
-
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
406
|
-
querySchema: QuerySchema;
|
|
407
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
408
|
-
delete<Path extends string>(path: Path, options: DocMeta, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
409
|
-
delete<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
410
|
-
middleware: [...M];
|
|
411
|
-
}, // Using tuple spread pattern
|
|
412
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
413
|
-
// Make it readonly for type inference
|
|
414
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
415
|
-
delete<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
416
|
-
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
417
|
-
querySchema: QuerySchema;
|
|
418
|
-
middleware: [...M];
|
|
419
|
-
}, // Using tuple spread pattern
|
|
420
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
421
|
-
// Make it readonly for type inference
|
|
422
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
423
|
-
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
424
|
-
querySchema: QuerySchema;
|
|
425
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
426
|
-
options<Path extends string>(path: Path, options: DocMeta, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
427
|
-
options<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
428
|
-
middleware: [...M];
|
|
429
|
-
}, // Using tuple spread pattern
|
|
430
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
431
|
-
// Make it readonly for type inference
|
|
432
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
433
|
-
options<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
434
|
-
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
435
|
-
querySchema: QuerySchema;
|
|
436
|
-
middleware: [...M];
|
|
437
|
-
}, // Using tuple spread pattern
|
|
438
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
439
|
-
// Make it readonly for type inference
|
|
440
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
441
|
-
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
442
|
-
querySchema: QuerySchema;
|
|
443
|
-
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req, Locals>): TypedRouter<Req, Locals>;
|
|
444
|
-
head<Path extends string>(path: Path, options: DocMeta, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
445
|
-
head<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
446
|
-
middleware: [...M];
|
|
447
|
-
}, // Using tuple spread pattern
|
|
448
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
449
|
-
// Make it readonly for type inference
|
|
450
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
451
|
-
head<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
452
|
-
all<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
453
|
-
bodySchema: BodySchema;
|
|
457
|
+
paramsSchema?: ParamsSchema;
|
|
458
|
+
middleware?: [...M];
|
|
459
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
460
|
+
all(path: RegExp, handler: SchemaRouteHandler<string, undefined, undefined, Req, Locals>): TypedRouter<Req, Locals>;
|
|
461
|
+
all<BodySchema extends SchemaLike | undefined = undefined, QuerySchema extends SchemaLike | undefined = undefined, ParamsSchema extends SchemaLike | undefined = undefined, M extends TypedMiddleware<any, any>[] = []>(path: RegExp, options: DocMeta & {
|
|
462
|
+
bodySchema?: BodySchema;
|
|
454
463
|
querySchema?: QuerySchema;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
459
|
-
all<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
460
|
-
bodySchema: BodySchema;
|
|
461
|
-
middleware: [...M];
|
|
462
|
-
}, // Using tuple spread pattern
|
|
463
|
-
handler: SchemaRouteHandler<Path, BodySchema, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
464
|
-
// Make it readonly for type inference
|
|
465
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
466
|
-
all<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
467
|
-
querySchema: QuerySchema;
|
|
468
|
-
middleware: [...M];
|
|
469
|
-
}, // Using tuple spread pattern
|
|
470
|
-
handler: SchemaRouteHandler<Path, unknown, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
471
|
-
// Make it readonly for type inference
|
|
472
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
473
|
-
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>;
|
|
474
|
-
all<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: DocMeta & {
|
|
475
|
-
middleware: [...M];
|
|
476
|
-
}, // Using tuple spread pattern
|
|
477
|
-
handler: SchemaRouteHandler<Path, unknown, unknown, Req & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference
|
|
478
|
-
// Make it readonly for type inference
|
|
479
|
-
Locals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<Req, Locals>;
|
|
480
|
-
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, Req, Locals>): TypedRouter<Req, Locals>;
|
|
464
|
+
paramsSchema?: ParamsSchema;
|
|
465
|
+
middleware?: [...M];
|
|
466
|
+
}, handler: SchemaRouteHandler<string, BodySchema, QuerySchema, Req & InferMiddlewareProps<readonly [...M]>, Locals & InferMiddlewareLocals<readonly [...M]>, ParamsSchema>): TypedRouter<Req, Locals>;
|
|
481
467
|
private registerRoute;
|
|
482
468
|
private createBodyValidationMiddleware;
|
|
469
|
+
private createParamsValidationMiddleware;
|
|
483
470
|
private createQueryValidationMiddleware;
|
|
484
471
|
}
|
|
485
472
|
/**
|
|
@@ -584,4 +571,4 @@ type RouterDocEntry = TypedRouter<any, any> | {
|
|
|
584
571
|
*/
|
|
585
572
|
declare function createDocs(routers: RouterDocEntry | RouterDocEntry[], options?: DocsOptions): express.Router & express.RequestHandler;
|
|
586
573
|
//#endregion
|
|
587
|
-
export { AdditionalLocals, AdditionalReqProps, AnyStandardSchema, DocsOptions, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, RouterDocEntry, SafeParseResult, SchemaRequest, SchemaRouteHandler, TypedMiddleware, TypedRouter, createDocs, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, inferJsonSchema, isSchemaError, parseSchema, safeParseSchema };
|
|
574
|
+
export { AdditionalLocals, AdditionalReqProps, AnyStandardSchema, DocsOptions, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, RouterDocEntry, SafeParseResult, SchemaLike, SchemaRequest, SchemaRouteHandler, TypedMiddleware, TypedRouter, createDocs, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, inferJsonSchema, isSchemaError, parseSchema, safeParseSchema };
|
package/dist/schema-router.mjs
CHANGED
|
@@ -1,12 +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){
|
|
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){o??=await a(`module`),s??=await a(`url`);let t=[],n=globalThis.process?.argv?.[1];n&&t.push(s.pathToFileURL(n).href);let r=globalThis.process?.cwd?.()??``;r&&t.push(s.pathToFileURL(r+`/`).href);for(let n of t)try{let t=o.createRequire(n).resolve(e);return await a(s.pathToFileURL(t).href)}catch{}return a(e)}const l=new WeakMap,u=/\(\?<[^>]+>/;function d(e){return typeof e==`string`&&u.test(e)?new RegExp(e):e}function f(e){if(typeof e.path==`string`)return e.path;if(e.pathExample)return e.pathExample;let t=0;return e.path.source.replace(/\\\//g,`/`).replace(/\((?!\?)[^()]*\)/g,()=>`:${t++}`)}function p(e){return e.replace(/\{([^{}]*)\}/g,`$1`).replace(/:([A-Za-z0-9_]+)(?:\([^)]*\))?[?+*]?/g,`{$1}`).replace(/\(\?<([A-Za-z0-9_]+)>[^)]*\)/g,`{$1}`).replace(/^\^|\$$/g,``).replace(/\/{2,}/g,`/`)}function m(e){let t=e.replace(/\{([^{}]*)\}/g,`$1`);return[...t.matchAll(/:([A-Za-z0-9_]+)/g),...t.matchAll(/\(\?<([A-Za-z0-9_]+)>/g)].map(e=>e[1])}function h(e){return e.startsWith(`:`)||e.startsWith(`*`)||e.includes(`(?<`)}function g(e){return e.replace(/^\^|\$$/g,``).replace(/[{}]/g,``).split(`/`).filter(Boolean).find(e=>!h(e))??`default`}function _(e,t){let n=t.replace(/^\^|\$$/g,``).replace(/[{}]/g,``).split(`/`).filter(e=>e&&!h(e)),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 v(e){let t=l.get(e);if(t)return t;let n=await y(e);return l.set(e,n),n}async function y(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 b(e){return x(e,0,new WeakSet)}function x(e,t,n){if(e==null)return{type:`null`};if(t>=12)return{};if(e instanceof Date)return{type:`string`,format:`date-time`};if(typeof e==`bigint`)return{type:`integer`};if(typeof e==`object`&&typeof e.toJSON==`function`)return x(e.toJSON(),t,n);if(Array.isArray(e)){if(e.length===0||n.has(e))return{type:`array`,items:{}};n.add(e);let r=Math.min(e.length,20),i=x(e[0],t+1,n);for(let a=1;a<r;a++)i=w(i,x(e[a],t+1,n));return n.delete(e),{type:`array`,items:i}}switch(typeof e){case`string`:return{type:`string`};case`boolean`:return{type:`boolean`};case`number`:return Number.isFinite(e)?{type:Number.isInteger(e)?`integer`:`number`}:{type:`null`};case`object`:{if(n.has(e))return{type:`object`};n.add(e);let r={},i=[];for(let[a,o]of Object.entries(e))typeof o!=`function`&&o!==void 0&&(r[a]=x(o,t+1,n),i.push(a));n.delete(e);let a={type:`object`,properties:r};return i.length&&(a.required=i),a}default:return{}}}function S(e){return!e||e.type===void 0?new Set:new Set(Array.isArray(e.type)?e.type:[e.type])}function C(e){e.has(`integer`)&&e.has(`number`)&&e.delete(`integer`);let t=[...e];if(t.length!==0)return t.length===1?t[0]:t}function w(e,t){if(!e||Object.keys(e).length===0)return t??{};if(!t||Object.keys(t).length===0)return e??{};let n=new Set([...S(e),...S(t)]),r={},i=C(n);if(i!==void 0&&(r.type=i),n.has(`object`)&&(e.properties||t.properties)){let n=e.properties??{},i=t.properties??{},a={};for(let e of new Set([...Object.keys(n),...Object.keys(i)]))a[e]=w(n[e],i[e]);r.properties=a;let o=e.required??[],s=t.required??[],c=o.filter(e=>s.includes(e));c.length&&(r.required=c)}if(n.has(`array`)){let n=w(e.items,t.items);n&&Object.keys(n).length&&(r.items=n)}return r}function T(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}const E=`https://cdn.jsdelivr.net/npm/@scalar/api-reference`;function D(e,t,n){return`<!doctype html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
|
-
<title>${
|
|
4
|
+
<title>${T(e)}</title>
|
|
5
5
|
<meta charset="utf-8" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
7
|
</head>
|
|
8
8
|
<body>
|
|
9
|
-
<script id="api-reference" data-url="${
|
|
10
|
-
<script src="${
|
|
9
|
+
<script id="api-reference" data-url="${T(t)}"><\/script>
|
|
10
|
+
<script src="${T(n)}"><\/script>
|
|
11
11
|
</body>
|
|
12
|
-
</html>`}async function
|
|
12
|
+
</html>`}async function O(e,t){let n=Object.create(null);for(let t of e){if(t.method===`all`||t.hidden)continue;let e=f(t),r=p(e);n[r]||(n[r]={});let i;if(t.paramsSchema){let e=await v(t.paramsSchema),n=e.properties??{},r=e.required??[];i=Object.entries(n).map(([e,t])=>({name:e,in:`path`,required:r.includes(e),schema:t}))}else i=m(e).map(e=>({name:e,in:`path`,required:!0,schema:{type:`string`}}));if(t.querySchema){let e=await v(t.querySchema),n=e.properties??{},r=e.required??[];for(let[e,t]of Object.entries(n))i.push({name:e,in:`query`,required:r.includes(e),schema:t})}let a={summary:t.summary??_(t.method,e),tags:t.tags??[g(e)],parameters:i};t.description&&(a.description=t.description),t.deprecated&&(a.deprecated=!0),t.bodySchema&&(a.requestBody={required:!0,content:{"application/json":{schema:await v(t.bodySchema)}}});let o={};for(let[e,n]of t.responseSamples){let t={schema:n.schema};n.example!==void 0&&(t.example=n.example),o[String(e)]={description:e<400?`Success`:`Error`,content:{"application/json":t}}}if(t.responseSchema){let e=await v(t.responseSchema);if(e&&Object.keys(e).length){let n=`200`;for(let e of t.responseSamples.keys())if(e>=200&&e<300){n=String(e);break}o[n]={description:`Success`,content:{"application/json":{schema:e}}}}}Object.keys(o).length===0&&(o[200]={description:`Success`}),a.responses=o,n[r][t.method]=a}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 k=new WeakMap;var A=class t{router;routes=[];mountedRouters=[];sampleMode=`off`;scheduleSpecWrite;constructor(){this.router=e.Router(),k.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.trackMounted(i,e),e.getRouter();let n=k.get(e);return n&&this.trackMounted(i,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.trackMounted(e,n)}else this.router.use(e.getRouter()),this.trackMounted(``,e);return this}getRouteMetadata(){let e=this.mountedRouters.flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,...typeof t.path==`string`?{path:e+t.path}:{path:t.path,pathExample:e+f(t)}})));return[...this.routes,...e]}enableSampling(e=`redacted`,t,n=new Set){if(!n.has(this)){n.add(this),this.sampleMode=e,this.scheduleSpecWrite=t;for(let{router:r}of this.mountedRouters)r.enableSampling(e,t,n)}}trackMounted(e,t){this.mountedRouters.some(n=>n.router===t&&n.prefix===e)||(this.mountedRouters.push({prefix:e,router:t}),this.sampleMode!==`off`&&t.enableSampling(this.sampleMode,this.scheduleSpecWrite))}hydrateResponses(e,t=``,n=new Set){if(n.has(this))return;n.add(this);let r=e?.paths??{};for(let e of this.routes){let n=r[p(t+f(e))]?.[e.method]?.responses;if(n)for(let t of Object.keys(n)){let r=Number(t);if(Number.isNaN(r)||e.responseSamples.has(r))continue;let i=n[t]?.content?.[`application/json`];i?.schema&&e.responseSamples.set(r,i.example===void 0?{schema:i.schema}:{schema:i.schema,example:i.example})}}for(let{prefix:r,router:i}of this.mountedRouters)i.hydrateResponses(e,t+r,n)}docs(t={}){let n=e.Router(),r;if(t.specOutputPath){let e=t.specOutputPath,n=!1,i=!1,o=async()=>{if(n){i=!0;return}n=!0;try{let n=await O(this.getRouteMetadata(),t),r=await a(`fs/promises`),i=e.replace(/[/\\][^/\\]*$/,``);i&&i!==e&&await r.mkdir(i,{recursive:!0}).catch(()=>{});let o=globalThis.process?.pid??`0`,s=`${e}.${o}.tmp`;await r.writeFile(s,JSON.stringify(n,null,2),`utf8`),await r.rename(s,e)}catch{}finally{n=!1,i&&(i=!1,o())}},s;r=()=>{s&&clearTimeout(s),s=setTimeout(o,300),s.unref?.()},setImmediate(async()=>{try{let t=await(await a(`fs/promises`)).readFile(e,`utf8`).catch(()=>null);if(t)try{this.hydrateResponses(JSON.parse(t))}catch{}}catch{}await o()})}return t.sampleResponses!==!1&&this.enableSampling(t.sampleResponses===`live`?`live`:`redacted`,r),n.get(`/openapi.json`,async(e,n)=>{try{let e=await O(this.getRouteMetadata(),t);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??E;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(D(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.paramsSchema=e.paramsSchema,a.tags=e.tags,a.description=e.description,a.summary=e.summary,a.deprecated=e.deprecated,a.responseSchema=e.responseSchema,a.hidden=e.hidden,a.pathExample=e.pathExample,e.middleware&&i.push(...e.middleware),e.bodySchema&&i.push(this.createBodyValidationMiddleware(e.bodySchema)),e.querySchema&&i.push(this.createQueryValidationMiddleware(e.querySchema)),e.paramsSchema&&i.push(this.createParamsValidationMiddleware(e.paramsSchema)),i.push(r)}else i.push(n);let o=0,s=(e,t)=>{let n=e.statusCode,r=b(t),i=a.responseSamples.get(n),o=i?w(i.schema,r):r,s=this.sampleMode===`live`?i?.example??t:void 0,c=!i||JSON.stringify(i.schema)!==JSON.stringify(o);a.responseSamples.set(n,{schema:o,example:s}),c&&this.scheduleSpecWrite?.()};return this.router[e](d(t),(e,t,n)=>{if(this.sampleMode===`off`||a.hidden||o>=50||a.responseSamples.size>=10){n();return}o++;let r=t.json;t.json=function(e){return s(t,e),r.call(this,e)};let i=t.send;t.send=function(e){return typeof e==`object`&&e&&!Buffer.isBuffer(e)&&s(t,e),i.call(this,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)}}}createParamsValidationMiddleware(e){return async(t,n,a)=>{try{let i=r(e,t.params),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.params=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 j(){return new A}function M(e){let t=new A;return e?.errorHandler&&t.getRouter().use(e.errorHandler),t}function N(...e){let t=new A;for(let n of e)t=t.useMiddleware(n);return t}function P(t,n={}){let r=(Array.isArray(t)?t:[t]).map(e=>`prefix`in e?e:{prefix:``,router:e});if(n.sampleResponses!==!1){let e=n.sampleResponses===`live`?`live`:`redacted`;for(let{router:t}of r)t.enableSampling(e)}let i=e.Router();return i.get(`/openapi.json`,async(e,t)=>{try{let e=await O(r.flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,...typeof t.path==`string`?{path:e+t.path}:{path:t.path,pathExample:e+f(t)}}))),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??E;t.setHeader(`Content-Type`,`text/html; charset=utf-8`),t.send(D(n.title??`API`,r,i))}),i}export{A as TypedRouter,P as createDocs,j as createTypedRouter,M as createTypedRouterWithConfig,N as createTypedRouterWithMiddleware,b as inferJsonSchema,i as isSchemaError,n as parseSchema,r as safeParseSchema};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minisylar/express-typed-router",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.5",
|
|
4
4
|
"description": "A strongly-typed Express router with Zod validation and automatic type inference for params, body, query, and middleware",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/schema-router.cjs",
|
|
@@ -75,14 +75,14 @@
|
|
|
75
75
|
"@standard-schema/utils": "^0.3.0"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
|
-
"@semantic-release/changelog": "^
|
|
79
|
-
"@semantic-release/git": "^
|
|
78
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
79
|
+
"@semantic-release/git": "^11.0.1",
|
|
80
80
|
"@types/express": "^5.0.6",
|
|
81
81
|
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
82
82
|
"rimraf": "^6.1.3",
|
|
83
|
-
"semantic-release": "^25.0.
|
|
84
|
-
"tsdown": "^0.22.
|
|
85
|
-
"typescript": "^5.9.
|
|
83
|
+
"semantic-release": "^25.0.9",
|
|
84
|
+
"tsdown": "^0.22.14",
|
|
85
|
+
"typescript": "^5.9.3"
|
|
86
86
|
},
|
|
87
87
|
"engines": {
|
|
88
88
|
"node": ">=18.0.0"
|