orval 8.13.0 → 8.15.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-CVTlXJY3.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, buildDynamicScope, 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.15.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) },
@@ -1218,6 +1247,7 @@ function normalizeHooks(hooks) {
1218
1247
  function normalizeHonoOptions(hono = {}, workspace) {
1219
1248
  return {
1220
1249
  ...hono.handlers ? { handlers: path.resolve(workspace, hono.handlers) } : {},
1250
+ handlerGenerationStrategy: hono.handlerGenerationStrategy ?? "smart",
1221
1251
  compositeRoute: hono.compositeRoute ? path.resolve(workspace, hono.compositeRoute) : "",
1222
1252
  validator: hono.validator ?? true,
1223
1253
  validatorOutputPath: hono.validatorOutputPath ? path.resolve(workspace, hono.validatorOutputPath) : ""
@@ -1258,6 +1288,10 @@ function normalizeQueryOptions(queryOptions = {}, outputWorkspace, globalOptions
1258
1288
  ...queryOptions.mutationOptions ? { mutationOptions: normalizeMutator(outputWorkspace, queryOptions.mutationOptions) } : {},
1259
1289
  ...isNullish(globalOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: globalOptions.shouldExportQueryKey },
1260
1290
  ...isNullish(queryOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: queryOptions.shouldExportQueryKey },
1291
+ ...isNullish(globalOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: globalOptions.shouldFilterQueryKey },
1292
+ ...isNullish(queryOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: queryOptions.shouldFilterQueryKey },
1293
+ ...isNullish(globalOptions.queryKeyFilter) ? {} : { queryKeyFilter: globalOptions.queryKeyFilter },
1294
+ ...isNullish(queryOptions.queryKeyFilter) ? {} : { queryKeyFilter: queryOptions.queryKeyFilter },
1261
1295
  ...isNullish(globalOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: globalOptions.shouldExportHttpClient },
1262
1296
  ...isNullish(queryOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: queryOptions.shouldExportHttpClient },
1263
1297
  ...isNullish(globalOptions.shouldExportMutatorHooks) ? {} : { shouldExportMutatorHooks: globalOptions.shouldExportMutatorHooks },
@@ -1325,30 +1359,29 @@ async function startWatcher(watchOptions, watchFn, defaultTarget = ".") {
1325
1359
  }
1326
1360
  //#endregion
1327
1361
  //#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
1362
  /**
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.
1363
+ * Resolve the export identifier for a `#/components/schemas/X` ref. We reuse
1364
+ * `@orval/core`'s `getRefInfo(...).name` (`pascal` + sanitize + component
1365
+ * suffix) so reusable zod schema exports match the operation wrappers and the
1366
+ * TS model types exactly. `namingConvention` deliberately does NOT influence
1367
+ * the identifier — it governs file names only, consistent with the rest of
1368
+ * orval. The same call powers the generator's `namedRef` emission, so the
1369
+ * definition name and every reference stay in sync.
1335
1370
  */
1336
- const resolveSchemaName = (ref, namingConvention) => conventionName(lastRefSegment(ref), namingConvention);
1337
- const JS_IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
1371
+ const resolveSchemaName = (ref, context) => getRefInfo(ref, context).name;
1338
1372
  /**
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.
1373
+ * Resolve names for a set of refs, throwing on conflicts (two distinct refs
1374
+ * collapsing to the same identifier). The mapping is the single source of
1375
+ * truth for cross-schema references — the generator, the orchestrator's graph,
1376
+ * and the sentinel rewriter all consult it.
1343
1377
  */
1344
- const resolveSchemaNames = (refs, namingConvention) => {
1378
+ const resolveSchemaNames = (refs, context) => {
1345
1379
  const resolved = /* @__PURE__ */ new Map();
1346
1380
  const reverse = /* @__PURE__ */ new Map();
1347
1381
  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.`);
1382
+ const name = resolveSchemaName(ref, context);
1350
1383
  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.`);
1384
+ 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
1385
  resolved.set(ref, name);
1353
1386
  reverse.set(name, ref);
1354
1387
  }
@@ -1366,19 +1399,30 @@ const generateReusableSchemaSet = (refs, context, options) => {
1366
1399
  const nameToRef = /* @__PURE__ */ new Map();
1367
1400
  for (const schemaName of Object.keys(componentSchemas)) {
1368
1401
  const ref = `#/components/schemas/${schemaName}`;
1369
- nameToRef.set(resolveSchemaName(ref, context.output.namingConvention), ref);
1402
+ nameToRef.set(resolveSchemaName(ref, context), ref);
1370
1403
  }
1371
1404
  const queue = [...refs];
1372
1405
  const seen = new Set(refs);
1373
1406
  const entries = [];
1374
1407
  for (const ref of queue) {
1375
- const schema = componentSchemas[ref.slice(21)];
1408
+ const schemaName = ref.slice(21);
1409
+ const schema = componentSchemas[schemaName];
1376
1410
  if (!schema) continue;
1377
- const name = resolveSchemaName(ref, context.output.namingConvention);
1378
- const parsed = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(schema, context, name, options.strict, options.isZodV4, {
1411
+ const name = resolveSchemaName(ref, context);
1412
+ const scopedContext = {
1413
+ ...context,
1414
+ dynamicScope: buildDynamicScope(schemaName, schema, context)
1415
+ };
1416
+ const parsed = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(schema, scopedContext, name, options.strict, options.isZodV4, {
1379
1417
  required: true,
1380
- useReusableSchemas: true
1381
- }), context, options.coerce ?? false, options.strict, options.isZodV4);
1418
+ useReusableSchemas: true,
1419
+ emitMeta: options.generateMeta
1420
+ }), scopedContext, options.coerce ?? false, options.strict, options.isZodV4, void 0, options.paramsMutator ? {
1421
+ mutator: options.paramsMutator,
1422
+ operationId: "",
1423
+ location: "schema",
1424
+ schemaName: name
1425
+ } : void 0);
1382
1426
  entries.push({
1383
1427
  ref,
1384
1428
  name,
@@ -1445,11 +1489,27 @@ const tarjan = (graph) => {
1445
1489
  };
1446
1490
  const SENTINEL_PATTERN = /__REF_([A-Za-z_$][A-Za-z0-9_$]*)__/g;
1447
1491
  /**
1492
+ * Replace every `__REF_<name>__` sentinel with the bare identifier. Use this
1493
+ * for schemas that sit at the top of the dependency graph (operation params,
1494
+ * bodies, responses) — they can never participate in a cycle with the
1495
+ * component schemas they reference, so every ref is a direct (non-lazy) one.
1496
+ */
1497
+ const rewriteSentinelsToDirect = (zod) => zod.replaceAll(SENTINEL_PATTERN, (_match, refName) => refName);
1498
+ /**
1448
1499
  * Replace every `__REF_<name>__` sentinel with either the bare identifier or
1449
1500
  * `zod.lazy(() => <name>)` based on whether the edge closes a cycle, then
1450
1501
  * reorder entries so that every non-lazy reference is emitted AFTER its
1451
1502
  * target. This avoids TDZ errors at module load.
1452
1503
  *
1504
+ * Entries that sit in a cycle (SCC of size > 1, or a self-loop) are flagged
1505
+ * `isRecursive`. Their generated `const` reads its own binding inside the
1506
+ * initializer (through the `zod.lazy` wrapper), which TypeScript rejects with
1507
+ * TS7022 ("'X' implicitly has type 'any' ... referenced directly or indirectly
1508
+ * in its own initializer") unless the `const` carries an explicit type
1509
+ * annotation. The writer (`write-zod-specs`) supplies that annotation —
1510
+ * `const X: zod.ZodType<X>` — backed by a generated TS type, which both
1511
+ * silences TS7022 and preserves full `z.infer` typing through the recursion.
1512
+ *
1453
1513
  * Both the lazy classification and the emit order come from a single Tarjan
1454
1514
  * run, guaranteeing they agree: a non-lazy edge u→v means v is visited (and
1455
1515
  * popped) before u in DFS, so v appears earlier in the SCC array → emitted
@@ -1459,13 +1519,17 @@ const rewriteReusableSchemas = (entries) => {
1459
1519
  const graph = new Map(entries.map((e) => [e.name, new Set(e.usedRefs)]));
1460
1520
  for (const e of entries) for (const ref of e.usedRefs) if (!graph.has(ref)) graph.set(ref, /* @__PURE__ */ new Set());
1461
1521
  const { sccs, lazyEdges } = tarjan(graph);
1522
+ const recursiveNames = /* @__PURE__ */ new Set();
1523
+ for (const scc of sccs) if (scc.length > 1) for (const name of scc) recursiveNames.add(name);
1524
+ else if (lazyEdges.has(edgeKey(scc[0], scc[0]))) recursiveNames.add(scc[0]);
1462
1525
  const rewritten = new Map(entries.map((entry) => {
1463
1526
  const newZod = entry.zod.replaceAll(SENTINEL_PATTERN, (_match, refName) => {
1464
1527
  return lazyEdges.has(edgeKey(entry.name, refName)) ? `zod.lazy(() => ${refName})` : refName;
1465
1528
  });
1466
1529
  return [entry.name, {
1467
1530
  ...entry,
1468
- zod: newZod
1531
+ zod: newZod,
1532
+ isRecursive: recursiveNames.has(entry.name)
1469
1533
  }];
1470
1534
  }));
1471
1535
  const out = [];
@@ -1477,18 +1541,99 @@ const rewriteReusableSchemas = (entries) => {
1477
1541
  };
1478
1542
  //#endregion
1479
1543
  //#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 }) => {
1544
+ /**
1545
+ * Render the `import { ... } from '...'` line for a resolved
1546
+ * `GeneratorMutator`. Mirrors the format produced by
1547
+ * `generateMutatorImports` in `@orval/core` but inlined to avoid pulling in
1548
+ * its full surface area for a single statement.
1549
+ */
1550
+ function buildMutatorImportStatement(mutator) {
1551
+ return `import ${mutator.default ? mutator.name : `{ ${mutator.name} }`} from '${mutator.path}';`;
1552
+ }
1553
+ /**
1554
+ * Whole-word substring check for a resolved mutator alias inside generated
1555
+ * code. Plain `String.includes` would false-positive when the user names the
1556
+ * mutator something like `min` against `.min(1)`.
1557
+ */
1558
+ function bodyReferencesMutator(body, mutator) {
1559
+ return new RegExp(String.raw`\b${mutator.name}\b`).test(body);
1560
+ }
1561
+ function generateZodSchemaFileContent(header, schemas, includeZodImport = true) {
1562
+ const refImports = [...new Set(schemas.flatMap((s) => s.importStatements ?? []))].toSorted();
1563
+ const importBlock = [...includeZodImport ? [`import { z as zod } from 'zod';`] : [], ...refImports].join("\n");
1564
+ const schemaContent = schemas.map(({ schemaName, consts, zodExpression }) => {
1484
1565
  return `${consts ? `${consts}\n` : ""}export const ${schemaName} = ${zodExpression}
1485
1566
 
1486
1567
  export type ${schemaName} = zod.input<typeof ${schemaName}>;
1487
1568
  export type ${schemaName}Output = zod.output<typeof ${schemaName}>;`;
1488
- }).join("\n\n")}
1489
- `;
1569
+ }).join("\n\n");
1570
+ return `${header}${importBlock ? `${importBlock}\n\n` : ""}${schemaContent}\n`;
1571
+ }
1572
+ function renderReusableSchemaEntry(entry, context) {
1573
+ const consts = entry.consts ? `${entry.consts}\n\n` : "";
1574
+ if (entry.isRecursive) {
1575
+ const rawName = isComponentRef(entry.ref) ? getRefInfo(entry.ref, context).originalName : void 0;
1576
+ const schema = rawName ? context.spec.components?.schemas?.[rawName] : void 0;
1577
+ const resolved = schema ? resolveValue({
1578
+ schema,
1579
+ name: entry.name,
1580
+ context
1581
+ }) : void 0;
1582
+ const typeBody = resolved ? resolved.value : "unknown";
1583
+ const seen = /* @__PURE__ */ new Set();
1584
+ const extraImports = [];
1585
+ for (const imp of resolved?.imports ?? []) {
1586
+ if (!imp.name || imp.name === entry.name) continue;
1587
+ const bindingKey = imp.alias ?? imp.name;
1588
+ if (seen.has(bindingKey)) continue;
1589
+ seen.add(bindingKey);
1590
+ extraImports.push({
1591
+ name: imp.name,
1592
+ ...imp.alias ? { alias: imp.alias } : {}
1593
+ });
1594
+ }
1595
+ return {
1596
+ 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}>;`,
1597
+ extraImports
1598
+ };
1599
+ }
1600
+ return {
1601
+ 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}>;`,
1602
+ extraImports: []
1603
+ };
1490
1604
  }
1491
1605
  const isValidSchemaIdentifier = (name) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
1606
+ /**
1607
+ * Build the sibling-file `import { … } from './…'` block for one reusable
1608
+ * schema file. Two sources feed in:
1609
+ * - `usedRefs` — names from the zod runtime expression. Sourced from the
1610
+ * sentinel parser, so always unaliased.
1611
+ * - `extraImports` — names the recursive TS body needs that the zod runtime
1612
+ * collapsed (`propertyNames` $refs, etc.). May carry `alias`.
1613
+ * Keyed by export name (`name`) so an aliased `extraImports` entry overrides
1614
+ * a bare `usedRefs` entry — the recursive TS body uses the local binding, so
1615
+ * the aliased form has to win for the file to compile. Self-refs and
1616
+ * non-component identifiers are filtered out.
1617
+ *
1618
+ * Exported for unit-test coverage of the alias-propagation path; no
1619
+ * `resolveValue` producer surfaces aliases here today, so the integration
1620
+ * tests can't exercise it.
1621
+ */
1622
+ function buildSiblingImports({ usedRefs, extraImports, entryName, componentNames, namingConvention, importExt }) {
1623
+ const importsByName = /* @__PURE__ */ new Map();
1624
+ for (const name of usedRefs) {
1625
+ if (name === entryName) continue;
1626
+ importsByName.set(name, { name });
1627
+ }
1628
+ for (const imp of extraImports) {
1629
+ if (imp.name === entryName || !componentNames.has(imp.name)) continue;
1630
+ importsByName.set(imp.name, imp);
1631
+ }
1632
+ return [...importsByName.values()].toSorted((a, b) => a.name.localeCompare(b.name)).map(({ name, alias }) => {
1633
+ const importedFile = conventionName(name, namingConvention);
1634
+ return `import { ${alias ? `${name} as ${alias}` : name} } from './${importedFile}${importExt}';`;
1635
+ }).join("\n");
1636
+ }
1492
1637
  const isPrimitiveSchemaName = (name) => [
1493
1638
  "string",
1494
1639
  "number",
@@ -1529,8 +1674,8 @@ async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNam
1529
1674
  const uniqueExports = [...new Set(allExports.split("\n"))].filter((line) => line.trim()).toSorted().join("\n");
1530
1675
  await fs.outputFile(indexPath, `${header}\n${uniqueExports}\n`);
1531
1676
  }
1532
- function generateZodSchemasInline(builder, output) {
1533
- if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output);
1677
+ function generateZodSchemasInline(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
1678
+ if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output, includeZodImport, paramsMutator, includeParamsImport);
1534
1679
  const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
1535
1680
  if (schemasWithOpenApiDef.length === 0) return "";
1536
1681
  const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
@@ -1545,7 +1690,10 @@ function generateZodSchemasInline(builder, output) {
1545
1690
  workspace: "",
1546
1691
  output
1547
1692
  };
1548
- const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, { required: true }), context, coerce, strict, isZodV4);
1693
+ const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
1694
+ required: true,
1695
+ emitMeta: output.override.zod.generateMeta
1696
+ }), context, coerce, strict, isZodV4);
1549
1697
  schemas.push({
1550
1698
  schemaName: name,
1551
1699
  consts: parsedZodDefinition.consts,
@@ -1553,11 +1701,9 @@ function generateZodSchemasInline(builder, output) {
1553
1701
  });
1554
1702
  }
1555
1703
  if (schemas.length === 0) return "";
1556
- return generateZodSchemaFileContent("", schemas);
1704
+ return generateZodSchemaFileContent("", schemas, includeZodImport);
1557
1705
  }
1558
- function generateZodSchemasInlineReusable(builder, output) {
1559
- const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
1560
- if (schemasWithOpenApiDef.length === 0) return "";
1706
+ function generateZodSchemasInlineReusable(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
1561
1707
  const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
1562
1708
  const strict = output.override.zod.strict.body;
1563
1709
  const coerce = output.override.zod.coerce.body;
@@ -1567,19 +1713,24 @@ function generateZodSchemasInlineReusable(builder, output) {
1567
1713
  workspace: "",
1568
1714
  output
1569
1715
  };
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, {
1716
+ const componentSchemas = builder.spec.components?.schemas ?? {};
1717
+ const refs = Object.keys(componentSchemas).map((schemaName) => `#/components/schemas/${schemaName}`);
1718
+ if (refs.length === 0) return "";
1719
+ resolveSchemaNames(refs, context);
1720
+ const body = rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
1573
1721
  strict,
1574
1722
  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`;
1723
+ coerce,
1724
+ generateMeta: output.override.zod.generateMeta,
1725
+ paramsMutator
1726
+ })).map((entry) => renderReusableSchemaEntry(entry, context).content).join("\n\n");
1727
+ const zodImport = includeZodImport ? `import { z as zod } from 'zod';\n` : "";
1728
+ const paramsImport = paramsMutator && includeParamsImport && bodyReferencesMutator(body, paramsMutator) ? `${buildMutatorImportStatement(paramsMutator)}\n` : "";
1729
+ return `${zodImport || paramsImport ? `${zodImport}${paramsImport}\n` : ""}${body}\n`;
1579
1730
  }
1580
- async function writeZodSchemas(builder, schemasPath, fileExtension, header, output) {
1731
+ async function writeZodSchemas(builder, schemasPath, fileExtension, header, output, paramsMutator) {
1581
1732
  if (output.override.zod.generateReusableSchemas) {
1582
- await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output);
1733
+ await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator);
1583
1734
  return;
1584
1735
  }
1585
1736
  const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
@@ -1598,7 +1749,10 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
1598
1749
  workspace: "",
1599
1750
  output
1600
1751
  };
1601
- const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, { required: true }), context, coerce, strict, isZodV4);
1752
+ const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
1753
+ required: true,
1754
+ emitMeta: output.override.zod.generateMeta
1755
+ }), context, coerce, strict, isZodV4);
1602
1756
  schemasToWrite.push({
1603
1757
  schemaName: name,
1604
1758
  filePath,
@@ -1613,7 +1767,7 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
1613
1767
  }
1614
1768
  if (output.indexFiles) await writeZodSchemaIndex(schemasPath, fileExtension, header, groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName), output.namingConvention, false);
1615
1769
  }
1616
- async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output) {
1770
+ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator) {
1617
1771
  const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
1618
1772
  const strict = output.override.zod.strict.body;
1619
1773
  const coerce = output.override.zod.coerce.body;
@@ -1623,25 +1777,33 @@ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, head
1623
1777
  workspace: "",
1624
1778
  output
1625
1779
  };
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);
1780
+ const componentSchemas = builder.spec.components?.schemas ?? {};
1781
+ const refs = Object.keys(componentSchemas).map((schemaName) => `#/components/schemas/${schemaName}`);
1782
+ resolveSchemaNames(refs, context);
1631
1783
  const rewritten = rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
1632
1784
  strict,
1633
1785
  isZodV4,
1634
- coerce
1786
+ coerce,
1787
+ generateMeta: output.override.zod.generateMeta,
1788
+ paramsMutator
1635
1789
  }));
