orval 8.13.0 → 8.14.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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { c as description, i as startWatcher, l as name, n as loadConfigFile, r as generateSpec, s as normalizeOptions, t as findConfigFile, u as version } from "../config-Cyybmy9c.mjs";
2
+ import { c as description, i as startWatcher, l as name, n as loadConfigFile, r as generateSpec, s as normalizeOptions, t as findConfigFile, u as version } from "../config-yIkhToq8.mjs";
3
3
  import path from "node:path";
4
4
  import { Option, program } from "@commander-js/extra-typings";
5
5
  import { ErrorWithTag, OutputClient, OutputMode, SupportedFormatter, getWarningCount, isString, log, logError, resetWarnings, setVerbose, startMessage } from "@orval/core";
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SupportedFormatter, asyncReduce, collectReferencedComponents, conventionName, createSuccessMessage, dynamicImport, fixCrossDirectoryImports, fixRegularSchemaImports, generateComponentDefinition, generateDependencyImports, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getBaseUrlRuntimeImports, getFileInfo, getFullRoute, getImportExtension, getMockFileExtensionByTypeName, getRoute, isBoolean, isFunction, isNullish, isObject, isReference, isString, isUrl, jsDoc, log, logError, logVerbose, logWarning, pascal, removeFilesAndEmptyFolders, resolveInstalledVersions, resolveRef, splitSchemasByType, upath, writeGeneratedFile, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode } from "@orval/core";
2
+ import { FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SupportedFormatter, asyncReduce, collectReferencedComponents, conventionName, createSuccessMessage, dynamicImport, fixCrossDirectoryImports, fixRegularSchemaImports, generateComponentDefinition, generateDependencyImports, generateMutator, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getBaseUrlRuntimeImports, getFileInfo, getFullRoute, getImportExtension, getMockFileExtensionByTypeName, getRefInfo, getRoute, isBoolean, isComponentRef, isFunction, isNullish, isObject, isReference, isString, isUrl, jsDoc, log, logError, logVerbose, logWarning, pascal, removeFilesAndEmptyFolders, resolveInstalledVersions, resolveRef, resolveValue, splitSchemasByType, upath, writeGeneratedFile, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode } from "@orval/core";
3
3
  import { bundle } from "@scalar/json-magic/bundle";
4
4
  import { fetchUrls, parseJson, parseYaml, readFiles } from "@scalar/json-magic/bundle/plugins/node";
5
5
  import { upgrade, validate } from "@scalar/openapi-parser";
@@ -8,6 +8,7 @@ import * as mock from "@orval/mock";
8
8
  import { generateFakerForSchemas, generateMockImports, getDefaultMockOptionsForType } from "@orval/mock";
9
9
  import angular from "@orval/angular";
10
10
  import axios from "@orval/axios";
11
+ import effect from "@orval/effect";
11
12
  import fetchClient from "@orval/fetch";
12
13
  import hono from "@orval/hono";
13
14
  import mcp from "@orval/mcp";
@@ -28,7 +29,7 @@ import { createJiti } from "jiti";
28
29
  //#region package.json
29
30
  var name = "orval";
30
31
  var description = "A swagger client generator for typescript";
31
- var version = "8.13.0";
32
+ var version = "8.14.0";
32
33
  //#endregion
33
34
  //#region src/client.ts
34
35
  const DEFAULT_CLIENT = OutputClient.AXIOS;
@@ -61,6 +62,7 @@ const getGeneratorClient = (outputClient, output) => {
61
62
  })(),
62
63
  swr: swr()(),
63
64
  zod: zod()(),
65
+ effect: effect()(),
64
66
  hono: hono()(),
65
67
  fetch: fetchClient()(),
66
68
  mcp: mcp()()
@@ -854,6 +856,26 @@ function normalizeSchemasOption(schemas, workspace) {
854
856
  type: schemas.type
855
857
  };
856
858
  }
859
+ function normalizeEffectOptions(effect) {
860
+ return {
861
+ strict: {
862
+ param: effect?.strict?.param ?? false,
863
+ query: effect?.strict?.query ?? false,
864
+ header: effect?.strict?.header ?? false,
865
+ body: effect?.strict?.body ?? false,
866
+ response: effect?.strict?.response ?? false
867
+ },
868
+ generate: {
869
+ param: effect?.generate?.param ?? true,
870
+ query: effect?.generate?.query ?? true,
871
+ header: effect?.generate?.header ?? true,
872
+ body: effect?.generate?.body ?? true,
873
+ response: effect?.generate?.response ?? true
874
+ },
875
+ generateEachHttpStatus: effect?.generateEachHttpStatus ?? false,
876
+ useBrandedTypes: effect?.useBrandedTypes ?? false
877
+ };
878
+ }
857
879
  async function normalizeOptions(optionsExport, workspace = process.cwd(), globalOptions = {}) {
858
880
  const options = await (isFunction(optionsExport) ? optionsExport() : optionsExport);
859
881
  if (!options.input) throw new Error(styleText("red", `Config requires an input.`));
@@ -908,6 +930,7 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
908
930
  shouldExportMutatorHooks: true,
909
931
  shouldExportHttpClient: true,
910
932
  shouldExportQueryKey: true,
933
+ shouldFilterQueryKey: false,
911
934
  shouldSplitQueryKey: false,
912
935
  ...normalizeQueryOptions(outputOptions.override?.query, workspace)
913
936
  };
