mobx-tanstack-query-api 0.37.0 → 0.38.0

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/cli.d.ts CHANGED
@@ -184,6 +184,32 @@ interface GenerateQueryApiParams {
184
184
  * [**Documentation**](https://js2me.github.io/mobx-tanstack-query-api/codegen/config/#nobarrelfiles)
185
185
  */
186
186
  noBarrelFiles?: boolean;
187
+ /**
188
+ * Generate Zod contracts (params + data schemas) for each endpoint and add `contracts` to the endpoint config.
189
+ * When truthy, can also enable validation via `validateContracts` in the endpoint config.
190
+ * Requires `zod` to be installed.
191
+ *
192
+ * - `true`: generate contracts and set `validateContracts: true` (validate params + data).
193
+ * - `false`: no contracts, no validation.
194
+ * - `{ validate: boolean }`: set `validateContracts` to that boolean.
195
+ * - `{ validate: string }`: set `validateContracts` to the expression (inserted as-is). E.g. `"process.env.NODE_ENV === 'development'"`.
196
+ * - `{ validate: { params?: boolean | string; data?: boolean | string } }`: set `validateContracts` to an object; each value is literal or expression (string inserted as-is).
197
+ *
198
+ * When using an object form, optional `throw` controls `throwContracts` (throw on validation errors vs warn):
199
+ * - `{ throw: boolean }`: set `throwContracts` to that boolean.
200
+ * - `{ throw: string }`: set `throwContracts` to the expression (inserted as-is).
201
+ * - `{ throw: { params?: boolean | string; data?: boolean | string } }`: set `throwContracts` to an object; each value is literal or expression (string inserted as-is).
202
+ */
203
+ zodContracts?: boolean | {
204
+ validate: boolean | string | {
205
+ params?: boolean | string;
206
+ data?: boolean | string;
207
+ };
208
+ throw?: boolean | string | {
209
+ params?: boolean | string;
210
+ data?: boolean | string;
211
+ };
212
+ };
187
213
  }
188
214
 
189
215
  type AllImportFileParams = Record<KeyOfByValue<Required<GenerateQueryApiParams>, 'builtin' | ImportFileParams>, ImportFileParams>;
package/cli.js CHANGED
@@ -216,6 +216,301 @@ const createShortModelType = (shortModelType) => {
216
216
  description: shortModelType.description || ""
217
217
  };
218
218
  };
