express-zod-api 29.0.0 → 29.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Version 29
4
4
 
5
+ ### v29.0.1
6
+
7
+ - Minor performance tuning for `Documentation` generator:
8
+ - Collecting security headers and cookie names only when the corresponding input sources are enabled;
9
+ - Reduced some calculations required to depict request body and parameters.
10
+ - Deprecating the `DocumentationError` export from `express-zod-api`:
11
+ - This export will be removed in the next major version;
12
+ - Import it from `express-zod-api/documentation` subpath instead.
13
+
5
14
  ### v29.0.0
6
15
 
7
16
  - Supported Node.js versions: `^22.19.0 || ^24.11.0 || ^26.0.0`;
@@ -1,11 +1,12 @@
1
1
  import { M as ResponseVariant, g as CommonConfig, t as Routing } from "./routing-xQnwfH2D.js";
2
2
  import {
3
- a as depictTags,
4
- n as Depicter,
5
- o as trimSummary,
6
- r as IsHeader,
7
- t as BrandHandling,
8
- } from "./documentation-helpers-C3vkQCz2.js";
3
+ c as IsHeader,
4
+ l as depictTags,
5
+ o as BrandHandling,
6
+ s as Depicter,
7
+ t as DocumentationError,
8
+ u as trimSummary,
9
+ } from "./errors-CkGs6EY3.js";
9
10
  import { InfoObject, OpenApiBuilder, ServerObject } from "openapi3-ts/oas32";
10
11
  type Component = `${ResponseVariant}Response` | "requestParameter" | "requestBody";
11
12
  /** @desc user defined function that creates a component description from its properties */
