@minisylar/express-typed-router 1.9.9 → 1.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/schema-router.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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)}function p(...e){return e}function m(e){e.res.status(400).json({error:`Validation failed`,details:e.details})}function h(e,t){if(e)try{e(t)?.catch?.(()=>{})}catch{}}const g=Function(`m`,`return import(m)`);let _,v;async function y(e){_??=await g(`module`),v??=await g(`url`);let t=[],n=globalThis.process?.argv?.[1];n&&t.push(v.pathToFileURL(n).href);let r=globalThis.process?.cwd?.()??``;r&&t.push(v.pathToFileURL(r+`/`).href);for(let n of t)try{let t=_.createRequire(n).resolve(e);return await g(v.pathToFileURL(t).href)}catch{}return g(e)}const b=new WeakMap,x=/\(\?<[^>]+>/;function S(e){return typeof e==`string`&&x.test(e)?new RegExp(e):e}function C(e){if(typeof e.path==`string`)return e.path;
|
|
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)}function p(...e){return e}function m(e){e.res.status(400).json({error:`Validation failed`,details:e.details})}function h(e,t){if(e)try{e(t)?.catch?.(()=>{})}catch{}}const g=Function(`m`,`return import(m)`);let _,v;async function y(e){_??=await g(`module`),v??=await g(`url`);let t=[],n=globalThis.process?.argv?.[1];n&&t.push(v.pathToFileURL(n).href);let r=globalThis.process?.cwd?.()??``;r&&t.push(v.pathToFileURL(r+`/`).href);for(let n of t)try{let t=_.createRequire(n).resolve(e);return await g(v.pathToFileURL(t).href)}catch{}return g(e)}const b=new WeakMap,x=/\(\?<[^>]+>/;function S(e){return typeof e==`string`&&x.test(e)?new RegExp(e):e}function C(e){let t=Array.isArray(e.pathExample)?e.pathExample[0]:e.pathExample;if(t)return t;if(typeof e.path==`string`)return e.path;let n=0;return e.path.source.replace(/\\\//g,`/`).replace(/\((?!\?)[^()]*\)/g,()=>`:${n++}`)}function w(e){let t=[],n=0,r=0,i=!1,a=!1;for(let o=0;o<e.length;o++){let s=e[o];if(a){a=!1;continue}if(s===`\\`){a=!0;continue}if(s===`[`){i=!0;continue}if(s===`]`){i=!1;continue}i||(s===`(`?r++:s===`)`?r=Math.max(0,r-1):s===`|`&&r===0&&(t.push(e.slice(n,o)),n=o+1))}return t.push(e.slice(n)),t.length>1?t:[e]}function T(e){if(!e.startsWith(`(`)||!e.endsWith(`)`))return e;let t=0,n=!1,r=!1;for(let i=0;i<e.length;i++){let a=e[i];if(r){r=!1;continue}if(a===`\\`){r=!0;continue}if(a===`[`)n=!0;else if(a===`]`)n=!1;else if(!n&&a===`(`)t++;else if(!n&&a===`)`&&(t--,t===0&&i!==e.length-1))return e}return e.slice(1,-1).replace(/^\?:/,``)}function E(e){let t=0;return T(e).replace(/^\^|\$$/g,``).replace(/\\\//g,`/`).replace(/\.\*|\.\+/g,`/:path`).replace(/\/?\?$/,``).replace(/\(\?<([A-Za-z0-9_]+)>[^()]*\)/g,`:$1`).replace(/\((?!\?)[^()]*\)/g,()=>`:${t++}`)}function D(e){if(Array.isArray(e.pathExample)&&e.pathExample.length>0)return e.pathExample;if(typeof e.path==`string`||e.pathExample)return[C(e)];let t=w(e.path.source);return t.length===1?[C(e)]:t.map(E)}function O(e){let t=e.replace(/\{([^{}]*)\}/g,`$1`).replace(/:([A-Za-z0-9_]+)(?:\([^)]*\))?[?+*]?/g,`{$1}`).replace(/\(\?<([A-Za-z0-9_]+)>[^)]*\)/g,`{$1}`).replace(/\*([A-Za-z0-9_]+)/g,`{$1}`).replace(/^\^|\$$/g,``),n=0;return t.replace(/\*/g,()=>`{${n++}}`).replace(/\/{2,}/g,`/`)}function k(e){let t=e.replace(/\{([^{}]*)\}/g,`$1`).replace(/^\^|\$$/g,``),n=[...t.matchAll(/:([A-Za-z0-9_]+)/g),...t.matchAll(/\(\?<([A-Za-z0-9_]+)>/g),...t.matchAll(/\*([A-Za-z0-9_]+)/g)].map(e=>e[1]),r=(t.replace(/\*[A-Za-z0-9_]+/g,``).match(/\*/g)??[]).length;for(let e=0;e<r;e++)n.push(String(e));return n}function A(e){return e.startsWith(`:`)||e.includes(`*`)||e.includes(`(?<`)}function j(e){return e.replace(/^\^|\$$/g,``).replace(/[{}]/g,``).split(`/`).filter(Boolean).find(e=>!A(e))??`default`}function M(e,t){let n=t.replace(/^\^|\$$/g,``).replace(/[{}]/g,``).split(`/`).filter(e=>e&&!A(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 N(e){let t=b.get(e);if(t)return t;let n=await P(e);return b.set(e,n),n}async function P(e){if(typeof e.toJsonSchema==`function`)try{return e.toJsonSchema()}catch{}let t=e[`~standard`]?.vendor;if(t===`zod`){try{let t=await y(`zod`);if(typeof t.toJSONSchema==`function`)return t.toJSONSchema(e)}catch{}try{let t=await y(`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 y(`@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 y(`effect`),n=t.JSONSchema?.make??t.default?.JSONSchema?.make;if(typeof n==`function`)return n(e)}catch{}return{}}function F(e){return I(e,0,new WeakSet)}function I(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 I(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=I(e[0],t+1,n);for(let a=1;a<r;a++)i=z(i,I(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]=I(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 L(e){return!e||e.type===void 0?new Set:new Set(Array.isArray(e.type)?e.type:[e.type])}function R(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 z(e,t){if(!e||Object.keys(e).length===0)return t??{};if(!t||Object.keys(t).length===0)return e??{};let n=new Set([...L(e),...L(t)]),r={},i=R(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]=z(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=z(e.items,t.items);n&&Object.keys(n).length&&(r.items=n)}return r}function B(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}const V=`https://cdn.jsdelivr.net/npm/@scalar/api-reference`;function H(e,t,n){return`<!doctype html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<title>${B(e)}</title>
|
|
@@ -9,4 +9,4 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.
|
|
|
9
9
|
<script id="api-reference" data-url="${B(t)}"><\/script>
|
|
10
10
|
<script src="${B(n)}"><\/script>
|
|
11
11
|
</body>
|
|
12
|
-
</html>`}async function U(e,t){let n=Object.create(null);for(let t of e)if(!(t.method===`all`||t.hidden))for(let e of D(t)){let r=O(e);n[r]||(n[r]={});let i;if(t.paramsSchema){let e=await N(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=k(e).map(e=>({name:e,in:`path`,required:!0,schema:{type:`string`}}));if(t.querySchema){let e=await N(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??M(t.method,e),tags:t.tags??[j(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 N(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 N(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 W=new WeakMap;var G=class e{router;routes=[];mountedRouters=[];sampleMode=`off`;scheduleSpecWrite;globalValidationFailureHook;constructor(){this.router=c.default.Router(),W.set(this.router,this)}onValidationFailure(e){return this.globalValidationFailureHook=e,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=W.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+C(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)for(let n of D(e)){let i=r[O(t+n)]?.[e.method]?.responses;if(i)for(let t of Object.keys(i)){let n=Number(t);if(Number.isNaN(n)||e.responseSamples.has(n))continue;let r=i[t]?.content?.[`application/json`];r?.schema&&e.responseSamples.set(n,r.example===void 0?{schema:r.schema}:{schema:r.schema,example:r.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 U(this.getRouteMetadata(),e),r=await g(`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 g(`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 U(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??V;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(H(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 o=n;a.bodySchema=o.bodySchema,a.querySchema=o.querySchema,a.paramsSchema=o.paramsSchema,a.tags=o.tags,a.description=o.description,a.summary=o.summary,a.deprecated=o.deprecated,a.responseSchema=o.responseSchema,a.hidden=o.hidden,a.pathExample=o.pathExample,o.middleware&&i.push(...o.middleware);let s=o.hooks?.onValidationFailure,c=o.hooks?.onBodyValidationFailure,l=o.hooks?.onQueryValidationFailure,u=o.hooks?.onParamsValidationFailure;o.bodySchema&&i.push(this.createBodyValidationMiddleware(o.bodySchema,e,t,o.pathExample,s,c)),o.querySchema&&i.push(this.createQueryValidationMiddleware(o.querySchema,e,t,o.pathExample,s,l)),o.paramsSchema&&i.push(this.createParamsValidationMiddleware(o.paramsSchema,e,t,o.pathExample,s,u)),i.push(r)}else i.push(n);let o=0,s=(e,t)=>{let n=e.statusCode,r=F(t),i=a.responseSamples.get(n),o=i?z(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](S(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}notifyValidationFailure(e,t,n,r,i,a,o){if(!o&&!this.globalValidationFailureHook)return;let s={source:e,error:t,details:n,method:r,path:i,req:a};h(o,s),h(this.globalValidationFailureHook,s)}async runSchemaValidationHook(e,t,n,r,i,a,o,s){if(!e)return!1;let c=!1,l={error:t,details:n,method:r,path:i,req:a,res:o,next:((...e)=>{c=!0,s(...e)})};try{await e(l)}catch{}return c||o.headersSent}createBodyValidationMiddleware(e,t,n,r,i,a){return async(o,s,c)=>{try{let l=d(e,o.body),u=l&&typeof l.then==`function`?await l:l;if(u&&`issues`in u&&u.issues){let e=u.errors||u.issues,l=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,e,t,l,o,s,c);this.notifyValidationFailure(`body`,`Validation failed`,e,t,l,o,i),d||m({res:s,details:e});return}o.body=u&&`value`in u?u.value:u,c()}catch(e){if(f(e)){let l=e.errors||e.issues,u=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,l,t,u,o,s,c);this.notifyValidationFailure(`body`,`Validation failed`,l,t,u,o,i),d||m({res:s,details:l})}else c(e)}}}createParamsValidationMiddleware(e,t,n,r,i,a){return async(o,s,c)=>{try{let l=d(e,o.params),u=l&&typeof l.then==`function`?await l:l;if(u&&`issues`in u&&u.issues){let e=u.errors||u.issues,l=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,e,t,l,o,s,c);this.notifyValidationFailure(`params`,`Validation failed`,e,t,l,o,i),d||m({res:s,details:e});return}o.params=u&&`value`in u?u.value:u,c()}catch(e){if(f(e)){let l=e.errors||e.issues,u=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,l,t,u,o,s,c);this.notifyValidationFailure(`params`,`Validation failed`,l,t,u,o,i),d||m({res:s,details:l})}else c(e)}}}createQueryValidationMiddleware(e,t,n,r,i,a){return async(o,s,c)=>{try{let l=d(e,o.query),u=l&&typeof l.then==`function`?await l:l;if(u&&`issues`in u&&u.issues){let e=u.errors||u.issues,l=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,e,t,l,o,s,c);this.notifyValidationFailure(`query`,`Validation failed`,e,t,l,o,i),d||m({res:s,details:e});return}let f=u&&`value`in u?u.value:u;Object.defineProperty(o,"query",{value:f,writable:!1,enumerable:!0,configurable:!0}),c()}catch(e){if(f(e)){let l=e.errors||e.issues,u=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,l,t,u,o,s,c);this.notifyValidationFailure(`query`,`Validation failed`,l,t,u,o,i),d||m({res:s,details:l})}else c(e)}}}};function K(){return new G}function q(e){let t=new G;return e?.errorHandler&&t.getRouter().use(e.errorHandler),e?.hooks?.onValidationFailure&&t.onValidationFailure(e.hooks.onValidationFailure),t}function J(...e){let t=new G;for(let n of e)t=t.useMiddleware(n);return t}function Y(e){return(Array.isArray(e)?e:[e]).map(e=>`prefix`in e?e:{prefix:``,router:e}).flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,...typeof t.path==`string`?{path:e+t.path}:{path:t.path,pathExample:e+C(t)}})))}async function X(e,t={}){return U(Y(e),t)}function Z(e,t={}){if(t.sampleResponses!==!1){let n=t.sampleResponses===`live`?`live`:`redacted`,r=Array.isArray(e)?e:[e];for(let e of r)(`prefix`in e?e.router:e).enableSampling(n)}let n=c.default.Router();return n.get(`/openapi.json`,async(n,r)=>{try{let n=await X(e,t);r.json(n)}catch(e){r.status(500).json({error:`Failed to generate spec`,details:String(e)})}}),n.get(`/`,(e,n)=>{let r=`${e.baseUrl}/openapi.json`,i=t.cdnUrl??V;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(H(t.title??`API`,r,i))}),n}exports.TypedRouter=G,exports.createDocs=Z,exports.createTypedRouter=K,exports.createTypedRouterWithConfig=q,exports.createTypedRouterWithMiddleware=J,exports.defaultValidationHandler=m,exports.defineMiddleware=p,exports.generateOpenApiSpec=X,exports.inferJsonSchema=F,exports.isSchemaError=f,exports.parseSchema=u,exports.safeParseSchema=d;
|
|
12
|
+
</html>`}async function U(e,t){let n=Object.create(null);for(let t of e)if(!(t.method===`all`||t.hidden))for(let e of D(t)){let r=O(e);n[r]||(n[r]={});let i;if(t.paramsSchema){let e=await N(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=k(e).map(e=>({name:e,in:`path`,required:!0,schema:{type:`string`}}));if(t.querySchema){let e=await N(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??M(t.method,e),tags:t.tags??[j(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 N(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 N(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 W=new WeakMap;var G=class e{router;routes=[];mountedRouters=[];sampleMode=`off`;scheduleSpecWrite;globalValidationFailureHook;constructor(){this.router=c.default.Router(),W.set(this.router,this)}onValidationFailure(e){return this.globalValidationFailureHook=e,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=W.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:D(t).map(t=>e+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)for(let n of D(e)){let i=r[O(t+n)]?.[e.method]?.responses;if(i)for(let t of Object.keys(i)){let n=Number(t);if(Number.isNaN(n)||e.responseSamples.has(n))continue;let r=i[t]?.content?.[`application/json`];r?.schema&&e.responseSamples.set(n,r.example===void 0?{schema:r.schema}:{schema:r.schema,example:r.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 U(this.getRouteMetadata(),e),r=await g(`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 g(`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 U(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??V;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(H(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 o=n;a.bodySchema=o.bodySchema,a.querySchema=o.querySchema,a.paramsSchema=o.paramsSchema,a.tags=o.tags,a.description=o.description,a.summary=o.summary,a.deprecated=o.deprecated,a.responseSchema=o.responseSchema,a.hidden=o.hidden,a.pathExample=o.pathExample,o.middleware&&i.push(...o.middleware);let s=o.hooks?.onValidationFailure,c=o.hooks?.onBodyValidationFailure,l=o.hooks?.onQueryValidationFailure,u=o.hooks?.onParamsValidationFailure;o.bodySchema&&i.push(this.createBodyValidationMiddleware(o.bodySchema,e,t,o.pathExample,s,c)),o.querySchema&&i.push(this.createQueryValidationMiddleware(o.querySchema,e,t,o.pathExample,s,l)),o.paramsSchema&&i.push(this.createParamsValidationMiddleware(o.paramsSchema,e,t,o.pathExample,s,u)),i.push(r)}else i.push(n);let o=0,s=(e,t)=>{let n=e.statusCode,r=F(t),i=a.responseSamples.get(n),o=i?z(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](S(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}notifyValidationFailure(e,t,n,r,i,a,o){if(!o&&!this.globalValidationFailureHook)return;let s={source:e,error:t,details:n,method:r,path:i,req:a};h(o,s),h(this.globalValidationFailureHook,s)}async runSchemaValidationHook(e,t,n,r,i,a,o,s){if(!e)return!1;let c=!1,l={error:t,details:n,method:r,path:i,req:a,res:o,next:((...e)=>{c=!0,s(...e)})};try{await e(l)}catch{}return c||o.headersSent}createBodyValidationMiddleware(e,t,n,r,i,a){return async(o,s,c)=>{try{let l=d(e,o.body),u=l&&typeof l.then==`function`?await l:l;if(u&&`issues`in u&&u.issues){let e=u.errors||u.issues,l=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,e,t,l,o,s,c);this.notifyValidationFailure(`body`,`Validation failed`,e,t,l,o,i),d||m({res:s,details:e});return}o.body=u&&`value`in u?u.value:u,c()}catch(e){if(f(e)){let l=e.errors||e.issues,u=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,l,t,u,o,s,c);this.notifyValidationFailure(`body`,`Validation failed`,l,t,u,o,i),d||m({res:s,details:l})}else c(e)}}}createParamsValidationMiddleware(e,t,n,r,i,a){return async(o,s,c)=>{try{let l=d(e,o.params),u=l&&typeof l.then==`function`?await l:l;if(u&&`issues`in u&&u.issues){let e=u.errors||u.issues,l=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,e,t,l,o,s,c);this.notifyValidationFailure(`params`,`Validation failed`,e,t,l,o,i),d||m({res:s,details:e});return}o.params=u&&`value`in u?u.value:u,c()}catch(e){if(f(e)){let l=e.errors||e.issues,u=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,l,t,u,o,s,c);this.notifyValidationFailure(`params`,`Validation failed`,l,t,u,o,i),d||m({res:s,details:l})}else c(e)}}}createQueryValidationMiddleware(e,t,n,r,i,a){return async(o,s,c)=>{try{let l=d(e,o.query),u=l&&typeof l.then==`function`?await l:l;if(u&&`issues`in u&&u.issues){let e=u.errors||u.issues,l=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,e,t,l,o,s,c);this.notifyValidationFailure(`query`,`Validation failed`,e,t,l,o,i),d||m({res:s,details:e});return}let f=u&&`value`in u?u.value:u;Object.defineProperty(o,"query",{value:f,writable:!1,enumerable:!0,configurable:!0}),c()}catch(e){if(f(e)){let l=e.errors||e.issues,u=C({path:n,pathExample:r}),d=await this.runSchemaValidationHook(a,`Validation failed`,l,t,u,o,s,c);this.notifyValidationFailure(`query`,`Validation failed`,l,t,u,o,i),d||m({res:s,details:l})}else c(e)}}}};function K(){return new G}function q(e){let t=new G;return e?.errorHandler&&t.getRouter().use(e.errorHandler),e?.hooks?.onValidationFailure&&t.onValidationFailure(e.hooks.onValidationFailure),t}function J(...e){let t=new G;for(let n of e)t=t.useMiddleware(n);return t}function Y(e){return(Array.isArray(e)?e:[e]).map(e=>`prefix`in e?e:{prefix:``,router:e}).flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,...typeof t.path==`string`?{path:e+t.path}:{path:t.path,pathExample:D(t).map(t=>e+t)}})))}async function X(e,t={}){return U(Y(e),t)}function Z(e,t={}){if(t.sampleResponses!==!1){let n=t.sampleResponses===`live`?`live`:`redacted`,r=Array.isArray(e)?e:[e];for(let e of r)(`prefix`in e?e.router:e).enableSampling(n)}let n=c.default.Router();return n.get(`/openapi.json`,async(n,r)=>{try{let n=await X(e,t);r.json(n)}catch(e){r.status(500).json({error:`Failed to generate spec`,details:String(e)})}}),n.get(`/`,(e,n)=>{let r=`${e.baseUrl}/openapi.json`,i=t.cdnUrl??V;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(H(t.title??`API`,r,i))}),n}exports.TypedRouter=G,exports.createDocs=Z,exports.createTypedRouter=K,exports.createTypedRouterWithConfig=q,exports.createTypedRouterWithMiddleware=J,exports.defaultValidationHandler=m,exports.defineMiddleware=p,exports.generateOpenApiSpec=X,exports.inferJsonSchema=F,exports.isSchemaError=f,exports.parseSchema=u,exports.safeParseSchema=d;
|
package/dist/schema-router.d.cts
CHANGED
|
@@ -276,13 +276,14 @@ interface RouteOptions<BodySchema extends SchemaLike | undefined = undefined, Qu
|
|
|
276
276
|
/** Exclude this route from the generated OpenAPI spec entirely. */
|
|
277
277
|
hidden?: boolean;
|
|
278
278
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
279
|
+
* Stand-in path(s) for the OpenAPI doc. Required for a `RegExp` route
|
|
280
|
+
* (defaults to `regex.toString()` otherwise); optional for a string
|
|
281
|
+
* route, to override an ambiguous auto-converted path. An array documents
|
|
282
|
+
* the route under multiple paths at once (e.g. a wildcard that's commonly
|
|
283
|
+
* hit a few different ways), one spec entry per element. Doc-only,
|
|
284
|
+
* doesn't affect `req.params`.
|
|
284
285
|
*/
|
|
285
|
-
pathExample?: string;
|
|
286
|
+
pathExample?: string | string[];
|
|
286
287
|
}
|
|
287
288
|
/**
|
|
288
289
|
* The route options accepted by {@link InferSchemaHandler}.
|
|
@@ -370,8 +371,8 @@ interface DocsOptions {
|
|
|
370
371
|
interface RouteMetadata {
|
|
371
372
|
method: HttpMethod;
|
|
372
373
|
path: string | RegExp;
|
|
373
|
-
/** Doc-only path override
|
|
374
|
-
pathExample?: string | undefined;
|
|
374
|
+
/** Doc-only path override — see RouteOptions.pathExample. */
|
|
375
|
+
pathExample?: string | string[] | undefined;
|
|
375
376
|
bodySchema?: AnyStandardSchema | undefined;
|
|
376
377
|
querySchema?: AnyStandardSchema | undefined;
|
|
377
378
|
paramsSchema?: AnyStandardSchema | undefined;
|
package/dist/schema-router.d.mts
CHANGED
|
@@ -276,13 +276,14 @@ interface RouteOptions<BodySchema extends SchemaLike | undefined = undefined, Qu
|
|
|
276
276
|
/** Exclude this route from the generated OpenAPI spec entirely. */
|
|
277
277
|
hidden?: boolean;
|
|
278
278
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
279
|
+
* Stand-in path(s) for the OpenAPI doc. Required for a `RegExp` route
|
|
280
|
+
* (defaults to `regex.toString()` otherwise); optional for a string
|
|
281
|
+
* route, to override an ambiguous auto-converted path. An array documents
|
|
282
|
+
* the route under multiple paths at once (e.g. a wildcard that's commonly
|
|
283
|
+
* hit a few different ways), one spec entry per element. Doc-only,
|
|
284
|
+
* doesn't affect `req.params`.
|
|
284
285
|
*/
|
|
285
|
-
pathExample?: string;
|
|
286
|
+
pathExample?: string | string[];
|
|
286
287
|
}
|
|
287
288
|
/**
|
|
288
289
|
* The route options accepted by {@link InferSchemaHandler}.
|
|
@@ -370,8 +371,8 @@ interface DocsOptions {
|
|
|
370
371
|
interface RouteMetadata {
|
|
371
372
|
method: HttpMethod;
|
|
372
373
|
path: string | RegExp;
|
|
373
|
-
/** Doc-only path override
|
|
374
|
-
pathExample?: string | undefined;
|
|
374
|
+
/** Doc-only path override — see RouteOptions.pathExample. */
|
|
375
|
+
pathExample?: string | string[] | undefined;
|
|
375
376
|
bodySchema?: AnyStandardSchema | undefined;
|
|
376
377
|
querySchema?: AnyStandardSchema | undefined;
|
|
377
378
|
paramsSchema?: AnyStandardSchema | undefined;
|
package/dist/schema-router.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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)}function a(...e){return e}function o(e){e.res.status(400).json({error:`Validation failed`,details:e.details})}function s(e,t){if(e)try{e(t)?.catch?.(()=>{})}catch{}}const c=Function(`m`,`return import(m)`);let l,u;async function d(e){l??=await c(`module`),u??=await c(`url`);let t=[],n=globalThis.process?.argv?.[1];n&&t.push(u.pathToFileURL(n).href);let r=globalThis.process?.cwd?.()??``;r&&t.push(u.pathToFileURL(r+`/`).href);for(let n of t)try{let t=l.createRequire(n).resolve(e);return await c(u.pathToFileURL(t).href)}catch{}return c(e)}const f=new WeakMap,p=/\(\?<[^>]+>/;function m(e){return typeof e==`string`&&p.test(e)?new RegExp(e):e}function h(e){if(typeof e.path==`string`)return e.path;
|
|
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)}function a(...e){return e}function o(e){e.res.status(400).json({error:`Validation failed`,details:e.details})}function s(e,t){if(e)try{e(t)?.catch?.(()=>{})}catch{}}const c=Function(`m`,`return import(m)`);let l,u;async function d(e){l??=await c(`module`),u??=await c(`url`);let t=[],n=globalThis.process?.argv?.[1];n&&t.push(u.pathToFileURL(n).href);let r=globalThis.process?.cwd?.()??``;r&&t.push(u.pathToFileURL(r+`/`).href);for(let n of t)try{let t=l.createRequire(n).resolve(e);return await c(u.pathToFileURL(t).href)}catch{}return c(e)}const f=new WeakMap,p=/\(\?<[^>]+>/;function m(e){return typeof e==`string`&&p.test(e)?new RegExp(e):e}function h(e){let t=Array.isArray(e.pathExample)?e.pathExample[0]:e.pathExample;if(t)return t;if(typeof e.path==`string`)return e.path;let n=0;return e.path.source.replace(/\\\//g,`/`).replace(/\((?!\?)[^()]*\)/g,()=>`:${n++}`)}function g(e){let t=[],n=0,r=0,i=!1,a=!1;for(let o=0;o<e.length;o++){let s=e[o];if(a){a=!1;continue}if(s===`\\`){a=!0;continue}if(s===`[`){i=!0;continue}if(s===`]`){i=!1;continue}i||(s===`(`?r++:s===`)`?r=Math.max(0,r-1):s===`|`&&r===0&&(t.push(e.slice(n,o)),n=o+1))}return t.push(e.slice(n)),t.length>1?t:[e]}function _(e){if(!e.startsWith(`(`)||!e.endsWith(`)`))return e;let t=0,n=!1,r=!1;for(let i=0;i<e.length;i++){let a=e[i];if(r){r=!1;continue}if(a===`\\`){r=!0;continue}if(a===`[`)n=!0;else if(a===`]`)n=!1;else if(!n&&a===`(`)t++;else if(!n&&a===`)`&&(t--,t===0&&i!==e.length-1))return e}return e.slice(1,-1).replace(/^\?:/,``)}function v(e){let t=0;return _(e).replace(/^\^|\$$/g,``).replace(/\\\//g,`/`).replace(/\.\*|\.\+/g,`/:path`).replace(/\/?\?$/,``).replace(/\(\?<([A-Za-z0-9_]+)>[^()]*\)/g,`:$1`).replace(/\((?!\?)[^()]*\)/g,()=>`:${t++}`)}function y(e){if(Array.isArray(e.pathExample)&&e.pathExample.length>0)return e.pathExample;if(typeof e.path==`string`||e.pathExample)return[h(e)];let t=g(e.path.source);return t.length===1?[h(e)]:t.map(v)}function b(e){let t=e.replace(/\{([^{}]*)\}/g,`$1`).replace(/:([A-Za-z0-9_]+)(?:\([^)]*\))?[?+*]?/g,`{$1}`).replace(/\(\?<([A-Za-z0-9_]+)>[^)]*\)/g,`{$1}`).replace(/\*([A-Za-z0-9_]+)/g,`{$1}`).replace(/^\^|\$$/g,``),n=0;return t.replace(/\*/g,()=>`{${n++}}`).replace(/\/{2,}/g,`/`)}function x(e){let t=e.replace(/\{([^{}]*)\}/g,`$1`).replace(/^\^|\$$/g,``),n=[...t.matchAll(/:([A-Za-z0-9_]+)/g),...t.matchAll(/\(\?<([A-Za-z0-9_]+)>/g),...t.matchAll(/\*([A-Za-z0-9_]+)/g)].map(e=>e[1]),r=(t.replace(/\*[A-Za-z0-9_]+/g,``).match(/\*/g)??[]).length;for(let e=0;e<r;e++)n.push(String(e));return n}function S(e){return e.startsWith(`:`)||e.includes(`*`)||e.includes(`(?<`)}function C(e){return e.replace(/^\^|\$$/g,``).replace(/[{}]/g,``).split(`/`).filter(Boolean).find(e=>!S(e))??`default`}function w(e,t){let n=t.replace(/^\^|\$$/g,``).replace(/[{}]/g,``).split(`/`).filter(e=>e&&!S(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 T(e){let t=f.get(e);if(t)return t;let n=await E(e);return f.set(e,n),n}async function E(e){if(typeof e.toJsonSchema==`function`)try{return e.toJsonSchema()}catch{}let t=e[`~standard`]?.vendor;if(t===`zod`){try{let t=await d(`zod`);if(typeof t.toJSONSchema==`function`)return t.toJSONSchema(e)}catch{}try{let t=await d(`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 d(`@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 d(`effect`),n=t.JSONSchema?.make??t.default?.JSONSchema?.make;if(typeof n==`function`)return n(e)}catch{}return{}}function D(e){return O(e,0,new WeakSet)}function O(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 O(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=O(e[0],t+1,n);for(let a=1;a<r;a++)i=j(i,O(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]=O(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 k(e){return!e||e.type===void 0?new Set:new Set(Array.isArray(e.type)?e.type:[e.type])}function A(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 j(e,t){if(!e||Object.keys(e).length===0)return t??{};if(!t||Object.keys(t).length===0)return e??{};let n=new Set([...k(e),...k(t)]),r={},i=A(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]=j(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=j(e.items,t.items);n&&Object.keys(n).length&&(r.items=n)}return r}function M(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}const N=`https://cdn.jsdelivr.net/npm/@scalar/api-reference`;function P(e,t,n){return`<!doctype html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<title>${M(e)}</title>
|
|
@@ -9,4 +9,4 @@ import e from"express";import{SchemaError as t}from"@standard-schema/utils";func
|
|
|
9
9
|
<script id="api-reference" data-url="${M(t)}"><\/script>
|
|
10
10
|
<script src="${M(n)}"><\/script>
|
|
11
11
|
</body>
|
|
12
|
-
</html>`}async function F(e,t){let n=Object.create(null);for(let t of e)if(!(t.method===`all`||t.hidden))for(let e of y(t)){let r=b(e);n[r]||(n[r]={});let i;if(t.paramsSchema){let e=await T(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=x(e).map(e=>({name:e,in:`path`,required:!0,schema:{type:`string`}}));if(t.querySchema){let e=await T(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??w(t.method,e),tags:t.tags??[C(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 T(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 T(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 I=new WeakMap;var L=class t{router;routes=[];mountedRouters=[];sampleMode=`off`;scheduleSpecWrite;globalValidationFailureHook;constructor(){this.router=e.Router(),I.set(this.router,this)}onValidationFailure(e){return this.globalValidationFailureHook=e,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=I.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+h(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)for(let n of y(e)){let i=r[b(t+n)]?.[e.method]?.responses;if(i)for(let t of Object.keys(i)){let n=Number(t);if(Number.isNaN(n)||e.responseSamples.has(n))continue;let r=i[t]?.content?.[`application/json`];r?.schema&&e.responseSamples.set(n,r.example===void 0?{schema:r.schema}:{schema:r.schema,example:r.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,a=async()=>{if(n){i=!0;return}n=!0;try{let n=await F(this.getRouteMetadata(),t),r=await c(`fs/promises`),i=e.replace(/[/\\][^/\\]*$/,``);i&&i!==e&&await r.mkdir(i,{recursive:!0}).catch(()=>{});let a=globalThis.process?.pid??`0`,o=`${e}.${a}.tmp`;await r.writeFile(o,JSON.stringify(n,null,2),`utf8`),await r.rename(o,e)}catch{}finally{n=!1,i&&(i=!1,a())}},o;r=()=>{o&&clearTimeout(o),o=setTimeout(a,300),o.unref?.()},setImmediate(async()=>{try{let t=await(await c(`fs/promises`)).readFile(e,`utf8`).catch(()=>null);if(t)try{this.hydrateResponses(JSON.parse(t))}catch{}}catch{}await a()})}return t.sampleResponses!==!1&&this.enableSampling(t.sampleResponses===`live`?`live`:`redacted`,r),n.get(`/openapi.json`,async(e,n)=>{try{let e=await F(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??N;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(P(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 o=n;a.bodySchema=o.bodySchema,a.querySchema=o.querySchema,a.paramsSchema=o.paramsSchema,a.tags=o.tags,a.description=o.description,a.summary=o.summary,a.deprecated=o.deprecated,a.responseSchema=o.responseSchema,a.hidden=o.hidden,a.pathExample=o.pathExample,o.middleware&&i.push(...o.middleware);let s=o.hooks?.onValidationFailure,c=o.hooks?.onBodyValidationFailure,l=o.hooks?.onQueryValidationFailure,u=o.hooks?.onParamsValidationFailure;o.bodySchema&&i.push(this.createBodyValidationMiddleware(o.bodySchema,e,t,o.pathExample,s,c)),o.querySchema&&i.push(this.createQueryValidationMiddleware(o.querySchema,e,t,o.pathExample,s,l)),o.paramsSchema&&i.push(this.createParamsValidationMiddleware(o.paramsSchema,e,t,o.pathExample,s,u)),i.push(r)}else i.push(n);let o=0,s=(e,t)=>{let n=e.statusCode,r=D(t),i=a.responseSamples.get(n),o=i?j(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](m(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}notifyValidationFailure(e,t,n,r,i,a,o){if(!o&&!this.globalValidationFailureHook)return;let c={source:e,error:t,details:n,method:r,path:i,req:a};s(o,c),s(this.globalValidationFailureHook,c)}async runSchemaValidationHook(e,t,n,r,i,a,o,s){if(!e)return!1;let c=!1,l={error:t,details:n,method:r,path:i,req:a,res:o,next:((...e)=>{c=!0,s(...e)})};try{await e(l)}catch{}return c||o.headersSent}createBodyValidationMiddleware(e,t,n,a,s,c){return async(l,u,d)=>{try{let i=r(e,l.body),f=i&&typeof i.then==`function`?await i:i;if(f&&`issues`in f&&f.issues){let e=f.errors||f.issues,r=h({path:n,pathExample:a}),i=await this.runSchemaValidationHook(c,`Validation failed`,e,t,r,l,u,d);this.notifyValidationFailure(`body`,`Validation failed`,e,t,r,l,s),i||o({res:u,details:e});return}l.body=f&&`value`in f?f.value:f,d()}catch(e){if(i(e)){let r=e.errors||e.issues,i=h({path:n,pathExample:a}),f=await this.runSchemaValidationHook(c,`Validation failed`,r,t,i,l,u,d);this.notifyValidationFailure(`body`,`Validation failed`,r,t,i,l,s),f||o({res:u,details:r})}else d(e)}}}createParamsValidationMiddleware(e,t,n,a,s,c){return async(l,u,d)=>{try{let i=r(e,l.params),f=i&&typeof i.then==`function`?await i:i;if(f&&`issues`in f&&f.issues){let e=f.errors||f.issues,r=h({path:n,pathExample:a}),i=await this.runSchemaValidationHook(c,`Validation failed`,e,t,r,l,u,d);this.notifyValidationFailure(`params`,`Validation failed`,e,t,r,l,s),i||o({res:u,details:e});return}l.params=f&&`value`in f?f.value:f,d()}catch(e){if(i(e)){let r=e.errors||e.issues,i=h({path:n,pathExample:a}),f=await this.runSchemaValidationHook(c,`Validation failed`,r,t,i,l,u,d);this.notifyValidationFailure(`params`,`Validation failed`,r,t,i,l,s),f||o({res:u,details:r})}else d(e)}}}createQueryValidationMiddleware(e,t,n,a,s,c){return async(l,u,d)=>{try{let i=r(e,l.query),f=i&&typeof i.then==`function`?await i:i;if(f&&`issues`in f&&f.issues){let e=f.errors||f.issues,r=h({path:n,pathExample:a}),i=await this.runSchemaValidationHook(c,`Validation failed`,e,t,r,l,u,d);this.notifyValidationFailure(`query`,`Validation failed`,e,t,r,l,s),i||o({res:u,details:e});return}let p=f&&`value`in f?f.value:f;Object.defineProperty(l,"query",{value:p,writable:!1,enumerable:!0,configurable:!0}),d()}catch(e){if(i(e)){let r=e.errors||e.issues,i=h({path:n,pathExample:a}),f=await this.runSchemaValidationHook(c,`Validation failed`,r,t,i,l,u,d);this.notifyValidationFailure(`query`,`Validation failed`,r,t,i,l,s),f||o({res:u,details:r})}else d(e)}}}};function R(){return new L}function z(e){let t=new L;return e?.errorHandler&&t.getRouter().use(e.errorHandler),e?.hooks?.onValidationFailure&&t.onValidationFailure(e.hooks.onValidationFailure),t}function B(...e){let t=new L;for(let n of e)t=t.useMiddleware(n);return t}function V(e){return(Array.isArray(e)?e:[e]).map(e=>`prefix`in e?e:{prefix:``,router:e}).flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,...typeof t.path==`string`?{path:e+t.path}:{path:t.path,pathExample:e+h(t)}})))}async function H(e,t={}){return F(V(e),t)}function U(t,n={}){if(n.sampleResponses!==!1){let e=n.sampleResponses===`live`?`live`:`redacted`,r=Array.isArray(t)?t:[t];for(let t of r)(`prefix`in t?t.router:t).enableSampling(e)}let r=e.Router();return r.get(`/openapi.json`,async(e,r)=>{try{let e=await H(t,n);r.json(e)}catch(e){r.status(500).json({error:`Failed to generate spec`,details:String(e)})}}),r.get(`/`,(e,t)=>{let r=`${e.baseUrl}/openapi.json`,i=n.cdnUrl??N;t.setHeader(`Content-Type`,`text/html; charset=utf-8`),t.send(P(n.title??`API`,r,i))}),r}export{L as TypedRouter,U as createDocs,R as createTypedRouter,z as createTypedRouterWithConfig,B as createTypedRouterWithMiddleware,o as defaultValidationHandler,a as defineMiddleware,H as generateOpenApiSpec,D as inferJsonSchema,i as isSchemaError,n as parseSchema,r as safeParseSchema};
|
|
12
|
+
</html>`}async function F(e,t){let n=Object.create(null);for(let t of e)if(!(t.method===`all`||t.hidden))for(let e of y(t)){let r=b(e);n[r]||(n[r]={});let i;if(t.paramsSchema){let e=await T(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=x(e).map(e=>({name:e,in:`path`,required:!0,schema:{type:`string`}}));if(t.querySchema){let e=await T(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??w(t.method,e),tags:t.tags??[C(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 T(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 T(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 I=new WeakMap;var L=class t{router;routes=[];mountedRouters=[];sampleMode=`off`;scheduleSpecWrite;globalValidationFailureHook;constructor(){this.router=e.Router(),I.set(this.router,this)}onValidationFailure(e){return this.globalValidationFailureHook=e,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=I.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:y(t).map(t=>e+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)for(let n of y(e)){let i=r[b(t+n)]?.[e.method]?.responses;if(i)for(let t of Object.keys(i)){let n=Number(t);if(Number.isNaN(n)||e.responseSamples.has(n))continue;let r=i[t]?.content?.[`application/json`];r?.schema&&e.responseSamples.set(n,r.example===void 0?{schema:r.schema}:{schema:r.schema,example:r.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,a=async()=>{if(n){i=!0;return}n=!0;try{let n=await F(this.getRouteMetadata(),t),r=await c(`fs/promises`),i=e.replace(/[/\\][^/\\]*$/,``);i&&i!==e&&await r.mkdir(i,{recursive:!0}).catch(()=>{});let a=globalThis.process?.pid??`0`,o=`${e}.${a}.tmp`;await r.writeFile(o,JSON.stringify(n,null,2),`utf8`),await r.rename(o,e)}catch{}finally{n=!1,i&&(i=!1,a())}},o;r=()=>{o&&clearTimeout(o),o=setTimeout(a,300),o.unref?.()},setImmediate(async()=>{try{let t=await(await c(`fs/promises`)).readFile(e,`utf8`).catch(()=>null);if(t)try{this.hydrateResponses(JSON.parse(t))}catch{}}catch{}await a()})}return t.sampleResponses!==!1&&this.enableSampling(t.sampleResponses===`live`?`live`:`redacted`,r),n.get(`/openapi.json`,async(e,n)=>{try{let e=await F(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??N;n.setHeader(`Content-Type`,`text/html; charset=utf-8`),n.send(P(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 o=n;a.bodySchema=o.bodySchema,a.querySchema=o.querySchema,a.paramsSchema=o.paramsSchema,a.tags=o.tags,a.description=o.description,a.summary=o.summary,a.deprecated=o.deprecated,a.responseSchema=o.responseSchema,a.hidden=o.hidden,a.pathExample=o.pathExample,o.middleware&&i.push(...o.middleware);let s=o.hooks?.onValidationFailure,c=o.hooks?.onBodyValidationFailure,l=o.hooks?.onQueryValidationFailure,u=o.hooks?.onParamsValidationFailure;o.bodySchema&&i.push(this.createBodyValidationMiddleware(o.bodySchema,e,t,o.pathExample,s,c)),o.querySchema&&i.push(this.createQueryValidationMiddleware(o.querySchema,e,t,o.pathExample,s,l)),o.paramsSchema&&i.push(this.createParamsValidationMiddleware(o.paramsSchema,e,t,o.pathExample,s,u)),i.push(r)}else i.push(n);let o=0,s=(e,t)=>{let n=e.statusCode,r=D(t),i=a.responseSamples.get(n),o=i?j(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](m(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}notifyValidationFailure(e,t,n,r,i,a,o){if(!o&&!this.globalValidationFailureHook)return;let c={source:e,error:t,details:n,method:r,path:i,req:a};s(o,c),s(this.globalValidationFailureHook,c)}async runSchemaValidationHook(e,t,n,r,i,a,o,s){if(!e)return!1;let c=!1,l={error:t,details:n,method:r,path:i,req:a,res:o,next:((...e)=>{c=!0,s(...e)})};try{await e(l)}catch{}return c||o.headersSent}createBodyValidationMiddleware(e,t,n,a,s,c){return async(l,u,d)=>{try{let i=r(e,l.body),f=i&&typeof i.then==`function`?await i:i;if(f&&`issues`in f&&f.issues){let e=f.errors||f.issues,r=h({path:n,pathExample:a}),i=await this.runSchemaValidationHook(c,`Validation failed`,e,t,r,l,u,d);this.notifyValidationFailure(`body`,`Validation failed`,e,t,r,l,s),i||o({res:u,details:e});return}l.body=f&&`value`in f?f.value:f,d()}catch(e){if(i(e)){let r=e.errors||e.issues,i=h({path:n,pathExample:a}),f=await this.runSchemaValidationHook(c,`Validation failed`,r,t,i,l,u,d);this.notifyValidationFailure(`body`,`Validation failed`,r,t,i,l,s),f||o({res:u,details:r})}else d(e)}}}createParamsValidationMiddleware(e,t,n,a,s,c){return async(l,u,d)=>{try{let i=r(e,l.params),f=i&&typeof i.then==`function`?await i:i;if(f&&`issues`in f&&f.issues){let e=f.errors||f.issues,r=h({path:n,pathExample:a}),i=await this.runSchemaValidationHook(c,`Validation failed`,e,t,r,l,u,d);this.notifyValidationFailure(`params`,`Validation failed`,e,t,r,l,s),i||o({res:u,details:e});return}l.params=f&&`value`in f?f.value:f,d()}catch(e){if(i(e)){let r=e.errors||e.issues,i=h({path:n,pathExample:a}),f=await this.runSchemaValidationHook(c,`Validation failed`,r,t,i,l,u,d);this.notifyValidationFailure(`params`,`Validation failed`,r,t,i,l,s),f||o({res:u,details:r})}else d(e)}}}createQueryValidationMiddleware(e,t,n,a,s,c){return async(l,u,d)=>{try{let i=r(e,l.query),f=i&&typeof i.then==`function`?await i:i;if(f&&`issues`in f&&f.issues){let e=f.errors||f.issues,r=h({path:n,pathExample:a}),i=await this.runSchemaValidationHook(c,`Validation failed`,e,t,r,l,u,d);this.notifyValidationFailure(`query`,`Validation failed`,e,t,r,l,s),i||o({res:u,details:e});return}let p=f&&`value`in f?f.value:f;Object.defineProperty(l,"query",{value:p,writable:!1,enumerable:!0,configurable:!0}),d()}catch(e){if(i(e)){let r=e.errors||e.issues,i=h({path:n,pathExample:a}),f=await this.runSchemaValidationHook(c,`Validation failed`,r,t,i,l,u,d);this.notifyValidationFailure(`query`,`Validation failed`,r,t,i,l,s),f||o({res:u,details:r})}else d(e)}}}};function R(){return new L}function z(e){let t=new L;return e?.errorHandler&&t.getRouter().use(e.errorHandler),e?.hooks?.onValidationFailure&&t.onValidationFailure(e.hooks.onValidationFailure),t}function B(...e){let t=new L;for(let n of e)t=t.useMiddleware(n);return t}function V(e){return(Array.isArray(e)?e:[e]).map(e=>`prefix`in e?e:{prefix:``,router:e}).flatMap(({prefix:e,router:t})=>t.getRouteMetadata().map(t=>({...t,...typeof t.path==`string`?{path:e+t.path}:{path:t.path,pathExample:y(t).map(t=>e+t)}})))}async function H(e,t={}){return F(V(e),t)}function U(t,n={}){if(n.sampleResponses!==!1){let e=n.sampleResponses===`live`?`live`:`redacted`,r=Array.isArray(t)?t:[t];for(let t of r)(`prefix`in t?t.router:t).enableSampling(e)}let r=e.Router();return r.get(`/openapi.json`,async(e,r)=>{try{let e=await H(t,n);r.json(e)}catch(e){r.status(500).json({error:`Failed to generate spec`,details:String(e)})}}),r.get(`/`,(e,t)=>{let r=`${e.baseUrl}/openapi.json`,i=n.cdnUrl??N;t.setHeader(`Content-Type`,`text/html; charset=utf-8`),t.send(P(n.title??`API`,r,i))}),r}export{L as TypedRouter,U as createDocs,R as createTypedRouter,z as createTypedRouterWithConfig,B as createTypedRouterWithMiddleware,o as defaultValidationHandler,a as defineMiddleware,H as generateOpenApiSpec,D 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.10",
|
|
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",
|