1790
+ const componentNames = new Set(Object.keys(builder.spec.components?.schemas ?? {}).map((schemaName) => resolveSchemaName(`#/components/schemas/${schemaName}`, context)));
1791
+ const paramsMutatorImport = paramsMutator ? buildMutatorImportStatement(paramsMutator) : void 0;
1636
1792
  for (const entry of rewritten) {
1637
1793
  const fileName = conventionName(entry.name, output.namingConvention);
1638
1794
  const filePath = path.join(schemasPath, `${fileName}${fileExtension}`);
1639
1795
  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`;
1796
+ const rendered = renderReusableSchemaEntry(entry, context);
1797
+ const refImports = buildSiblingImports({
1798
+ usedRefs: entry.usedRefs,
1799
+ extraImports: rendered.extraImports,
1800
+ entryName: entry.name,
1801
+ componentNames,
1802
+ namingConvention: output.namingConvention,
1803
+ importExt
1804
+ });
1805
+ const imports = [...!!paramsMutator && bodyReferencesMutator(entry.zod, paramsMutator) && paramsMutatorImport ? [paramsMutatorImport] : [], ...refImports ? [refImports] : []].join("\n");
1806
+ const fileContent = `${header}import { z as zod } from 'zod';\n` + (imports ? `${imports}\n\n` : "\n") + `${rendered.content}\n`;
1645
1807
  await fs.outputFile(filePath, fileContent);
1646
1808
  }
1647
1809
  if (output.indexFiles && rewritten.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, rewritten.map((e) => e.name), output.namingConvention, true);
@@ -1713,11 +1875,21 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
1713
1875
  required: true,
1714
1876
  useReusableSchemas
1715
1877
  }), zodContext, coerce, strict, isZodV4);
1878
+ let zodExpression = parsedZodDefinition.zod;
1879
+ let importStatements;
1880
+ if (useReusableSchemas && parsedZodDefinition.usedRefs.size > 0) {
1881
+ zodExpression = rewriteSentinelsToDirect(zodExpression);
1882
+ const importExt = fileExtension.replace(/\.ts$/, "");
1883
+ importStatements = [...parsedZodDefinition.usedRefs].filter((refName) => refName !== name).toSorted().map((refName) => {
1884
+ return `import { ${refName} } from './${conventionName(refName, output.namingConvention)}${importExt}';`;
1885
+ });
1886
+ }
1716
1887
  schemasToWrite.push({
1717
1888
  schemaName: name,
1718
1889
  filePath,
1719
1890
  consts: parsedZodDefinition.consts,
1720
- zodExpression: parsedZodDefinition.zod
1891
+ zodExpression,
1892
+ importStatements
1721
1893
  });
1722
1894
  }
1723
1895
  const groupedSchemasToWrite = groupSchemasByFilePath(schemasToWrite);
@@ -1837,6 +2009,17 @@ async function writeFakerSchemaMocks(builder, options, header) {
1837
2009
  await writeGeneratedFile(filePath, content);
1838
2010
  return filePath;
1839
2011
  }
2012
+ function isSchemaValidatorClient(client) {
2013
+ return client === "zod" || client === "effect";
2014
+ }
2015
+ function shouldGenerateZodSchemasInline(output, hasOperations) {
2016
+ if (output.client !== "zod" || output.schemas) return false;
2017
+ if (output.override.zod.generateReusableSchemas) return true;
2018
+ return !hasOperations;
2019
+ }
2020
+ function shouldGenerateSchemas(output, hasOperations) {
2021
+ return !output.schemas && !isSchemaValidatorClient(output.client) || shouldGenerateZodSchemasInline(output, hasOperations);
2022
+ }
1840
2023
  async function writeSpecs(builder, workspace, options, projectName) {
1841
2024
  const { info, schemas, target } = builder;
1842
2025
  const { output } = options;
@@ -1846,7 +2029,13 @@ async function writeSpecs(builder, workspace, options, projectName) {
1846
2029
  const schemasPath = isString(output.schemas) ? output.schemas : output.schemas.path;
1847
2030
  if (!isString(output.schemas) && output.schemas.type === "zod" || isString(output.schemas) && output.client === "zod" && output.override.zod.generateReusableSchemas) {
1848
2031
  const fileExtension = output.schemaFileExtension;
1849
- await writeZodSchemas(builder, schemasPath, fileExtension, header, output);
2032
+ await writeZodSchemas(builder, schemasPath, fileExtension, header, output, output.override.zod.params ? await generateMutator({
2033
+ output: path.join(schemasPath, `__params__${fileExtension}`),
2034
+ mutator: output.override.zod.params,
2035
+ name: "zodParams",
2036
+ workspace,
2037
+ tsconfig: output.tsconfig
2038
+ }) : void 0);
1850
2039
  await writeZodSchemasFromVerbs(builder.verbOptions, schemasPath, fileExtension, header, output, {
1851
2040
  spec: builder.spec,
1852
2041
  target: builder.target,
@@ -1903,17 +2092,26 @@ async function writeSpecs(builder, workspace, options, projectName) {
1903
2092
  let implementationPaths = [];
1904
2093
  if (output.target) {
1905
2094
  const writeMode = getWriteMode(output.mode);
1906
- const isZodClient = output.client === "zod";
1907
2095
  const hasOperations = Object.keys(builder.operations).length > 0;
1908
- const needZodSchemasInline = isZodClient && !output.schemas && !hasOperations;
2096
+ const needZodSchemasInline = shouldGenerateZodSchemasInline(output, hasOperations);
2097
+ const includeZodImport = !Object.values(builder.operations).some((operation) => /\bzod\b/.test(operation.implementation));
2098
+ const inlineSchemasParamsMutator = needZodSchemasInline && output.override.zod.params ? await generateMutator({
2099
+ output: output.target,
2100
+ mutator: output.override.zod.params,
2101
+ name: "zodParams",
2102
+ workspace,
2103
+ tsconfig: output.tsconfig
2104
+ }) : void 0;
2105
+ const isSchemasInSeparateFile = output.mode !== OutputMode.SINGLE;
2106
+ const includeParamsImport = !hasOperations || isSchemasInSeparateFile;
1909
2107
  implementationPaths = await writeMode({
1910
2108
  builder,
1911
2109
  workspace,
1912
2110
  output,
1913
2111
  projectName,
1914
2112
  header,
1915
- needSchema: !output.schemas && !isZodClient || needZodSchemasInline,
1916
- generateSchemasInline: needZodSchemasInline ? () => generateZodSchemasInline(builder, output) : void 0
2113
+ needSchema: shouldGenerateSchemas(output, hasOperations),
2114
+ generateSchemasInline: needZodSchemasInline ? () => generateZodSchemasInline(builder, output, includeZodImport, inlineSchemasParamsMutator, includeParamsImport) : void 0
1917
2115
  });
1918
2116
  }
1919
2117
  if (output.workspace) {
@@ -2072,4 +2270,4 @@ async function loadConfigFile(configFilePath) {
2072
2270
  //#endregion
2073
2271
  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
2272
 
2075
- //# sourceMappingURL=config-Cyybmy9c.mjs.map
2273
+ //# sourceMappingURL=config-CVTlXJY3.mjs.map