@@ -72,4 +73,4 @@ interface DocumentationParams {
72
73
  declare class Documentation extends OpenApiBuilder {
73
74
  constructor({ hasHeadMethod, ...rest }: DocumentationParams);
74
75
  }
75
- export { type Depicter, Documentation };
76
+ export { type Depicter, Documentation, DocumentationError };
@@ -1 +1 @@
1
- import{$ as e,A as t,G as n,H as r,J as i,K as a,M as o,N as s,O as c,U as l,V as u,X as d,Y as f,g as p,n as m,o as h,q as g,t as _,v,x as y,z as b}from"./routing-walker-DjzH37jF.js";import{t as x}from"./json-schema-helpers-BY2DVvm4.js";import{n as S,t as C}from"./security-ts3gqWG9.js";import{z as w}from"zod";import*as T from"ramda";import{OpenApiBuilder as E,isReferenceObject as D,isSchemaObject as O}from"openapi3-ts/oas32";let k;const A=()=>k??=new Set(`a-im.accept.accept-additions.accept-charset.accept-datetime.accept-encoding.accept-features.accept-language.accept-signature.access-control.access-control-request-headers.access-control-request-method.alpn.alt-used.amp-cache-transform.apply-to-redirect-ref.authorization.available-dictionary.c-ext.c-man.c-opt.c-pep.c-pep-info.cache-control.cal-managed-id.caldav-timezones.capsule-protocol.cdn-loop.cert-not-after.cert-not-before.client-cert.client-cert-chain.close.cmcd-object.cmcd-request.cmcd-session.cmcd-status.cmsd-dynamic.cmsd-static.concealed-auth-export.configuration-context.connection.content-digest.content-disposition.content-encoding.content-id.content-language.content-length.content-location.content-md5.content-range.content-script-type.content-type.cookie.cookie2.cta-common-access-token.dasl.date.dav.default-style.delta-base.deprecation.depth.derived-from.destination.detached-jws.dictionary-id.differential-id.digest.dpop.early-data.ediint-features.expect.expect-ct.ext.forwarded.from.getprofile.hobareg.host.http2-settings.if.if-match.if-modified-since.if-none-match.if-range.if-schedule-tag-match.if-unmodified-since.im.include-referred-token-binding-id.incremental.isolation.keep-alive.label.last-event-id.link.link-template.lock-token.man.max-forwards.memento-datetime.meter.method-check.method-check-expires.mime-version.negotiate.odata-entityid.odata-isolation.odata-maxversion.odata-version.opt.ordering-type.origin.origin-agent-cluster.oscore.oslc-core-version.overwrite.pep.pep-info.permissions-policy.pics-label.ping-from.ping-to.position.pragma.prefer.preference-applied.priority.profileobject.protocol.protocol-info.protocol-query.protocol-request.proxy-authorization.proxy-features.proxy-instruction.range.redirect-ref.referer.referer-root.referrer-policy.repeatability-client-id.repeatability-first-sent.repeatability-request-id.repeatability-result.replay-nonce.reporting-endpoints.repr-digest.safe.schedule-reply.schedule-tag.sec-fetch-storage-access.sec-gpc.sec-purpose.sec-token-binding.sec-websocket-extensions.sec-websocket-key.sec-websocket-protocol.sec-websocket-version.security-scheme.signature.signature-input.slug.soapaction.status-uri.sunset.surrogate-capability.tcn.te.timeout.topic.traceparent.tracestate.trailer.transfer-encoding.ttl.upgrade.urgency.uri.user-agent.variant-vary.via.want-content-digest.want-digest.want-repr-digest.want-unencoded-digest.warning.x-content-type-options.x-frame-options`.split(`.`)),j={integer:0,number:0,string:``,boolean:!1,object:{},null:null,array:[]},ee=e=>e.replace(i,e=>`{${e.slice(1)}}`),M=({},e)=>{if(e.isResponse)throw new y(`Please use ez.upload() only for input.`,e);return{type:`string`,format:`binary`}},te=({jsonSchema:e})=>({...e,externalDocs:{description:`raw binary data`,url:`https://swagger.io/specification/#working-with-binary-data`}}),ne=({zodSchema:e,jsonSchema:t})=>{if(!n(e,`union`)||!(`discriminator`in e._zod.def))return t;let r=e._zod.def.discriminator;return{...t,discriminator:t.discriminator??{propertyName:r}}},N=T.tryCatch(({jsonSchema:e})=>{if(!e.allOf)throw`no allOf`;return x(e,`throw`)},(e,{jsonSchema:t})=>t),P=({jsonSchema:e})=>{if(!e.anyOf||!e.anyOf.length)return e;let t=e.anyOf[0];return Object.assign(t,{type:V(t.type)})},F=e=>e,I=({jsonSchema:e},t)=>{if(t.isResponse)throw new y(`Please use ez.dateOut() for output.`,t);return e},L=({jsonSchema:e},t)=>{if(!t.isResponse)throw new y(`Please use ez.dateIn() for input.`,t);return e},R=()=>({type:`string`,format:`bigint`,pattern:`^-?\\d+$`}),z=({zodSchema:e,jsonSchema:t})=>e._zod.def.rest===null?{...t,items:{not:{}}}:t,B=e=>{let t=Array.isArray(e.type)?e.type[0]:e.type;return j?.[t]},V=e=>e===`null`?e:typeof e==`string`?[e,`null`]:e&&[...new Set(e).add(`null`)],H=({zodSchema:e,jsonSchema:t},i)=>{let a=e._zod.def[i.isResponse?`out`:`in`],o=e._zod.def[i.isResponse?`in`:`out`];if(!n(a,`transform`))return t;let s=F(Y(o,{ctx:i}));if(O(s))if(i.isResponse){let e=r(a,B(s));if(e&&[`number`,`string`,`boolean`].includes(e))return{...t,type:e}}else{let{type:e,...t}=s;return{...t,format:`${t.format||e} (preprocessed)`}}return t},U=({jsonSchema:e})=>{if(e.type!==`object`)return e;let t=e;return!t.properties||!(`raw`in t.properties)||!l(t.properties.raw)?e:t.properties.raw},W=e=>e.length?T.fromPairs(T.zip(T.times(e=>`example${e+1}`,e.length),T.map(T.objOf(`dataValue`),e))):void 0,G=(e,t)=>t?.has(e)||e.startsWith(`x-`)||A().has(e),K=({path:e,method:t,request:n,inputSources:r,makeRef:i,composition:o,isHeader:s,securityHeaders:c,securityCookies:d,description:f=`${t.toUpperCase()} ${e} Parameter`})=>{let p=x(n),m=new Set(u(e)),h=r.includes(`query`),g=r.includes(`params`),_=r.includes(`headers`),v=r.includes(`cookies`)||r.includes(`signedCookies`),b=n=>{if(g&&m.has(n)&&m.delete(n))return`path`;if(v&&d?.has(n))return`cookie`;if(_&&(s?.(n,t,e)??G(n,c)))return`header`;if(h&&t!==`query`)return`query`},S=Object.entries(p.properties).reduce((e,[t,n])=>{if(!l(n))return e;let r=b(t);if(!r)return e;let s=F(n),c=o===`components`?i(n.id||JSON.stringify(n),s,n.id||a(f,t)):s;return e.concat({name:t,in:r,deprecated:n.deprecated,required:p.required?.includes(t)||!1,description:s.description||f,schema:c,examples:W(O(s)&&s.examples?.length?s.examples:T.pluck(t,p.examples?.filter(T.both(l,T.has(t)))||[]))})},[]);if(m.size)throw new y(`The input schema is missing the path parameter "${[...m][0]}"`,{method:t,path:e,isResponse:!1});return S},q={nullable:P,union:ne,bigint:R,intersection:N,tuple:z,pipe:H,[t]:I,[c]:L,[v]:M,[p]:U,[o]:te},J=(e,t,n)=>{let r=[e,t],i=e=>/schema\d+$/.test(e)?void 0:e;for(let e=0;e<r.length;e++){let a=r[e];if(T.is(Object,a)){if(D(a)&&!a.$ref.startsWith(`#/components`)){let e=a.$ref.split(`/`).pop(),r=t[e];if(r){let t=r.id||i(e);a.$ref=n.makeRef(t||r,F(r),t).$ref}continue}r.push(...T.values(a))}T.is(Array,a)&&r.push(...T.values(a))}return e},Y=(e,{ctx:t,rules:n=q})=>{let{$defs:r={},properties:i={}}=w.toJSONSchema(w.object({subject:e}),{unrepresentable:`any`,io:t.isResponse?`output`:`input`,override:e=>{let r=w.globalRegistry.get(e.zodSchema)?.id;if(r){let n=t.seenIds.get(r);if(n&&n!==e.zodSchema)throw new y(`The meta id "${r}" is used by two different schemas. Please make the ids unique or reuse the same schema instance.`,t);t.seenIds.set(r,e.zodSchema)}let i=s(e.zodSchema),a=n[i&&i in n?i:e.zodSchema._zod.def.type];if(a){let n={...a(e,t)};for(let t in e.jsonSchema)delete e.jsonSchema[t];Object.assign(e.jsonSchema,n)}}});return J(l(i.subject)?i.subject:{},r,t)},X=(e,t)=>{if(D(e))return[e,!1];let n=!1,r=T.map(e=>{let[r,i]=X(e,t);return n||=i,r}),i=T.omit(t),a={properties:i,examples:T.map(i),required:T.without(t),allOf:r,oneOf:r,anyOf:r},o=T.evolve(a,e);return[o,n||!!o.required?.length]},Z=({method:t,path:n,schema:r,mimeTypes:i,variant:o,makeRef:s,composition:c,hasMultipleStatusCodes:l,statusCode:u,brandHandling:p,seenIds:m,description:h=`${t.toUpperCase()} ${n} ${d(o)} response ${l?u:``}`.trim()})=>{if(!f(t,i))return{description:h};let g=F(Y(r,{rules:{...p,...q},ctx:{isResponse:!0,makeRef:s,path:n,method:t,seenIds:m}})),_=[];O(g)&&g.examples&&(_.push(...g.examples),delete g.examples);let v=c===`components`?s(r,g,a(h)):g;return{description:h,content:T.fromPairs(i.map(t=>[t,{[t===e.sse?`itemSchema`:`schema`]:v,examples:W(_)}]))}},re=({format:e})=>{let t={type:`http`,scheme:`bearer`};return e&&(t.bearerFormat=e),t},ie=({name:e},t)=>{let n={type:`apiKey`,in:`query`,name:e};return t?.includes(`body`)&&(t?.includes(`query`)?(n[`x-in-alternative`]=`body`,n.description=`${e} CAN also be supplied within the request body`):(n[`x-in-actual`]=`body`,n.description=`${e} MUST be supplied within the request body instead of query`)),n},ae=({name:e})=>({type:`apiKey`,in:`header`,name:e}),oe=({name:e})=>({type:`apiKey`,in:`cookie`,name:e}),se=({url:e})=>({type:`openIdConnect`,openIdConnectUrl:e}),Q=({flows:e={},oauth2MetadataUrl:t})=>({type:`oauth2`,flows:T.map(e=>({...e,scopes:e.scopes||{}}),T.reject(T.isNil,e)),oauth2MetadataUrl:t}),ce=(e,t=[])=>{let n=e=>e.type===`basic`?{type:`http`,scheme:`basic`}:e.type===`bearer`?re(e):e.type===`input`?ie(e,t):e.type===`header`?ae(e):e.type===`cookie`?oe(e):e.type===`openid`?se(e):Q(e);return e.map(e=>e.map(({deprecated:e,...t})=>({...n(t),deprecated:e})))},le=(e,t,n)=>e.map(e=>e.reduce((e,r)=>{let i=n(r),a=[`oauth2`,`openIdConnect`].includes(r.type);return Object.assign(e,{[i]:a?t:[]})},{})),ue=({schema:e,brandHandling:t,makeRef:n,path:r,method:i,seenIds:a})=>Y(e,{rules:{...t,...q},ctx:{isResponse:!1,makeRef:n,path:r,method:i,seenIds:a}}),de=({method:t,path:n,schema:r,request:i,mimeType:o,makeRef:s,composition:c,paramNames:u,description:d=`${t.toUpperCase()} ${n} Request body`})=>{let[f,p]=X(i,u),m=F(f),h=[];O(m)&&m.examples&&(h.push(...m.examples),delete m.examples);let g={schema:c===`components`?s(r,m,a(d)):m,examples:W(h.length?h:x(i).examples?.filter(e=>l(e)&&!Array.isArray(e)).map(T.omit(u))||[])},_={description:d,content:{[o]:g}};return(p||o===e.raw)&&(_.required=!0),_},fe=e=>Object.entries(e).reduce((e,[t,n])=>{if(!n)return e;if(typeof n==`string`)return e.concat({name:t,description:n});let{url:r,...i}=n,a={...i,name:t};return r&&(a.externalDocs={...a.externalDocs,url:r}),e.concat(a)},[]),pe=(e,t=50)=>!e||e.length<=t?e:e.slice(0,Math.max(1,t||0)-1)+`…`,$=e=>e.length?e.slice():void 0,me=({description:e,summary:t=e,trim:n})=>n(t);var he=class extends E{#e=new Map;#t=new Map;#n=new Map;#r=new Map;#i(e,t,n){let r=this.#n.get(e);if(!r){let t=+!n;do r=`${n??`Schema`}${t?this.#n.size+t:``}`,t++;while(this.rootDoc.components?.schemas?.[r]);this.#n.set(e,r)}return this.addSchema(r,t),{$ref:`#/components/schemas/${r}`}}#a(e,t,n){let r=n||a(t,e),i=this.#t.get(r);if(i===void 0)return this.#t.set(r,1),r;if(n)throw new y(`Duplicated operationId: "${n}"`,{method:t,isResponse:!1,path:e});return i++,this.#t.set(r,i),`${r}${i}`}#o(e){let t=JSON.stringify(e);for(let e in this.rootDoc.components?.securitySchemes||{})if(t===JSON.stringify(this.rootDoc.components?.securitySchemes?.[e]))return e;let n=(this.#e.get(e.type)||0)+1;return this.#e.set(e.type,n),`${e.type.toUpperCase()}_${n}`}#s({tags:e,server:t,info:n={title:`Generated by Express Zod API`,version:`0.0.0`}}){if(this.addInfo(n),e&&(this.rootDoc.tags=fe(e)),t)for(let e of Array.isArray(t)?t:[t])this.addServer(typeof e==`string`?{url:e}:e)}#c(e,t){if(e===`head`||!t.includes(`:`))return;let n=g(t),r=this.#r.get(n);if(r!==void 0&&r!==t)throw new y(`Path has a duplicate: the normalized path "${n}" is already registered with different parameter names at "${r}"`,{method:e,path:t,isResponse:!1});r===void 0&&this.#r.set(n,t)}#l({config:t,descriptions:n,brandHandling:r,isHeader:i,summarizer:a=me,composition:o=`inline`}){let s={composition:o,brandHandling:r,makeRef:this.#i.bind(this),seenIds:new Map};return(r,o,c)=>{this.#c(r,o);let l={...s,path:o,method:r,endpoint:c},{description:u,summary:d,scopes:f,inputSchema:p}=c,m=b(r,t.inputSources),g=this.#a(o,r,c.getOperationId(r)),_=ue({...l,schema:p}),v=K({...l,inputSources:m,isHeader:i,securityHeaders:C(c.security,`header`),securityCookies:C(c.security,`cookie`),request:_,description:n?.requestParameter?.({method:r,path:o,operationId:g})}),y={};for(let e of h){let t=c.getResponses(e);for(let{mimeTypes:i,schema:a,statusCodes:s}of t)for(let c of s)y[c]=Z({...l,variant:e,schema:a,mimeTypes:i,statusCode:c,hasMultipleStatusCodes:t.length>1||s.length>1,description:n?.[`${e}Response`]?.({method:r,path:o,operationId:g,statusCode:c})})}let x=m.includes(`body`)?de({...l,request:_,paramNames:T.pluck(`name`,v),schema:p,mimeType:e[c.getProbableRequestType(r)],description:n?.requestBody?.({method:r,path:o,operationId:g})}):void 0,w=le(ce(S(c.security),m),f,e=>{let t=this.#o(e);return this.addSecurityScheme(t,e),t}),E={operationId:g,summary:a({summary:d,description:u,trim:pe}),description:u,deprecated:c.isDeprecated||void 0,tags:$(c.tags),parameters:$(v),requestBody:x,security:$(w),responses:y};this.addPath(ee(o),{[r]:E})}}constructor({hasHeadMethod:e=!0,...t}){super(),this.#s(t);let n=this.#l(t),r=e?m(n):n;_({...t,onEndpoint:r})}};export{he as Documentation};
1
+ import{$ as e,A as t,G as n,H as r,J as i,K as a,M as o,N as s,O as c,U as l,V as u,X as d,Y as f,g as p,n as m,o as h,q as g,t as _,v,x as y,z as b}from"./routing-walker-DjzH37jF.js";import{t as x}from"./json-schema-helpers-BY2DVvm4.js";import{n as S,t as C}from"./security-ts3gqWG9.js";import{z as w}from"zod";import*as T from"ramda";import{OpenApiBuilder as E,isReferenceObject as D,isSchemaObject as O}from"openapi3-ts/oas32";let k;const A=()=>k??=new Set(`a-im.accept.accept-additions.accept-charset.accept-datetime.accept-encoding.accept-features.accept-language.accept-signature.access-control.access-control-request-headers.access-control-request-method.alpn.alt-used.amp-cache-transform.apply-to-redirect-ref.authorization.available-dictionary.c-ext.c-man.c-opt.c-pep.c-pep-info.cache-control.cal-managed-id.caldav-timezones.capsule-protocol.cdn-loop.cert-not-after.cert-not-before.client-cert.client-cert-chain.close.cmcd-object.cmcd-request.cmcd-session.cmcd-status.cmsd-dynamic.cmsd-static.concealed-auth-export.configuration-context.connection.content-digest.content-disposition.content-encoding.content-id.content-language.content-length.content-location.content-md5.content-range.content-script-type.content-type.cookie.cookie2.cta-common-access-token.dasl.date.dav.default-style.delta-base.deprecation.depth.derived-from.destination.detached-jws.dictionary-id.differential-id.digest.dpop.early-data.ediint-features.expect.expect-ct.ext.forwarded.from.getprofile.hobareg.host.http2-settings.if.if-match.if-modified-since.if-none-match.if-range.if-schedule-tag-match.if-unmodified-since.im.include-referred-token-binding-id.incremental.isolation.keep-alive.label.last-event-id.link.link-template.lock-token.man.max-forwards.memento-datetime.meter.method-check.method-check-expires.mime-version.negotiate.odata-entityid.odata-isolation.odata-maxversion.odata-version.opt.ordering-type.origin.origin-agent-cluster.oscore.oslc-core-version.overwrite.pep.pep-info.permissions-policy.pics-label.ping-from.ping-to.position.pragma.prefer.preference-applied.priority.profileobject.protocol.protocol-info.protocol-query.protocol-request.proxy-authorization.proxy-features.proxy-instruction.range.redirect-ref.referer.referer-root.referrer-policy.repeatability-client-id.repeatability-first-sent.repeatability-request-id.repeatability-result.replay-nonce.reporting-endpoints.repr-digest.safe.schedule-reply.schedule-tag.sec-fetch-storage-access.sec-gpc.sec-purpose.sec-token-binding.sec-websocket-extensions.sec-websocket-key.sec-websocket-protocol.sec-websocket-version.security-scheme.signature.signature-input.slug.soapaction.status-uri.sunset.surrogate-capability.tcn.te.timeout.topic.traceparent.tracestate.trailer.transfer-encoding.ttl.upgrade.urgency.uri.user-agent.variant-vary.via.want-content-digest.want-digest.want-repr-digest.want-unencoded-digest.warning.x-content-type-options.x-frame-options`.split(`.`)),j={integer:0,number:0,string:``,boolean:!1,object:{},null:null,array:[]},M=e=>e.replace(i,e=>`{${e.slice(1)}}`),ee=({},e)=>{if(e.isResponse)throw new y(`Please use ez.upload() only for input.`,e);return{type:`string`,format:`binary`}},te=({jsonSchema:e})=>({...e,externalDocs:{description:`raw binary data`,url:`https://swagger.io/specification/#working-with-binary-data`}}),N=({zodSchema:e,jsonSchema:t})=>{if(!n(e,`union`)||!(`discriminator`in e._zod.def))return t;let r=e._zod.def.discriminator;return{...t,discriminator:t.discriminator??{propertyName:r}}},P=T.tryCatch(({jsonSchema:e})=>{if(!e.allOf)throw`no allOf`;return x(e,`throw`)},(e,{jsonSchema:t})=>t),F=({jsonSchema:e})=>{if(!e.anyOf||!e.anyOf.length)return e;let t=e.anyOf[0];return Object.assign(t,{type:H(t.type)})},I=e=>e,L=({jsonSchema:e},t)=>{if(t.isResponse)throw new y(`Please use ez.dateOut() for output.`,t);return e},R=({jsonSchema:e},t)=>{if(!t.isResponse)throw new y(`Please use ez.dateIn() for input.`,t);return e},z=()=>({type:`string`,format:`bigint`,pattern:`^-?\\d+$`}),B=({zodSchema:e,jsonSchema:t})=>e._zod.def.rest===null?{...t,items:{not:{}}}:t,V=e=>{let t=Array.isArray(e.type)?e.type[0]:e.type;return j?.[t]},H=e=>e===`null`?e:typeof e==`string`?[e,`null`]:e&&[...new Set(e).add(`null`)],U=({zodSchema:e,jsonSchema:t},i)=>{let a=e._zod.def[i.isResponse?`out`:`in`],o=e._zod.def[i.isResponse?`in`:`out`];if(!n(a,`transform`))return t;let s=I(Y(o,{ctx:i}));if(O(s))if(i.isResponse){let e=r(a,V(s));if(e&&[`number`,`string`,`boolean`].includes(e))return{...t,type:e}}else{let{type:e,...t}=s;return{...t,format:`${t.format||e} (preprocessed)`}}return t},W=({jsonSchema:e})=>{if(e.type!==`object`)return e;let t=e;return!t.properties||!(`raw`in t.properties)||!l(t.properties.raw)?e:t.properties.raw},G=e=>e.length?T.fromPairs(T.zip(T.times(e=>`example${e+1}`,e.length),T.map(T.objOf(`dataValue`),e))):void 0,K=(e,t)=>t?.has(e)||e.startsWith(`x-`)||A().has(e),q=({path:e,method:t,flatRequest:n,inputSources:r,makeRef:i,composition:o,isHeader:s,security:c,description:d=`${t.toUpperCase()} ${e} Parameter`})=>{let f=new Set(u(e)),p=r.includes(`query`),m=r.includes(`params`),h=r.includes(`headers`),g=r.includes(`cookies`)||r.includes(`signedCookies`),_;h&&c&&(_=C(c,`header`));let v;g&&c&&(v=C(c,`cookie`));let b=n=>{if(m&&f.has(n)&&f.delete(n))return`path`;if(g&&v?.has(n))return`cookie`;if(h&&(s?.(n,t,e)??K(n,_)))return`header`;if(p&&t!==`query`)return`query`},x=Object.entries(n.properties).reduce((e,[t,r])=>{if(!l(r))return e;let s=b(t);if(!s)return e;let c=I(r),u=o===`components`?i(r.id||JSON.stringify(r),c,r.id||a(d,t)):c;return e.concat({name:t,in:s,deprecated:r.deprecated,required:n.required?.includes(t)||!1,description:c.description||d,schema:u,examples:G(O(c)&&c.examples?.length?c.examples:T.pluck(t,n.examples?.filter(T.both(l,T.has(t)))||[]))})},[]);if(f.size)throw new y(`The input schema is missing the path parameter "${[...f][0]}"`,{method:t,path:e,isResponse:!1});return x},J={nullable:F,union:N,bigint:z,intersection:P,tuple:B,pipe:U,[t]:L,[c]:R,[v]:ee,[p]:W,[o]:te},ne=(e,t,n)=>{let r=[e,t],i=e=>/schema\d+$/.test(e)?void 0:e;for(let e=0;e<r.length;e++){let a=r[e];if(T.is(Object,a)){if(D(a)&&!a.$ref.startsWith(`#/components`)){let e=a.$ref.split(`/`).pop(),r=t[e];if(r){let t=r.id||i(e);a.$ref=n.makeRef(t||r,I(r),t).$ref}continue}r.push(...T.values(a))}T.is(Array,a)&&r.push(...T.values(a))}return e},Y=(e,{ctx:t,rules:n=J})=>{let{$defs:r={},properties:i={}}=w.toJSONSchema(w.object({subject:e}),{unrepresentable:`any`,io:t.isResponse?`output`:`input`,override:e=>{let r=w.globalRegistry.get(e.zodSchema)?.id;if(r){let n=t.seenIds.get(r);if(n&&n!==e.zodSchema)throw new y(`The meta id "${r}" is used by two different schemas. Please make the ids unique or reuse the same schema instance.`,t);t.seenIds.set(r,e.zodSchema)}let i=s(e.zodSchema),a=n[i&&i in n?i:e.zodSchema._zod.def.type];if(a){let n={...a(e,t)};for(let t in e.jsonSchema)delete e.jsonSchema[t];Object.assign(e.jsonSchema,n)}}});return ne(l(i.subject)?i.subject:{},r,t)},X=(e,t)=>{if(D(e))return[e,!1];let n=!1,r=T.map(e=>{let[r,i]=X(e,t);return n||=i,r}),i=T.omit(t),a={properties:i,examples:T.map(i),required:T.without(t),allOf:r,oneOf:r,anyOf:r},o=T.evolve(a,e);return[o,n||!!o.required?.length]},Z=({method:t,path:n,schema:r,mimeTypes:i,variant:o,makeRef:s,composition:c,hasMultipleStatusCodes:l,statusCode:u,brandHandling:p,seenIds:m,description:h=`${t.toUpperCase()} ${n} ${d(o)} response ${l?u:``}`.trim()})=>{if(!f(t,i))return{description:h};let g=I(Y(r,{rules:{...p,...J},ctx:{isResponse:!0,makeRef:s,path:n,method:t,seenIds:m}})),_=[];O(g)&&g.examples&&(_.push(...g.examples),delete g.examples);let v=c===`components`?s(r,g,a(h)):g;return{description:h,content:T.fromPairs(i.map(t=>[t,{[t===e.sse?`itemSchema`:`schema`]:v,examples:G(_)}]))}},re=({format:e})=>{let t={type:`http`,scheme:`bearer`};return e&&(t.bearerFormat=e),t},ie=({name:e},t)=>{let n={type:`apiKey`,in:`query`,name:e};return t?.includes(`body`)&&(t?.includes(`query`)?(n[`x-in-alternative`]=`body`,n.description=`${e} CAN also be supplied within the request body`):(n[`x-in-actual`]=`body`,n.description=`${e} MUST be supplied within the request body instead of query`)),n},ae=({name:e})=>({type:`apiKey`,in:`header`,name:e}),oe=({name:e})=>({type:`apiKey`,in:`cookie`,name:e}),se=({url:e})=>({type:`openIdConnect`,openIdConnectUrl:e}),Q=({flows:e={},oauth2MetadataUrl:t})=>({type:`oauth2`,flows:T.map(e=>({...e,scopes:e.scopes||{}}),T.reject(T.isNil,e)),oauth2MetadataUrl:t}),ce=(e,t=[])=>{let n=e=>e.type===`basic`?{type:`http`,scheme:`basic`}:e.type===`bearer`?re(e):e.type===`input`?ie(e,t):e.type===`header`?ae(e):e.type===`cookie`?oe(e):e.type===`openid`?se(e):Q(e);return e.map(e=>e.map(({deprecated:e,...t})=>({...n(t),deprecated:e})))},le=(e,t,n)=>e.map(e=>e.reduce((e,r)=>{let i=n(r),a=[`oauth2`,`openIdConnect`].includes(r.type);return Object.assign(e,{[i]:a?t:[]})},{})),ue=({schema:e,brandHandling:t,makeRef:n,path:r,method:i,seenIds:a})=>Y(e,{rules:{...t,...J},ctx:{isResponse:!1,makeRef:n,path:r,method:i,seenIds:a}}),de=({method:t,path:n,schema:r,bodyJsonSchema:i,hasRequiredBodyProps:o,flatRequest:s,mimeType:c,makeRef:u,composition:d,paramNames:f,description:p=`${t.toUpperCase()} ${n} Request body`})=>{let m=I(i),h=[];O(m)&&m.examples&&(h.push(...m.examples),delete m.examples);let g={schema:d===`components`?u(r,m,a(p)):m,examples:G(h.length?h:s.examples?.filter(e=>l(e)&&!Array.isArray(e)).map(T.omit(f))||[])},_={description:p,content:{[c]:g}};return(o||c===e.raw)&&(_.required=!0),_},fe=e=>Object.entries(e).reduce((e,[t,n])=>{if(!n)return e;if(typeof n==`string`)return e.concat({name:t,description:n});let{url:r,...i}=n,a={...i,name:t};return r&&(a.externalDocs={...a.externalDocs,url:r}),e.concat(a)},[]),pe=(e,t=50)=>!e||e.length<=t?e:e.slice(0,Math.max(1,t||0)-1)+`…`,$=e=>e.length?e.slice():void 0,me=({description:e,summary:t=e,trim:n})=>n(t);var he=class extends E{#e=new Map;#t=new Map;#n=new Map;#r=new Map;#i(e,t,n){let r=this.#n.get(e);if(!r){let t=+!n;do r=`${n??`Schema`}${t?this.#n.size+t:``}`,t++;while(this.rootDoc.components?.schemas?.[r]);this.#n.set(e,r)}return this.addSchema(r,t),{$ref:`#/components/schemas/${r}`}}#a(e,t,n){let r=n||a(t,e),i=this.#t.get(r);if(i===void 0)return this.#t.set(r,1),r;if(n)throw new y(`Duplicated operationId: "${n}"`,{method:t,isResponse:!1,path:e});return i++,this.#t.set(r,i),`${r}${i}`}#o(e){let t=JSON.stringify(e);for(let e in this.rootDoc.components?.securitySchemes||{})if(t===JSON.stringify(this.rootDoc.components?.securitySchemes?.[e]))return e;let n=(this.#e.get(e.type)||0)+1;return this.#e.set(e.type,n),`${e.type.toUpperCase()}_${n}`}#s({tags:e,server:t,info:n={title:`Generated by Express Zod API`,version:`0.0.0`}}){if(this.addInfo(n),e&&(this.rootDoc.tags=fe(e)),t)for(let e of Array.isArray(t)?t:[t])this.addServer(typeof e==`string`?{url:e}:e)}#c(e,t){if(e===`head`||!t.includes(`:`))return;let n=g(t),r=this.#r.get(n);if(r!==void 0&&r!==t)throw new y(`Path has a duplicate: the normalized path "${n}" is already registered with different parameter names at "${r}"`,{method:e,path:t,isResponse:!1});r===void 0&&this.#r.set(n,t)}#l({config:t,descriptions:n,brandHandling:r,isHeader:i,summarizer:a=me,composition:o=`inline`}){let s={composition:o,brandHandling:r,makeRef:this.#i.bind(this),seenIds:new Map};return(r,o,c)=>{this.#c(r,o);let l={...s,path:o,method:r,endpoint:c},{description:u,summary:d,scopes:f,inputSchema:p,security:m}=c,g=b(r,t.inputSources),_=this.#a(o,r,c.getOperationId(r)),v=ue({...l,schema:p}),y=x(v),C=q({...l,inputSources:g,isHeader:i,flatRequest:y,security:m,description:n?.requestParameter?.({method:r,path:o,operationId:_})}),w={};for(let e of h){let t=c.getResponses(e);for(let{mimeTypes:i,schema:a,statusCodes:s}of t)for(let c of s)w[c]=Z({...l,variant:e,schema:a,mimeTypes:i,statusCode:c,hasMultipleStatusCodes:t.length>1||s.length>1,description:n?.[`${e}Response`]?.({method:r,path:o,operationId:_,statusCode:c})})}let E;if(g.includes(`body`)){let t=T.pluck(`name`,C),[i,a]=X(v,t);E=de({...l,bodyJsonSchema:i,hasRequiredBodyProps:a,flatRequest:y,paramNames:t,schema:p,mimeType:e[c.getProbableRequestType(r)],description:n?.requestBody?.({method:r,path:o,operationId:_})})}let D=le(ce(S(m),g),f,e=>{let t=this.#o(e);return this.addSecurityScheme(t,e),t}),O={operationId:_,summary:a({summary:d,description:u,trim:pe}),description:u,deprecated:c.isDeprecated||void 0,tags:$(c.tags),parameters:$(C),requestBody:E,security:$(D),responses:w};this.addPath(M(o),{[r]:O})}}constructor({hasHeadMethod:e=!0,...t}){super(),this.#s(t);let n=this.#l(t),r=e?m(n):n;_({...t,onEndpoint:r})}};export{he as Documentation,y as DocumentationError};
@@ -0,0 +1,79 @@
1
+ import { D as ClientMethod, O as Method, f as Tag } from "./routing-xQnwfH2D.js";
2
+ import { z } from "zod";
3
+ import { ReferenceObject, SchemaObjectValue, TagObject } from "openapi3-ts/oas32";
4
+ interface ReqResCommons {
5
+ makeRef: (key: object | string, value: SchemaObjectValue | ReferenceObject, proposedName?: string) => ReferenceObject;
6
+ path: string;
7
+ method: ClientMethod;
8
+ seenIds: Map<string, z.core.$ZodType>;
9
+ }
10
+ interface OpenAPIContext extends ReqResCommons {
11
+ isResponse: boolean;
12
+ }
13
+ type Depicter = (
14
+ zodCtx: {
15
+ zodSchema: z.core.$ZodType;
16
+ jsonSchema: z.core.JSONSchema.BaseSchema;
17
+ },
18
+ oasCtx: OpenAPIContext,
19
+ ) => z.core.JSONSchema.BaseSchema | SchemaObjectValue;
20
+ /** @desc Using defaultIsHeader when returns null or undefined */
21
+ type IsHeader = (name: string, method: ClientMethod, path: string) => boolean | null | undefined;
22
+ type BrandHandling = Record<string | symbol, Depicter>;
23
+ interface TagDetails extends Pick<TagObject, "summary" | "description" | "externalDocs" | "kind"> {
24
+ /** @desc shorthand for externalDocs.url */
25
+ url?: string;
26
+ parent?: Tag;
27
+ }
28
+ declare const depictTags: (tags: Partial<Record<Tag, string | TagDetails>>) => TagObject[];
29
+ /** @desc Ensures the summary string does not exceed the limit */
30
+ declare const trimSummary: (summary?: string, limit?: number) => string | undefined;
31
+ /** @desc An error related to the wrong Routing declaration */
32
+ declare class RoutingError extends Error {
33
+ name: string;
34
+ readonly cause: {
35
+ method: Method;
36
+ path: string;
37
+ };
38
+ constructor(message: string, method: Method, path: string);
39
+ }
40
+ /**
41
+ * @desc An error related to the generating of the documentation
42
+ * */
43
+ declare class DocumentationError extends Error {
44
+ name: string;
45
+ readonly cause: string;
46
+ constructor(message: string, { method, path, isResponse }: Pick<OpenAPIContext, "path" | "method" | "isResponse">);
47
+ }
48
+ /** @desc An error related to the input and output schemas declaration */
49
+ declare class IOSchemaError extends Error {
50
+ name: string;
51
+ }
52
+ /** @desc An error of validating the Endpoint handler's returns against the Endpoint output schema */
53
+ declare class OutputValidationError extends IOSchemaError {
54
+ readonly cause: z.ZodError;
55
+ name: string;
56
+ constructor(cause: z.ZodError);
57
+ }
58
+ /** @desc An error of validating the input sources against the Middleware or Endpoint input schema */
59
+ declare class InputValidationError extends IOSchemaError {
60
+ readonly cause: z.ZodError;
61
+ name: string;
62
+ constructor(cause: z.ZodError);
63
+ }
64
+ declare class MissingPeerError extends Error {
65
+ name: string;
66
+ constructor(module: string);
67
+ }
68
+ export {
69
+ RoutingError as a,
70
+ IsHeader as c,
71
+ OutputValidationError as i,
72
+ depictTags as l,
73
+ InputValidationError as n,
74
+ BrandHandling as o,
75
+ MissingPeerError as r,
76
+ Depicter as s,
77
+ DocumentationError as t,
78
+ trimSummary as u,
79
+ };
package/dist/index.d.ts CHANGED
@@ -32,7 +32,13 @@ import {
32
32
  x as arrayResultHandler,
33
33
  y as AbstractResultHandler,
34
34
  } from "./routing-xQnwfH2D.js";
35
- import { i as OpenAPIContext } from "./documentation-helpers-C3vkQCz2.js";
35
+ import {
36
+ a as RoutingError,
37
+ i as OutputValidationError,
38
+ n as InputValidationError,
39
+ r as MissingPeerError,
40
+ t as DocumentationError,
41
+ } from "./errors-CkGs6EY3.js";
36
42
  import { z } from "zod";
37
43
  import express, { CookieOptions, Request, Response } from "express";
38
44
  import http from "node:http";
@@ -457,53 +463,16 @@ declare const attachRouting: (
457
463
  import("qs").ParsedQs,
458
464
  Record<string, any>
459
465
  >;
460
- logger: AbstractLogger | BuiltinLogger;
466
+ logger: BuiltinLogger | AbstractLogger;
461
467
  };
462
468
  declare const createServer: (
463
469
  config: ServerConfig,
464
470
  routing: Routing,
465
471
  ) => {
466
472
  app: import("express-serve-static-core").Express;
467
- logger: AbstractLogger | BuiltinLogger;
473
+ logger: BuiltinLogger | AbstractLogger;
468
474
  servers: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>[];
469
475
  };
470
- /** @desc An error related to the wrong Routing declaration */
471
- declare class RoutingError extends Error {
472
- name: string;
473
- readonly cause: {
474
- method: Method;
475
- path: string;
476
- };
477
- constructor(message: string, method: Method, path: string);
478
- }
479
- /**
480
- * @desc An error related to the generating of the documentation
481
- * */
482
- declare class DocumentationError extends Error {
483
- name: string;
484
- readonly cause: string;
485
- constructor(message: string, { method, path, isResponse }: Pick<OpenAPIContext, "path" | "method" | "isResponse">);
486
- }
487
- /** @desc An error related to the input and output schemas declaration */
488
- declare class IOSchemaError extends Error {
489
- name: string;
490
- }
491
- /** @desc An error of validating the Endpoint handler's returns against the Endpoint output schema */
492
- declare class OutputValidationError extends IOSchemaError {
493
- readonly cause: z.ZodError;
494
- name: string;
495
- constructor(cause: z.ZodError);
496
- }
497
- /** @desc An error of validating the input sources against the Middleware or Endpoint input schema */
498
- declare class InputValidationError extends IOSchemaError {
499
- readonly cause: z.ZodError;
500
- name: string;
501
- constructor(cause: z.ZodError);
502
- }
503
- declare class MissingPeerError extends Error {
504
- name: string;
505
- constructor(module: string);
506
- }
507
476
  interface TestingProps<REQ, LOG> {
508
477
  /**
509
478
  * @desc Additional properties to set on Request mock
@@ -546,7 +515,7 @@ declare const testEndpoint: <LOG extends FlatObject, REQ extends RequestOptions>
546
515
  responseMock: import("node-mocks-http").MockResponse<Response<any, Record<string, any>>>;
547
516
  loggerMock: AbstractLogger &
548
517
  LOG & {
549
- _getLogs: () => Record<"debug" | "info" | "warn" | "error", unknown[]>;
518
+ _getLogs: () => Record<"error" | "warn" | "info" | "debug", unknown[]>;
550
519
  };
551
520
  }>;
552
521
  interface MiddlewareLike<RET extends FlatObject> {
@@ -576,7 +545,7 @@ declare const testMiddleware: <LOG extends FlatObject, REQ extends RequestOption
576
545
  responseMock: import("node-mocks-http").MockResponse<Response<any, Record<string, any>>>;
577
546
  loggerMock: AbstractLogger &
578
547
  LOG & {
579
- _getLogs: () => Record<"debug" | "info" | "warn" | "error", unknown[]>;
548
+ _getLogs: () => Record<"error" | "warn" | "info" | "debug", unknown[]>;
580
549
  };
581
550
  }>;
582
551
  type EventsMap = Record<string, z.ZodType>;
package/dist/index.js CHANGED
@@ -3,5 +3,5 @@ import{$ as e,B as t,C as n,D as r,E as i,I as a,L as o,P as s,Q as c,R as l,S a
3
3
  `+i.join(`
4
4
  `)+`
5
5
 
6
- `)},Q=e=>{e.startupLogo!==!1&&st(process.stdout);let t=e.errorHandler||M,n=Le(e.logger)?e.logger:new U(e.logger);n.debug(`Running`,{build:`v29.0.0`,env:process.env.NODE_ENV||`development`}),at(n);let r=rt({logger:n,config:e}),i={getLogger:it(n),errorHandler:t},a=Ze(i),o=Xe(i);return{...i,logger:n,notFoundHandler:a,catcher:o,loggingMiddleware:r}},ct=(e,t)=>{let{logger:n,getLogger:r,notFoundHandler:i,loggingMiddleware:a}=Q(e),o=e.app.use(a);return e.cors&&o.use(q(e.cors)),G({app:o,routing:t,getLogger:r,config:e}),{notFoundHandler:i,logger:n}},lt=(e,t)=>{let{logger:n,getLogger:r,notFoundHandler:i,catcher:a,loggingMiddleware:o}=Q(e),s=k().disable(`x-powered-by`).set(`query parser`,e.queryParser??`simple`).use(o);if(e.beforeParsers?.({app:s,getLogger:r}),e.compression){let t=S(`compression`);s.use(t(typeof e.compression==`object`?e.compression:void 0))}return e.cookies&&s.use($e({config:e})),e.cors&&s.use(q(e.cors)),s.use(e.jsonParser||k.json()).use(e.formParser||k.urlencoded()).use(e.rawParser||k.raw(),nt),e.upload&&s.use(...tt({config:e,getLogger:r})),e.beforeRouting?.({app:s,getLogger:r}),G({app:s,routing:t,getLogger:r,config:e}),e.afterRouting?.({app:s,getLogger:r}),s.use(a,i),{app:s,logger:n}},ut=(e,t)=>{let{app:n,logger:r}=lt(e,t),i=[],a=(e,t)=>()=>e.listen(t,()=>r.info(`Listening`,t)),o=[];if(e.http){let t=Ce.createServer(n);i.push(t),o.push(a(t,e.http.listen))}if(e.https){let t=we.createServer(e.https.options,n);i.push(t),o.push(a(t,e.https.listen))}return i.length||r.warn(`No servers configured.`),e.gracefulShutdown&&ot({logger:r,servers:i,options:e.gracefulShutdown===!0?{}:e.gracefulShutdown}),{app:n,logger:r,servers:o.map(e=>e())}},dt=t=>Ee({...t,headers:{"content-type":e.json,...t?.headers}}),ft=e=>De(e),pt=e=>{let t={warn:[],error:[],info:[],debug:[]};return new Proxy(e||{},{get(e,n,r){return n===`_getLogs`?()=>t:Re(n)?(...e)=>t[n].push(e):Reflect.get(e,n,r)}})},$=({requestProps:e,responseOptions:t,configProps:n,loggerProps:r})=>{let i=dt(e),a=ft({req:i,...t});a.req=t?.req||i,i.res=a;let o=pt(r);return{requestMock:i,responseMock:a,loggerMock:o,configMock:{cors:!1,logger:o,...n}}},mt=async({endpoint:e,...t})=>{let{requestMock:n,responseMock:r,loggerMock:i,configMock:a}=$(t);return await e.execute({request:n,response:r,config:a,logger:i}),{requestMock:n,responseMock:r,loggerMock:i}},ht=async({middleware:e,ctx:t={},...n})=>{let{configMock:{inputSources:r,errorHandler:i=M},...a}=$(n),s=l(a.requestMock,r),c={request:a.requestMock,response:a.responseMock,logger:a.loggerMock,input:s,ctx:t};try{let t=await e.execute(c);return{...a,output:t}}catch(e){return await i.execute({...c,error:o(e),output:null}),{...a,output:{}}}},gt=(e,t)=>w.object({data:t,event:w.literal(e),id:w.string().optional(),retry:w.int().positive().optional()}),_t=(e,t,n)=>gt(String(t),e[t]).transform(e=>[`event: ${e.event}`,`data: ${JSON.stringify(e.data)}`,``,``].join(`
6
+ `)},Q=e=>{e.startupLogo!==!1&&st(process.stdout);let t=e.errorHandler||M,n=Le(e.logger)?e.logger:new U(e.logger);n.debug(`Running`,{build:`v29.0.1`,env:process.env.NODE_ENV||`development`}),at(n);let r=rt({logger:n,config:e}),i={getLogger:it(n),errorHandler:t},a=Ze(i),o=Xe(i);return{...i,logger:n,notFoundHandler:a,catcher:o,loggingMiddleware:r}},ct=(e,t)=>{let{logger:n,getLogger:r,notFoundHandler:i,loggingMiddleware:a}=Q(e),o=e.app.use(a);return e.cors&&o.use(q(e.cors)),G({app:o,routing:t,getLogger:r,config:e}),{notFoundHandler:i,logger:n}},lt=(e,t)=>{let{logger:n,getLogger:r,notFoundHandler:i,catcher:a,loggingMiddleware:o}=Q(e),s=k().disable(`x-powered-by`).set(`query parser`,e.queryParser??`simple`).use(o);if(e.beforeParsers?.({app:s,getLogger:r}),e.compression){let t=S(`compression`);s.use(t(typeof e.compression==`object`?e.compression:void 0))}return e.cookies&&s.use($e({config:e})),e.cors&&s.use(q(e.cors)),s.use(e.jsonParser||k.json()).use(e.formParser||k.urlencoded()).use(e.rawParser||k.raw(),nt),e.upload&&s.use(...tt({config:e,getLogger:r})),e.beforeRouting?.({app:s,getLogger:r}),G({app:s,routing:t,getLogger:r,config:e}),e.afterRouting?.({app:s,getLogger:r}),s.use(a,i),{app:s,logger:n}},ut=(e,t)=>{let{app:n,logger:r}=lt(e,t),i=[],a=(e,t)=>()=>e.listen(t,()=>r.info(`Listening`,t)),o=[];if(e.http){let t=Ce.createServer(n);i.push(t),o.push(a(t,e.http.listen))}if(e.https){let t=we.createServer(e.https.options,n);i.push(t),o.push(a(t,e.https.listen))}return i.length||r.warn(`No servers configured.`),e.gracefulShutdown&&ot({logger:r,servers:i,options:e.gracefulShutdown===!0?{}:e.gracefulShutdown}),{app:n,logger:r,servers:o.map(e=>e())}},dt=t=>Ee({...t,headers:{"content-type":e.json,...t?.headers}}),ft=e=>De(e),pt=e=>{let t={warn:[],error:[],info:[],debug:[]};return new Proxy(e||{},{get(e,n,r){return n===`_getLogs`?()=>t:Re(n)?(...e)=>t[n].push(e):Reflect.get(e,n,r)}})},$=({requestProps:e,responseOptions:t,configProps:n,loggerProps:r})=>{let i=dt(e),a=ft({req:i,...t});a.req=t?.req||i,i.res=a;let o=pt(r);return{requestMock:i,responseMock:a,loggerMock:o,configMock:{cors:!1,logger:o,...n}}},mt=async({endpoint:e,...t})=>{let{requestMock:n,responseMock:r,loggerMock:i,configMock:a}=$(t);return await e.execute({request:n,response:r,config:a,logger:i}),{requestMock:n,responseMock:r,loggerMock:i}},ht=async({middleware:e,ctx:t={},...n})=>{let{configMock:{inputSources:r,errorHandler:i=M},...a}=$(n),s=l(a.requestMock,r),c={request:a.requestMock,response:a.responseMock,logger:a.loggerMock,input:s,ctx:t};try{let t=await e.execute(c);return{...a,output:t}}catch(e){return await i.execute({...c,error:o(e),output:null}),{...a,output:{}}}},gt=(e,t)=>w.object({data:t,event:w.literal(e),id:w.string().optional(),retry:w.int().positive().optional()}),_t=(e,t,n)=>gt(String(t),e[t]).transform(e=>[`event: ${e.event}`,`data: ${JSON.stringify(e.data)}`,``,``].join(`
7
7
  `)).parse({event:t,data:n}),vt=t=>t.headersSent||t.writeHead(200,{connection:`keep-alive`,"content-type":e.sse,"cache-control":`no-cache`}),yt=e=>new b({handler:async({request:t,response:n})=>{let r=new AbortController,i=setTimeout(()=>vt(n),1e4);return t.once(`close`,()=>{clearTimeout(i),r.abort()}),{isClosed:()=>n.writableEnded||n.closed,signal:r.signal,emit:(t,r)=>{vt(n),n.write(_t(e,t,r),`utf-8`),n.flush?.()}}}}),bt=t=>new A({positive:()=>{let[n,...r]=Object.entries(t).map(([e,t])=>gt(e,t));if(!n)throw new d(Error(`At least one SSE event is required.`));return{mimeType:e.sse,schema:r.length?w.discriminatedUnion(`event`,[n,...r]):n}},negative:{mimeType:`text/plain`,schema:w.string()},handler:async({response:e,error:t,logger:n,request:r,input:i})=>{if(t){let a=h(t);x(a,n,r,i),e.headersSent||e.status(a.statusCode).type(`text/plain`).write(v(a),`utf-8`)}e.end()}});var xt=class extends z{constructor(e){super(bt(e)),this.middlewares=[yt(e)]}};const St=[`total`,`limit`,`offset`],Ct=[`nextCursor`,`limit`];function wt({style:e,itemSchema:t,itemsName:n=`items`,maxLimit:r=100,defaultLimit:i=20}){if(!Number.isInteger(r)||r<1)throw Error(`ez.paginated: maxLimit must be a positive integer`);if(!Number.isInteger(i)||i<1)throw Error(`ez.paginated: defaultLimit must be a positive integer`);if(i>r)throw Error(`ez.paginated: defaultLimit must not be greater than maxLimit`);if(e===`offset`&&St.includes(n))throw Error(`ez.paginated: itemsName must not match reserved keys for offset output (${St.join(`, `)})`);if(e===`cursor`&&Ct.includes(n))throw Error(`ez.paginated: itemsName must not match reserved keys for cursor output (${Ct.join(`, `)})`);let a=w.coerce.number().int().min(1).max(r).default(i).describe(`Page size (number of ${n} per page)`);if(e===`offset`){let e=w.coerce.number().int().min(0).default(0).describe(`Number of ${n} to skip`);return{input:w.object({limit:a,offset:e}),output:w.object({[n]:w.array(t).describe(`Page of ${n}`),total:w.number().int().min(0).describe(`Total number of ${n}`),limit:w.number().int().min(1).describe(`Page size used`),offset:w.number().int().min(0).describe(`Offset used`)})}}let o=w.string().optional().describe(`Cursor for the next page; omit for first page`);return{input:w.object({cursor:o,limit:a}),output:w.object({[n]:w.array(t).describe(`Page of ${n}`),nextCursor:w.string().nullable().describe(`Cursor for the next page, or null if no more pages`),limit:w.number().int().min(1).describe(`Page size used`)})}}const Tt={dateIn:ae,dateOut:r,form:ne,upload:fe,raw:te,buffer:ie,paginated:wt};export{U as BuiltinLogger,de as DocumentationError,z as EndpointsFactory,xt as EventStreamFactory,u as InputValidationError,b as Middleware,n as MissingPeerError,ue as OutputValidationError,A as ResultHandler,i as RoutingError,se as ServeStatic,Ie as arrayEndpointsFactory,P as arrayResultHandler,ct as attachRouting,re as createApiResponse,I as createCacheMiddleware,Oe as createConfig,L as createCookieMiddleware,R as createRateLimitMiddleware,ut as createServer,Fe as defaultEndpointsFactory,M as defaultResultHandler,h as ensureHttpError,Tt as ez,t as getMessageFromError,mt as testEndpoint,ht as testMiddleware};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-zod-api",
3
- "version": "29.0.0",
3
+ "version": "29.0.1",
4
4
  "description": "A Typescript framework to help you get an API server up and running with I/O schema validation and custom middlewares in minutes.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -64,7 +64,7 @@
64
64
  "http-errors": "^2.0.1",
65
65
  "typescript": "^5.1.3 || ^6.0.2",
66
66
  "zod": "^4.3.4",
67
- "@express-zod-api/zod-plugin": "^6.0.0"
67
+ "@express-zod-api/zod-plugin": "^6.0.1"
68
68
  },
69
69
  "peerDependenciesMeta": {
70
70
  "@express-zod-api/zod-plugin": {
@@ -125,7 +125,7 @@
125
125
  "typescript": "^6.0.2",
126
126
  "undici": "^8.2.0",
127
127
  "zod": "^4.4.3",
128
- "@express-zod-api/zod-plugin": "^6.0.0"
128
+ "@express-zod-api/zod-plugin": "^6.0.1"
129
129
  },
130
130
  "keywords": [
131
131
  "nodejs",
@@ -1,31 +0,0 @@
1
- import { D as ClientMethod, f as Tag } from "./routing-xQnwfH2D.js";
2
- import { z } from "zod";
3
- import { ReferenceObject, SchemaObjectValue, TagObject } from "openapi3-ts/oas32";
4
- interface ReqResCommons {
5
- makeRef: (key: object | string, value: SchemaObjectValue | ReferenceObject, proposedName?: string) => ReferenceObject;
6
- path: string;
7
- method: ClientMethod;
8
- seenIds: Map<string, z.core.$ZodType>;
9
- }
10
- interface OpenAPIContext extends ReqResCommons {
11
- isResponse: boolean;
12
- }
13
- type Depicter = (
14
- zodCtx: {
15
- zodSchema: z.core.$ZodType;
16
- jsonSchema: z.core.JSONSchema.BaseSchema;
17
- },
18
- oasCtx: OpenAPIContext,
19
- ) => z.core.JSONSchema.BaseSchema | SchemaObjectValue;
20
- /** @desc Using defaultIsHeader when returns null or undefined */
21
- type IsHeader = (name: string, method: ClientMethod, path: string) => boolean | null | undefined;
22
- type BrandHandling = Record<string | symbol, Depicter>;
23
- interface TagDetails extends Pick<TagObject, "summary" | "description" | "externalDocs" | "kind"> {
24
- /** @desc shorthand for externalDocs.url */
25
- url?: string;
26
- parent?: Tag;
27
- }
28
- declare const depictTags: (tags: Partial<Record<Tag, string | TagDetails>>) => TagObject[];
29
- /** @desc Ensures the summary string does not exceed the limit */
30
- declare const trimSummary: (summary?: string, limit?: number) => string | undefined;
31
- export { depictTags as a, OpenAPIContext as i, Depicter as n, trimSummary as o, IsHeader as r, BrandHandling as t };