@@ -1013,12 +1036,15 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
1013
1036
  ...outputOptions.override?.zod?.preprocess?.body ? { body: normalizeMutator(workspace, outputOptions.override.zod.preprocess.body) } : {},
1014
1037
  ...outputOptions.override?.zod?.preprocess?.response ? { response: normalizeMutator(workspace, outputOptions.override.zod.preprocess.response) } : {}
1015
1038
  },
1039
+ ...outputOptions.override?.zod?.params ? { params: normalizeMutator(workspace, outputOptions.override.zod.params) } : {},
1016
1040
  generateEachHttpStatus: outputOptions.override?.zod?.generateEachHttpStatus ?? false,
1017
1041
  useBrandedTypes: outputOptions.override?.zod?.useBrandedTypes ?? false,
1018
1042
  generateReusableSchemas: outputOptions.override?.zod?.generateReusableSchemas ?? false,
1043
+ generateMeta: outputOptions.override?.zod?.generateMeta ?? false,
1019
1044
  dateTimeOptions: outputOptions.override?.zod?.dateTimeOptions ?? { offset: true },
1020
1045
  timeOptions: outputOptions.override?.zod?.timeOptions ?? {}
1021
1046
  },
1047
+ effect: normalizeEffectOptions(outputOptions.override?.effect),
1022
1048
  swr: {
1023
1049
  generateErrorTypes: false,
1024
1050
  ...outputOptions.override?.swr
@@ -1144,7 +1170,7 @@ function normalizePath(path$1, workspace) {
1144
1170
  return path.resolve(workspace, path$1);
1145
1171
  }
1146
1172
  function normalizeOperationsAndTags(operationsOrTags, workspace, global) {
1147
- return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, paramsFilter, query, angular, zod, ...rest }]) => {
1173
+ return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, paramsFilter, query, angular, zod, effect, ...rest }]) => {
1148
1174
  return [key, {
1149
1175
  ...rest,
1150
1176
  ...angular ? { angular: {
@@ -1183,12 +1209,15 @@ function normalizeOperationsAndTags(operationsOrTags, workspace, global) {
1183
1209
  ...zod.preprocess?.body ? { body: normalizeMutator(workspace, zod.preprocess.body) } : {},
1184
1210
  ...zod.preprocess?.response ? { response: normalizeMutator(workspace, zod.preprocess.response) } : {}
1185
1211
  },
1212
+ ...zod.params ? { params: normalizeMutator(workspace, zod.params) } : {},
1186
1213
  generateEachHttpStatus: zod.generateEachHttpStatus ?? false,
1187
1214
  useBrandedTypes: zod.useBrandedTypes ?? false,
1188
1215
  generateReusableSchemas: zod.generateReusableSchemas ?? false,
1216
+ generateMeta: zod.generateMeta ?? false,
1189
1217
  dateTimeOptions: zod.dateTimeOptions ?? { offset: true },
1190
1218
  timeOptions: zod.timeOptions ?? {}
1191
1219
  } } : {},
1220
+ ...effect ? { effect: normalizeEffectOptions(effect) } : {},
1192
1221
  ...transformer ? { transformer: normalizePath(transformer, workspace) } : {},
1193
1222
  ...mutator ? { mutator: normalizeMutator(workspace, mutator) } : {},
1194
1223
  ...formData === void 0 ? {} : { formData: createFormData(workspace, formData) },
@@ -1258,6 +1287,8 @@ function normalizeQueryOptions(queryOptions = {}, outputWorkspace, globalOptions
1258
1287
  ...queryOptions.mutationOptions ? { mutationOptions: normalizeMutator(outputWorkspace, queryOptions.mutationOptions) } : {},
1259
1288
  ...isNullish(globalOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: globalOptions.shouldExportQueryKey },
1260
1289
  ...isNullish(queryOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: queryOptions.shouldExportQueryKey },
1290
+ ...isNullish(globalOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: globalOptions.shouldFilterQueryKey },
1291
+ ...isNullish(queryOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: queryOptions.shouldFilterQueryKey },
1261
1292
  ...isNullish(globalOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: globalOptions.shouldExportHttpClient },
1262
1293
  ...isNullish(queryOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: queryOptions.shouldExportHttpClient },
1263
1294
  ...isNullish(globalOptions.shouldExportMutatorHooks) ? {} : { shouldExportMutatorHooks: globalOptions.shouldExportMutatorHooks },
@@ -1325,30 +1356,29 @@ async function startWatcher(watchOptions, watchFn, defaultTarget = ".") {
1325
1356
  }
1326
1357
  //#endregion
1327
1358
  //#region src/reusable-schemas.ts
1328
- const lastRefSegment = (ref) => {
1329
- const raw = ref.split("/").pop() ?? "";
1330
- return decodeURIComponent(raw).replaceAll("~1", "/").replaceAll("~0", "~");
1331
- };
1332
1359
  /**
1333
- * Convert a single `#/components/schemas/X` ref into the export name we will
1334
- * emit for it: the last `$ref` segment with `namingConvention` applied.
1360
+ * Resolve the export identifier for a `#/components/schemas/X` ref. We reuse
1361
+ * `@orval/core`'s `getRefInfo(...).name` (`pascal` + sanitize + component
1362
+ * suffix) so reusable zod schema exports match the operation wrappers and the
1363
+ * TS model types exactly. `namingConvention` deliberately does NOT influence
1364
+ * the identifier — it governs file names only, consistent with the rest of
1365
+ * orval. The same call powers the generator's `namedRef` emission, so the
1366
+ * definition name and every reference stay in sync.
1335
1367
  */
1336
- const resolveSchemaName = (ref, namingConvention) => conventionName(lastRefSegment(ref), namingConvention);
1337
- const JS_IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
1368
+ const resolveSchemaName = (ref, context) => getRefInfo(ref, context).name;
1338
1369
  /**
1339
- * Resolve names for a set of refs, throwing on conflicts or on names that
1340
- * aren't valid JS identifiers (e.g. `kebab-case` produces dashes). The mapping
1341
- * is the single source of truth for cross-schema references — the generator,
1342
- * the orchestrator's graph, and the sentinel rewriter all consult it.
1370
+ * Resolve names for a set of refs, throwing on conflicts (two distinct refs
1371
+ * collapsing to the same identifier). The mapping is the single source of
1372
+ * truth for cross-schema references — the generator, the orchestrator's graph,
1373
+ * and the sentinel rewriter all consult it.
1343
1374
  */
1344
- const resolveSchemaNames = (refs, namingConvention) => {
1375
+ const resolveSchemaNames = (refs, context) => {
1345
1376
  const resolved = /* @__PURE__ */ new Map();
1346
1377
  const reverse = /* @__PURE__ */ new Map();
1347
1378
  for (const ref of refs) {
1348
- const name = resolveSchemaName(ref, namingConvention);
1349
- if (!JS_IDENTIFIER_PATTERN.test(name)) throw new Error(`[orval/zod] generateReusableSchemas: ref ${ref} converts to "${name}" under namingConvention=${namingConvention}, which is not a valid JS identifier. Use camelCase, PascalCase, or snake_case for the project's namingConvention when this flag is enabled.`);
1379
+ const name = resolveSchemaName(ref, context);
1350
1380
  const previous = reverse.get(name);
1351
- if (previous !== void 0 && previous !== ref) throw new Error(`[orval/zod] generateReusableSchemas: refs ${previous} and ${ref} both convert to "${name}" under namingConvention=${namingConvention}. Rename one in the OpenAPI source or change the convention.`);
1381
+ if (previous !== void 0 && previous !== ref) throw new Error(`[orval/zod] generateReusableSchemas: refs ${previous} and ${ref} both resolve to the export name "${name}". Rename one in the OpenAPI source.`);
1352
1382
  resolved.set(ref, name);
1353
1383
  reverse.set(name, ref);
1354
1384
  }
@@ -1366,7 +1396,7 @@ const generateReusableSchemaSet = (refs, context, options) => {
1366
1396
  const nameToRef = /* @__PURE__ */ new Map();
1367
1397
  for (const schemaName of Object.keys(componentSchemas)) {
1368
1398
  const ref = `#/components/schemas/${schemaName}`;
1369
- nameToRef.set(resolveSchemaName(ref, context.output.namingConvention), ref);
1399
+ nameToRef.set(resolveSchemaName(ref, context), ref);
1370
1400
  }
1371
1401
  const queue = [...refs];
1372
1402
  const seen = new Set(refs);
@@ -1374,11 +1404,17 @@ const generateReusableSchemaSet = (refs, context, options) => {
1374
1404
  for (const ref of queue) {
1375
1405
  const schema = componentSchemas[ref.slice(21)];
1376
1406
  if (!schema) continue;
1377
- const name = resolveSchemaName(ref, context.output.namingConvention);
1407
+ const name = resolveSchemaName(ref, context);
1378
1408
  const parsed = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(schema, context, name, options.strict, options.isZodV4, {
1379
1409
  required: true,
1380
- useReusableSchemas: true
1381
- }), context, options.coerce ?? false, options.strict, options.isZodV4);
1410
+ useReusableSchemas: true,
1411
+ emitMeta: options.generateMeta
1412
+ }), context, options.coerce ?? false, options.strict, options.isZodV4, void 0, options.paramsMutator ? {
1413
+ mutator: options.paramsMutator,
1414
+ operationId: "",
1415
+ location: "schema",
1416
+ schemaName: name
1417
+ } : void 0);
1382
1418
  entries.push({
1383
1419
  ref,
1384
1420
  name,
@@ -1445,11 +1481,27 @@ const tarjan = (graph) => {
1445
1481
  };
1446
1482
  const SENTINEL_PATTERN = /__REF_([A-Za-z_$][A-Za-z0-9_$]*)__/g;
1447
1483
  /**
1484
+ * Replace every `__REF_<name>__` sentinel with the bare identifier. Use this
1485
+ * for schemas that sit at the top of the dependency graph (operation params,
1486
+ * bodies, responses) — they can never participate in a cycle with the
1487
+ * component schemas they reference, so every ref is a direct (non-lazy) one.
1488
+ */
1489
+ const rewriteSentinelsToDirect = (zod) => zod.replaceAll(SENTINEL_PATTERN, (_match, refName) => refName);
1490
+ /**
1448
1491
  * Replace every `__REF_<name>__` sentinel with either the bare identifier or
1449
1492
  * `zod.lazy(() => <name>)` based on whether the edge closes a cycle, then
1450
1493
  * reorder entries so that every non-lazy reference is emitted AFTER its
1451
1494
  * target. This avoids TDZ errors at module load.
1452
1495
  *
1496
+ * Entries that sit in a cycle (SCC of size > 1, or a self-loop) are flagged
1497
+ * `isRecursive`. Their generated `const` reads its own binding inside the
1498
+ * initializer (through the `zod.lazy` wrapper), which TypeScript rejects with
1499
+ * TS7022 ("'X' implicitly has type 'any' ... referenced directly or indirectly
1500
+ * in its own initializer") unless the `const` carries an explicit type
1501
+ * annotation. The writer (`write-zod-specs`) supplies that annotation —
1502
+ * `const X: zod.ZodType<X>` — backed by a generated TS type, which both
1503
+ * silences TS7022 and preserves full `z.infer` typing through the recursion.
1504
+ *
1453
1505
  * Both the lazy classification and the emit order come from a single Tarjan
1454
1506
  * run, guaranteeing they agree: a non-lazy edge u→v means v is visited (and
1455
1507
  * popped) before u in DFS, so v appears earlier in the SCC array → emitted
@@ -1459,13 +1511,17 @@ const rewriteReusableSchemas = (entries) => {
1459
1511
  const graph = new Map(entries.map((e) => [e.name, new Set(e.usedRefs)]));
1460
1512
  for (const e of entries) for (const ref of e.usedRefs) if (!graph.has(ref)) graph.set(ref, /* @__PURE__ */ new Set());
1461
1513
  const { sccs, lazyEdges } = tarjan(graph);
1514
+ const recursiveNames = /* @__PURE__ */ new Set();
1515
+ for (const scc of sccs) if (scc.length > 1) for (const name of scc) recursiveNames.add(name);
1516
+ else if (lazyEdges.has(edgeKey(scc[0], scc[0]))) recursiveNames.add(scc[0]);
1462
1517
  const rewritten = new Map(entries.map((entry) => {
1463
1518
  const newZod = entry.zod.replaceAll(SENTINEL_PATTERN, (_match, refName) => {
1464
1519
  return lazyEdges.has(edgeKey(entry.name, refName)) ? `zod.lazy(() => ${refName})` : refName;
1465
1520
  });
1466
1521
  return [entry.name, {
1467
1522
  ...entry,
1468
- zod: newZod
1523
+ zod: newZod,
1524
+ isRecursive: recursiveNames.has(entry.name)
1469
1525
  }];
1470
1526
  }));
1471
1527
  const out = [];
@@ -1477,18 +1533,99 @@ const rewriteReusableSchemas = (entries) => {
1477
1533
  };
1478
1534
  //#endregion
1479
1535
  //#region src/write-zod-specs.ts
1480
- function generateZodSchemaFileContent(header, schemas) {
1481
- return `${header}import { z as zod } from 'zod';
1482
-
1483
- ${schemas.map(({ schemaName, consts, zodExpression }) => {
1536
+ /**
1537
+ * Render the `import { ... } from '...'` line for a resolved
1538
+ * `GeneratorMutator`. Mirrors the format produced by
1539
+ * `generateMutatorImports` in `@orval/core` but inlined to avoid pulling in
1540
+ * its full surface area for a single statement.
1541
+ */
1542
+ function buildMutatorImportStatement(mutator) {
1543
+ return `import ${mutator.default ? mutator.name : `{ ${mutator.name} }`} from '${mutator.path}';`;
1544
+ }
1545
+ /**
1546
+ * Whole-word substring check for a resolved mutator alias inside generated
1547
+ * code. Plain `String.includes` would false-positive when the user names the
1548
+ * mutator something like `min` against `.min(1)`.
1549
+ */
1550
+ function bodyReferencesMutator(body, mutator) {
1551
+ return new RegExp(String.raw`\b${mutator.name}\b`).test(body);
1552
+ }
1553
+ function generateZodSchemaFileContent(header, schemas, includeZodImport = true) {
1554
+ const refImports = [...new Set(schemas.flatMap((s) => s.importStatements ?? []))].toSorted();
1555
+ const importBlock = [...includeZodImport ? [`import { z as zod } from 'zod';`] : [], ...refImports].join("\n");
1556
+ const schemaContent = schemas.map(({ schemaName, consts, zodExpression }) => {
1484
1557
  return `${consts ? `${consts}\n` : ""}export const ${schemaName} = ${zodExpression}
1485
1558
 
1486
1559
  export type ${schemaName} = zod.input<typeof ${schemaName}>;
1487
1560
  export type ${schemaName}Output = zod.output<typeof ${schemaName}>;`;
1488
- }).join("\n\n")}
1489
- `;
1561
+ }).join("\n\n");
1562
+ return `${header}${importBlock ? `${importBlock}\n\n` : ""}${schemaContent}\n`;
1563
+ }
1564
+ function renderReusableSchemaEntry(entry, context) {
1565
+ const consts = entry.consts ? `${entry.consts}\n\n` : "";
1566
+ if (entry.isRecursive) {
1567
+ const rawName = isComponentRef(entry.ref) ? getRefInfo(entry.ref, context).originalName : void 0;
1568
+ const schema = rawName ? context.spec.components?.schemas?.[rawName] : void 0;
1569
+ const resolved = schema ? resolveValue({
1570
+ schema,
1571
+ name: entry.name,
1572
+ context
1573
+ }) : void 0;
1574
+ const typeBody = resolved ? resolved.value : "unknown";
1575
+ const seen = /* @__PURE__ */ new Set();
1576
+ const extraImports = [];
1577
+ for (const imp of resolved?.imports ?? []) {
1578
+ if (!imp.name || imp.name === entry.name) continue;
1579
+ const bindingKey = imp.alias ?? imp.name;
1580
+ if (seen.has(bindingKey)) continue;
1581
+ seen.add(bindingKey);
1582
+ extraImports.push({
1583
+ name: imp.name,
1584
+ ...imp.alias ? { alias: imp.alias } : {}
1585
+ });
1586
+ }
1587
+ return {
1588
+ content: `${consts}export type ${entry.name} = ${typeBody};\n\nexport const ${entry.name}: zod.ZodType<${entry.name}> = ${entry.zod};\n\nexport type ${entry.name}Output = zod.output<typeof ${entry.name}>;`,
1589
+ extraImports
1590
+ };
1591
+ }
1592
+ return {
1593
+ content: `${consts}export const ${entry.name} = ${entry.zod};\n\nexport type ${entry.name} = zod.input<typeof ${entry.name}>;\nexport type ${entry.name}Output = zod.output<typeof ${entry.name}>;`,
1594
+ extraImports: []
1595
+ };
1490
1596
  }
1491
1597
  const isValidSchemaIdentifier = (name) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
1598
+ /**
1599
+ * Build the sibling-file `import { … } from './…'` block for one reusable
1600
+ * schema file. Two sources feed in:
1601
+ * - `usedRefs` — names from the zod runtime expression. Sourced from the
1602
+ * sentinel parser, so always unaliased.
1603
+ * - `extraImports` — names the recursive TS body needs that the zod runtime
1604
+ * collapsed (`propertyNames` $refs, etc.). May carry `alias`.
1605
+ * Keyed by export name (`name`) so an aliased `extraImports` entry overrides
1606
+ * a bare `usedRefs` entry — the recursive TS body uses the local binding, so
1607
+ * the aliased form has to win for the file to compile. Self-refs and
1608
+ * non-component identifiers are filtered out.
1609
+ *
1610
+ * Exported for unit-test coverage of the alias-propagation path; no
1611
+ * `resolveValue` producer surfaces aliases here today, so the integration
1612
+ * tests can't exercise it.
1613
+ */
1614
+ function buildSiblingImports({ usedRefs, extraImports, entryName, componentNames, namingConvention, importExt }) {
1615
+ const importsByName = /* @__PURE__ */ new Map();
1616
+ for (const name of usedRefs) {
1617
+ if (name === entryName) continue;
1618
+ importsByName.set(name, { name });
1619
+ }
1620
+ for (const imp of extraImports) {
1621
+ if (imp.name === entryName || !componentNames.has(imp.name)) continue;
1622
+ importsByName.set(imp.name, imp);
1623
+ }
1624
+ return [...importsByName.values()].toSorted((a, b) => a.name.localeCompare(b.name)).map(({ name, alias }) => {
1625
+ const importedFile = conventionName(name, namingConvention);
1626
+ return `import { ${alias ? `${name} as ${alias}` : name} } from './${importedFile}${importExt}';`;
1627
+ }).join("\n");
1628
+ }
1492
1629
  const isPrimitiveSchemaName = (name) => [
1493
1630
  "string",
1494
1631
  "number",
@@ -1529,8 +1666,8 @@ async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNam
1529
1666
  const uniqueExports = [...new Set(allExports.split("\n"))].filter((line) => line.trim()).toSorted().join("\n");
1530
1667
  await fs.outputFile(indexPath, `${header}\n${uniqueExports}\n`);
1531
1668
  }
1532
- function generateZodSchemasInline(builder, output) {
1533
- if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output);
1669
+ function generateZodSchemasInline(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
1670
+ if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output, includeZodImport, paramsMutator, includeParamsImport);
1534
1671
  const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
1535
1672
  if (schemasWithOpenApiDef.length === 0) return "";
1536
1673
  const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
@@ -1545,7 +1682,10 @@ function generateZodSchemasInline(builder, output) {
1545
1682
  workspace: "",
1546
1683
  output
1547
1684
  };
1548
- const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, { required: true }), context, coerce, strict, isZodV4);
1685
+ const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
1686
+ required: true,
1687
+ emitMeta: output.override.zod.generateMeta
1688
+ }), context, coerce, strict, isZodV4);
1549
1689
  schemas.push({
1550
1690
  schemaName: name,
1551
1691
  consts: parsedZodDefinition.consts,
@@ -1553,11 +1693,9 @@ function generateZodSchemasInline(builder, output) {
1553
1693
  });
1554
1694
  }