219
+ const REF_PREFIX = "#/components/schemas/";
220
+ function parseRef(ref) {
221
+ if (typeof ref !== "string" || !ref.startsWith(REF_PREFIX)) return null;
222
+ return ref.slice(REF_PREFIX.length);
223
+ }
224
+ function getResponseSchemaKeyFromOperation(rawOperation) {
225
+ const op = rawOperation;
226
+ const responses = op?.responses;
227
+ if (!responses || typeof responses !== "object") return null;
228
+ const successStatus = Object.keys(responses).find((s) => {
229
+ const code = Number.parseInt(s, 10);
230
+ return code >= 200 && code < 300;
231
+ });
232
+ if (!successStatus) return null;
233
+ const successResponse = responses[successStatus];
234
+ const content = successResponse?.content;
235
+ if (!content || typeof content !== "object") return null;
236
+ const jsonContent = content["application/json"] ?? Object.values(content)[0];
237
+ const ref = jsonContent?.schema?.$ref;
238
+ if (typeof ref !== "string") return null;
239
+ return parseRef(ref);
240
+ }
241
+ function typeNameToSchemaKey(typeName, typeSuffix = "DC") {
242
+ const t = typeName.trim();
243
+ if (typeSuffix && t.endsWith(typeSuffix))
244
+ return t.slice(0, -typeSuffix.length);
245
+ return t;
246
+ }
247
+ function schemaToString(schema) {
248
+ let s = "z.string()";
249
+ if (schema.minLength != null) s += `.min(${schema.minLength})`;
250
+ if (schema.maxLength != null) s += `.max(${schema.maxLength})`;
251
+ if (schema.pattern != null)
252
+ s += `.regex(new RegExp(${JSON.stringify(schema.pattern)}))`;
253
+ if (schema.enum != null)
254
+ s = `z.enum([${schema.enum.map((e) => JSON.stringify(e)).join(", ")}])`;
255
+ return s;
256
+ }
257
+ function schemaToNumber(schema) {
258
+ let n = "z.number()";
259
+ if (schema.type === "integer") n += ".int()";
260
+ if (schema.minimum != null) n += `.min(${schema.minimum})`;
261
+ if (schema.maximum != null) n += `.max(${schema.maximum})`;
262
+ return n;
263
+ }
264
+ function schemaToArray(schema, schemas, schemaKeyToVarName2, visited) {
265
+ const items = schema.items ? schemaToZodExpr(schema.items, schemas, schemaKeyToVarName2) : "z.any()";
266
+ let a = `z.array(${items})`;
267
+ if (schema.minItems != null) a += `.min(${schema.minItems})`;
268
+ if (schema.maxItems != null) a += `.max(${schema.maxItems})`;
269
+ return a;
270
+ }
271
+ function schemaToObject(schema, schemas, schemaKeyToVarName2, visited) {
272
+ if (schema.properties && Object.keys(schema.properties).length > 0) {
273
+ const required = new Set(schema.required ?? []);
274
+ const entries = Object.entries(schema.properties).map(
275
+ ([propName, propSchema]) => {
276
+ const expr = schemaToZodExpr(
277
+ propSchema,
278
+ schemas,
279
+ schemaKeyToVarName2
280
+ );
281
+ const optional = !required.has(propName);
282
+ const field = optional ? `${expr}.optional()` : expr;
283
+ return ` ${JSON.stringify(propName)}: ${field}`;
284
+ }
285
+ );
286
+ return `z.object({
287
+ ${entries.join(",\n")}
288
+ })`;
289
+ }
290
+ if (schema.additionalProperties === true)
291
+ return "z.record(z.string(), z.any())";
292
+ if (typeof schema.additionalProperties === "object") {
293
+ const value = schemaToZodExpr(
294
+ schema.additionalProperties,
295
+ schemas,
296
+ schemaKeyToVarName2
297
+ );
298
+ return `z.record(z.string(), ${value})`;
299
+ }
300
+ return "z.record(z.string(), z.any())";
301
+ }
302
+ function schemaToZodExpr(schema, schemas, schemaKeyToVarName2, visited) {
303
+ if (!schema) return "z.any()";
304
+ if (schema.$ref) {
305
+ const key = parseRef(schema.$ref);
306
+ if (key && key in schemas)
307
+ return `z.lazy(() => ${schemaKeyToVarName2(key)})`;
308
+ return "z.any()";
309
+ }
310
+ if (schema.allOf && schema.allOf.length > 0) {
311
+ const parts = schema.allOf.map(
312
+ (part) => schemaToZodExpr(part, schemas, schemaKeyToVarName2)
313
+ );
314
+ const base2 = parts.length === 1 ? parts[0] : parts.reduce((acc, p) => `z.intersection(${acc}, ${p})`);
315
+ return schema.nullable === true ? `${base2}.nullable()` : base2;
316
+ }
317
+ const nullable = schema.nullable === true;
318
+ let base;
319
+ switch (schema.type) {
320
+ case "string":
321
+ base = schemaToString(schema);
322
+ break;
323
+ case "integer":
324
+ case "number":
325
+ base = schemaToNumber(schema);
326
+ break;
327
+ case "boolean":
328
+ base = "z.boolean()";
329
+ break;
330
+ case "array":
331
+ base = schemaToArray(schema, schemas, schemaKeyToVarName2);
332
+ break;
333
+ case "object":
334
+ base = schemaToObject(schema, schemas, schemaKeyToVarName2);
335
+ break;
336
+ default:
337
+ base = "z.any()";
338
+ }
339
+ return nullable ? `${base}.nullable()` : base;
340
+ }
341
+ function collectRefs(schema, schemas, out) {
342
+ if (!schema) return;
343
+ if (schema.$ref) {
344
+ const key = parseRef(schema.$ref);
345
+ if (key && key in schemas && !out.has(key)) {
346
+ out.add(key);
347
+ collectRefs(schemas[key], schemas, out);
348
+ }
349
+ return;
350
+ }
351
+ if (schema.allOf) {
352
+ for (const part of schema.allOf) collectRefs(part, schemas, out);
353
+ }
354
+ if (schema.properties) {
355
+ for (const v of Object.values(schema.properties)) {
356
+ collectRefs(v, schemas, out);
357
+ }
358
+ }
359
+ if (schema.items) collectRefs(schema.items, schemas, out);
360
+ if (typeof schema.additionalProperties === "object") {
361
+ collectRefs(schema.additionalProperties, schemas, out);
362
+ }
363
+ }
364
+ function schemaKeyToVarName(key, utils) {
365
+ const _ = utils._;
366
+ return `${_.camelCase(key)}Schema`;
367
+ }
368
+ function generateAuxiliarySchemas(schemaKeys, schemas, schemaKeyToVarNameFn, visited) {
369
+ const lines = [];
370
+ for (const key of schemaKeys) {
371
+ if (visited.has(key)) continue;
372
+ visited.add(key);
373
+ const schema = schemas[key];
374
+ if (!schema) continue;
375
+ const varName = schemaKeyToVarNameFn(key);
376
+ const expr = schemaToZodExpr(
377
+ schema,
378
+ schemas,
379
+ schemaKeyToVarNameFn
380
+ );
381
+ lines.push(`export const ${varName} = ${expr};`);
382
+ }
383
+ return lines;
384
+ }
385
+ function buildCentralZodSchemasFile(params) {
386
+ const { componentsSchemas, utils } = params;
387
+ const schemaKeyToVarNameFn = (key) => schemaKeyToVarName(key, utils);
388
+ const lines = generateAuxiliarySchemas(
389
+ Object.keys(componentsSchemas),
390
+ componentsSchemas,
391
+ schemaKeyToVarNameFn,
392
+ /* @__PURE__ */ new Set()
393
+ );
394
+ return `import * as z from "zod";
395
+
396
+ ${lines.join("\n\n")}
397
+ `;
398
+ }
399
+ function typeToZodSchemaFallback(typeStr) {
400
+ const t = typeStr.trim();
401
+ if (t === "RequestParams") return "z.any()";
402
+ if (t === "Record<string, any>" || t === "Record<string, unknown>")
403
+ return "z.record(z.string(), z.any())";
404
+ if (/^Record\s*<\s*string\s*,/i.test(t))
405
+ return "z.record(z.string(), z.any())";
406
+ if (t === "string") return "z.string()";
407
+ if (t === "number") return "z.number()";
408
+ if (t === "boolean") return "z.boolean()";
409
+ if (t === "any") return "z.any()";
410
+ if (t === "unknown") return "z.unknown()";
411
+ return "z.any()";
412
+ }
413
+ function typeToZodSchemaWithSchema(typeStr, schemas, utils, typeSuffix) {
414
+ const t = typeStr.trim();
415
+ if (!schemas || Object.keys(schemas).length === 0) {
416
+ return { expr: typeToZodSchemaFallback(t), refs: [] };
417
+ }
418
+ if (t === "RequestParams") return { expr: "z.any()", refs: [] };
419
+ const schemaKey = typeNameToSchemaKey(t, typeSuffix);
420
+ const schema = schemas[schemaKey];
421
+ if (!schema) {
422
+ return { expr: typeToZodSchemaFallback(t), refs: [] };
423
+ }
424
+ const refs = /* @__PURE__ */ new Set();
425
+ collectRefs(schema, schemas, refs);
426
+ const schemaKeyToVarNameFn = (key) => schemaKeyToVarName(key, utils);
427
+ const expr = schemaToZodExpr(
428
+ schema,
429
+ schemas,
430
+ schemaKeyToVarNameFn
431
+ );
432
+ return { expr, refs: [...refs] };
433
+ }
434
+ function schemaKeyToZod(schemaKey, schemas, utils) {
435
+ const schema = schemas[schemaKey];
436
+ if (!schema) return { expr: "z.any()", refs: [] };
437
+ const refs = /* @__PURE__ */ new Set();
438
+ collectRefs(schema, schemas, refs);
439
+ const schemaKeyToVarNameFn = (key) => schemaKeyToVarName(key, utils);
440
+ const expr = schemaToZodExpr(
441
+ schema,
442
+ schemas,
443
+ schemaKeyToVarNameFn
444
+ );
445
+ return { expr, refs: [...refs] };
446
+ }
447
+ function buildEndpointZodContractsCode(params) {
448
+ const {
449
+ routeNameUsage,
450
+ inputParams,
451
+ responseDataTypeName,
452
+ contractsVarName,
453
+ utils,
454
+ componentsSchemas = null,
455
+ typeSuffix = "DC",
456
+ responseSchemaKey,
457
+ useExternalZodSchemas = false
458
+ } = params;
459
+ const _ = utils._;
460
+ const paramsSchemaName = `${_.camelCase(routeNameUsage)}ParamsSchema`;
461
+ const dataSchemaName = `${_.camelCase(routeNameUsage)}DataSchema`;
462
+ const allAuxiliaryKeys = /* @__PURE__ */ new Set();
463
+ const paramParts = [];
464
+ for (const p of inputParams) {
465
+ const { expr, refs: refKeys } = typeToZodSchemaWithSchema(
466
+ p.type,
467
+ componentsSchemas,
468
+ utils,
469
+ typeSuffix
470
+ );
471
+ for (const k of refKeys) allAuxiliaryKeys.add(k);
472
+ const schemaWithOptional = p.optional ? `${expr}.optional()` : expr;
473
+ paramParts.push(`${p.name}: ${schemaWithOptional}`);
474
+ }
475
+ const responseResult = responseSchemaKey && componentsSchemas && responseSchemaKey in componentsSchemas ? schemaKeyToZod(responseSchemaKey, componentsSchemas, utils) : typeToZodSchemaWithSchema(
476
+ responseDataTypeName,
477
+ componentsSchemas,
478
+ utils,
479
+ typeSuffix
480
+ );
481
+ const schemaKeyToVarNameFn = (key) => schemaKeyToVarName(key, utils);
482
+ const useDataSchemaFromCentral = useExternalZodSchemas && responseSchemaKey && componentsSchemas && responseSchemaKey in componentsSchemas;
483
+ if (useDataSchemaFromCentral) {
484
+ allAuxiliaryKeys.add(responseSchemaKey);
485
+ } else {
486
+ for (const k of responseResult.refs) allAuxiliaryKeys.add(k);
487
+ }
488
+ const zodSchemaImportNames = useExternalZodSchemas && allAuxiliaryKeys.size > 0 ? [...allAuxiliaryKeys].map(schemaKeyToVarNameFn) : [];
489
+ const allAuxiliary = useExternalZodSchemas ? [] : generateAuxiliarySchemas(
490
+ [...allAuxiliaryKeys],
491
+ componentsSchemas ?? {},
492
+ schemaKeyToVarNameFn,
493
+ /* @__PURE__ */ new Set()
494
+ );
495
+ const paramsFields = paramParts.join(",\n ");
496
+ const paramsSchemaCode = `export const ${paramsSchemaName} = z.object({
497
+ ${paramsFields},
498
+ });`;
499
+ const dataSchemaCode = useDataSchemaFromCentral ? `export const ${dataSchemaName} = ${schemaKeyToVarNameFn(responseSchemaKey)};` : `export const ${dataSchemaName} = ${responseResult.expr};`;
500
+ const contractsCode = `export const ${contractsVarName} = {
501
+ params: ${paramsSchemaName},
502
+ data: ${dataSchemaName},
503
+ };`;
504
+ const auxiliaryBlock = allAuxiliary.length > 0 ? `${allAuxiliary.join("\n\n")}
505
+
506
+ ` : "";
507
+ const content = `${auxiliaryBlock}${paramsSchemaCode}
508
+
509
+ ${dataSchemaCode}
510
+
511
+ ${contractsCode}`;
512
+ return { content, zodSchemaImportNames };
513
+ }
219
514
  const formatGroupNameEnumKey = (groupName, { _ }) => _.upperFirst(_.camelCase(groupName));
