express-zod-api 29.8.2 → 30.0.0-beta.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
@@ -1,7 +1,65 @@
1
1
  # Changelog
2
2
 
3
+ ## Version 30
4
+
5
+ ### v30.0.0
6
+
7
+ - Supported Zod versions: `^4.5.4`:
8
+ - Using [compiled](https://zod.dev/compile) I/O schemas for Endpoint runtime performance boost.
9
+ - Supported TypeScript versions (optional peer): `^6.0.3`:
10
+ - Entirely `#private` props are now restored in the classes of the distributed `.d.ts` files.
11
+ - Config option `trySyncValidation` is now enabled by default (potentially breaking behavior change):
12
+ - First request to Endpoints having schemas with async refinements would execute those refinements twice. If those
13
+ refinements have side effects sensitive to the number of calls, then the option should be disabled.
14
+ - The `config` property on the `Middleware::execute()` argument is now required: `testMiddleware()` already handles it.
15
+ - `DocumentationError` removed from main entrypoint: import from `express-zod-api/documentation` instead.
16
+ - The rate-limiting Middleware now explicitly declares the `statusCode` that it may interrupt the request handling:
17
+ - Uses the `statusCode` option given to `EndpointsFactory::useRateLimit()` and `createRateLimitMiddleware()` or 429;
18
+ - This will override or narrow the negative status codes declared in ResultHandler when generating Documentation;
19
+ - The Endpoints using this Middleware should declare its other negative status codes explicitly to preserve them.
20
+ - The `defaultResultHandler` (and `defaultEndpointsFactory`) changed both positive and negative response schemas:
21
+ - The positive response is now the Endpoint output as is: the `{ status: "success", data: {...} }` wrapper removed;
22
+ - The negative response is now the `{ message }`: the `{ status: "error", error: {...} }` wrapper removed;
23
+ - Existing APIs can migrate to `legacyResultHandler` and `legacyEndpointsFactory` for preserving those wrappers.
24
+ - The `Integration` generator changed to discriminate responses by HTTP status code or legacy `success|error` fallback:
25
+ - New `EncodedResponse` interface holding payloads along with `status: number, discriminator: "success" | "error"`;
26
+ - New public static method `Client::discriminate(status: number): "success" | "error"`;
27
+ - If using a custom `Implementation`, it now has to return `Promise<{ status: number, data: any }>`;
28
+ - The `Client::provide()` method now returns `Promise<{ status, discriminator, data }>` for further discrimination;
29
+ - The basic approach is to discriminate by `status` (the actual `Response.status`);
30
+ - In case of receiving an unlisted status, use the `discriminator` ("success" or "error" fallback);
31
+ - Unlike previous versions, the `data` property always relays the original payload, including the error `{ message }`.
32
+
33
+ ```ts
34
+ const client = new Client();
35
+ const { status, discriminator, data } = await client.provide(
36
+ "get /v1/user/retrieve",
37
+ { id: "10" },
38
+ );
39
+ if (status === 200)
40
+ console.log(data.name); // success
41
+ else if (status === 400 || discriminator === "error")
42
+ console.error(data.message); // error
43
+ ```
44
+
45
+ ```diff
46
+ @@ Keeping the response wrappers: @@
47
+ - import { defaultEndpointsFactory } from "express-zod-api";
48
+ + import { legacyEndpointsFactory } from "express-zod-api";
49
+ const factory = new EndpointsFactory(
50
+ - defaultResultHandler
51
+ + legacyResultHandler
52
+ );
53
+ ```
54
+
3
55
  ## Version 29
4
56
 
57
+ ### v29.8.3
58
+
59
+ - Integration generator refactoring:
60
+ - Consolidated the production of aliases for traversing lazy and recursive schemas;
61
+ - Suggested and implemented by [@giovio](https://github.com/giovio).
62
+
5
63
  ### v29.8.2
6
64
 
7
65
  - Fixed alias production for Integration generator:
package/README.md CHANGED
@@ -279,7 +279,7 @@ curl -L -X GET 'localhost:8090/v1/hello?name=Rick'
279
279
  You should receive the following response:
280
280
 
281
281
  ```json
282
- { "status": "success", "data": { "greetings": "Hello, Rick. Happy coding!" } }
282
+ { "greetings": "Hello, Rick. Happy coding!" }
283
283
  ```
284
284
 
285
285
  # Basic features
@@ -456,43 +456,15 @@ const factory = defaultEndpointsFactory.use(auth(), {
456
456
 
457
457
  ## Refinements
458
458
 
459
- You can implement additional validations within schemas using refinements.
460
- Validation errors are reported in a response with a status code `400`.
459
+ [Refinements](https://zod.dev/api#refinements) provide additional validations for your schemas. Albeit those functions
460
+ can be async, the framework attempts to parse the first request to an Endpoint synchronously, which will run such async
461
+ functions twice. Therefore, refinements should avoid side effects sensitive to the number of calls.
461
462
 
462
463
  ```ts
463
- import { z } from "zod";
464
- import { Middleware } from "express-zod-api";
465
-
466
- const nicknameConstraintMiddleware = new Middleware({
467
- input: z.object({
468
- nickname: z
469
- .string()
470
- .min(1)
471
- .refine(
472
- (nick) => !/^\d.*$/.test(nick),
473
- "Nickname cannot start with a digit",
474
- ),
475
- }),
476
- // ...,
477
- });
478
- ```
479
-
480
- By the way, you can also refine the whole I/O object, for example, in case you need a complex validation of its props.
481
-
482
- ```ts
483
- const endpoint = endpointsFactory.build({
484
- input: z
485
- .object({
486
- email: z.email().optional(),
487
- id: z.string().optional(),
488
- otherThing: z.string().optional(),
489
- })
490
- .refine(
491
- (inputs) => Object.keys(inputs).length >= 1,
492
- "Please provide at least one property",
493
- ),
494
- // ...,
495
- });
464
+ const nickname = z
465
+ .string()
466
+ .min(1)
467
+ .refine((nick) => !/^\d.*$/.test(nick), "Nickname cannot start with a digit");
496
468
  ```
497
469
 
498
470
  ## Query string parser
@@ -905,11 +877,7 @@ Install `express-rate-limit`. Consider the `createRateLimitMiddleware()` to enab
905
877
 
906
878
  ```ts
907
879
  const endpoint = factory
908
- .useRateLimit({
909
- windowMs: 60000,
910
- max: 100,
911
- statusCode: 429, // when set explicitly, it will be reflected in the Documentation
912
- }) // shorthand, or .addMiddleware(createRateLimitMiddleware())
880
+ .useRateLimit({ windowMs: 60000, max: 100 }) // shorthand, or .addMiddleware(createRateLimitMiddleware())
913
881
  .buildVoid({
914
882
  handler: async ({ ctx: { rateLimit, logger } }) => {
915
883
  logger.debug("Features", rateLimit); // { limit, used, remaining, resetTime, getKey, resetKey }
@@ -924,8 +892,8 @@ The `defaultResultHandler` sets the HTTP status code and ensures the following t
924
892
 
925
893
  ```ts
926
894
  type DefaultResponse<OUT> =
927
- | { status: "success"; data: OUT } // Positive response
928
- | { status: "error"; error: { message: string } }; // or Negative response
895
+ | OUT // Positive response
896
+ | { message: string }; // or Negative response
929
897
  ```
930
898
 
931
899
  You can create your own result handler by using this example as a template:
@@ -939,18 +907,18 @@ import {
939
907
  } from "express-zod-api";
940
908
 
941
909
  const yourResultHandler = new ResultHandler({
942
- positive: (data) => ({
943
- schema: z.object({ data }),
910
+ positive: (output) => ({
911
+ schema: output,
944
912
  mimeType: "application/json", // optional or array
945
913
  }),
946
- negative: z.object({ error: z.string() }),
914
+ negative: z.object({ message: z.string() }),
947
915
  handler: ({ error, input, output, request, response, logger }) => {
948
916
  if (error) {
949
917
  const { statusCode } = ensureHttpError(error);
950
918
  const message = getMessageFromError(error);
951
- return void response.status(statusCode).json({ error: message });
919
+ return void response.status(statusCode).json({ message });
952
920
  }
953
- response.status(200).json({ data: output });
921
+ response.status(200).json(output);
954
922
  },
955
923
  });
956
924
  ```
@@ -1165,7 +1133,7 @@ test("should respond successfully", async () => {
1165
1133
  expect(loggerMock._getLogs().error).toHaveLength(0);
1166
1134
  expect(responseMock._getStatusCode()).toBe(200);
1167
1135
  expect(responseMock._getHeaders()).toHaveProperty("x-custom", "one"); // lower case!
1168
- expect(responseMock._getJSONData()).toEqual({ status: "success" });
1136
+ expect(responseMock._getJSONData()).toEqual({ greetings: "Hello, World" });
1169
1137
  });
1170
1138
  ```
1171
1139
 
@@ -1398,18 +1366,18 @@ response schemas and their corresponding status codes.
1398
1366
  import { ResultHandler } from "express-zod-api";
1399
1367
 
1400
1368
  const resultHandler = new ResultHandler({
1401
- positive: (data) => ({
1369
+ positive: (output) => ({
1402
1370
  statusCode: [201, 202], // created or will be created
1403
- schema: z.object({ status: z.literal("created"), data }),
1371
+ schema: output,
1404
1372
  }),
1405
1373
  negative: [
1406
1374
  {
1407
1375
  statusCode: 409, // conflict: entity already exists
1408
- schema: z.object({ status: z.literal("exists"), id: z.int() }),
1376
+ schema: z.object({ id: z.int().describe("id of the existing entity") }),
1409
1377
  },
1410
1378
  {
1411
1379
  statusCode: [400, 500], // validation or internal error
1412
- schema: z.object({ status: z.literal("error"), reason: z.string() }),
1380
+ schema: z.object({ reason: z.string() }),
1413
1381
  },
1414
1382
  ],
1415
1383
  });
package/SECURITY.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  | Version | Code name | Release | Supported |
6
6
  | ------: | :------------ | :------ | :----------------: |
7
+ | 30.x.x | Rita | 11.2026 | :white_check_mark: |
7
8
  | 29.x.x | Angie | 08.2026 | :white_check_mark: |
8
9
  | 28.x.x | Koko | 05.2026 | :white_check_mark: |
9
10
  | 27.x.x | Nikki | 02.2026 | :white_check_mark: |
@@ -0,0 +1 @@
1
+ import{$ as e,F as t,G as n,I as r,J as i,K as a,N as o,R as s,S as c,Y as l,Z as u,b as d,et as f,j as p,q as m,r as h,rt as g,w as _}from"./routing-walker-BpLERmiE.js";import{z as v}from"zod";import*as y from"ramda";import{isReferenceObject as b,isSchemaObject as x}from"openapi3-ts/oas32";const ee=e=>e.type===`object`,te=y.mergeDeepWith((e,t)=>{if(Array.isArray(e)&&Array.isArray(t))return y.concat(e,t);if(e===t)return t;throw Error(`Can not flatten properties`,{cause:{a:e,b:t}})}),S=/* @__PURE__ */ new Set([`type`,`properties`,`required`,`examples`,`description`,`additionalProperties`]),C=e=>{for(let t of Object.keys(e))if(!S.has(t))return!1;return!0},w=y.pair(!0),ne=(e,t,n)=>!(`allOf`in e)||!e.allOf?[]:e.allOf.map(e=>{if(t===`throw`&&!(e.type===`object`&&C(e)))throw Error(`Can not merge`);return y.pair(n,e)}),re=e=>{let t=[];return e.anyOf&&t.push(...y.map(w,e.anyOf)),e.oneOf&&t.push(...y.map(w,e.oneOf)),t},ie=(e,t,n,r)=>{if(!m(e.propertyNames))return;let i=[];typeof e.propertyNames.const==`string`&&i.push(e.propertyNames.const),e.propertyNames.enum&&i.push(...e.propertyNames.enum.filter(e=>typeof e==`string`));let a={...Object(e.additionalProperties)};for(let e of i)t.properties[e]??=a;r||n.push(...i)},ae=(e,t,n)=>{t.examples?.length&&(e.examples=n?y.concat(e.examples||[],t.examples):s(e.examples?.filter(m)||[],t.examples.filter(m),([e,t])=>y.mergeDeepRight(e,t)))},T=(e,t=`coerce`)=>{let n=[y.pair(!1,e)],r={type:`object`,properties:{}},i=[];for(let[e,a]of n)a.description&&(r.description??=a.description),n.push(...ne(a,t,e)),n.push(...re(a)),ae(r,a,e),ee(a)&&(n.push([e,{examples:E(a)}]),a.properties&&(r.properties=(t===`throw`?te:y.mergeDeepRight)(r.properties,a.properties),!e&&a.required&&i.push(...a.required)),ie(a,r,i,e));return i.length&&(r.required=[...new Set(i)]),r},E=e=>Object.entries(e.properties||{}).reduce((e,[t,n])=>{let{examples:r=[]}=m(n)?n:{};return s(e,r.map(y.objOf(t)),([e,t])=>({...e,...t}))},[]),D=`x-coerce`,O=/* @__PURE__ */ new Set([`array`,`object`]),k=(e,t)=>{let n=[e.items,e.prefixItems,e.additionalItems,Object.values(e.properties??{}),e.additionalProperties,e.propertyNames];for(let e of n){if(!m(e))continue;let n=Array.isArray(e)?e:[e];for(let e of n)if(m(e)&&!A(e,t))return!1}return!0},A=(e,t)=>e[`x-coerce`]===!0?!0:e.anyOf?e.anyOf.some(e=>A(e,t)):e.oneOf?e.oneOf.some(e=>A(e,t)):e.allOf?e.allOf.every(e=>A(e,t)):e.type===void 0||e.type===`string`?!0:Array.isArray(e.type)?A({anyOf:e.type.map(t=>({...e,type:t}))},t):t!==`path`&&O.has(e.type)&&k(e,t);let j;const M=()=>j??=/* @__PURE__ */ new Set([`session`,`session_id`,`sessionId`]);let N;const P=()=>N??=/* @__PURE__ */ new Set(/* @__PURE__ */ `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.connect-udp-bind.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(`.`)),F={integer:0,number:0,string:``,boolean:!1,object:{},null:null,array:[]},I=e=>e.replace(u,e=>`{${e.slice(1)}}`),L=({},e)=>{if(e.isResponse)throw new _(`Please use ez.upload() only for input.`,e);return{type:`string`,format:`binary`}},R=({jsonSchema:e})=>({...e,externalDocs:{description:`raw binary data`,url:`https://swagger.io/specification/#working-with-binary-data`}}),z=({zodSchema:e,jsonSchema:t})=>{if(!i(e,`union`)||!(`discriminator`in e._zod.def))return t;let n=e._zod.def.discriminator;return{...t,discriminator:t.discriminator??{propertyName:n}}},B=y.tryCatch(({jsonSchema:e})=>{if(!e.allOf)throw`no allOf`;return T(e,`throw`)},(e,{jsonSchema:t})=>t),V=({jsonSchema:e})=>{if(!e.anyOf||!e.anyOf.length)return e;let t=e.anyOf[0];return Object.assign(t,{type:J(t.type)})},H=e=>e,U=({jsonSchema:e},t)=>{if(t.isResponse)throw new _(`Please use ez.dateOut() for output.`,t);return e},W=({jsonSchema:e},t)=>{if(!t.isResponse)throw new _(`Please use ez.dateIn() for input.`,t);return e},G=()=>({type:`string`,format:`bigint`,pattern:`^-?\\d+$`}),K=({zodSchema:e,jsonSchema:t})=>e._zod.def.rest===null?{...t,items:{not:{}}}:t,q=e=>{let t=Array.isArray(e.type)?e.type[0]:e.type;return F?.[t]},J=e=>e===`null`?e:typeof e==`string`?[e,`null`]:e&&[...new Set(e).add(`null`)],oe=/* @__PURE__ */ new Set([`number`,`string`,`boolean`]),se=({zodSchema:e,jsonSchema:t},n)=>{let r=e._zod.def[n.isResponse?`out`:`in`],o=e._zod.def[n.isResponse?`in`:`out`];if(!i(r,`transform`))return t;let s=H(Z(o,{ctx:n}));if(x(s)){if(n.isResponse){let e=a(r,q(s));if(e&&oe.has(e))return{...t,type:e}}else{let{type:e,...t}=s;return{...t,format:`${t.format||e} (preprocessed)`}}}return t},ce=({jsonSchema:e})=>{if(e.type!==`object`)return e;let t=e;return!t.properties||!(`raw`in t.properties)||!m(t.properties.raw)?e:t.properties.raw},Y=e=>e.length?y.fromPairs(y.zip(y.times(e=>`example${e+1}`,e.length),y.map(y.objOf(`dataValue`),e))):void 0,le=(e,t)=>t?.has(e)||e.startsWith(`x-`)||P().has(e),ue=(e,t)=>t?.has(e)||M().has(e),de=({method:e,path:t,security:r,inputSources:i,isHeader:a,isCookie:o})=>{let s=new Set(n(t)),c=i.includes(`query`),l=i.includes(`body`),u=i.includes(`params`),d=i.includes(`headers`),f=i.includes(`cookies`)||i.includes(`signedCookies`),p;d&&r&&(p=h(r,`header`));let m;f&&r&&(m=h(r,`cookie`));let g=n=>o?.(n,e,t)??ue(n,m);return{pathParams:s,getLocation:n=>{if(u&&s.has(n)&&s.delete(n))return`path`;if(f&&g(n))return`cookie`;if(d&&(a?.(n,e,t)??le(n,p)))return`header`;if(!l){if(c)return`query`;if(f)return`cookie`}},isQueryEnabled:c,isBodyEnabled:l}},fe=({path:e,method:t,flatRequest:n,makeRef:r,composition:i,getLocation:a,description:o=`${t.toUpperCase()} ${e} Parameter`})=>{let s=[];for(let[e,t]of Object.entries(n.properties)){if(!m(t))continue;let c=a(e);if(!c)continue;let u=H(t),d=i===`components`?r(t.id||JSON.stringify(t),u,t.id||l(o,e)):u;s.push({name:e,in:c,deprecated:t.deprecated,required:n.required?.includes(e)||c===`path`,description:u.description||o,schema:d,examples:Y(x(u)&&u.examples?.length?u.examples:y.pluck(e,n.examples?.filter(y.both(m,y.has(e)))||[]))})}return s},X={nullable:V,union:z,bigint:G,intersection:B,tuple:K,pipe:se,[o]:U,[p]:W,[c]:L,[d]:ce,[t]:R},pe=(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(y.is(Object,a)){if(b(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,H(r),t).$ref}continue}r.push(...y.values(a))}y.is(Array,a)&&r.push(...y.values(a))}return e},Z=(e,{ctx:t,rules:n=X})=>{let{$defs:i={},properties:a={}}=v.toJSONSchema(v.object({subject:e}),{unrepresentable:`any`,io:t.isResponse?`output`:`input`,override:e=>{let i=v.globalRegistry.get(e.zodSchema)?.id;if(i){let n=t.seenIds.get(i);if(n&&n!==e.zodSchema)throw new _(`The meta id "${i}" is used by two different schemas. Please make the ids unique or reuse the same schema instance.`,t);t.seenIds.set(i,e.zodSchema)}let a=r(e.zodSchema),o=n[a&&a in n?a:e.zodSchema._zod.def.type];if(o){let n={...o(e,t)};for(let t in e.jsonSchema)delete e.jsonSchema[t];Object.assign(e.jsonSchema,n)}}});return pe(m(a.subject)?a.subject:{},i,t)},Q=(e,t)=>{if(b(e))return[e,!1];let n=!1,r=y.map(e=>{let[r,i]=Q(e,t);return n||=i,r}),i=y.omit(t),a={properties:i,examples:y.map(i),required:y.without(t),allOf:r,oneOf:r,anyOf:r},o=y.evolve(a,e);return[o,n||!!o.required?.length]},me=({method:t,path:n,schema:r,mimeTypes:i,variant:a,makeRef:o,composition:s,hasMultipleStatusCodes:c,statusCode:u,brandHandling:d,seenIds:p,description:m=`${t.toUpperCase()} ${n} ${f(a)} response ${c?u:``}`.trim()})=>{if(!e(t,i))return{description:m};let h=H(Z(r,{rules:{...d,...X},ctx:{isResponse:!0,makeRef:o,path:n,method:t,seenIds:p}})),_=[];x(h)&&h.examples&&(_.push(...h.examples),delete h.examples);let v=s===`components`?o(r,h,l(m)):h;return{description:m,content:y.fromPairs(i.map(e=>[e,{[e===g.sse?`itemSchema`:`schema`]:v,examples:Y(_)}]))}},he=({format:e})=>{let t={type:`http`,scheme:`bearer`};return e&&(t.bearerFormat=e),t},ge=({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},_e=({name:e})=>({type:`apiKey`,in:`header`,name:e}),ve=({name:e})=>({type:`apiKey`,in:`cookie`,name:e}),ye=({url:e})=>({type:`openIdConnect`,openIdConnectUrl:e}),be=({flows:e={},oauth2MetadataUrl:t})=>({type:`oauth2`,flows:y.map(e=>({...e,scopes:e.scopes||{}}),y.reject(y.isNil,e)),oauth2MetadataUrl:t}),$=(e,t=[])=>{let n=e=>e.type===`basic`?{type:`http`,scheme:`basic`}:e.type===`bearer`?he(e):e.type===`input`?ge(e,t):e.type===`header`?_e(e):e.type===`cookie`?ve(e):e.type===`openid`?ye(e):be(e);return e.map(e=>e.map(({deprecated:e,...t})=>({...n(t),deprecated:e})))},xe=Set.prototype.has.bind(/* @__PURE__ */ new Set([`oauth2`,`openIdConnect`])),Se=(e,t,n)=>{let r=Array.from(t);return e.map(e=>{let t={};for(let i of e){let e=n(i);t[e]=xe(i.type)?r:[]}return t})},Ce=({schema:e,brandHandling:t,makeRef:n,path:r,method:i,seenIds:a})=>Z(e,{rules:{...t,...X},ctx:{isResponse:!1,makeRef:n,path:r,method:i,seenIds:a}}),we=({method:e,path:t,bodyJsonSchema:n,hasRequiredBodyProps:r,flatRequest:i,mimeType:a,makeRef:o,composition:s,paramNames:c,description:u=`${e.toUpperCase()} ${t} Request body`})=>{let d=H(n),f=[];x(d)&&d.examples&&(f.push(...d.examples),delete d.examples);let p={schema:s===`components`?o(JSON.stringify(d),d,l(u)):d,examples:Y(f.length?f:i.examples?.filter(e=>m(e)&&!Array.isArray(e)).map(y.omit(c))||[])},h={description:u,content:{[a]:p}};return(r||a===g.raw)&&(h.required=!0),h},Te=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)},[]),Ee=(e,t=50)=>!e||e.length<=t?e:e.slice(0,Math.max(1,t||0)-1)+`…`,De=e=>{let t=Array.from(e);return t.length?t:void 0};export{$ as a,Q as c,I as d,Ee as f,A as h,me as i,de as l,T as m,Ce as n,Se as o,D as p,fe as r,Te as s,we as t,De as u};
@@ -1,22 +1,17 @@
1
- import { M as ResponseVariant, g as CommonConfig, t as Routing } from "./routing-oAHiW3zy.js";
2
- import {
3
- c as ParamRecognizer,
4
- l as depictTags,
5
- o as BrandHandling,
6
- s as Depicter,
7
- t as DocumentationError,
8
- u as trimSummary,
9
- } from "./errors-Dbnie2Mk.js";
1
+ import { N as ResponseVariant, g as CommonConfig, t as Routing } from "./routing-BE8APxgy.js";
2
+ import { c as ParamRecognizer, l as depictTags, o as BrandHandling, s as Depicter, t as DocumentationError, u as trimSummary } from "./errors-masq5u05.js";
10
3
  import { z } from "zod";