1555
1695
  if (schemas.length === 0) return "";
1556
- return generateZodSchemaFileContent("", schemas);
1696
+ return generateZodSchemaFileContent("", schemas, includeZodImport);
1557
1697
  }
1558
- function generateZodSchemasInlineReusable(builder, output) {
1559
- const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
1560
- if (schemasWithOpenApiDef.length === 0) return "";
1698
+ function generateZodSchemasInlineReusable(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
1561
1699
  const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
1562
1700
  const strict = output.override.zod.strict.body;
1563
1701
  const coerce = output.override.zod.coerce.body;
@@ -1567,19 +1705,24 @@ function generateZodSchemasInlineReusable(builder, output) {
1567
1705
  workspace: "",
1568
1706
  output
1569
1707
  };
1570
- const refs = schemasWithOpenApiDef.map(({ name }) => `#/components/schemas/${name}`);
1571
- resolveSchemaNames(refs, output.namingConvention);
1572
- return `import { z as zod } from 'zod';\n\n${rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
1708
+ const componentSchemas = builder.spec.components?.schemas ?? {};
1709
+ const refs = Object.keys(componentSchemas).map((schemaName) => `#/components/schemas/${schemaName}`);
1710
+ if (refs.length === 0) return "";
1711
+ resolveSchemaNames(refs, context);
1712
+ const body = rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
1573
1713
  strict,
1574
1714
  isZodV4,
1575
- coerce
1576
- })).map((entry) => {
1577
- return `${entry.consts ? `${entry.consts}\n\n` : ""}export const ${entry.name} = ${entry.zod};\n\nexport type ${entry.name} = zod.input<typeof ${entry.name}>;\nexport type ${entry.name}Output = zod.output<typeof ${entry.name}>;`;
1578
- }).join("\n\n")}\n`;
1715
+ coerce,
1716
+ generateMeta: output.override.zod.generateMeta,
1717
+ paramsMutator
1718
+ })).map((entry) => renderReusableSchemaEntry(entry, context).content).join("\n\n");
1719
+ const zodImport = includeZodImport ? `import { z as zod } from 'zod';\n` : "";
1720
+ const paramsImport = paramsMutator && includeParamsImport && bodyReferencesMutator(body, paramsMutator) ? `${buildMutatorImportStatement(paramsMutator)}\n` : "";
1721
+ return `${zodImport || paramsImport ? `${zodImport}${paramsImport}\n` : ""}${body}\n`;
1579
1722
  }
1580
- async function writeZodSchemas(builder, schemasPath, fileExtension, header, output) {
1723
+ async function writeZodSchemas(builder, schemasPath, fileExtension, header, output, paramsMutator) {
1581
1724
  if (output.override.zod.generateReusableSchemas) {
1582
- await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output);
1725
+ await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator);
1583
1726
  return;
1584
1727
  }
1585
1728
  const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
@@ -1598,7 +1741,10 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
1598
1741
  workspace: "",
1599
1742
  output
1600
1743
  };
1601
- const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, { required: true }), context, coerce, strict, isZodV4);
1744
+ const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
1745
+ required: true,
1746
+ emitMeta: output.override.zod.generateMeta
1747
+ }), context, coerce, strict, isZodV4);
1602
1748
  schemasToWrite.push({
1603
1749
  schemaName: name,
1604
1750
  filePath,
@@ -1613,7 +1759,7 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
1613
1759
  }
1614
1760
  if (output.indexFiles) await writeZodSchemaIndex(schemasPath, fileExtension, header, groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName), output.namingConvention, false);
1615
1761
  }
1616
- async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output) {
1762
+ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator) {
1617
1763
  const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
1618
1764
  const strict = output.override.zod.strict.body;
1619
1765
  const coerce = output.override.zod.coerce.body;
@@ -1623,25 +1769,33 @@ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, head
1623
1769
  workspace: "",
1624
1770
  output
1625
1771
  };
1626
- const refs = builder.schemas.map(({ name }) => `#/components/schemas/${name}`).filter((ref) => {
1627
- const schemaName = ref.slice(21);
1628
- return (builder.spec.components?.schemas ?? {})[schemaName] !== void 0;
1629
- });
1630
- resolveSchemaNames(refs, output.namingConvention);
1772
+ const componentSchemas = builder.spec.components?.schemas ?? {};
1773
+ const refs = Object.keys(componentSchemas).map((schemaName) => `#/components/schemas/${schemaName}`);
1774
+ resolveSchemaNames(refs, context);
1631
1775
  const rewritten = rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
