express-zod-api 29.8.1 → 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,74 @@
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
+
63
+ ### v29.8.2
64
+
65
+ - Fixed alias production for Integration generator:
66
+ - Recursive and lazy schemas that are reused both for input and output used to get the same alias;
67
+ - In certain cases the direction affects the generated type, for example for schemas having `.default()`;
68
+ - This version emits an alias for each direction for such schemas (even if they are the same);
69
+ - An alias that used to be named `Type1` will now be either `InputType1` or `ResponseType1` or both;
70
+ - The issue was found and reported by [@giovio](https://github.com/giovio).
71
+
5
72
  ### v29.8.1
6
73
 
7
74
  - Fixed the `HEAD` method of the generated client to avoid parsing the response body by the default implementation.
package/README.md CHANGED
@@ -89,6 +89,7 @@ Therefore, many basic tasks can be achieved faster and easier, in particular:
89
89
 
90
90
  These people contributed to the improvement of the framework by reporting bugs, making changes, and suggesting ideas:
91
91
 
92
+ [<img src="https://github.com/giovio.png" alt="@giovio" width="50" />](https://github.com/giovio)
92
93
  [<img src="https://github.com/marco-carvalho.png" alt="@marco-carvalho" width="50" />](https://github.com/marco-carvalho)
93
94
  [<img src="https://github.com/BetterAndBetterII.png" alt="@BetterAndBetterII" width="50" />](https://github.com/BetterAndBetterII)
94
95
  [<img src="https://github.com/pycanis.png" alt="@pycanis" width="50" />](https://github.com/pycanis)
@@ -278,7 +279,7 @@ curl -L -X GET 'localhost:8090/v1/hello?name=Rick'
278
279
  You should receive the following response:
279
280
 
280
281
  ```json
281
- { "status": "success", "data": { "greetings": "Hello, Rick. Happy coding!" } }
282
+ { "greetings": "Hello, Rick. Happy coding!" }
282
283
  ```
283
284
 
284
285
  # Basic features
@@ -455,43 +456,15 @@ const factory = defaultEndpointsFactory.use(auth(), {
455
456
 
456
457
  ## Refinements
457
458
 
458
- You can implement additional validations within schemas using refinements.
459
- 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.
460
462
 
461
463
  ```ts
462
- import { z } from "zod";
463
- import { Middleware } from "express-zod-api";
464
-
465
- const nicknameConstraintMiddleware = new Middleware({
466
- input: z.object({
467
- nickname: z
468
- .string()
469
- .min(1)
470
- .refine(
471
- (nick) => !/^\d.*$/.test(nick),
472
- "Nickname cannot start with a digit",
473
- ),
474
- }),
475
- // ...,
476
- });
477
- ```
478
-
479
- By the way, you can also refine the whole I/O object, for example, in case you need a complex validation of its props.
480
-
481
- ```ts
482
- const endpoint = endpointsFactory.build({
483
- input: z
484
- .object({
485
- email: z.email().optional(),
486
- id: z.string().optional(),
487
- otherThing: z.string().optional(),
488
- })
489
- .refine(
490
- (inputs) => Object.keys(inputs).length >= 1,
491
- "Please provide at least one property",
492
- ),
493
- // ...,
494
- });
464
+ const nickname = z
465
+ .string()
466
+ .min(1)
467
+ .refine((nick) => !/^\d.*$/.test(nick), "Nickname cannot start with a digit");
495
468
  ```
496
469
 
497
470
  ## Query string parser
@@ -904,11 +877,7 @@ Install `express-rate-limit`. Consider the `createRateLimitMiddleware()` to enab
904
877
 
905
878
  ```ts
906
879
  const endpoint = factory
907
- .useRateLimit({
908
- windowMs: 60000,
909
- max: 100,
910
- statusCode: 429, // when set explicitly, it will be reflected in the Documentation
911
- }) // shorthand, or .addMiddleware(createRateLimitMiddleware())
880
+ .useRateLimit({ windowMs: 60000, max: 100 }) // shorthand, or .addMiddleware(createRateLimitMiddleware())
912
881
  .buildVoid({
913
882
  handler: async ({ ctx: { rateLimit, logger } }) => {
914
883
  logger.debug("Features", rateLimit); // { limit, used, remaining, resetTime, getKey, resetKey }
@@ -923,8 +892,8 @@ The `defaultResultHandler` sets the HTTP status code and ensures the following t
923
892
 
924
893
  ```ts
925
894
  type DefaultResponse<OUT> =
926
- | { status: "success"; data: OUT } // Positive response
927
- | { status: "error"; error: { message: string } }; // or Negative response
895
+ | OUT // Positive response
896
+ | { message: string }; // or Negative response
928
897
  ```
929
898
 
930
899
  You can create your own result handler by using this example as a template:
@@ -938,18 +907,18 @@ import {
938
907
  } from "express-zod-api";
939
908
 
940
909
  const yourResultHandler = new ResultHandler({
941
- positive: (data) => ({
942
- schema: z.object({ data }),
943
- mimeType: "application/json", // optinal or array
910
+ positive: (output) => ({
911
+ schema: output,
912
+ mimeType: "application/json", // optional or array
944
913
  }),
945
- negative: z.object({ error: z.string() }),
914
+ negative: z.object({ message: z.string() }),
946
915
  handler: ({ error, input, output, request, response, logger }) => {
947
916
  if (error) {
948
917
  const { statusCode } = ensureHttpError(error);
949
918
  const message = getMessageFromError(error);
950
- return void response.status(statusCode).json({ error: message });
919
+ return void response.status(statusCode).json({ message });
951
920
  }
952
- response.status(200).json({ data: output });
921
+ response.status(200).json(output);
953
922
  },
954
923
  });
955
924
  ```
@@ -1164,7 +1133,7 @@ test("should respond successfully", async () => {
1164
1133
  expect(loggerMock._getLogs().error).toHaveLength(0);
1165
1134
  expect(responseMock._getStatusCode()).toBe(200);
1166
1135
  expect(responseMock._getHeaders()).toHaveProperty("x-custom", "one"); // lower case!
1167
- expect(responseMock._getJSONData()).toEqual({ status: "success" });
1136
+ expect(responseMock._getJSONData()).toEqual({ greetings: "Hello, World" });
1168
1137
  });
1169
1138
  ```
1170
1139
 
@@ -1397,18 +1366,18 @@ response schemas and their corresponding status codes.
1397
1366
  import { ResultHandler } from "express-zod-api";
1398
1367
 
1399
1368
  const resultHandler = new ResultHandler({
1400
- positive: (data) => ({
1369
+ positive: (output) => ({
1401
1370
  statusCode: [201, 202], // created or will be created
1402
- schema: z.object({ status: z.literal("created"), data }),
1371
+ schema: output,
1403
1372
  }),
1404
1373
  negative: [
1405
1374
  {
1406
1375
  statusCode: 409, // conflict: entity already exists
1407
- schema: z.object({ status: z.literal("exists"), id: z.int() }),
1376
+ schema: z.object({ id: z.int().describe("id of the existing entity") }),
1408
1377
  },
1409
1378
  {
1410
1379
  statusCode: [400, 500], // validation or internal error
1411
- schema: z.object({ status: z.literal("error"), reason: z.string() }),
1380
+ schema: z.object({ reason: z.string() }),
1412
1381
  },
1413
1382
  ],
1414
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 };