11
4
  import { InfoObject, OpenApiBuilder, SchemaObjectValue, ServerObject } from "openapi3-ts/oas32";
12
5
  type Component = `${ResponseVariant}Response` | "requestParameter" | "requestBody";
13
6
  /** @desc user defined function that creates a component description from its properties */
14
- type Descriptor = (
15
- props: Record<"method" | "path" | "operationId", string> & {
16
- statusCode?: number | string;
17
- },
18
- ) => string;
19
- type Summarizer = (params: { summary?: string; description?: string; trim: typeof trimSummary }) => string | undefined;
7
+ type Descriptor = (props: Record<"method" | "path" | "operationId", string> & {
8
+ statusCode?: number | string;
9
+ }) => string;
10
+ type Summarizer = (params: {
11
+ summary?: string;
12
+ description?: string;
13
+ trim: typeof trimSummary;
14
+ }) => string | undefined;
20
15
  interface DocumentationParams {
21
16
  /**
22
17
  * @desc The metadata about the API
@@ -84,6 +79,7 @@ interface DocumentationParams {
84
79
  tags?: Parameters<typeof depictTags>[0];
85
80
  }
86
81
  export declare class Documentation extends OpenApiBuilder {
82
+ #private;
87
83
  /**
88
84
  * @desc Resolves a ReferenceObject to the referenced schema. Non-reference input is returned as is.
89
85
  * @see getSpec
@@ -92,4 +88,4 @@ export declare class Documentation extends OpenApiBuilder {
92
88
  resolve<T extends SchemaObjectValue | z.core.JSONSchema.BaseSchema>(subject: T): T;
93
89
  constructor({ hasHeadMethod, ...rest }: DocumentationParams);
94
90
  }
95
- export { type Depicter, DocumentationError };
91
+ export { type Depicter, DocumentationError };
@@ -1 +1 @@
1
- import{H as e,J as t,Y as n,i as r,n as i,nt as a,p as o,t as s,w as c}from"./routing-walker-CelPngqp.js";import{a as l,c as u,d,f,i as p,l as m,m as h,n as g,o as _,r as v,s as y,t as b,u as x}from"./documentation-helpers-C5Tkir01.js";import"zod";import*as S from"ramda";import{OpenApiBuilder as C,isReferenceObject as w}from"openapi3-ts/oas32";const T=e=>`${Math.floor(e/100)}XX`,E=e=>{let t=new Map;for(let{schema:n,mimeTypes:r,statusCodes:i}of e){let e=r?[...r].sort().join(`,`):``,a=t.get(n)||new Map;t.set(n,a);let o=a.get(e),s=new Set(i);a.set(e,{schema:n,mimeTypes:r,statusCodes:o?s.union(o.statusCodes):s})}let n=[];for(let e of t.values())for(let t of e.values())n.push(t);return n},D=({schema:e,mimeTypes:t,statusCodes:n},r)=>{let i=[],a=new Map;for(let e of n){let t=T(e),n=a.get(t);a.set(t,n?new Set([e]).union(n):new Set([e]))}for(let[n,o]of a){let a=r.some(e=>T(e)===n&&!o.has(e));if(o.size>1&&!a)i.push({schema:e,mimeTypes:t,statusCodes:[n]});else for(let n of o)i.push({schema:e,mimeTypes:t,statusCodes:[n]})}return i},O=({statusCodes:e})=>typeof e[0]==`string`,k=e=>{let t=S.chain(S.prop(`statusCodes`),e),n=S.chain(e=>D(e,t),E(e));return n.some(O)?n:e},A=({description:e,summary:t=e,trim:n})=>n(t);var j=class extends C{#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}`}}resolve(e){let t=e,n=new Set;for(;w(t);){if(n.has(t.$ref))return e;n.add(t.$ref);let r=S.path(t.$ref.split(`/`).slice(1),this.rootDoc);if(!r)return e;t=r}return t}#a(e,n,r){let i=r||t(n,e),a=this.#t.get(i);if(a===void 0)return this.#t.set(i,1),i;if(r)throw new c(`Duplicated operationId: "${r}"`,{method:n,isResponse:!1,path:e});return a++,this.#t.set(i,a),`${i}${a}`}#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=y(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 r=n(t),i=this.#r.get(r);if(i!==void 0&&i!==t)throw new c(`Path has a duplicate: the normalized path "${r}" is already registered with different parameter names at "${i}"`,{method:e,path:t,isResponse:!1});i===void 0&&this.#r.set(r,t)}#l({config:t,descriptions:n,brandHandling:i,isHeader:s,isCookie:y,hasStatusCodeRanges:C=!1,summarizer:w=A,composition:T=`inline`}){let E={composition:T,brandHandling:i,makeRef:this.#i.bind(this),seenIds:new Map};return(i,T,D)=>{this.#c(i,T);let O={...E,path:T,method:i,endpoint:D},{description:A,summary:j,scopes:M,inputSchema:N,security:P}=D,F=e(i,t.inputSources),{pathParams:I,getLocation:L,isBodyEnabled:R}=m({method:i,path:T,security:P,inputSources:F,isHeader:s,isCookie:y}),z=this.#a(T,i,D.getOperationId(i)),B=g({...O,schema:N}),V=this.resolve(B),H=h(V),U=v({...O,getLocation:L,flatRequest:H,description:n?.requestParameter?.({method:i,path:T,operationId:z})});if(I.size)throw new c(`The input schema is missing the path parameter "${[...I][0]}"`,{method:i,path:T,isResponse:!1});let W={};for(let e of o){let t=D.getResponses(e),r=C?k(t):t;for(let{mimeTypes:t,schema:a,statusCodes:o}of r)for(let s of o)W[s]=p({...O,variant:e,schema:a,mimeTypes:t,statusCode:s,hasMultipleStatusCodes:r.length>1||o.length>1,description:n?.[`${e}Response`]?.({method:i,path:T,operationId:z,statusCode:s})})}let G;if(R){let e=S.pluck(`name`,U),[t,r]=u(e.length?V:B,e);G=b({...O,bodyJsonSchema:t,hasRequiredBodyProps:r,flatRequest:H,paramNames:e,mimeType:a[D.getProbableRequestType(i)],description:n?.requestBody?.({method:i,path:T,operationId:z})})}let K=_(l(r(P),F),M,e=>{let t=this.#o(e);return this.addSecurityScheme(t,e),t}),q={operationId:z,summary:w({summary:j,description:A,trim:f}),description:A,deprecated:D.isDeprecated||void 0,tags:x(D.tags),parameters:x(U),requestBody:G,security:x(K),responses:W};this.addPath(d(T),{[i]:q})}}constructor({hasHeadMethod:e=!0,...t}){super(),this.#s(t);let n=this.#l(t),r=e?i(n):n;s({...t,onEndpoint:r})}};export{j as Documentation,c as DocumentationError};
1
+ import{U as e,X as t,Y as n,i as r,n as i,p as a,rt as o,t as s,w as c}from"./routing-walker-BpLERmiE.js";import{a as l,c as u,d,f,i as p,l as m,m as h,n as g,o as _,r as v,s as y,t as b,u as x}from"./documentation-helpers-Df2sKJq9.js";import"zod";import*as S from"ramda";import{OpenApiBuilder as C,isReferenceObject as w}from"openapi3-ts/oas32";const T=e=>`${Math.floor(e/100)}XX`,E=e=>{let t=/* @__PURE__ */ new Map;for(let{schema:n,mimeTypes:r,statusCodes:i}of e){let e=r?[...r].sort().join(`,`):``,a=t.get(n)||/* @__PURE__ */ new Map;t.set(n,a);let o=a.get(e),s=new Set(i);a.set(e,{schema:n,mimeTypes:r,statusCodes:o?s.union(o.statusCodes):s})}let n=[];for(let e of t.values())for(let t of e.values())n.push(t);return n},D=({schema:e,mimeTypes:t,statusCodes:n},r)=>{let i=[],a=/* @__PURE__ */ new Map;for(let e of n){let t=T(e),n=a.get(t);a.set(t,n?(/* @__PURE__ */ new Set([e])).union(n):/* @__PURE__ */ new Set([e]))}for(let[n,o]of a){let a=r.some(e=>T(e)===n&&!o.has(e));if(o.size>1&&!a)i.push({schema:e,mimeTypes:t,statusCodes:[n]});else for(let n of o)i.push({schema:e,mimeTypes:t,statusCodes:[n]})}return i},O=({statusCodes:e})=>typeof e[0]==`string`,k=e=>{let t=S.chain(S.prop(`statusCodes`),e),n=S.chain(e=>D(e,t),E(e));return n.some(O)?n:e},A=({description:e,summary:t=e,trim:n})=>n(t);var j=class extends C{#e=/* @__PURE__ */ new Map;#t=/* @__PURE__ */ new Map;#n=/* @__PURE__ */ new Map;#r=/* @__PURE__ */ 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}`}}resolve(e){let t=e,n=/* @__PURE__ */ new Set;for(;w(t);){if(n.has(t.$ref))return e;n.add(t.$ref);let r=S.path(t.$ref.split(`/`).slice(1),this.rootDoc);if(!r)return e;t=r}return t}#a(e,t,r){let i=r||n(t,e),a=this.#t.get(i);if(a===void 0)return this.#t.set(i,1),i;if(r)throw new c(`Duplicated operationId: "${r}"`,{method:t,isResponse:!1,path:e});return a++,this.#t.set(i,a),`${i}${a}`}#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=y(e)),t)for(let e of Array.isArray(t)?t:[t])this.addServer(typeof e==`string`?{url:e}:e)}#c(e,n){if(e===`head`||!n.includes(`:`))return;let r=t(n),i=this.#r.get(r);if(i!==void 0&&i!==n)throw new c(`Path has a duplicate: the normalized path "${r}" is already registered with different parameter names at "${i}"`,{method:e,path:n,isResponse:!1});i===void 0&&this.#r.set(r,n)}#l({config:t,descriptions:n,brandHandling:i,isHeader:s,isCookie:y,hasStatusCodeRanges:C=!1,summarizer:w=A,composition:T=`inline`}){let E={composition:T,brandHandling:i,makeRef:this.#i.bind(this),seenIds:/* @__PURE__ */ new Map};return(i,T,D)=>{this.#c(i,T);let O={...E,path:T,method:i,endpoint:D},{description:A,summary:j,scopes:M,inputSchema:N,security:P}=D,F=e(i,t.inputSources),{pathParams:I,getLocation:L,isBodyEnabled:R}=m({method:i,path:T,security:P,inputSources:F,isHeader:s,isCookie:y}),z=this.#a(T,i,D.getOperationId(i)),B=g({...O,schema:N}),V=this.resolve(B),H=h(V),U=v({...O,getLocation:L,flatRequest:H,description:n?.requestParameter?.({method:i,path:T,operationId:z})});if(I.size)throw new c(`The input schema is missing the path parameter "${[...I][0]}"`,{method:i,path:T,isResponse:!1});let W={};for(let e of a){let t=D.getResponses(e),r=C?k(t):t;for(let{mimeTypes:t,schema:a,statusCodes:o}of r)for(let s of o)W[s]=p({...O,variant:e,schema:a,mimeTypes:t,statusCode:s,hasMultipleStatusCodes:r.length>1||o.length>1,description:n?.[`${e}Response`]?.({method:i,path:T,operationId:z,statusCode:s})})}let G;if(R){let e=S.pluck(`name`,U),[t,r]=u(e.length?V:B,e);G=b({...O,bodyJsonSchema:t,hasRequiredBodyProps:r,flatRequest:H,paramNames:e,mimeType:o[D.getProbableRequestType(i)],description:n?.requestBody?.({method:i,path:T,operationId:z})})}let K=_(l(r(P),F),M,e=>{let t=this.#o(e);return this.addSecurityScheme(t,e),t}),q={operationId:z,summary:w({summary:j,description:A,trim:f}),description:A,deprecated:D.isDeprecated||void 0,tags:x(D.tags),parameters:x(U),requestBody:G,security:x(K),responses:W};this.addPath(d(T),{[i]:q})}}constructor({hasHeadMethod:e=!0,...t}){super(),this.#s(t);let n=this.#l(t),r=e?i(n):n;s({...t,onEndpoint:r})}};export{j as Documentation,c as DocumentationError};
@@ -1,4 +1,4 @@
1
- import { D as ClientMethod, O as Method, f as Tag } from "./routing-oAHiW3zy.js";
1
+ import { O as ClientMethod, f as Tag, k as Method } from "./routing-BE8APxgy.js";
2
2
  import { z } from "zod";
3
3
  import { ReferenceObject, SchemaObjectValue, TagObject } from "openapi3-ts/oas32";
4
4
  interface ReqResCommons {
@@ -10,13 +10,10 @@ interface ReqResCommons {
10
10
  interface OpenAPIContext extends ReqResCommons {
11
11
  isResponse: boolean;
12
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;
13
+ type Depicter = (zodCtx: {
14
+ zodSchema: z.core.$ZodType;
15
+ jsonSchema: z.core.JSONSchema.BaseSchema;
16
+ }, oasCtx: OpenAPIContext) => z.core.JSONSchema.BaseSchema | SchemaObjectValue;
20
17
  /** @desc Using defaultIsHeader or defaultIsCookie when returns null or undefined */
21
18
  type ParamRecognizer = (name: string, method: ClientMethod, path: string) => boolean | null | undefined;
22
19
  type BrandHandling = Record<string | symbol, Depicter>;
@@ -65,15 +62,4 @@ declare class MissingPeerError extends Error {
65
62
  name: string;
66
63
  constructor(module: string);
67
64
  }
68
- export {
69
- RoutingError as a,
70
- ParamRecognizer 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
- };
65
+ export { RoutingError as a, ParamRecognizer as c, OutputValidationError as i, depictTags as l, InputValidationError as n, BrandHandling as o, MissingPeerError as r, Depicter as s, DocumentationError as t, trimSummary as u };