1632
1776
  strict,
1633
1777
  isZodV4,
1634
- coerce
1778
+ coerce,
1779
+ generateMeta: output.override.zod.generateMeta,
1780
+ paramsMutator
1635
1781
  }));
1782
+ const componentNames = new Set(Object.keys(builder.spec.components?.schemas ?? {}).map((schemaName) => resolveSchemaName(`#/components/schemas/${schemaName}`, context)));
1783
+ const paramsMutatorImport = paramsMutator ? buildMutatorImportStatement(paramsMutator) : void 0;
1636
1784
  for (const entry of rewritten) {
1637
1785
  const fileName = conventionName(entry.name, output.namingConvention);
1638
1786
  const filePath = path.join(schemasPath, `${fileName}${fileExtension}`);
1639
1787
  const importExt = fileExtension.replace(/\.ts$/, "");
1640
- const imports = [...entry.usedRefs].filter((r) => r !== entry.name).toSorted().map((r) => {
1641
- return `import { ${r} } from './${conventionName(r, output.namingConvention)}${importExt}';`;
1642
- }).join("\n");
1643
- const consts = entry.consts ? `${entry.consts}\n\n` : "";
1644
- const fileContent = `${header}import { z as zod } from 'zod';\n` + (imports ? `${imports}\n\n` : "\n") + `${consts}export const ${entry.name} = ${entry.zod};\n\nexport type ${entry.name} = zod.input<typeof ${entry.name}>;\nexport type ${entry.name}Output = zod.output<typeof ${entry.name}>;\n`;
1788
+ const rendered = renderReusableSchemaEntry(entry, context);
1789
+ const refImports = buildSiblingImports({
1790
+ usedRefs: entry.usedRefs,
1791
+ extraImports: rendered.extraImports,
1792
+ entryName: entry.name,
1793
+ componentNames,
1794
+ namingConvention: output.namingConvention,
1795
+ importExt
1796
+ });
1797
+ const imports = [...!!paramsMutator && bodyReferencesMutator(entry.zod, paramsMutator) && paramsMutatorImport ? [paramsMutatorImport] : [], ...refImports ? [refImports] : []].join("\n");
1798
+ const fileContent = `${header}import { z as zod } from 'zod';\n` + (imports ? `${imports}\n\n` : "\n") + `${rendered.content}\n`;
1645
1799
  await fs.outputFile(filePath, fileContent);
1646
1800
  }