220
515
  const formatTagNameEnumKey = (tagName, utils) => formatGroupNameEnumKey(tagName, utils);
221
516
  const metaInfoTmpl = async ({
@@ -367,8 +662,17 @@ const newEndpointTmpl = ({
367
662
  groupName,
368
663
  metaInfo,
369
664
  filterTypes,
370
- configuration
665
+ configuration,
666
+ zodContracts,
667
+ relativePathZodSchemas
668
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: codegen template, many branches by design
371
669
  }) => {
670
+ const zodContractsIsObject = typeof zodContracts === "object" && zodContracts !== null;
671
+ const hasZodContracts = zodContracts === true || zodContractsIsObject;
672
+ const validateOpt = zodContractsIsObject ? zodContracts.validate : zodContracts === true ? true : void 0;
673
+ const throwOpt = zodContractsIsObject ? zodContracts.throw : void 0;
674
+ const validateOptObj = validateOpt != null && typeof validateOpt === "object" && !Array.isArray(validateOpt) ? validateOpt : null;
675
+ const throwOptObj = throwOpt != null && typeof throwOpt === "object" && !Array.isArray(throwOpt) ? throwOpt : null;
372
676
  const { _ } = utils;
373
677
  const positiveResponseTypes = route.raw.responsesTypes?.filter(
374
678
  (it) => +it.status >= 200 && +it.status < 300 && (!it.typeData || filterTypes(it.typeData))
@@ -481,9 +785,95 @@ const newEndpointTmpl = ({
481
785
  }`
482
786
  });
483
787
  const isAllowedInputType = filterTypes(requestInputTypeDc);
788
+ const defaultOkResponseType = positiveResponseTypes?.[0]?.type ?? "unknown";
789
+ const contractsVarName = hasZodContracts ? `${_.camelCase(route.routeName.usage)}Contracts` : null;
790
+ const swaggerSchema = configuration.config?.swaggerSchema ?? configuration?.swaggerSchema;
791
+ const componentsSchemas = swaggerSchema?.components?.schemas;
792
+ let operationFromSpec = null;
793
+ const pathKeyForSpec = path2?.startsWith("/") ? path2 : `/${path2 || ""}`;
794
+ const methodKey = method?.toLowerCase?.() ?? method;
795
+ if (pathKeyForSpec && methodKey && swaggerSchema?.paths?.[pathKeyForSpec]) {
796
+ operationFromSpec = swaggerSchema.paths[pathKeyForSpec][methodKey] ?? null;
797
+ }
798
+ if (!operationFromSpec && swaggerSchema?.paths && raw?.operationId) {
799
+ for (const pathItem of Object.values(swaggerSchema.paths)) {
800
+ const op = pathItem?.[methodKey];
801
+ if (op?.operationId === raw.operationId) {
802
+ operationFromSpec = op;
803
+ break;
804
+ }
805
+ }
806
+ }
807
+ let responseSchemaKey = getResponseSchemaKeyFromOperation(
808
+ operationFromSpec ?? raw
809
+ );
810
+ if (!responseSchemaKey && componentsSchemas && configuration.modelTypes) {
811
+ const aliasType = configuration.modelTypes.find(
812
+ (m) => m.name === defaultOkResponseType
813
+ );
814
+ if (aliasType?.typeIdentifier === "type" && typeof aliasType.content === "string" && /^[A-Za-z0-9_]+$/.test(aliasType.content.trim())) {
815
+ const resolved = typeNameToSchemaKey(aliasType.content.trim(), "DC");
816
+ if (resolved in componentsSchemas) responseSchemaKey = resolved;
817
+ }
818
+ }
819
+ if (!responseSchemaKey && componentsSchemas) {
820
+ const match = defaultOkResponseType.match(/^Get(.+)DataDC$/);
821
+ if (match) {
822
+ const candidate = match[1];
823
+ if (candidate in componentsSchemas) responseSchemaKey = candidate;
824
+ }
825
+ }
826
+ const contractsCode = hasZodContracts && contractsVarName ? buildEndpointZodContractsCode({
827
+ routeNameUsage: route.routeName.usage,
828
+ inputParams,
829
+ responseDataTypeName: defaultOkResponseType,
830
+ contractsVarName,
831
+ utils,
832
+ componentsSchemas: componentsSchemas ?? void 0,
833
+ typeSuffix: "DC",
834
+ responseSchemaKey: responseSchemaKey ?? void 0,
835
+ useExternalZodSchemas: Boolean(relativePathZodSchemas)
836
+ }) : null;
837
+ const contractsLine = contractsVarName != null ? `contracts: ${contractsVarName},` : "";
838
+ const validateContractsLine = (() => {
839
+ if (validateOpt === void 0) return "";
840
+ if (typeof validateOpt === "string")
841
+ return `validateContracts: ${validateOpt},`;
842
+ if (typeof validateOpt === "boolean")
843
+ return `validateContracts: ${validateOpt},`;
844
+ if (validateOptObj !== null) {
845
+ const parts = [];
846
+ if (validateOptObj.params !== void 0)
847
+ parts.push(
848
+ `params: ${typeof validateOptObj.params === "string" ? validateOptObj.params : validateOptObj.params}`
849
+ );
850
+ if (validateOptObj.data !== void 0)
851
+ parts.push(
852
+ `data: ${typeof validateOptObj.data === "string" ? validateOptObj.data : validateOptObj.data}`
853
+ );
854
+ return parts.length > 0 ? `validateContracts: { ${parts.join(", ")} },` : "";
855
+ }
856
+ return "";
857
+ })();
858
+ const throwContractsLine = (() => {
859
+ if (throwOpt === void 0) return "";
860
+ if (typeof throwOpt === "string") return `throwContracts: ${throwOpt},`;
861
+ if (typeof throwOpt === "boolean") return `throwContracts: ${throwOpt},`;
862
+ if (throwOptObj !== null) {
863
+ const parts = [];
864
+ if (throwOptObj.params !== void 0)
865
+ parts.push(`params: ${throwOptObj.params}`);
866
+ if (throwOptObj.data !== void 0)
867
+ parts.push(`data: ${throwOptObj.data}`);
868
+ return parts.length > 0 ? `throwContracts: { ${parts.join(", ")} },` : "";
869
+ }
870
+ return "";
871
+ })();
484
872
  return {
485
873
  reservedDataContractNames,
486
874
  localModelTypes: isAllowedInputType ? [requestInputTypeDc] : [],
875
+ contractsCode: contractsCode ?? void 0,
876
+ contractsVarName: contractsVarName ?? void 0,
487
877
  content: `
488
878
  new ${importFileParams.endpoint.exportName}<
489
879
  ${getHttpRequestGenerics()},
@@ -516,6 +906,9 @@ new ${importFileParams.endpoint.exportName}<
516
906
  ${groupName ? `group: ${metaInfo ? `Group.${formatGroupNameEnumKey(groupName, utils)}` : `"${groupName}"`},` : ""}
517
907
  ${metaInfo?.namespace ? `namespace,` : ""}
518
908
  meta: ${requestInfoMeta?.tmplData ?? "{} as any"},
909
+ ${contractsLine}
910
+ ${validateContractsLine}
911
+ ${throwContractsLine}
519
912
  },
520
913
  ${importFileParams.queryClient.exportName},
521
914
  ${importFileParams.httpClient.exportName},
@@ -533,7 +926,8 @@ const allEndpointPerFileTmpl = async (params) => {
533
926
  utils,
534
927
  relativePathDataContracts,
535
928
  groupName,
536
- metaInfo
929
+ metaInfo,
930
+ relativePathZodSchemas
537
931
  } = params;
538
932
  const { _ } = utils;
539
933
  const dataContractNamesInThisFile = [];
@@ -545,7 +939,11 @@ const allEndpointPerFileTmpl = async (params) => {
545
939
  const newEndpointTemplates = routes.map((route) => {
546
940
  const newEndpointTemplateData = newEndpointTmpl({
547
941
  ...params,
548
- route
942
+ route,
943
+ groupName,
944
+ metaInfo,
945
+ zodContracts: codegenParams.zodContracts,
946
+ relativePathZodSchemas: relativePathZodSchemas ?? void 0
549
947
  });
550
948
  const { reservedDataContractNames } = newEndpointTemplateData;
551
949
  reservedDataContractNames.forEach((reservedDataContractName) => {
@@ -556,12 +954,27 @@ const allEndpointPerFileTmpl = async (params) => {
556
954
  return { ...newEndpointTemplateData, route };
557
955
  });
558
956
  const extraImportLines = [];
957
+ const hasAnyZodContracts = newEndpointTemplates.some(
958
+ (t) => t.contractsCode != null
959
+ );
960
+ const allZodSchemaImportNames = /* @__PURE__ */ new Set();
961
+ newEndpointTemplates.forEach((t) => {
962
+ const c = t.contractsCode;
963
+ if (c != null && typeof c === "object" && c.zodSchemaImportNames?.length) {
964
+ for (const n of c.zodSchemaImportNames) {
965
+ allZodSchemaImportNames.add(n);
966
+ }
967
+ }
968
+ });
969
+ const zodImportLine = hasAnyZodContracts ? 'import * as z from "zod";' : "";
970
+ const zodSchemasImportLine = allZodSchemaImportNames.size && relativePathZodSchemas ? `import { ${[...allZodSchemaImportNames].sort().join(", ")} } from "${relativePathZodSchemas}";` : "";
559
971
  const endpointTemplates = await Promise.all(
560
972
  newEndpointTemplates.map(
561
973
  async ({
562
974
  content: requestInfoInstanceContent,
563
975
  localModelTypes,
564
- route
976
+ route,
977
+ contractsCode
565
978
  }) => {
566
979
  const requestInfoMeta = codegenParams.getEndpointMeta?.(route, utils);
567
980
  if (requestInfoMeta?.typeNameImportPath && requestInfoMeta.typeName) {
@@ -569,6 +982,12 @@ const allEndpointPerFileTmpl = async (params) => {
569
982
  `import { ${requestInfoMeta.typeName} } from "${requestInfoMeta.typeNameImportPath}";`
570
983
  );
571
984
  }
985
+ const contractsResult = contractsCode != null && typeof contractsCode === "object" ? contractsCode : null;
986
+ const contractsBlock = contractsResult != null ? `
987
+
988
+ ${contractsResult.content}
989
+
990
+ ` : "";
572
991
  return `
573
992
  ${(await Promise.all(
574
993
  localModelTypes.map(async (modelType) => {
@@ -580,7 +999,7 @@ const allEndpointPerFileTmpl = async (params) => {
580
999
  return contractType;
581
1000
  })
582
1001
  )).filter(Boolean).join("\n\n")}
583
-
1002
+ ${contractsBlock}
584
1003
  ${endpointJSDocTmpl({
585
1004
  ...params,
586
1005
  route
@@ -607,6 +1026,7 @@ const allEndpointPerFileTmpl = async (params) => {
607
1026
  import { ${importFileParams.httpClient.exportName} } from "${importFileParams.httpClient.path}";
608
1027
  import { ${importFileParams.queryClient.exportName} } from "${importFileParams.queryClient.path}";
609
1028
  ${extraImportLines.join("\n")}
1029
+ ${[zodImportLine, zodSchemasImportLine].filter(Boolean).join("\n")}
610
1030
  ${dataContractImportToken}
611
1031
 
612
1032
  ${(await Promise.all(
@@ -646,11 +1066,12 @@ const allEndpointPerFileTmpl = async (params) => {
646
1066
  const allExportsTmpl = async ({
647
1067
  collectedExportFiles,
648
1068
  metaInfo,
649
- formatTSContent
1069
+ formatTSContent,
1070
+ exportSchemas
650
1071
  }) => {
651
1072
  return await formatTSContent(`${LINTERS_IGNORE}
652
1073
  export * from './data-contracts';
653
- ${collectedExportFiles.map((fileName) => `export * from './${fileName}';`).join("\n")}
1074
+ ${exportSchemas ? " export * from './schemas';\n " : ""}${collectedExportFiles.map((fileName) => `export * from './${fileName}';`).join("\n")}
654
1075
  ${metaInfo ? 'export * from "./meta-info";' : ""}
655
1076
  `);
656
1077
  };
@@ -689,18 +1110,22 @@ const endpointPerFileTmpl = async (params) => {
689
1110
  utils,
690
1111
  relativePathDataContracts,
691
1112
  groupName,
692
- metaInfo
1113
+ metaInfo,
1114
+ relativePathZodSchemas
693
1115
  } = params;
694
1116
  const { _ } = utils;
695
1117
  const {
696
1118
  content: requestInfoInstanceContent,
697
1119
  reservedDataContractNames,
698
- localModelTypes
1120
+ localModelTypes,
1121
+ contractsCode
699
1122
  } = newEndpointTmpl({
700
1123
  ...params,
701
1124
  route,
702
1125
  groupName,
703
- metaInfo
1126
+ metaInfo,
1127
+ zodContracts: codegenParams.zodContracts,
1128
+ relativePathZodSchemas: relativePathZodSchemas ?? void 0
704
1129
  });
705
1130
  const dataContactNames = new Set(
706
1131
  Object.keys(
@@ -726,6 +1151,15 @@ const endpointPerFileTmpl = async (params) => {
726
1151
  );
727
1152
  }
728
1153
  const dataContractImportToken = "/*__DATA_CONTRACT_IMPORTS__*/";
1154
+ const contractsResult = contractsCode != null && typeof contractsCode === "object" ? contractsCode : null;
1155
+ const zodImportLine = contractsResult != null ? 'import * as z from "zod";' : "";
1156
+ const zodSchemasImportLine = contractsResult?.zodSchemaImportNames?.length && relativePathZodSchemas ? `import { ${contractsResult.zodSchemaImportNames.join(", ")} } from "${relativePathZodSchemas}";` : "";
1157
+ const contractsBlock = contractsResult != null ? `
1158
+
1159
+ ${contractsResult.content}
1160
+
1161
+ ` : "";
1162
+ const zodImportsBlock = [zodImportLine, zodSchemasImportLine].filter(Boolean).join("\n");
729
1163
  const contentWithImportToken = await formatTSContent(`${LINTERS_IGNORE}
730
1164
  import {
731
1165
  RequestParams,
@@ -736,6 +1170,7 @@ const endpointPerFileTmpl = async (params) => {
736
1170
  import { ${importFileParams.httpClient.exportName} } from "${importFileParams.httpClient.path}";
737
1171
  import { ${importFileParams.queryClient.exportName} } from "${importFileParams.queryClient.path}";
738
1172
  ${extraImportLines.join("\n")}
1173
+ ${zodImportsBlock}
739
1174
  ${dataContractImportToken}
740
1175
 
741
1176
  ${(await Promise.all(
@@ -765,7 +1200,7 @@ const endpointPerFileTmpl = async (params) => {
765
1200
  return contractType;
766
1201
  })
767
1202
  )).filter(Boolean).join("\n\n")}
768
-
1203
+ ${contractsBlock}
769
1204
  ${endpointJSDocTmpl({
770
1205
  ...params,
771
1206
  route
@@ -813,7 +1248,7 @@ const unpackFilterOption = (option, argsToString, defaultReturnValue = true) =>
813
1248
  const removeUnusedTypesItteration = async ({
814
1249
  directory,
815
1250
  keepTypes
816
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
1251
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: iterative AST traversal
817
1252
  }) => {
818
1253
  const project = new Project();
819
1254
  project.addSourceFilesAtPaths([
@@ -1131,6 +1566,8 @@ const generateApi = async (params) => {
1131
1566
  filterTypes
1132
1567
  };
1133
1568
  const reservedDataContractNamesMap = /* @__PURE__ */ new Map();
1569
+ const componentsSchemasForZod = generated.configuration.config?.swaggerSchema?.components?.schemas ?? generated.configuration.swaggerSchema?.components?.schemas;
1570
+ const hasZodSchemasFile = (params.zodContracts === true || typeof params.zodContracts === "object" && params.zodContracts != null) && componentsSchemasForZod && typeof componentsSchemasForZod === "object" && Object.keys(componentsSchemasForZod).length > 0;
1134
1571
  const collectedExportFilesFromIndexFile = [];
1135
1572
  const groupsMap = /* @__PURE__ */ new Map();
1136
1573
  const nonEmptyGroups = /* @__PURE__ */ new Set();
@@ -1152,7 +1589,8 @@ const generateApi = async (params) => {
1152
1589
  metaInfo: params.noMetaInfo ? null : {
1153
1590
  groupNames: [],
1154
1591
  namespace
1155
- }
1592
+ },
1593
+ relativePathZodSchemas: hasZodSchemasFile ? "../schemas" : null
1156
1594
  });
1157
1595
  if (Array.isArray(route.raw.tags)) {
1158
1596
  route.raw.tags.forEach((tag) => {
@@ -1196,7 +1634,8 @@ const generateApi = async (params) => {
1196
1634
  metaInfo: params.noMetaInfo ? null : {
1197
1635
  namespace,
1198
1636
  groupNames: []
1199
- }
1637
+ },
1638
+ relativePathZodSchemas: hasZodSchemasFile ? "./schemas" : null
1200
1639
  });
1201
1640
  reservedDataContractNames.forEach((name) => {
1202
1641
  reservedDataContractNamesMap.set(
@@ -1392,6 +1831,22 @@ export * as ${exportGroupName} from './endpoints';
1392
1831
  withPrefix: false,
1393
1832
  content: dataContractsContent
1394
1833
  });
1834
+ if (hasZodSchemasFile && componentsSchemasForZod) {
1835
+ const schemasTsContent = buildCentralZodSchemasFile({
1836
+ componentsSchemas: componentsSchemasForZod,
1837
+ utils
1838
+ });
1839
+ const formattedSchemasContent = await generated.formatTSContent(
1840
+ `${LINTERS_IGNORE}
1841
+ ${schemasTsContent}`
1842
+ );
1843
+ codegenFs.createFile({
1844
+ path: paths.outputDir,
1845
+ fileName: "schemas.ts",
1846
+ withPrefix: false,
1847
+ content: formattedSchemasContent
1848
+ });
1849
+ }
1395
1850
  if (metaInfo) {
1396
1851
  codegenFs.createFile({
1397
1852
  path: paths.outputDir,
@@ -1411,7 +1866,8 @@ export * as ${exportGroupName} from './endpoints';
1411
1866
  content: await allExportsTmpl({
1412
1867
  ...baseTmplParams,
1413
1868
  collectedExportFiles: collectedExportFilesFromIndexFile,
1414
- metaInfo
1869
+ metaInfo,
1870
+ exportSchemas: hasZodSchemasFile
1415
1871
  })
1416
1872
  });
1417
1873
  if (shouldGenerateBarrelFiles) {
@@ -1433,7 +1889,8 @@ export * as ${namespace} from './__exports';
1433
1889
  content: await allExportsTmpl({
1434
1890
  ...baseTmplParams,
1435
1891
  collectedExportFiles: collectedExportFilesFromIndexFile,
1436
- metaInfo
1892
+ metaInfo,
1893
+ exportSchemas: hasZodSchemasFile
1437
1894
  })
1438
1895
  });
1439
1896
  }