1647
1801
  if (output.indexFiles && rewritten.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, rewritten.map((e) => e.name), output.namingConvention, true);
@@ -1713,11 +1867,21 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
1713
1867
  required: true,
1714
1868
  useReusableSchemas
1715
1869
  }), zodContext, coerce, strict, isZodV4);
1870
+ let zodExpression = parsedZodDefinition.zod;
1871
+ let importStatements;
1872
+ if (useReusableSchemas && parsedZodDefinition.usedRefs.size > 0) {
1873
+ zodExpression = rewriteSentinelsToDirect(zodExpression);
1874
+ const importExt = fileExtension.replace(/\.ts$/, "");
1875
+ importStatements = [...parsedZodDefinition.usedRefs].filter((refName) => refName !== name).toSorted().map((refName) => {
1876
+ return `import { ${refName} } from './${conventionName(refName, output.namingConvention)}${importExt}';`;
1877
+ });
1878
+ }
1716
1879
  schemasToWrite.push({
1717
1880
  schemaName: name,
1718
1881
  filePath,
1719
1882
  consts: parsedZodDefinition.consts,
1720
- zodExpression: parsedZodDefinition.zod
1883
+ zodExpression,
1884
+ importStatements
1721
1885
  });
1722
1886
  }
1723
1887
  const groupedSchemasToWrite = groupSchemasByFilePath(schemasToWrite);
@@ -1837,6 +2001,17 @@ async function writeFakerSchemaMocks(builder, options, header) {
1837
2001
  await writeGeneratedFile(filePath, content);
1838
2002
  return filePath;
1839
2003
  }
2004
+ function isSchemaValidatorClient(client) {
2005
+ return client === "zod" || client === "effect";
2006
+ }
2007
+ function shouldGenerateZodSchemasInline(output, hasOperations) {
2008
+ if (output.client !== "zod" || output.schemas) return false;
2009
+ if (output.override.zod.generateReusableSchemas) return true;
2010
+ return !hasOperations;
2011
+ }
2012
+ function shouldGenerateSchemas(output, hasOperations) {
2013
+ return !output.schemas && !isSchemaValidatorClient(output.client) || shouldGenerateZodSchemasInline(output, hasOperations);
2014
+ }
1840
2015
  async function writeSpecs(builder, workspace, options, projectName) {
1841
2016
  const { info, schemas, target } = builder;
1842
2017
  const { output } = options;
@@ -1846,7 +2021,13 @@ async function writeSpecs(builder, workspace, options, projectName) {
1846
2021
  const schemasPath = isString(output.schemas) ? output.schemas : output.schemas.path;
1847
2022
  if (!isString(output.schemas) && output.schemas.type === "zod" || isString(output.schemas) && output.client === "zod" && output.override.zod.generateReusableSchemas) {
1848
2023
  const fileExtension = output.schemaFileExtension;
1849
- await writeZodSchemas(builder, schemasPath, fileExtension, header, output);
2024
+ await writeZodSchemas(builder, schemasPath, fileExtension, header, output, output.override.zod.params ? await generateMutator({
2025
+ output: path.join(schemasPath, `__params__${fileExtension}`),
2026
+ mutator: output.override.zod.params,
2027
+ name: "zodParams",
2028
+ workspace,
2029
+ tsconfig: output.tsconfig
2030
+ }) : void 0);
1850
2031
  await writeZodSchemasFromVerbs(builder.verbOptions, schemasPath, fileExtension, header, output, {
1851
2032
  spec: builder.spec,
1852
2033
  target: builder.target,
@@ -1903,17 +2084,26 @@ async function writeSpecs(builder, workspace, options, projectName) {
1903
2084
  let implementationPaths = [];
1904
2085
  if (output.target) {
1905
2086
  const writeMode = getWriteMode(output.mode);
1906
- const isZodClient = output.client === "zod";
1907
2087
  const hasOperations = Object.keys(builder.operations).length > 0;
1908
- const needZodSchemasInline = isZodClient && !output.schemas && !hasOperations;
2088
+ const needZodSchemasInline = shouldGenerateZodSchemasInline(output, hasOperations);
2089
+ const includeZodImport = !Object.values(builder.operations).some((operation) => /\bzod\b/.test(operation.implementation));
2090
+ const inlineSchemasParamsMutator = needZodSchemasInline && output.override.zod.params ? await generateMutator({
2091
+ output: output.target,
2092
+ mutator: output.override.zod.params,
2093
+ name: "zodParams",
2094
+ workspace,
2095
+ tsconfig: output.tsconfig
2096
+ }) : void 0;
2097
+ const isSchemasInSeparateFile = output.mode !== OutputMode.SINGLE;
2098
+ const includeParamsImport = !hasOperations || isSchemasInSeparateFile;
1909
2099
  implementationPaths = await writeMode({
1910
2100
  builder,
1911
2101
  workspace,
1912
2102
  output,
1913
2103
  projectName,
1914
2104
  header,
1915
- needSchema: !output.schemas && !isZodClient || needZodSchemasInline,
1916
- generateSchemasInline: needZodSchemasInline ? () => generateZodSchemasInline(builder, output) : void 0
2105
+ needSchema: shouldGenerateSchemas(output, hasOperations),
2106
+ generateSchemasInline: needZodSchemasInline ? () => generateZodSchemasInline(builder, output, includeZodImport, inlineSchemasParamsMutator, includeParamsImport) : void 0
1917
2107
  });
1918
2108
  }
1919
2109
  if (output.workspace) {
@@ -2072,4 +2262,4 @@ async function loadConfigFile(configFilePath) {
2072
2262
  //#endregion
2073
2263
  export { defineConfig as a, description as c, startWatcher as i, name as l, loadConfigFile as n, defineTransformer as o, generateSpec as r, normalizeOptions as s, findConfigFile as t, version as u };
2074
2264
 
2075
- //# sourceMappingURL=config-Cyybmy9c.mjs.map
2265
+ //# sourceMappingURL=config-yIkhToq8.mjs.map