@orval/core 8.10.0 → 8.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
2
  import { createRequire } from "node:module";
3
- import { entries, groupBy, isArray, isBoolean, isBoolean as isBoolean$1, isEmptyish, isFunction, isNullish, isNullish as isNullish$1, isNumber, isString, isString as isString$1, prop, unique, uniqueBy, uniqueWith } from "remeda";
3
+ import { entries, groupBy, isArray, isBoolean, isBoolean as isBoolean$1, isEmptyish, isFunction, isFunction as isFunction$1, isNullish, isNullish as isNullish$1, isNumber, isString, isString as isString$1, prop, unique, uniqueBy, uniqueWith } from "remeda";
4
4
  import { keyword } from "esutils";
5
5
  import path from "node:path";
6
6
  import { compare } from "compare-versions";
@@ -8,7 +8,7 @@ import debug from "debug";
8
8
  import { pathToFileURL } from "node:url";
9
9
  import { createJiti } from "jiti";
10
10
  import fs, { existsSync, readFileSync } from "node:fs";
11
- import { globby } from "globby";
11
+ import { glob } from "tinyglobby";
12
12
  import readline from "node:readline";
13
13
  import { styleText } from "node:util";
14
14
  import { isDereferenced } from "@scalar/openapi-types/helpers";
@@ -63,7 +63,10 @@ const OutputMode = {
63
63
  TAGS: "tags",
64
64
  TAGS_SPLIT: "tags-split"
65
65
  };
66
- const OutputMockType = { MSW: "msw" };
66
+ const OutputMockType = {
67
+ MSW: "msw",
68
+ FAKER: "faker"
69
+ };
67
70
  const FormDataArrayHandling = {
68
71
  SERIALIZE: "serialize",
69
72
  EXPLODE: "explode",
@@ -77,6 +80,10 @@ const Verbs = {
77
80
  DELETE: "delete",
78
81
  HEAD: "head"
79
82
  };
83
+ /**
84
+ * Canonical tag name used for the generated bucket that collects untagged operations.
85
+ */
86
+ const DefaultTag = "default";
80
87
  const GetterPropType = {
81
88
  PARAM: "param",
82
89
  NAMED_PATH_PARAMS: "namedPathParams",
@@ -173,6 +180,20 @@ function isUrl(str) {
173
180
  return false;
174
181
  }
175
182
  }
183
+ /**
184
+ * Type guard for the MSW mock generator. Use to narrow a
185
+ * `GlobalMockOptions | ClientMockBuilder` value to `MswMockOptions`.
186
+ */
187
+ function isMswMock(mock) {
188
+ return !isFunction$1(mock) && mock.type === OutputMockType.MSW;
189
+ }
190
+ /**
191
+ * Type guard for the Faker mock generator. Use to narrow a
192
+ * `GlobalMockOptions | ClientMockBuilder` value to `FakerMockOptions`.
193
+ */
194
+ function isFakerMock(mock) {
195
+ return !isFunction$1(mock) && mock.type === OutputMockType.FAKER;
196
+ }
176
197
  //#endregion
177
198
  //#region src/utils/async-reduce.ts
178
199
  async function asyncReduce(array, reducer, initValue) {
@@ -361,7 +382,30 @@ function createDebugger(ns, options = {}) {
361
382
  //#region src/utils/doc.ts
362
383
  const search = String.raw`\*/`;
363
384
  const replacement = String.raw`*\/`;
364
- const regex$1 = new RegExp(search, "g");
385
+ const regex = new RegExp(search, "g");
386
+ const itemValidationKeys = [
387
+ "minLength",
388
+ "maxLength",
389
+ "minimum",
390
+ "maximum",
391
+ "exclusiveMinimum",
392
+ "exclusiveMaximum",
393
+ "minItems",
394
+ "maxItems",
395
+ "pattern"
396
+ ];
397
+ function getItemValidationDocEntries(schema, prefix = "items", visited = /* @__PURE__ */ new WeakSet()) {
398
+ if (!schema) return [];
399
+ if (visited.has(schema)) return [];
400
+ visited.add(schema);
401
+ return [...itemValidationKeys.flatMap((key) => {
402
+ const value = schema[key];
403
+ return value === void 0 ? [] : [{
404
+ key: `${prefix}.${key}`,
405
+ value
406
+ }];
407
+ }), ...getItemValidationDocEntries(schema.items, `${prefix}.items`, visited)];
408
+ }
365
409
  function jsDoc(schema, tryOneLine = false, context) {
366
410
  if (context?.output.override.jsDoc) {
367
411
  const { filter } = context.output.override.jsDoc;
@@ -369,7 +413,8 @@ function jsDoc(schema, tryOneLine = false, context) {
369
413
  }
370
414
  const { description, deprecated, summary, minLength, maxLength, minimum, maximum, exclusiveMinimum, exclusiveMaximum, minItems, maxItems, pattern } = schema;
371
415
  const isNullable = schema.type === "null" || Array.isArray(schema.type) && schema.type.includes("null");
372
- const lines = (Array.isArray(description) ? description.filter((d) => !d.includes("eslint-disable")) : [description ?? ""]).map((line) => line.replaceAll(regex$1, replacement));
416
+ const itemValidationDocEntries = getItemValidationDocEntries(schema.items);
417
+ const lines = (Array.isArray(description) ? description.filter((d) => !d.includes("eslint-disable")) : [description ?? ""]).flatMap((line) => line.split(/\r?\n/)).map((line) => line.replaceAll(regex, replacement));
373
418
  const count = [
374
419
  description,
375
420
  deprecated,
@@ -383,11 +428,12 @@ function jsDoc(schema, tryOneLine = false, context) {
383
428
  minItems?.toString(),
384
429
  maxItems?.toString(),
385
430
  isNullable ? "null" : "",
386
- pattern
431
+ pattern,
432
+ ...itemValidationDocEntries.map(({ value }) => value.toString())
387
433
  ].filter(Boolean).length;
388
434
  if (!count) return "";
389
435
  const oneLine = count === 1 && tryOneLine;
390
- const eslintDisable = Array.isArray(description) ? description.find((d) => d.includes("eslint-disable"))?.replaceAll(regex$1, replacement) : void 0;
436
+ const eslintDisable = Array.isArray(description) ? description.find((d) => d.includes("eslint-disable"))?.replaceAll(regex, replacement) : void 0;
391
437
  let doc = `${eslintDisable ? `/* ${eslintDisable} */\n` : ""}/**`;
392
438
  if (description) {
393
439
  if (!oneLine) doc += `\n${tryOneLine ? " " : ""} *`;
@@ -399,7 +445,7 @@ function jsDoc(schema, tryOneLine = false, context) {
399
445
  function tryAppendStringDocLine(key, value) {
400
446
  if (value) {
401
447
  appendPrefix();
402
- doc += ` @${key} ${value.replaceAll(regex$1, replacement)}`;
448
+ doc += ` @${key} ${value.replaceAll(regex, replacement)}`;
403
449
  }
404
450
  }
405
451
  function tryAppendBooleanDocLine(key, value) {
@@ -415,7 +461,7 @@ function jsDoc(schema, tryOneLine = false, context) {
415
461
  }
416
462
  }
417
463
  tryAppendBooleanDocLine("deprecated", deprecated);
418
- tryAppendStringDocLine("summary", summary?.replaceAll(regex$1, replacement));
464
+ tryAppendStringDocLine("summary", summary?.replaceAll(regex, replacement));
419
465
  tryAppendNumberDocLine("minLength", minLength);
420
466
  tryAppendNumberDocLine("maxLength", maxLength);
421
467
  tryAppendNumberDocLine("minimum", minimum);
@@ -426,6 +472,17 @@ function jsDoc(schema, tryOneLine = false, context) {
426
472
  tryAppendNumberDocLine("maxItems", maxItems);
427
473
  tryAppendBooleanDocLine("nullable", isNullable);
428
474
  tryAppendStringDocLine("pattern", pattern);
475
+ for (const { key, value } of itemValidationDocEntries) {
476
+ if (typeof value === "string") {
477
+ tryAppendStringDocLine(key, value);
478
+ continue;
479
+ }
480
+ if (typeof value === "number") {
481
+ tryAppendNumberDocLine(key, value);
482
+ continue;
483
+ }
484
+ tryAppendBooleanDocLine(key, value);
485
+ }
429
486
  doc += oneLine ? " " : `\n ${tryOneLine ? " " : ""}`;
430
487
  doc += "*/\n";
431
488
  return doc;
@@ -487,12 +544,12 @@ function getFileInfo(target = "", { backupFilename = "filename", extension = ".t
487
544
  };
488
545
  }
489
546
  async function removeFilesAndEmptyFolders(patterns, dir) {
490
- const files = await globby(patterns, {
547
+ const files = await glob(patterns, {
491
548
  cwd: dir,
492
549
  absolute: true
493
550
  });
494
551
  await Promise.all(files.map((file) => fs.promises.unlink(file)));
495
- const sortedDirectories = (await globby(["**/*"], {
552
+ const sortedDirectories = (await glob(["**/*"], {
496
553
  cwd: dir,
497
554
  absolute: true,
498
555
  onlyDirectories: true
@@ -506,11 +563,17 @@ async function removeFilesAndEmptyFolders(patterns, dir) {
506
563
  }
507
564
  //#endregion
508
565
  //#region src/utils/file-extensions.ts
566
+ /**
567
+ * Returns the filename suffix for a given mock entry's output file. For
568
+ * example a `{ type: OutputMockType.MSW }` entry produces `<file>.msw.ts` and
569
+ * a `{ type: OutputMockType.FAKER }` entry produces `<file>.faker.ts`.
570
+ *
571
+ * Custom `ClientMockBuilder` functions default to the `msw` suffix to preserve
572
+ * the historical behavior.
573
+ */
509
574
  function getMockFileExtensionByTypeName(mock) {
510
- if (isFunction(mock)) return "msw";
511
- switch (mock.type) {
512
- default: return "msw";
513
- }
575
+ if (isFunction(mock)) return OutputMockType.MSW;
576
+ return mock.type;
514
577
  }
515
578
  //#endregion
516
579
  //#region src/utils/get-property-safe.ts
@@ -1001,6 +1064,24 @@ function isSyntheticDefaultImportsAllow(config) {
1001
1064
  if (!config) return true;
1002
1065
  return !!(config.compilerOptions?.allowSyntheticDefaultImports ?? config.compilerOptions?.esModuleInterop);
1003
1066
  }
1067
+ const NODE_NEXT_MODULES = new Set(["nodenext", "node16"]);
1068
+ const NODE_NEXT_EXTENSION_MAP = [
1069
+ [".tsx", ".jsx"],
1070
+ [".mts", ".mjs"],
1071
+ [".cts", ".cjs"],
1072
+ [".ts", ".js"]
1073
+ ];
1074
+ function getImportExtension(fileExtension, tsconfig) {
1075
+ const compilerOptions = tsconfig?.compilerOptions;
1076
+ if (compilerOptions?.allowImportingTsExtensions) return fileExtension;
1077
+ const module = compilerOptions?.module?.toLowerCase();
1078
+ const moduleResolution = compilerOptions?.moduleResolution?.toLowerCase();
1079
+ if (module && NODE_NEXT_MODULES.has(module) || moduleResolution && NODE_NEXT_MODULES.has(moduleResolution)) {
1080
+ for (const [from, to] of NODE_NEXT_EXTENSION_MAP) if (fileExtension.endsWith(from)) return `${fileExtension.slice(0, -from.length)}${to}`;
1081
+ return fileExtension;
1082
+ }
1083
+ return fileExtension.replace(/\.ts$/, "") || "";
1084
+ }
1004
1085
  //#endregion
1005
1086
  //#region src/getters/enum.ts
1006
1087
  /**
@@ -1210,13 +1291,36 @@ function getCombinedEnumValue(inputs) {
1210
1291
  }
1211
1292
  //#endregion
1212
1293
  //#region src/getters/ref.ts
1294
+ /**
1295
+ * `$ref`s targeting these sections under `#/components/...` are emitted as
1296
+ * named TypeScript imports (e.g. `import type { Pet } from './model'`).
1297
+ * Refs to any other location — for example `#/paths/.../schema` produced by
1298
+ * JSON-Schema-Ref-Parser `bundle()` — have no corresponding `export type`
1299
+ * and must be inlined by the resolver. See issue #398.
1300
+ */
1301
+ const NAMED_COMPONENT_SECTIONS = [
1302
+ "schemas",
1303
+ "responses",
1304
+ "parameters",
1305
+ "requestBodies"
1306
+ ];
1213
1307
  const RefComponentSuffix = {
1214
1308
  schemas: "",
1215
1309
  responses: "Response",
1216
1310
  parameters: "Parameter",
1217
1311
  requestBodies: "Body"
1218
1312
  };
1219
- const regex = /* @__PURE__ */ new RegExp("~1", "g");
1313
+ const COMPONENT_REF_PATTERN = new RegExp(String.raw`^#\/components\/(${NAMED_COMPONENT_SECTIONS.join("|")})\/[^/]+$`);
1314
+ /**
1315
+ * True iff `ref` targets a named slot eligible for emission as a TypeScript
1316
+ * import. Used by `resolveValue` to decide between named import vs inlining
1317
+ * the resolved schema.
1318
+ */
1319
+ function isComponentRef(ref) {
1320
+ return COMPONENT_REF_PATTERN.test(ref);
1321
+ }
1322
+ const TILDE_1 = /~1/g;
1323
+ const TILDE_0 = /~0/g;
1220
1324
  /**
1221
1325
  * Return the output type from the $ref
1222
1326
  *
@@ -1224,7 +1328,7 @@ const regex = /* @__PURE__ */ new RegExp("~1", "g");
1224
1328
  */
1225
1329
  function getRefInfo($ref, context) {
1226
1330
  const [pathname, ref] = $ref.split("#");
1227
- const refPaths = ref.slice(1).split("/").map((part) => decodeURIComponent(part.replaceAll(regex, "/")));
1331
+ const refPaths = ref.slice(1).split("/").map((part) => decodeURIComponent(part).replaceAll(TILDE_1, "/").replaceAll(TILDE_0, "~"));
1228
1332
  const getOverrideSuffix = (override, paths) => {
1229
1333
  const firstLevel = override[paths[0]];
1230
1334
  if (!firstLevel) return "";
@@ -1358,7 +1462,34 @@ function resolveExampleRefs(examples, context) {
1358
1462
  //#region src/resolvers/value.ts
1359
1463
  function resolveValue({ schema, name, context, formDataContext }) {
1360
1464
  if (isReference(schema)) {
1465
+ const refValue = schema.$ref;
1361
1466
  const { schema: schemaObject, imports } = resolveRef(schema, context);
1467
+ if (refValue && !isComponentRef(refValue)) {
1468
+ if (context.parents?.includes(refValue)) return {
1469
+ value: "unknown",
1470
+ imports: [],
1471
+ schemas: [],
1472
+ type: "unknown",
1473
+ isEnum: false,
1474
+ originalSchema: schemaObject,
1475
+ hasReadonlyProps: false,
1476
+ isRef: false,
1477
+ dependencies: []
1478
+ };
1479
+ return {
1480
+ ...getScalar({
1481
+ item: schemaObject,
1482
+ name,
1483
+ context: {
1484
+ ...context,
1485
+ parents: [...context.parents ?? [], refValue]
1486
+ },
1487
+ formDataContext
1488
+ }),
1489
+ originalSchema: schemaObject,
1490
+ isRef: false
1491
+ };
1492
+ }
1362
1493
  const resolvedImport = imports[0];
1363
1494
  let hasReadonlyProps = false;
1364
1495
  const refName = resolvedImport.name;
@@ -1568,7 +1699,7 @@ function getArray({ schema, name, context, formDataContext }) {
1568
1699
  }
1569
1700
  //#endregion
1570
1701
  //#region src/getters/res-req-types.ts
1571
- const getSchemaType = (s) => s.type;
1702
+ const getSchemaType$1 = (s) => s.type;
1572
1703
  const getSchemaCombined = (s) => s.oneOf ?? s.anyOf ?? s.allOf;
1573
1704
  const getSchemaOneOf = (s) => s.oneOf;
1574
1705
  const getSchemaAnyOf = (s) => s.anyOf;
@@ -1581,9 +1712,14 @@ const formDataContentTypes = new Set(["multipart/form-data"]);
1581
1712
  const formUrlEncodedContentTypes = new Set(["application/x-www-form-urlencoded"]);
1582
1713
  function getResReqContentTypes({ mediaType, propName, context, isFormData, contentType }) {
1583
1714
  if (!mediaType.schema) return;
1715
+ const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1584
1716
  const formDataContext = isFormData ? {
1585
1717
  atPart: false,
1586
1718
  encoding: mediaType.encoding ?? {}
1719
+ } : isFormUrlEncoded ? {
1720
+ atPart: false,
1721
+ encoding: mediaType.encoding ?? {},
1722
+ urlEncoded: true
1587
1723
  } : void 0;
1588
1724
  const resolvedObject = resolveObject({
1589
1725
  schema: mediaType.schema,
@@ -1849,25 +1985,37 @@ function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequest
1849
1985
  form += `Object.entries(${propName} ?? {}).forEach(([key, value]) => {\n`;
1850
1986
  form += skipLine;
1851
1987
  form += ` if (value !== undefined && value !== null) {\n`;
1852
- form += ` if ((typeof File !== 'undefined' && value instanceof File) || value instanceof Blob) {\n`;
1853
- form += ` ${variableName}.append(key, value);\n`;
1854
- form += ` } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {\n`;
1855
- form += ` ${variableName}.append(key, new Blob([Uint8Array.from(value)]));\n`;
1856
- form += ` } else if (Array.isArray(value)) {\n`;
1857
- form += ` value.forEach(v => {\n`;
1858
- form += ` if ((typeof File !== 'undefined' && v instanceof File) || v instanceof Blob) {\n`;
1859
- form += ` ${variableName}.append(key, v);\n`;
1860
- form += ` } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(v)) {\n`;
1861
- form += ` ${variableName}.append(key, new Blob([Uint8Array.from(v)]));\n`;
1862
- form += ` } else {\n`;
1863
- form += ` ${variableName}.append(key, typeof v === 'object' ? JSON.stringify(v) : String(v));\n`;
1864
- form += ` }\n`;
1865
- form += ` });\n`;
1866
- form += ` } else if (typeof value === 'object') {\n`;
1867
- form += ` ${variableName}.append(key, JSON.stringify(value));\n`;
1868
- form += ` } else {\n`;
1869
- form += ` ${variableName}.append(key, String(value));\n`;
1870
- form += ` }\n`;
1988
+ if (isUrlEncoded) {
1989
+ form += ` if (Array.isArray(value)) {\n`;
1990
+ form += ` value.forEach(v => {\n`;
1991
+ form += ` ${variableName}.append(key, typeof v === 'object' ? JSON.stringify(v) : String(v));\n`;
1992
+ form += ` });\n`;
1993
+ form += ` } else if (typeof value === 'object') {\n`;
1994
+ form += ` ${variableName}.append(key, JSON.stringify(value));\n`;
1995
+ form += ` } else {\n`;
1996
+ form += ` ${variableName}.append(key, String(value));\n`;
1997
+ form += ` }\n`;
1998
+ } else {
1999
+ form += ` if ((typeof File !== 'undefined' && value instanceof File) || value instanceof Blob) {\n`;
2000
+ form += ` ${variableName}.append(key, value);\n`;
2001
+ form += ` } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {\n`;
2002
+ form += ` ${variableName}.append(key, new Blob([Uint8Array.from(value)]));\n`;
2003
+ form += ` } else if (Array.isArray(value)) {\n`;
2004
+ form += ` value.forEach(v => {\n`;
2005
+ form += ` if ((typeof File !== 'undefined' && v instanceof File) || v instanceof Blob) {\n`;
2006
+ form += ` ${variableName}.append(key, v);\n`;
2007
+ form += ` } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(v)) {\n`;
2008
+ form += ` ${variableName}.append(key, new Blob([Uint8Array.from(v)]));\n`;
2009
+ form += ` } else {\n`;
2010
+ form += ` ${variableName}.append(key, typeof v === 'object' ? JSON.stringify(v) : String(v));\n`;
2011
+ form += ` }\n`;
2012
+ form += ` });\n`;
2013
+ form += ` } else if (typeof value === 'object') {\n`;
2014
+ form += ` ${variableName}.append(key, JSON.stringify(value));\n`;
2015
+ form += ` } else {\n`;
2016
+ form += ` ${variableName}.append(key, String(value));\n`;
2017
+ form += ` }\n`;
2018
+ }
1871
2019
  form += ` }\n`;
1872
2020
  form += `});\n`;
1873
2021
  } else {
@@ -1912,6 +2060,7 @@ function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequest
1912
2060
  }
1913
2061
  function resolveSchemaPropertiesToFormData({ schema, variableName, propName, context, isRequestBodyOptional, keyPrefix = "", depth = 0, encoding }) {
1914
2062
  let formDataValues = "";
2063
+ const isUrlEncoded = variableName === "formUrlEncoded";
1915
2064
  const schemaProps = getSchemaProperties(schema) ?? {};
1916
2065
  for (const [key, value] of Object.entries(schemaProps)) {
1917
2066
  const { schema: property } = resolveSchemaRef(value, context);
@@ -1924,7 +2073,8 @@ function resolveSchemaPropertiesToFormData({ schema, variableName, propName, con
1924
2073
  const nonOptionalValueKey = `${propName}${formattedKey}`;
1925
2074
  const fileType = getFormDataFieldFileType(property, partContentType);
1926
2075
  const effectiveContentType = partContentType ?? property.contentMediaType;
1927
- if (fileType === "binary" || property.format === "binary") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
2076
+ if (isUrlEncoded && (fileType || property.format === "binary")) formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
2077
+ else if (fileType === "binary" || property.format === "binary") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
1928
2078
  else if (fileType === "text") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey} instanceof Blob ? ${nonOptionalValueKey} : new Blob([${nonOptionalValueKey}], { type: '${effectiveContentType}' }));\n`;
1929
2079
  else if (property.type === "object") formDataValue = context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE ? resolveSchemaPropertiesToFormData({
1930
2080
  schema: property,
@@ -1957,7 +2107,7 @@ function resolveSchemaPropertiesToFormData({ schema, variableName, propName, con
1957
2107
  ${resolvedValue}});\n`;
1958
2108
  } else valueStr = "JSON.stringify(value)";
1959
2109
  else {
1960
- const itemType = getSchemaType(itemSchema);
2110
+ const itemType = getSchemaType$1(itemSchema);
1961
2111
  if (itemType === "number" || Array.isArray(itemType) && itemType.includes("number") || itemType === "integer" || Array.isArray(itemType) && itemType.includes("integer") || itemType === "boolean" || Array.isArray(itemType) && itemType.includes("boolean")) valueStr = "value.toString()";
1962
2112
  }
1963
2113
  }
@@ -1965,7 +2115,7 @@ function resolveSchemaPropertiesToFormData({ schema, variableName, propName, con
1965
2115
  if (!hasNonPrimitiveChild) formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => ${variableName}.append(\`${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}]\`, ${valueStr}));\n`;
1966
2116
  } else formDataValue = `${valueKey}.forEach(value => ${variableName}.append(\`${keyPrefix}${key}${context.output.override.formData.arrayHandling === FormDataArrayHandling.SERIALIZE_WITH_BRACKETS ? "[]" : ""}\`, ${valueStr}));\n`;
1967
2117
  } else if ((() => {
1968
- const propType = getSchemaType(property);
2118
+ const propType = getSchemaType$1(property);
1969
2119
  return propType === "number" || Array.isArray(propType) && propType.includes("number") || propType === "integer" || Array.isArray(propType) && propType.includes("integer") || propType === "boolean" || Array.isArray(propType) && propType.includes("boolean");
1970
2120
  })()) formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey}.toString())\n`;
1971
2121
  else formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
@@ -1989,7 +2139,7 @@ function resolveSchemaPropertiesToFormData({ schema, variableName, propName, con
1989
2139
  })) existSubSchemaNullable = true;
1990
2140
  }
1991
2141
  const isRequired = getSchemaRequired(schema)?.includes(key) && !isRequestBodyOptional;
1992
- const propType = getSchemaType(property);
2142
+ const propType = getSchemaType$1(property);
1993
2143
  if (property.nullable || Array.isArray(propType) && propType.includes("null") || existSubSchemaNullable) {
1994
2144
  if (isRequired) {
1995
2145
  formDataValues += `if(${valueKey} !== null) {\n ${formDataValue} }\n`;
@@ -2241,7 +2391,8 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2241
2391
  if (Object.keys(allSpecSchemas).some((schemaName) => pascal(schemaName) === propName)) propName = propName + "Property";
2242
2392
  const propertyFormDataContext = formDataContext && !formDataContext.atPart ? {
2243
2393
  atPart: true,
2244
- partContentType: formDataContext.encoding[key]?.contentType
2394
+ partContentType: formDataContext.encoding[key]?.contentType,
2395
+ urlEncoded: formDataContext.urlEncoded
2245
2396
  } : void 0;
2246
2397
  const resolvedValue = resolveObject({
2247
2398
  schema,
@@ -2253,7 +2404,7 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2253
2404
  if (!index) acc.value += "{";
2254
2405
  const doc = jsDoc(schema, true, context);
2255
2406
  const propertyDoc = doc ? `${doc.trimEnd().split("\n").map((line) => ` ${line}`).join("\n")}\n` : "";
2256
- if (isReadOnly) acc.hasReadonlyProps = true;
2407
+ if (isReadOnly || resolvedValue.hasReadonlyProps) acc.hasReadonlyProps = true;
2257
2408
  const constValue = "const" in schema ? schema.const : void 0;
2258
2409
  const hasConst = constValue !== void 0;
2259
2410
  let constLiteral;
@@ -2518,11 +2669,13 @@ function getScalar({ item, name, context, formDataContext }) {
2518
2669
  value = enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `);
2519
2670
  isEnum = true;
2520
2671
  }
2521
- if (schemaFormat === "binary") value = "Blob";
2522
- else if (formDataContext?.atPart) {
2523
- const fileType = getFormDataFieldFileType(item, formDataContext.partContentType);
2524
- if (fileType) value = fileType === "binary" ? "Blob" : "Blob | string";
2525
- } else if (schemaContentMediaType === "application/octet-stream" && !schemaContentEncoding) value = "Blob";
2672
+ if (!formDataContext?.urlEncoded) {
2673
+ if (schemaFormat === "binary") value = "Blob";
2674
+ else if (formDataContext?.atPart) {
2675
+ const fileType = getFormDataFieldFileType(item, formDataContext.partContentType);
2676
+ if (fileType) value = fileType === "binary" ? "Blob" : "Blob | string";
2677
+ } else if (schemaContentMediaType === "application/octet-stream" && !schemaContentEncoding) value = "Blob";
2678
+ }
2526
2679
  if (context.output.override.useDates && (schemaFormat === "date" || schemaFormat === "date-time")) value = "Date";
2527
2680
  value += nullable;
2528
2681
  if (schemaConst) value = `'${schemaConst}'`;
@@ -2863,10 +3016,13 @@ function getParameters({ parameters, context }) {
2863
3016
  const { schema, imports } = resolveRef(p, context);
2864
3017
  const parameter = schema;
2865
3018
  const location = parameter.in;
2866
- if (location === "path" || location === "query" || location === "header") result[location].push({
2867
- parameter,
2868
- imports
2869
- });
3019
+ if (location === "path" || location === "query" || location === "header") {
3020
+ const safeImports = p.$ref && isComponentRef(p.$ref) ? imports : [];
3021
+ result[location].push({
3022
+ parameter,
3023
+ imports: safeImports
3024
+ });
3025
+ }
2870
3026
  } else if (p.in === "query" || p.in === "path" || p.in === "header") result[p.in].push({
2871
3027
  parameter: p,
2872
3028
  imports: []
@@ -2999,6 +3155,50 @@ const isOpenApiSchemaObject = (value) => {
2999
3155
  if (!value || typeof value !== "object") return false;
3000
3156
  return !("$ref" in value);
3001
3157
  };
3158
+ /**
3159
+ * A `$ref` schema object (e.g. array `items` or a oneOf/anyOf/allOf variant
3160
+ * pointing at a component). We don't resolve the reference here, but a query
3161
+ * parameter behind a `$ref` is virtually always a complex (object-like) type,
3162
+ * so it must be treated as non-primitive. Over-flagging is harmless: the only
3163
+ * consumer (the Angular `nonPrimitiveKeys` passthrough) is gated on a
3164
+ * configured `paramsSerializer`, which is precisely what handles raw values.
3165
+ */
3166
+ const isRefObject = (value) => !!value && typeof value === "object" && "$ref" in value;
3167
+ const getSchemaType = (schema) => {
3168
+ const type = schema.type;
3169
+ if (typeof type === "string") return type;
3170
+ if (Array.isArray(type) && type.every((variant) => typeof variant === "string")) return type;
3171
+ };
3172
+ /**
3173
+ * Detects whether a query parameter's resolved schema is non-primitive — i.e.
3174
+ * an object, an array of objects, or a composition (oneOf/anyOf/allOf) that
3175
+ * resolves to a non-primitive shape.
3176
+ *
3177
+ * Used by Angular generators so the default `filterParams` helper preserves
3178
+ * such values instead of silently dropping them. Angular's `HttpParams` only
3179
+ * accepts primitives, but a user-provided `paramsSerializer`, `mutator`, or
3180
+ * `paramsFilter` may need the raw object to flatten or stringify it.
3181
+ */
3182
+ const isSchemaNonPrimitive = (schema) => {
3183
+ const schemaType = getSchemaType(schema);
3184
+ const type = Array.isArray(schemaType) ? schemaType.filter((variant) => variant !== "null") : schemaType;
3185
+ const additionalProperties = schema.additionalProperties;
3186
+ if (type === "object") return true;
3187
+ if (Array.isArray(type) && type.includes("object")) return true;
3188
+ if (type === "array" || Array.isArray(type) && type.includes("array")) {
3189
+ const items = schema.items;
3190
+ if (isOpenApiSchemaObject(items)) return isSchemaNonPrimitive(items);
3191
+ return true;
3192
+ }
3193
+ const compositions = [
3194
+ ...Array.isArray(schema.oneOf) ? schema.oneOf : [],
3195
+ ...Array.isArray(schema.anyOf) ? schema.anyOf : [],
3196
+ ...Array.isArray(schema.allOf) ? schema.allOf : []
3197
+ ];
3198
+ if (compositions.length > 0) return compositions.some((variant) => isOpenApiSchemaObject(variant) ? isSchemaNonPrimitive(variant) : isRefObject(variant));
3199
+ if (!type && (schema.properties !== void 0 || additionalProperties !== void 0 && additionalProperties !== false)) return true;
3200
+ return false;
3201
+ };
3002
3202
  const isSchemaNullable = (schema) => {
3003
3203
  if (schema.nullable === true) return true;
3004
3204
  if (schema.type === "null") return true;
@@ -3076,6 +3276,7 @@ function getQueryParams({ queryParams, operationName, context, suffix = "params"
3076
3276
  const type = types.map(({ definition }) => definition).join("\n");
3077
3277
  const allOptional = queryParams.every(({ parameter }) => !parameter.required);
3078
3278
  const requiredNullableKeys = types.filter(({ required, originalSchema }) => required && isSchemaNullable(originalSchema)).map(({ name }) => name);
3279
+ const nonPrimitiveKeys = types.filter(({ originalSchema }) => isSchemaNonPrimitive(originalSchema)).map(({ name }) => name);
3079
3280
  return {
3080
3281
  schema: {
3081
3282
  name,
@@ -3084,7 +3285,8 @@ function getQueryParams({ queryParams, operationName, context, suffix = "params"
3084
3285
  },
3085
3286
  deps: schemas,
3086
3287
  isOptional: allOptional,
3087
- requiredNullableKeys
3288
+ requiredNullableKeys,
3289
+ ...nonPrimitiveKeys.length > 0 ? { nonPrimitiveKeys } : {}
3088
3290
  };
3089
3291
  }
3090
3292
  //#endregion
@@ -3109,7 +3311,7 @@ function getResponse({ responses, operationName, context, contentType }) {
3109
3311
  success: success || (defaultType ?? "unknown"),
3110
3312
  errors: errors || (defaultType ?? "unknown")
3111
3313
  },
3112
- isBlob: groupedByStatus.success.some((t) => !!t.contentType && isBinaryContentType(t.contentType) || t.value === "Blob" && !t.isRef),
3314
+ isBlob: groupedByStatus.success.some((t) => !!t.contentType && isBinaryContentType(t.contentType) || t.originalSchema?.format === "binary" || t.originalSchema?.contentMediaType === "application/octet-stream" && !t.originalSchema.contentEncoding),
3113
3315
  types: groupedByStatus,
3114
3316
  contentTypes,
3115
3317
  schemas,
@@ -3202,6 +3404,8 @@ function getBaseUrlRuntimeImports(baseUrl) {
3202
3404
  values: imp.values ?? true
3203
3405
  }));
3204
3406
  }
3407
+ const wrapRouteParameters = (route, prepend, append) => route.replaceAll(TEMPLATE_TAG_REGEX, `\${${prepend}$1${append}}`);
3408
+ const makeRouteSafe = (route) => wrapRouteParameters(route, "encodeURIComponent(String(", "))");
3205
3409
  function getRouteAsArray(route) {
3206
3410
  return route.split("/").filter((i) => i !== "").flatMap((segment) => {
3207
3411
  if (!segment.includes("${")) return [`'${segment}'`];
@@ -3240,11 +3444,11 @@ function generateComponentDefinition(responses = {}, context, suffix) {
3240
3444
  }
3241
3445
  //#endregion
3242
3446
  //#region src/generators/imports.ts
3243
- function generateImports({ imports, namingConvention = NamingConvention.CAMEL_CASE }) {
3447
+ function generateImports({ imports, namingConvention = NamingConvention.CAMEL_CASE, importExtension = "" }) {
3244
3448
  if (imports.length === 0) return "";
3245
3449
  const grouped = groupBy(uniqueWith(imports, (a, b) => a.name === b.name && a.default === b.default && a.alias === b.alias && a.values === b.values && a.isConstant === b.isConstant && a.namespaceImport === b.namespaceImport && a.syntheticDefaultImport === b.syntheticDefaultImport && a.importPath === b.importPath).map((imp) => ({
3246
3450
  ...imp,
3247
- importPath: imp.importPath ?? `./${conventionName(imp.name, namingConvention)}`
3451
+ importPath: imp.importPath ?? `./${conventionName(imp.name, namingConvention)}${importExtension}`
3248
3452
  })), (imp) => !imp.default && !imp.namespaceImport && !imp.syntheticDefaultImport && !imp.values && !imp.isConstant ? `aggregate|${imp.importPath}` : `single|${imp.importPath}|${imp.name}|${imp.alias ?? ""}|${String(imp.default)}|${String(imp.namespaceImport)}|${String(imp.syntheticDefaultImport)}|${String(imp.values)}|${String(imp.isConstant)}`);
3249
3453
  return Object.entries(grouped).toSorted(([a], [b]) => a.localeCompare(b, "en", { numeric: true })).map(([, group]) => {
3250
3454
  const sample = group[0];
@@ -3256,7 +3460,8 @@ function generateImports({ imports, namingConvention = NamingConvention.CAMEL_CA
3256
3460
  function generateMutatorImports({ mutators, implementation, oneMore }) {
3257
3461
  let imports = "";
3258
3462
  for (const mutator of uniqueWith(mutators, (a, b) => a.name === b.name && a.default === b.default)) {
3259
- const path = `${oneMore ? "../" : ""}${mutator.path}`;
3463
+ const isRelativeImport = mutator.path.startsWith(".");
3464
+ const path = `${oneMore && isRelativeImport ? "../" : ""}${mutator.path}`;
3260
3465
  const importDefault = mutator.default ? mutator.name : `{ ${mutator.name} }`;
3261
3466
  imports += `import ${importDefault} from '${path}';`;
3262
3467
  imports += "\n";
@@ -3451,7 +3656,7 @@ function generateModelsInline(obj) {
3451
3656
  //#region src/generators/mutator-info.ts
3452
3657
  async function getMutatorInfo(filePath, options) {
3453
3658
  const { root = process.cwd(), namedExport = "default", alias, external, tsconfig } = options ?? {};
3454
- return parseFile(await bundleFile(root, filePath, alias, external, tsconfig?.compilerOptions), namedExport, getEcmaVersion(tsconfig?.compilerOptions?.target));
3659
+ return parseFile(await bundleFile(root, filePath, alias, external, tsconfig?.compilerOptions), namedExport);
3455
3660
  }
3456
3661
  async function bundleFile(root, fileName, alias, external, compilerOptions) {
3457
3662
  const { text } = (await build({
@@ -3474,10 +3679,10 @@ async function bundleFile(root, fileName, alias, external, compilerOptions) {
3474
3679
  })).outputFiles[0];
3475
3680
  return text;
3476
3681
  }
3477
- function parseFile(file, name, ecmaVersion = 6) {
3682
+ function parseFile(file, name) {
3478
3683
  try {
3479
3684
  const ast = Parser.parse(file, {
3480
- ecmaVersion,
3685
+ ecmaVersion: "latest",
3481
3686
  sourceType: "module"
3482
3687
  });
3483
3688
  const foundSpecifier = ast.body.filter((x) => x.type === "ExportNamedDeclaration").flatMap((x) => x.specifiers).find((x) => x.exported.type === "Identifier" && x.exported.name === name && x.local.type === "Identifier");
@@ -3534,15 +3739,6 @@ function parseFunction(ast, funcName) {
3534
3739
  }
3535
3740
  }
3536
3741
  }
3537
- function getEcmaVersion(target) {
3538
- if (!target) return;
3539
- if (target.toLowerCase() === "esnext") return "latest";
3540
- try {
3541
- return Number(target.toLowerCase().replace("es", ""));
3542
- } catch {
3543
- return;
3544
- }
3545
- }
3546
3742
  //#endregion
3547
3743
  //#region src/generators/mutator.ts
3548
3744
  const BODY_TYPE_NAME = "BodyType";
@@ -3603,16 +3799,24 @@ function removeComments(file) {
3603
3799
  * (e.g. observe-mode branches), prefer getAngularFilteredParamsCallExpression +
3604
3800
  * getAngularFilteredParamsHelperBody instead.
3605
3801
  */
3606
- const getAngularFilteredParamsExpression = (paramsExpression, requiredNullableParamKeys = [], preserveRequiredNullables = false) => {
3607
- const filteredParamValueType = `string | number | boolean${preserveRequiredNullables ? " | null" : ""} | Array<string | number | boolean>`;
3802
+ const getAngularFilteredParamsExpression = (paramsExpression, requiredNullableParamKeys = [], preserveRequiredNullables = false, nonPrimitiveKeys = []) => {
3803
+ const hasPassthrough = nonPrimitiveKeys.length > 0;
3804
+ const filteredParamValueType = hasPassthrough ? "unknown" : `string | number | boolean${preserveRequiredNullables ? " | null" : ""} | Array<string | number | boolean>`;
3805
+ const passthroughBranch = hasPassthrough ? ` if (passthroughKeys.has(key)) {
3806
+ if (value !== undefined) {
3807
+ filteredParams[key] = value;
3808
+ }
3809
+ continue;
3810
+ }
3811
+ ` : "";
3608
3812
  const preserveNullableBranch = preserveRequiredNullables ? ` } else if (value === null && requiredNullableParamKeys.has(key)) {
3609
3813
  filteredParams[key] = null;
3610
3814
  ` : "";
3611
3815
  return `(() => {
3612
- const requiredNullableParamKeys = new Set<string>(${JSON.stringify(requiredNullableParamKeys)});
3816
+ ${hasPassthrough ? ` const passthroughKeys = new Set<string>(${JSON.stringify(nonPrimitiveKeys)});\n` : ""} const requiredNullableParamKeys = new Set<string>(${JSON.stringify(requiredNullableParamKeys)});
3613
3817
  const filteredParams: Record<string, ${filteredParamValueType}> = {};
3614
3818
  for (const [key, value] of Object.entries(${paramsExpression})) {
3615
- if (Array.isArray(value)) {
3819
+ ${passthroughBranch} if (Array.isArray(value)) {
3616
3820
  const filtered = value.filter(
3617
3821
  (item) =>
3618
3822
  item != null &&
@@ -3647,19 +3851,34 @@ function filterParams(
3647
3851
  params: Record<string, unknown>,
3648
3852
  requiredNullableKeys?: ReadonlySet<string>,
3649
3853
  preserveRequiredNullables?: false,
3854
+ passthroughKeys?: undefined,
3650
3855
  ): Record<string, AngularHttpParamValue>;
3651
3856
  function filterParams(
3652
3857
  params: Record<string, unknown>,
3653
3858
  requiredNullableKeys: ReadonlySet<string> | undefined,
3654
3859
  preserveRequiredNullables: true,
3860
+ passthroughKeys?: undefined,
3655
3861
  ): Record<string, AngularHttpParamValueWithNullable>;
3862
+ function filterParams(
3863
+ params: Record<string, unknown>,
3864
+ requiredNullableKeys: ReadonlySet<string> | undefined,
3865
+ preserveRequiredNullables: boolean | undefined,
3866
+ passthroughKeys: ReadonlySet<string>,
3867
+ ): Record<string, unknown>;
3656
3868
  function filterParams(
3657
3869
  params: Record<string, unknown>,
3658
3870
  requiredNullableKeys: ReadonlySet<string> = new Set(),
3659
3871
  preserveRequiredNullables = false,
3660
- ): Record<string, AngularHttpParamValueWithNullable> {
3661
- const filteredParams: Record<string, AngularHttpParamValueWithNullable> = {};
3872
+ passthroughKeys: ReadonlySet<string> = new Set(),
3873
+ ): Record<string, unknown> {
3874
+ const filteredParams: Record<string, unknown> = {};
3662
3875
  for (const [key, value] of Object.entries(params)) {
3876
+ if (passthroughKeys.has(key)) {
3877
+ if (value !== undefined) {
3878
+ filteredParams[key] = value;
3879
+ }
3880
+ continue;
3881
+ }
3663
3882
  if (Array.isArray(value)) {
3664
3883
  const filtered = value.filter(
3665
3884
  (item) =>
@@ -3691,14 +3910,34 @@ function filterParams(
3691
3910
  /**
3692
3911
  * Returns a call expression to the `filterParams` helper function.
3693
3912
  */
3694
- const getAngularFilteredParamsCallExpression = (paramsExpression, requiredNullableParamKeys = [], preserveRequiredNullables = false) => `filterParams(${paramsExpression}, new Set<string>(${JSON.stringify(requiredNullableParamKeys)})${preserveRequiredNullables ? ", true" : ""})`;
3913
+ const getAngularFilteredParamsCallExpression = (paramsExpression, requiredNullableParamKeys = [], preserveRequiredNullables = false, nonPrimitiveKeys = []) => {
3914
+ const baseArgs = `${paramsExpression}, new Set<string>(${JSON.stringify(requiredNullableParamKeys)})`;
3915
+ if (nonPrimitiveKeys.length > 0) return `filterParams(${baseArgs}, ${preserveRequiredNullables}, new Set<string>(${JSON.stringify(nonPrimitiveKeys)}))`;
3916
+ return `filterParams(${baseArgs}${preserveRequiredNullables ? ", true" : ""})`;
3917
+ };
3918
+ /**
3919
+ * Returns the filter call/IIFE used to massage query params before passing
3920
+ * them to Angular's HttpParams. When the user supplied a `paramsFilter`
3921
+ * mutator, the built-in `filterParams` is bypassed entirely and the user's
3922
+ * function is called with the raw params — they own nullish-stripping and
3923
+ * any object/array handling. Otherwise the built-in filter is used (either
3924
+ * the shared helper or an inline IIFE), and callers should only pass
3925
+ * `nonPrimitiveKeys` when a downstream serializer or custom consumer can
3926
+ * legally handle raw object/array values.
3927
+ */
3928
+ const buildAngularParamsFilterExpression = ({ paramsExpression, requiredNullableParamKeys = [], preserveRequiredNullables = false, nonPrimitiveKeys = [], paramsFilter, useSharedHelper }) => {
3929
+ if (paramsFilter) return `${paramsFilter.name}(${paramsExpression})`;
3930
+ if (useSharedHelper) return getAngularFilteredParamsCallExpression(paramsExpression, requiredNullableParamKeys, preserveRequiredNullables, nonPrimitiveKeys);
3931
+ return getAngularFilteredParamsExpression(paramsExpression, requiredNullableParamKeys, preserveRequiredNullables, nonPrimitiveKeys);
3932
+ };
3695
3933
  function generateBodyOptions(body, isFormData, isFormUrlEncoded) {
3696
3934
  if (isFormData && body.formData) return "formData";
3697
3935
  if (isFormUrlEncoded && body.formUrlEncoded) return "formUrlEncoded";
3698
3936
  if (body.implementation) return body.implementation;
3699
3937
  }
3700
- function generateAxiosOptions({ response, isExactOptionalPropertyTypes, angularObserve, angularParamsRef, requiredNullableQueryParamKeys, queryParams, headers, requestOptions, hasSignal, hasSignalParam = false, isVue, isAngular, paramsSerializer, paramsSerializerOptions }) {
3938
+ function generateAxiosOptions({ response, isExactOptionalPropertyTypes, angularObserve, angularParamsRef, requiredNullableQueryParamKeys, nonPrimitiveQueryParamKeys, queryParams, headers, requestOptions, hasSignal, hasSignalParam = false, isVue, isAngular, paramsSerializer, paramsSerializerOptions, paramsFilter }) {
3701
3939
  const isRequestOptions = requestOptions !== false;
3940
+ const angularPassthroughQueryParamKeys = paramsSerializer ? nonPrimitiveQueryParamKeys : [];
3702
3941
  const signalVar = hasSignalParam ? "querySignal" : "signal";
3703
3942
  const signalProp = hasSignalParam ? `signal: ${signalVar}` : "signal";
3704
3943
  if (!queryParams && !headers && !response.isBlob && response.definition.success !== "string") {
@@ -3712,7 +3951,14 @@ function generateAxiosOptions({ response, isExactOptionalPropertyTypes, angularO
3712
3951
  let value = "";
3713
3952
  if (!isRequestOptions) {
3714
3953
  if (queryParams) if (isAngular) {
3715
- const iifeExpr = getAngularFilteredParamsExpression("params ?? {}", requiredNullableQueryParamKeys, !!paramsSerializer);
3954
+ const iifeExpr = buildAngularParamsFilterExpression({
3955
+ paramsExpression: "params ?? {}",
3956
+ requiredNullableParamKeys: requiredNullableQueryParamKeys,
3957
+ preserveRequiredNullables: !!paramsSerializer,
3958
+ nonPrimitiveKeys: angularPassthroughQueryParamKeys,
3959
+ paramsFilter,
3960
+ useSharedHelper: false
3961
+ });
3716
3962
  value += paramsSerializer ? `\n params: ${paramsSerializer.name}(${iifeExpr}),` : `\n params: ${iifeExpr},`;
3717
3963
  } else value += "\n params,";
3718
3964
  if (headers) value += "\n headers,";
@@ -3729,10 +3975,25 @@ function generateAxiosOptions({ response, isExactOptionalPropertyTypes, angularO
3729
3975
  if (queryParams) if (isVue) value += "\n params: {...unref(params), ...options?.params},";
3730
3976
  else if (isAngular && angularParamsRef) value += `\n params: ${angularParamsRef},`;
3731
3977
  else if (isAngular && paramsSerializer) {
3732
- const callExpr = getAngularFilteredParamsCallExpression("{...params, ...options?.params}", requiredNullableQueryParamKeys, true);
3978
+ const callExpr = buildAngularParamsFilterExpression({
3979
+ paramsExpression: "{...params, ...options?.params}",
3980
+ requiredNullableParamKeys: requiredNullableQueryParamKeys,
3981
+ preserveRequiredNullables: true,
3982
+ nonPrimitiveKeys: angularPassthroughQueryParamKeys,
3983
+ paramsFilter,
3984
+ useSharedHelper: true
3985
+ });
3733
3986
  value += `\n params: ${paramsSerializer.name}(${callExpr}),`;
3734
- } else if (isAngular) value += `\n params: ${getAngularFilteredParamsCallExpression("{...params, ...options?.params}", requiredNullableQueryParamKeys)},`;
3735
- else value += "\n params: {...params, ...options?.params},";
3987
+ } else if (isAngular) {
3988
+ const callExpr = buildAngularParamsFilterExpression({
3989
+ paramsExpression: "{...params, ...options?.params}",
3990
+ requiredNullableParamKeys: requiredNullableQueryParamKeys,
3991
+ nonPrimitiveKeys: angularPassthroughQueryParamKeys,
3992
+ paramsFilter,
3993
+ useSharedHelper: true
3994
+ });
3995
+ value += `\n params: ${callExpr},`;
3996
+ } else value += "\n params: {...params, ...options?.params},";
3736
3997
  if (headers) value += "\n headers: {...headers, ...options?.headers},";
3737
3998
  }
3738
3999
  if (!isAngular && queryParams && (paramsSerializer || paramsSerializerOptions?.qs)) {
@@ -3741,13 +4002,14 @@ function generateAxiosOptions({ response, isExactOptionalPropertyTypes, angularO
3741
4002
  }
3742
4003
  return value;
3743
4004
  }
3744
- function generateOptions({ route, body, angularObserve, angularParamsRef, headers, queryParams, response, verb, requestOptions, isFormData, isFormUrlEncoded, isAngular, isExactOptionalPropertyTypes, hasSignal, hasSignalParam, isVue, paramsSerializer, paramsSerializerOptions }) {
4005
+ function generateOptions({ route, body, angularObserve, angularParamsRef, headers, queryParams, response, verb, requestOptions, isFormData, isFormUrlEncoded, isAngular, isExactOptionalPropertyTypes, hasSignal, hasSignalParam, isVue, paramsSerializer, paramsSerializerOptions, paramsFilter }) {
3745
4006
  const bodyIdentifier = getIsBodyVerb(verb) ? generateBodyOptions(body, isFormData, isFormUrlEncoded) : void 0;
3746
4007
  const axiosOptions = generateAxiosOptions({
3747
4008
  response,
3748
4009
  angularObserve,
3749
4010
  angularParamsRef,
3750
4011
  requiredNullableQueryParamKeys: queryParams?.requiredNullableKeys,
4012
+ nonPrimitiveQueryParamKeys: queryParams?.nonPrimitiveKeys,
3751
4013
  queryParams: queryParams?.schema,
3752
4014
  headers: headers?.schema,
3753
4015
  requestOptions,
@@ -3757,7 +4019,8 @@ function generateOptions({ route, body, angularObserve, angularParamsRef, header
3757
4019
  isVue: isVue ?? false,
3758
4020
  isAngular: isAngular ?? false,
3759
4021
  paramsSerializer,
3760
- paramsSerializerOptions
4022
+ paramsSerializerOptions,
4023
+ paramsFilter
3761
4024
  });
3762
4025
  const trimmedAxiosOptions = axiosOptions.trim();
3763
4026
  const isRawOptionsArgument = trimmedAxiosOptions === "options" || trimmedAxiosOptions.startsWith("(") && trimmedAxiosOptions.endsWith(")") || trimmedAxiosOptions.startsWith("{") && trimmedAxiosOptions.endsWith("}");
@@ -3776,18 +4039,26 @@ function generateBodyMutatorConfig(body, isFormData, isFormUrlEncoded) {
3776
4039
  if (body.implementation) return `,\n data: ${body.implementation}`;
3777
4040
  return "";
3778
4041
  }
3779
- function generateQueryParamsAxiosConfig(response, isVue, isAngular, requiredNullableQueryParamKeys, queryParams) {
4042
+ function generateQueryParamsAxiosConfig(response, isVue, isAngular, requiredNullableQueryParamKeys, queryParams, paramsFilter) {
3780
4043
  if (!queryParams && !response.isBlob) return "";
3781
4044
  let value = "";
3782
4045
  if (queryParams) if (isVue) value += ",\n params: unref(params)";
3783
- else if (isAngular) value += `,\n params: ${getAngularFilteredParamsExpression("params ?? {}", requiredNullableQueryParamKeys)}`;
3784
- else value += ",\n params";
4046
+ else if (isAngular) {
4047
+ const paramsExpr = buildAngularParamsFilterExpression({
4048
+ paramsExpression: "params ?? {}",
4049
+ requiredNullableParamKeys: requiredNullableQueryParamKeys,
4050
+ nonPrimitiveKeys: queryParams.nonPrimitiveKeys,
4051
+ paramsFilter,
4052
+ useSharedHelper: false
4053
+ });
4054
+ value += `,\n params: ${paramsExpr}`;
4055
+ } else value += ",\n params";
3785
4056
  if (response.isBlob) value += `,\n responseType: 'blob'`;
3786
4057
  return value;
3787
4058
  }
3788
- function generateMutatorConfig({ route, body, headers, queryParams, response, verb, isFormData, isFormUrlEncoded, hasSignal, hasSignalParam = false, isExactOptionalPropertyTypes, isVue, isAngular }) {
4059
+ function generateMutatorConfig({ route, body, headers, queryParams, response, verb, isFormData, isFormUrlEncoded, hasSignal, hasSignalParam = false, isExactOptionalPropertyTypes, isVue, isAngular, paramsFilter }) {
3789
4060
  const bodyOptions = getIsBodyVerb(verb) ? generateBodyMutatorConfig(body, isFormData, isFormUrlEncoded) : "";
3790
- const queryParamsOptions = generateQueryParamsAxiosConfig(response, isVue ?? false, isAngular ?? false, queryParams?.requiredNullableKeys, queryParams);
4061
+ const queryParamsOptions = generateQueryParamsAxiosConfig(response, isVue ?? false, isAngular ?? false, queryParams?.requiredNullableKeys, queryParams, paramsFilter);
3791
4062
  const ignoreContentTypes = isAngular ? ["multipart/form-data"] : [];
3792
4063
  const headerOptions = body.contentType && !ignoreContentTypes.includes(body.contentType) ? `,\n headers: {'Content-Type': '${body.contentType}', ${headers ? "...headers" : ""}}` : headers ? ",\n headers" : "";
3793
4064
  const signalVar = hasSignalParam ? "querySignal" : "signal";
@@ -4100,6 +4371,13 @@ async function buildVerbOption({ verb, output, operation, route, pathRoute, verb
4100
4371
  workspace: context.workspace,
4101
4372
  tsconfig: context.output.tsconfig
4102
4373
  }) : void 0,
4374
+ paramsFilter: isString(override.paramsFilter) || isObject(override.paramsFilter) ? await generateMutator({
4375
+ output: output.target,
4376
+ name: "paramsFilter",
4377
+ mutator: override.paramsFilter,
4378
+ workspace: context.workspace,
4379
+ tsconfig: context.output.tsconfig
4380
+ }) : void 0,
4103
4381
  fetchReviver: isString(override.fetch.jsonReviver) || isObject(override.fetch.jsonReviver) ? await generateMutator({
4104
4382
  output: output.target,
4105
4383
  name: "fetchReviver",
@@ -4264,19 +4542,12 @@ function splitSchemasByType(schemas) {
4264
4542
  };
4265
4543
  }
4266
4544
  /**
4267
- * Get the import extension from a file extension.
4268
- * Removes `.ts` suffix since TypeScript doesn't need it in imports.
4269
- */
4270
- function getImportExtension(fileExtension) {
4271
- return fileExtension.replace(/\.ts$/, "") || "";
4272
- }
4273
- /**
4274
4545
  * Fix cross-directory imports when schemas reference other schemas in a different directory.
4275
4546
  * Updates import paths to use correct relative paths between directories.
4276
4547
  */
4277
- function fixSchemaImports(schemas, targetSchemaNames, fromPath, toPath, namingConvention, fileExtension) {
4548
+ function fixSchemaImports(schemas, targetSchemaNames, fromPath, toPath, namingConvention, fileExtension, tsconfig) {
4278
4549
  const relativePath = relativeSafe(fromPath, toPath);
4279
- const importExtension = getImportExtension(fileExtension);
4550
+ const importExtension = getImportExtension(fileExtension, tsconfig);
4280
4551
  for (const schema of schemas) schema.imports = schema.imports.map((imp) => {
4281
4552
  if (targetSchemaNames.has(imp.name)) {
4282
4553
  const fileName = conventionName(imp.name, namingConvention);
@@ -4291,14 +4562,14 @@ function fixSchemaImports(schemas, targetSchemaNames, fromPath, toPath, namingCo
4291
4562
  /**
4292
4563
  * Fix imports in operation schemas that reference regular schemas.
4293
4564
  */
4294
- function fixCrossDirectoryImports(operationSchemas, regularSchemaNames, schemaPath, operationSchemaPath, namingConvention, fileExtension) {
4295
- fixSchemaImports(operationSchemas, regularSchemaNames, operationSchemaPath, schemaPath, namingConvention, fileExtension);
4565
+ function fixCrossDirectoryImports(operationSchemas, regularSchemaNames, schemaPath, operationSchemaPath, namingConvention, fileExtension, tsconfig) {
4566
+ fixSchemaImports(operationSchemas, regularSchemaNames, operationSchemaPath, schemaPath, namingConvention, fileExtension, tsconfig);
4296
4567
  }
4297
4568
  /**
4298
4569
  * Fix imports in regular schemas that reference operation schemas.
4299
4570
  */
4300
- function fixRegularSchemaImports(regularSchemas, operationSchemaNames, schemaPath, operationSchemaPath, namingConvention, fileExtension) {
4301
- fixSchemaImports(regularSchemas, operationSchemaNames, schemaPath, operationSchemaPath, namingConvention, fileExtension);
4571
+ function fixRegularSchemaImports(regularSchemas, operationSchemaNames, schemaPath, operationSchemaPath, namingConvention, fileExtension, tsconfig) {
4572
+ fixSchemaImports(regularSchemas, operationSchemaNames, schemaPath, operationSchemaPath, namingConvention, fileExtension, tsconfig);
4302
4573
  }
4303
4574
  function getSchemaKey(schemaPath, schemaName, namingConvention, fileExtension) {
4304
4575
  return getPath(schemaPath, conventionName(schemaName, namingConvention), fileExtension).toLowerCase().replaceAll("\\", "/");
@@ -4322,14 +4593,16 @@ function getCanonicalMap(schemaGroups, schemaPath, namingConvention, fileExtensi
4322
4593
  canonicalNameMap
4323
4594
  };
4324
4595
  }
4325
- function normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension) {
4596
+ function normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension, tsconfig) {
4597
+ const importExtension = getImportExtension(fileExtension, tsconfig);
4326
4598
  for (const schema of schemas) schema.imports = schema.imports.map((imp) => {
4327
4599
  const canonicalByName = canonicalNameMap.get(imp.name);
4328
4600
  const resolvedImportKey = resolveImportKey(schemaPath, imp.importPath ?? `./${conventionName(imp.name, namingConvention)}`, fileExtension);
4329
4601
  const canonicalByPath = canonicalPathMap.get(resolvedImportKey);
4330
4602
  const canonical = canonicalByName ?? canonicalByPath;
4331
4603
  if (!canonical?.importPath) return imp;
4332
- const importPath = removeTSExtension(relativeSafe(schemaPath, canonical.importPath.replaceAll("\\", "/")));
4604
+ const relative = relativeSafe(schemaPath, canonical.importPath.replaceAll("\\", "/"));
4605
+ const importPath = `${relative.endsWith(fileExtension) ? relative.slice(0, -fileExtension.length) : relative.replace(/\.ts$/, "")}${importExtension}`;
4333
4606
  return {
4334
4607
  ...imp,
4335
4608
  importPath
@@ -4352,14 +4625,12 @@ function mergeSchemaGroup(schemas) {
4352
4625
  function resolveImportKey(schemaPath, importPath, fileExtension) {
4353
4626
  return join(schemaPath, `${importPath}${fileExtension}`).toLowerCase().replaceAll("\\", "/");
4354
4627
  }
4355
- function removeTSExtension(path) {
4356
- return path.endsWith(".ts") ? path.slice(0, -3) : path;
4357
- }
4358
- function getSchema({ schema: { imports, model }, header, namingConvention = NamingConvention.CAMEL_CASE }) {
4628
+ function getSchema({ schema: { imports, model }, header, namingConvention = NamingConvention.CAMEL_CASE, importExtension }) {
4359
4629
  let file = header;
4360
4630
  file += generateImports({
4361
4631
  imports: imports.filter((imp) => !model.includes(`type ${imp.alias ?? imp.name} =`) && !model.includes(`interface ${imp.alias ?? imp.name} {`)),
4362
- namingConvention
4632
+ namingConvention,
4633
+ importExtension
4363
4634
  });
4364
4635
  file += imports.length > 0 ? "\n\n" : "\n";
4365
4636
  file += model;
@@ -4376,23 +4647,24 @@ function writeModelsInline(array) {
4376
4647
  for (const { model } of array) acc = writeModelInline(acc, model);
4377
4648
  return acc;
4378
4649
  }
4379
- async function writeSchema({ path, schema, target, namingConvention, fileExtension, header }) {
4650
+ async function writeSchema({ path, schema, target, namingConvention, fileExtension, header, tsconfig }) {
4380
4651
  const name = conventionName(schema.name, namingConvention);
4381
4652
  try {
4382
4653
  await writeGeneratedFile(getPath(path, name, fileExtension), getSchema({
4383
4654
  schema,
4384
4655
  target,
4385
4656
  header,
4386
- namingConvention
4657
+ namingConvention,
4658
+ importExtension: getImportExtension(fileExtension, tsconfig)
4387
4659
  }));
4388
4660
  } catch (error) {
4389
4661
  throw new Error(`Oups... 🍻. An Error occurred while writing schema ${name} => ${String(error)}`, { cause: error });
4390
4662
  }
4391
4663
  }
4392
- async function writeSchemas({ schemaPath, schemas, target, namingConvention, fileExtension, header, indexFiles }) {
4664
+ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fileExtension, header, indexFiles, tsconfig }) {
4393
4665
  const schemaGroups = getSchemaGroups(schemaPath, schemas, namingConvention, fileExtension);
4394
4666
  const { canonicalPathMap, canonicalNameMap } = getCanonicalMap(schemaGroups, schemaPath, namingConvention, fileExtension);
4395
- normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension);
4667
+ normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension, tsconfig);
4396
4668
  for (const groupSchemas of Object.values(schemaGroups)) {
4397
4669
  if (groupSchemas.length === 1) {
4398
4670
  await writeSchema({
@@ -4401,7 +4673,8 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
4401
4673
  target,
4402
4674
  namingConvention,
4403
4675
  fileExtension,
4404
- header
4676
+ header,
4677
+ tsconfig
4405
4678
  });
4406
4679
  continue;
4407
4680
  }
@@ -4411,13 +4684,14 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
4411
4684
  target,
4412
4685
  namingConvention,
4413
4686
  fileExtension,
4414
- header
4687
+ header,
4688
+ tsconfig
4415
4689
  });
4416
4690
  }
4417
4691
  if (indexFiles) {
4418
4692
  const schemaFilePath = path.join(schemaPath, `index.ts`);
4419
4693
  await fs$1.ensureFile(schemaFilePath);
4420
- const ext = fileExtension.endsWith(".ts") ? fileExtension.slice(0, -3) : fileExtension;
4694
+ const ext = getImportExtension(fileExtension, tsconfig);
4421
4695
  const conventionNamesSet = new Set(Object.values(schemaGroups).map((group) => conventionName(group[0].name, namingConvention)));
4422
4696
  try {
4423
4697
  await writeGeneratedFile(schemaFilePath, `${header}\n${[...conventionNamesSet].map((schemaName) => `export * from './${schemaName}${ext}';`).toSorted((a, b) => a.localeCompare(b, "en", { numeric: true })).join("\n")}\n`);
@@ -4441,7 +4715,7 @@ function generateImportsForBuilder(output, imports, relativeSchemasPath) {
4441
4715
  else {
4442
4716
  const importsByDependency = /* @__PURE__ */ new Map();
4443
4717
  for (const schemaImport of imports.filter((i) => !i.importPath)) {
4444
- const dependency = joinSafe(relativeSchemasPath, `${conventionName(isZodSchemaOutput ? schemaImport.name : schemaImport.schemaName ?? schemaImport.name, output.namingConvention)}${isZodSchemaOutput ? ".zod" : ""}${output.fileExtension.replace(/\.ts$/, "") || ""}`);
4718
+ const dependency = joinSafe(relativeSchemasPath, `${conventionName(isZodSchemaOutput ? schemaImport.name : schemaImport.schemaName ?? schemaImport.name, output.namingConvention)}${isZodSchemaOutput ? ".zod" : ""}${getImportExtension(output.fileExtension, output.tsconfig)}`);
4445
4719
  if (!importsByDependency.has(dependency)) importsByDependency.set(dependency, []);
4446
4720
  importsByDependency.get(dependency)?.push(schemaImport);
4447
4721
  }
@@ -4459,7 +4733,39 @@ function generateImportsForBuilder(output, imports, relativeSchemasPath) {
4459
4733
  return [...schemaImports, ...otherImports];
4460
4734
  }
4461
4735
  //#endregion
4736
+ //#region src/writers/mock-outputs.ts
4737
+ /**
4738
+ * Collapses the per-generator mock outputs for "inline" writer modes
4739
+ * (`single`, `tags`) where every mock generator's content is concatenated
4740
+ * into the implementation file. The MSW generator already emits the
4741
+ * response-factory functions (`get<Op>ResponseMock`) that Faker would emit,
4742
+ * so when both generators are configured we keep MSW and drop Faker to
4743
+ * avoid duplicate function declarations and re-imported faker bindings.
4744
+ */
4745
+ function collapseInlineMockOutputs(mockOutputs) {
4746
+ if (!mockOutputs.some((m) => m.type === OutputMockType.MSW)) return mockOutputs;
4747
+ return mockOutputs.filter((m) => m.type !== OutputMockType.FAKER);
4748
+ }
4749
+ //#endregion
4462
4750
  //#region src/writers/target.ts
4751
+ function emptyMockOutputFull$1(type) {
4752
+ return {
4753
+ type,
4754
+ implementation: {
4755
+ function: "",
4756
+ handler: "",
4757
+ handlerName: ""
4758
+ },
4759
+ imports: []
4760
+ };
4761
+ }
4762
+ function flattenMockOutput$1(full) {
4763
+ return {
4764
+ type: full.type,
4765
+ implementation: full.implementation.function + full.implementation.handler,
4766
+ imports: full.imports
4767
+ };
4768
+ }
4463
4769
  function generateTarget(builder, options) {
4464
4770
  const operationNames = Object.values(builder.operations).map(({ operationName }) => operationName);
4465
4771
  const isAngularClient = options.client === OutputClient.ANGULAR;
@@ -4472,32 +4778,38 @@ function generateTarget(builder, options) {
4472
4778
  const target = {
4473
4779
  imports: [],
4474
4780
  implementation: "",
4475
- implementationMock: {
4476
- function: "",
4477
- handler: "",
4478
- handlerName: ""
4479
- },
4480
- importsMock: [],
4781
+ mockOutputs: [],
4481
4782
  mutators: [],
4482
4783
  clientMutators: [],
4483
4784
  formData: [],
4484
4785
  formUrlEncoded: [],
4485
4786
  paramsSerializer: [],
4787
+ paramsFilter: [],
4486
4788
  fetchReviver: []
4487
4789
  };
4488
4790
  const operations = Object.values(builder.operations);
4489
4791
  for (const [index, operation] of operations.entries()) {
4490
4792
  target.imports.push(...operation.imports);
4491
- target.importsMock.push(...operation.importsMock);
4492
4793
  target.implementation += operation.implementation + "\n";
4493
- target.implementationMock.function += operation.implementationMock.function;
4494
- target.implementationMock.handler += operation.implementationMock.handler;
4495
- const handlerNameSeparator = target.implementationMock.handlerName.length > 0 ? ",\n " : " ";
4496
- target.implementationMock.handlerName += handlerNameSeparator + operation.implementationMock.handlerName + "()";
4794
+ for (const opMock of operation.mockOutputs) {
4795
+ let acc = target.mockOutputs.find((m) => m.type === opMock.type);
4796
+ if (!acc) {
4797
+ acc = emptyMockOutputFull$1(opMock.type);
4798
+ target.mockOutputs.push(acc);
4799
+ }
4800
+ acc.imports.push(...opMock.imports);
4801
+ acc.implementation.function += opMock.implementation.function;
4802
+ acc.implementation.handler += opMock.implementation.handler;
4803
+ if (opMock.implementation.handlerName) {
4804
+ const separator = acc.implementation.handlerName.length > 0 ? ",\n " : " ";
4805
+ acc.implementation.handlerName += separator + opMock.implementation.handlerName + "()";
4806
+ }
4807
+ }
4497
4808
  if (operation.mutator) target.mutators.push(operation.mutator);
4498
4809
  if (operation.formData) target.formData.push(operation.formData);
4499
4810
  if (operation.formUrlEncoded) target.formUrlEncoded.push(operation.formUrlEncoded);
4500
4811
  if (operation.paramsSerializer) target.paramsSerializer.push(operation.paramsSerializer);
4812
+ if (operation.paramsFilter) target.paramsFilter.push(operation.paramsFilter);
4501
4813
  if (operation.clientMutators) target.clientMutators.push(...operation.clientMutators);
4502
4814
  if (operation.fetchReviver) target.fetchReviver.push(operation.fetchReviver);
4503
4815
  if (index === operations.length - 1) {
@@ -4516,7 +4828,6 @@ function generateTarget(builder, options) {
4516
4828
  clientImplementation: target.implementation
4517
4829
  });
4518
4830
  target.implementation = header.implementation + target.implementation;
4519
- target.implementationMock.handler = target.implementationMock.handler + header.implementationMock + target.implementationMock.handlerName;
4520
4831
  const footer = builder.footer({
4521
4832
  outputClient: options.client,
4522
4833
  operationNames,
@@ -4526,12 +4837,20 @@ function generateTarget(builder, options) {
4526
4837
  output: options
4527
4838
  });
4528
4839
  target.implementation += footer.implementation;
4529
- target.implementationMock.handler += footer.implementationMock;
4840
+ for (const acc of target.mockOutputs) if (acc.implementation.handlerName) acc.implementation.handler = acc.implementation.handler + header.implementationMock + acc.implementation.handlerName + footer.implementationMock;
4530
4841
  }
4531
4842
  }
4532
4843
  return {
4533
- ...target,
4534
- implementationMock: target.implementationMock.function + target.implementationMock.handler
4844
+ imports: target.imports,
4845
+ implementation: target.implementation,
4846
+ mockOutputs: target.mockOutputs.map((m) => flattenMockOutput$1(m)),
4847
+ mutators: target.mutators,
4848
+ clientMutators: target.clientMutators,
4849
+ formData: target.formData,
4850
+ formUrlEncoded: target.formUrlEncoded,
4851
+ paramsSerializer: target.paramsSerializer,
4852
+ paramsFilter: target.paramsFilter,
4853
+ fetchReviver: target.fetchReviver
4535
4854
  };
4536
4855
  }
4537
4856
  //#endregion
@@ -4580,7 +4899,10 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
4580
4899
  backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
4581
4900
  extension: output.fileExtension
4582
4901
  });
4583
- const { imports, importsMock, implementation, implementationMock, mutators, clientMutators, formData, formUrlEncoded, paramsSerializer, fetchReviver } = generateTarget(builder, output);
4902
+ const { imports, mockOutputs: rawMockOutputs, implementation, mutators, clientMutators, formData, formUrlEncoded, paramsSerializer, paramsFilter, fetchReviver } = generateTarget(builder, output);
4903
+ const mockOutputs = collapseInlineMockOutputs(rawMockOutputs);
4904
+ const implementationMock = mockOutputs.map((m) => m.implementation).join("\n\n");
4905
+ const importsMock = mockOutputs.flatMap((m) => m.imports);
4584
4906
  let data = header;
4585
4907
  const schemasPath = output.schemas ? getRelativeImportPath(path, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : void 0;
4586
4908
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
@@ -4608,16 +4930,17 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
4608
4930
  packageJson: output.packageJson,
4609
4931
  output
4610
4932
  });
4611
- if (output.mock) {
4612
- const filteredMockImports = importsMock.filter((impMock) => !normalizedImports.some((imp) => imp.name === impMock.name && (imp.alias ?? "") === (impMock.alias ?? "")));
4933
+ for (const mockOutput of mockOutputs) {
4934
+ const entry = output.mock.generators.find((g) => !isFunction(g) && g.type === mockOutput.type);
4935
+ const filteredMockImports = mockOutput.imports.filter((impMock) => !normalizedImports.some((imp) => imp.name === impMock.name && (imp.alias ?? "") === (impMock.alias ?? "")));
4613
4936
  const importsMockForBuilder = schemasPath ? generateImportsForBuilder(output, filteredMockImports, schemasPath) : generateImportsForBuilder(output, filteredMockImports.filter((imp) => !!imp.importPath), ".");
4614
4937
  data += builder.importsMock({
4615
- implementation: implementationMock,
4938
+ implementation: mockOutput.implementation,
4616
4939
  imports: importsMockForBuilder,
4617
4940
  projectName,
4618
4941
  hasSchemaDir: !!output.schemas,
4619
4942
  isAllowSyntheticDefaultImports,
4620
- options: isFunction(output.mock) ? void 0 : output.mock
4943
+ options: entry && !isFunction(entry) ? entry : void 0
4621
4944
  });
4622
4945
  }
4623
4946
  if (mutators) data += generateMutatorImports({
@@ -4628,6 +4951,7 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
4628
4951
  if (formData) data += generateMutatorImports({ mutators: formData });
4629
4952
  if (formUrlEncoded) data += generateMutatorImports({ mutators: formUrlEncoded });
4630
4953
  if (paramsSerializer) data += generateMutatorImports({ mutators: paramsSerializer });
4954
+ if (paramsFilter) data += generateMutatorImports({ mutators: paramsFilter });
4631
4955
  if (fetchReviver) data += generateMutatorImports({ mutators: fetchReviver });
4632
4956
  if (implementation.includes("NonReadonly<")) {
4633
4957
  data += getOrvalGeneratedTypes();
@@ -4639,7 +4963,7 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
4639
4963
  }
4640
4964
  if (!output.schemas && needSchema) data += generateSchemasInline ? generateSchemasInline() : generateModelsInline(builder.schemas);
4641
4965
  data += `${implementation.trim()}\n`;
4642
- if (output.mock) {
4966
+ if (mockOutputs.length > 0) {
4643
4967
  data += "\n\n";
4644
4968
  data += implementationMock;
4645
4969
  }
@@ -4658,9 +4982,8 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
4658
4982
  backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
4659
4983
  extension: output.fileExtension
4660
4984
  });
4661
- const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, formUrlEncoded, paramsSerializer, fetchReviver } = generateTarget(builder, output);
4985
+ const { imports, implementation, mockOutputs, mutators, clientMutators, formData, formUrlEncoded, paramsSerializer, paramsFilter, fetchReviver } = generateTarget(builder, output);
4662
4986
  let implementationData = header;
4663
- let mockData = header;
4664
4987
  const relativeSchemasPath = output.schemas ? getRelativeImportPath(targetPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas" + extension.replace(/\.ts$/, "");
4665
4988
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
4666
4989
  const importsForBuilder = generateImportsForBuilder(output, imports, relativeSchemasPath);
@@ -4677,15 +5000,6 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
4677
5000
  packageJson: output.packageJson,
4678
5001
  output
4679
5002
  });
4680
- const importsMockForBuilder = generateImportsForBuilder(output, importsMock, relativeSchemasPath);
4681
- mockData += builder.importsMock({
4682
- implementation: implementationMock,
4683
- imports: importsMockForBuilder,
4684
- projectName,
4685
- hasSchemaDir: !!output.schemas,
4686
- isAllowSyntheticDefaultImports,
4687
- options: isFunction(output.mock) ? void 0 : output.mock
4688
- });
4689
5003
  const schemasPath = !output.schemas && needSchema ? path.join(dirname, filename + ".schemas" + extension) : void 0;
4690
5004
  if (schemasPath) await writeGeneratedFile(schemasPath, generateSchemasInline ? header + generateSchemasInline() : header + generateModelsInline(builder.schemas));
4691
5005
  if (mutators) implementationData += generateMutatorImports({
@@ -4696,6 +5010,7 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
4696
5010
  if (formData) implementationData += generateMutatorImports({ mutators: formData });
4697
5011
  if (formUrlEncoded) implementationData += generateMutatorImports({ mutators: formUrlEncoded });
4698
5012
  if (paramsSerializer) implementationData += generateMutatorImports({ mutators: paramsSerializer });
5013
+ if (paramsFilter) implementationData += generateMutatorImports({ mutators: paramsFilter });
4699
5014
  if (fetchReviver) implementationData += generateMutatorImports({ mutators: fetchReviver });
4700
5015
  if (implementation.includes("NonReadonly<")) {
4701
5016
  implementationData += getOrvalGeneratedTypes();
@@ -4706,16 +5021,32 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
4706
5021
  implementationData += "\n";
4707
5022
  }
4708
5023
  implementationData += `\n${implementation}`;
4709
- mockData += `\n${implementationMock}`;
4710
5024
  const implementationFilename = filename + (OutputClient.ANGULAR === output.client ? ".service" : "") + extension;
4711
5025
  const implementationPath = path.join(dirname, implementationFilename);
4712
5026
  await writeGeneratedFile(implementationPath, implementationData);
4713
- const mockPath = output.mock ? path.join(dirname, filename + "." + getMockFileExtensionByTypeName(output.mock) + extension) : void 0;
4714
- if (mockPath) await writeGeneratedFile(mockPath, mockData);
5027
+ const mockPaths = [];
5028
+ for (const mockOutput of mockOutputs) {
5029
+ const entry = output.mock.generators.find((g) => !isFunction(g) && g.type === mockOutput.type);
5030
+ if (!entry) continue;
5031
+ const importsMockForBuilder = generateImportsForBuilder(output, mockOutput.imports, relativeSchemasPath);
5032
+ let mockData = header;
5033
+ mockData += builder.importsMock({
5034
+ implementation: mockOutput.implementation,
5035
+ imports: importsMockForBuilder,
5036
+ projectName,
5037
+ hasSchemaDir: !!output.schemas,
5038
+ isAllowSyntheticDefaultImports,
5039
+ options: entry
5040
+ });
5041
+ mockData += `\n${mockOutput.implementation}`;
5042
+ const mockPath = path.join(dirname, filename + "." + getMockFileExtensionByTypeName(entry) + extension);
5043
+ await writeGeneratedFile(mockPath, mockData);
5044
+ mockPaths.push(mockPath);
5045
+ }
4715
5046
  return [
4716
5047
  implementationPath,
4717
5048
  ...schemasPath ? [schemasPath] : [],
4718
- ...mockPath ? [mockPath] : []
5049
+ ...mockPaths
4719
5050
  ];
4720
5051
  } catch (error) {
4721
5052
  throw new Error(`Oups... 🍻. An Error occurred while splitting => ${String(error)}`, { cause: error });
@@ -4723,30 +5054,83 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
4723
5054
  }
4724
5055
  //#endregion
4725
5056
  //#region src/writers/target-tags.ts
5057
+ /**
5058
+ * Ensures every operation has at least one tag by falling back to the
5059
+ * {@link DefaultTag} constant for untagged operations, so the tag-routing
5060
+ * logic in {@link generateTargetTags} always has a bucket to assign the
5061
+ * operation to.
5062
+ */
4726
5063
  function addDefaultTagIfEmpty(operation) {
4727
5064
  return {
4728
5065
  ...operation,
4729
- tags: operation.tags.length > 0 ? operation.tags : ["default"]
5066
+ tags: operation.tags.length > 0 ? operation.tags : [DefaultTag]
5067
+ };
5068
+ }
5069
+ function emptyMockOutputFull(type) {
5070
+ return {
5071
+ type,
5072
+ implementation: {
5073
+ function: "",
5074
+ handler: "",
5075
+ handlerName: ""
5076
+ },
5077
+ imports: []
5078
+ };
5079
+ }
5080
+ function flattenMockOutput(full) {
5081
+ return {
5082
+ type: full.type,
5083
+ implementation: full.implementation.function + full.implementation.handler,
5084
+ imports: full.imports
4730
5085
  };
4731
5086
  }
5087
+ function mergeOperationMockOutputs(accMockOutputs, opMockOutputs) {
5088
+ const result = accMockOutputs.map((m) => ({
5089
+ type: m.type,
5090
+ implementation: { ...m.implementation },
5091
+ imports: [...m.imports]
5092
+ }));
5093
+ for (const op of opMockOutputs) {
5094
+ let acc = result.find((m) => m.type === op.type);
5095
+ if (!acc) {
5096
+ acc = emptyMockOutputFull(op.type);
5097
+ result.push(acc);
5098
+ }
5099
+ acc.imports.push(...op.imports);
5100
+ acc.implementation.function += op.implementation.function;
5101
+ acc.implementation.handler += op.implementation.handler;
5102
+ if (op.implementation.handlerName) {
5103
+ const separator = acc.implementation.handlerName.length > 0 ? ",\n " : " ";
5104
+ acc.implementation.handlerName += separator + op.implementation.handlerName + "()";
5105
+ }
5106
+ }
5107
+ return result;
5108
+ }
5109
+ function initialMockOutputsForOperation(op) {
5110
+ return op.mockOutputs.map((m) => ({
5111
+ type: m.type,
5112
+ implementation: {
5113
+ function: m.implementation.function,
5114
+ handler: m.implementation.handler,
5115
+ handlerName: m.implementation.handlerName ? " " + m.implementation.handlerName + "()" : ""
5116
+ },
5117
+ imports: [...m.imports]
5118
+ }));
5119
+ }
4732
5120
  function generateTargetTags(currentAcc, operation) {
4733
5121
  const tag = kebab(operation.tags[0]);
4734
5122
  if (!(tag in currentAcc)) {
4735
5123
  currentAcc[tag] = {
4736
5124
  imports: operation.imports,
4737
- importsMock: operation.importsMock,
5125
+ mockOutputs: initialMockOutputsForOperation(operation),
4738
5126
  mutators: operation.mutator ? [operation.mutator] : [],
4739
5127
  clientMutators: operation.clientMutators ?? [],
4740
5128
  formData: operation.formData ? [operation.formData] : [],
4741
5129
  formUrlEncoded: operation.formUrlEncoded ? [operation.formUrlEncoded] : [],
4742
5130
  paramsSerializer: operation.paramsSerializer ? [operation.paramsSerializer] : [],
5131
+ paramsFilter: operation.paramsFilter ? [operation.paramsFilter] : [],
4743
5132
  fetchReviver: operation.fetchReviver ? [operation.fetchReviver] : [],
4744
- implementation: operation.implementation,
4745
- implementationMock: {
4746
- function: operation.implementationMock.function,
4747
- handler: operation.implementationMock.handler,
4748
- handlerName: " " + operation.implementationMock.handlerName + "()"
4749
- }
5133
+ implementation: operation.implementation
4750
5134
  };
4751
5135
  return currentAcc;
4752
5136
  }
@@ -4754,17 +5138,13 @@ function generateTargetTags(currentAcc, operation) {
4754
5138
  currentAcc[tag] = {
4755
5139
  implementation: currentOperation.implementation + operation.implementation,
4756
5140
  imports: [...currentOperation.imports, ...operation.imports],
4757
- importsMock: [...currentOperation.importsMock, ...operation.importsMock],
4758
- implementationMock: {
4759
- function: currentOperation.implementationMock.function + operation.implementationMock.function,
4760
- handler: currentOperation.implementationMock.handler + operation.implementationMock.handler,
4761
- handlerName: currentOperation.implementationMock.handlerName + ",\n " + operation.implementationMock.handlerName + "()"
4762
- },
5141
+ mockOutputs: mergeOperationMockOutputs(currentOperation.mockOutputs, operation.mockOutputs),
4763
5142
  mutators: operation.mutator ? [...currentOperation.mutators ?? [], operation.mutator] : currentOperation.mutators,
4764
5143
  clientMutators: operation.clientMutators ? [...currentOperation.clientMutators ?? [], ...operation.clientMutators] : currentOperation.clientMutators,
4765
5144
  formData: operation.formData ? [...currentOperation.formData ?? [], operation.formData] : currentOperation.formData,
4766
5145
  formUrlEncoded: operation.formUrlEncoded ? [...currentOperation.formUrlEncoded ?? [], operation.formUrlEncoded] : currentOperation.formUrlEncoded,
4767
5146
  paramsSerializer: operation.paramsSerializer ? [...currentOperation.paramsSerializer ?? [], operation.paramsSerializer] : currentOperation.paramsSerializer,
5147
+ paramsFilter: operation.paramsFilter ? [...currentOperation.paramsFilter ?? [], operation.paramsFilter] : currentOperation.paramsFilter,
4768
5148
  fetchReviver: operation.fetchReviver ? [...currentOperation.fetchReviver ?? [], operation.fetchReviver] : currentOperation.fetchReviver
4769
5149
  };
4770
5150
  return currentAcc;
@@ -4806,22 +5186,28 @@ function generateTargetForTags(builder, options) {
4806
5186
  output: options,
4807
5187
  verbOptions: builder.verbOptions,
4808
5188
  tag,
5189
+ isDefaultTagBucket: tag === "default" && Object.values(builder.operations).some((operation) => operation.tags.length === 0),
4809
5190
  clientImplementation: target.implementation
4810
5191
  });
5192
+ const wrappedMockOutputs = target.mockOutputs.map((m) => ({
5193
+ type: m.type,
5194
+ implementation: {
5195
+ function: m.implementation.function,
5196
+ handler: m.implementation.handlerName ? m.implementation.handler + header.implementationMock + m.implementation.handlerName + footer.implementationMock : m.implementation.handler,
5197
+ handlerName: m.implementation.handlerName
5198
+ },
5199
+ imports: m.imports
5200
+ }));
4811
5201
  transformed[tag] = {
4812
5202
  implementation: header.implementation + target.implementation + footer.implementation,
4813
- implementationMock: {
4814
- function: target.implementationMock.function,
4815
- handler: target.implementationMock.handler + header.implementationMock + target.implementationMock.handlerName + footer.implementationMock,
4816
- handlerName: target.implementationMock.handlerName
4817
- },
5203
+ mockOutputs: wrappedMockOutputs,
4818
5204
  imports: target.imports,
4819
- importsMock: target.importsMock,
4820
5205
  mutators: target.mutators,
4821
5206
  clientMutators: target.clientMutators,
4822
5207
  formData: target.formData,
4823
5208
  formUrlEncoded: target.formUrlEncoded,
4824
5209
  paramsSerializer: target.paramsSerializer,
5210
+ paramsFilter: target.paramsFilter,
4825
5211
  fetchReviver: target.fetchReviver
4826
5212
  };
4827
5213
  }
@@ -4831,7 +5217,7 @@ function generateTargetForTags(builder, options) {
4831
5217
  const result = {};
4832
5218
  for (const [tag, target] of Object.entries(allTargetTags)) result[tag] = {
4833
5219
  ...target,
4834
- implementationMock: target.implementationMock.function + target.implementationMock.handler
5220
+ mockOutputs: target.mockOutputs.map((m) => flattenMockOutput(m))
4835
5221
  };
4836
5222
  return result;
4837
5223
  }
@@ -4844,15 +5230,20 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4844
5230
  });
4845
5231
  const target = generateTargetForTags(builder, output);
4846
5232
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
4847
- const mockOption = output.mock && !isFunction(output.mock) ? output.mock : void 0;
4848
- const indexFilePath = mockOption?.indexMockFiles ? path.join(dirname, "index." + getMockFileExtensionByTypeName(mockOption) + extension) : void 0;
4849
- if (indexFilePath) await fs$1.outputFile(indexFilePath, "");
5233
+ if (output.mock.generators.some((g) => isFunction(g))) throw new Error("Function mock generators (ClientMockBuilder) are not supported in tags-split mode. Use typed generators ({ type: \"msw\" } or { type: \"faker\" }).");
5234
+ const generatorEntries = output.mock.generators.filter((g) => !isFunction(g));
5235
+ const indexFilePathsByType = /* @__PURE__ */ new Map();
5236
+ if (output.mock.indexMockFiles) for (const entry of generatorEntries) {
5237
+ const ext = getMockFileExtensionByTypeName(entry);
5238
+ const indexPath = path.join(dirname, `index.${ext}${extension}`);
5239
+ indexFilePathsByType.set(ext, indexPath);
5240
+ await fs$1.outputFile(indexPath, "");
5241
+ }
4850
5242
  const tagEntries = Object.entries(target);
4851
5243
  const generatedFilePathsArray = await Promise.all(tagEntries.map(async ([tag, target]) => {
4852
5244
  try {
4853
- const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, fetchReviver, formUrlEncoded, paramsSerializer } = target;
5245
+ const { imports, implementation, mockOutputs, mutators, clientMutators, formData, fetchReviver, formUrlEncoded, paramsSerializer, paramsFilter } = target;
4854
5246
  let implementationData = header;
4855
- let mockData = header;
4856
5247
  const importerPath = path.join(dirname, tag, tag + extension);
4857
5248
  const relativeSchemasPath = output.schemas ? getRelativeImportPath(importerPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "../" + filename + ".schemas" + extension.replace(/\.ts$/, "");
4858
5249
  const tagNames = new Set(tagEntries.map(([t]) => t));
@@ -4886,15 +5277,6 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4886
5277
  packageJson: output.packageJson,
4887
5278
  output
4888
5279
  });
4889
- const importsMockForBuilder = generateImportsForBuilder(output, importsMock, relativeSchemasPath);
4890
- mockData += builder.importsMock({
4891
- implementation: implementationMock,
4892
- imports: importsMockForBuilder,
4893
- projectName,
4894
- hasSchemaDir: !!output.schemas,
4895
- isAllowSyntheticDefaultImports,
4896
- options: isFunction(output.mock) ? void 0 : output.mock
4897
- });
4898
5280
  const schemasPath = !output.schemas && needSchema ? path.join(dirname, filename + ".schemas" + extension) : void 0;
4899
5281
  if (schemasPath) await writeGeneratedFile(schemasPath, generateSchemasInline ? header + generateSchemasInline() : header + generateModelsInline(builder.schemas));
4900
5282
  if (mutators) implementationData += generateMutatorImports({
@@ -4918,6 +5300,10 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4918
5300
  mutators: paramsSerializer,
4919
5301
  oneMore: true
4920
5302
  });
5303
+ if (paramsFilter) implementationData += generateMutatorImports({
5304
+ mutators: paramsFilter,
5305
+ oneMore: true
5306
+ });
4921
5307
  if (fetchReviver) implementationData += generateMutatorImports({
4922
5308
  mutators: fetchReviver,
4923
5309
  oneMore: true
@@ -4931,29 +5317,48 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4931
5317
  implementationData += "\n";
4932
5318
  }
4933
5319
  implementationData += `\n${implementation}`;
4934
- mockData += `\n${implementationMock}`;
4935
5320
  const implementationFilename = tag + (OutputClient.ANGULAR === output.client ? ".service" : "") + extension;
4936
5321
  const implementationPath = path.join(dirname, tag, implementationFilename);
4937
5322
  await writeGeneratedFile(implementationPath, implementationData);
4938
- const mockPath = output.mock ? path.join(dirname, tag, tag + "." + getMockFileExtensionByTypeName(output.mock) + extension) : void 0;
4939
- if (mockPath) await writeGeneratedFile(mockPath, mockData);
5323
+ const mockPaths = [];
5324
+ for (const mockOutput of mockOutputs) {
5325
+ const entry = output.mock.generators.find((g) => !isFunction(g) && g.type === mockOutput.type);
5326
+ if (!entry) continue;
5327
+ const importsMockForBuilder = generateImportsForBuilder(output, mockOutput.imports, relativeSchemasPath);
5328
+ let mockData = header;
5329
+ mockData += builder.importsMock({
5330
+ implementation: mockOutput.implementation,
5331
+ imports: importsMockForBuilder,
5332
+ projectName,
5333
+ hasSchemaDir: !!output.schemas,
5334
+ isAllowSyntheticDefaultImports,
5335
+ options: entry
5336
+ });
5337
+ mockData += `\n${mockOutput.implementation}`;
5338
+ const mockPath = path.join(dirname, tag, tag + "." + getMockFileExtensionByTypeName(entry) + extension);
5339
+ await writeGeneratedFile(mockPath, mockData);
5340
+ mockPaths.push(mockPath);
5341
+ }
4940
5342
  return [
4941
5343
  implementationPath,
4942
5344
  ...schemasPath ? [schemasPath] : [],
4943
- ...mockPath ? [mockPath] : []
5345
+ ...mockPaths
4944
5346
  ];
4945
5347
  } catch (error) {
4946
5348
  throw new Error(`Oups... 🍻. An Error occurred while splitting tag ${tag} => ${String(error)}`, { cause: error });
4947
5349
  }
4948
5350
  }));
4949
- if (indexFilePath && mockOption) {
5351
+ if (output.mock.indexMockFiles) for (const entry of generatorEntries) {
5352
+ const ext = getMockFileExtensionByTypeName(entry);
5353
+ const indexFilePath = indexFilePathsByType.get(ext);
5354
+ if (!indexFilePath) continue;
4950
5355
  const indexContent = tagEntries.map(([tag]) => {
4951
- const localMockPath = joinSafe("./", tag, tag + "." + getMockFileExtensionByTypeName(mockOption));
4952
- return `export { get${pascal(tag)}Mock } from '${localMockPath}'\n`;
5356
+ const localMockPath = joinSafe("./", tag, tag + "." + ext);
5357
+ return ext === OutputMockType.MSW ? `export { get${pascal(tag)}Mock } from '${localMockPath}'\n` : `export * from '${localMockPath}'\n`;
4953
5358
  }).join("");
4954
5359
  await fs$1.appendFile(indexFilePath, indexContent);
4955
5360
  }
4956
- return [...new Set([...indexFilePath ? [indexFilePath] : [], ...generatedFilePathsArray.flat()])];
5361
+ return [...new Set([...indexFilePathsByType.values(), ...generatedFilePathsArray.flat()])];
4957
5362
  }
4958
5363
  //#endregion
4959
5364
  //#region src/writers/tags-mode.ts
@@ -4966,7 +5371,10 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema,
4966
5371
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
4967
5372
  return (await Promise.all(Object.entries(target).map(async ([tag, target]) => {
4968
5373
  try {
4969
- const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, formUrlEncoded, fetchReviver, paramsSerializer } = target;
5374
+ const { imports, implementation, mockOutputs: rawMockOutputs, mutators, clientMutators, formData, formUrlEncoded, fetchReviver, paramsSerializer, paramsFilter } = target;
5375
+ const mockOutputs = collapseInlineMockOutputs(rawMockOutputs);
5376
+ const importsMock = mockOutputs.flatMap((m) => m.imports);
5377
+ const implementationMock = mockOutputs.map((m) => m.implementation).join("\n\n");
4970
5378
  let data = header;
4971
5379
  const schemasPathRelative = output.schemas ? getRelativeImportPath(targetPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas" + extension.replace(/\.ts$/, "");
4972
5380
  const normalizedImports = imports.filter((imp) => {
@@ -4993,15 +5401,16 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema,
4993
5401
  packageJson: output.packageJson,
4994
5402
  output
4995
5403
  });
4996
- if (output.mock) {
4997
- const importsMockForBuilder = generateImportsForBuilder(output, importsMock.filter((impMock) => !normalizedImports.some((imp) => imp.name === impMock.name && (imp.alias ?? "") === (impMock.alias ?? ""))), schemasPathRelative);
5404
+ for (const mockOutput of mockOutputs) {
5405
+ const entry = output.mock.generators.find((g) => !isFunction(g) && g.type === mockOutput.type);
5406
+ const importsMockForBuilder = generateImportsForBuilder(output, mockOutput.imports.filter((impMock) => !normalizedImports.some((imp) => imp.name === impMock.name && (imp.alias ?? "") === (impMock.alias ?? ""))), schemasPathRelative);
4998
5407
  data += builder.importsMock({
4999
- implementation: implementationMock,
5408
+ implementation: mockOutput.implementation,
5000
5409
  imports: importsMockForBuilder,
5001
5410
  projectName,
5002
5411
  hasSchemaDir: !!output.schemas,
5003
5412
  isAllowSyntheticDefaultImports,
5004
- options: isFunction(output.mock) ? void 0 : output.mock
5413
+ options: entry && !isFunction(entry) ? entry : void 0
5005
5414
  });
5006
5415
  }
5007
5416
  const schemasPath = !output.schemas && needSchema ? path.join(dirname, filename + ".schemas" + extension) : void 0;
@@ -5014,6 +5423,7 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema,
5014
5423
  if (formData) data += generateMutatorImports({ mutators: formData });
5015
5424
  if (formUrlEncoded) data += generateMutatorImports({ mutators: formUrlEncoded });
5016
5425
  if (paramsSerializer) data += generateMutatorImports({ mutators: paramsSerializer });
5426
+ if (paramsFilter) data += generateMutatorImports({ mutators: paramsFilter });
5017
5427
  if (fetchReviver) data += generateMutatorImports({ mutators: fetchReviver });
5018
5428
  data += "\n\n";
5019
5429
  if (implementation.includes("NonReadonly<")) {
@@ -5025,7 +5435,7 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema,
5025
5435
  data += "\n";
5026
5436
  }
5027
5437
  data += implementation;
5028
- if (output.mock) {
5438
+ if (mockOutputs.length > 0) {
5029
5439
  data += "\n\n";
5030
5440
  data += implementationMock;
5031
5441
  }
@@ -5038,6 +5448,6 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema,
5038
5448
  }))).flat();
5039
5449
  }
5040
5450
  //#endregion
5041
- export { BODY_TYPE_NAME, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, SupportedFormatter, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, addDependency, asyncReduce, camel, collectReferencedComponents, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicImport, escape, escapeRegExp, filterByContentType, filteredVerbs, fixCrossDirectoryImports, fixRegularSchemaImports, generalJSTypes, generalJSTypesWithArray, generateAxiosOptions, generateBodyMutatorConfig, generateBodyOptions, generateComponentDefinition, generateDependencyImports, generateFormDataAndUrlEncodedFunction, generateImports, generateModelInline, generateModelsInline, generateMutator, generateMutatorConfig, generateMutatorImports, generateMutatorRequestOptions, generateOptions, generateParameterDefinition, generateQueryParamsAxiosConfig, generateSchemasDefinition, generateTarget, generateTargetForTags, generateVerbImports, generateVerbOptions, generateVerbsOptions, getAngularFilteredParamsCallExpression, getAngularFilteredParamsExpression, getAngularFilteredParamsHelperBody, getArray, getBaseUrlRuntimeImports, getBodiesByContentType, getBody, getCombinedEnumValue, getDefaultContentType, getEnum, getEnumDescriptions, getEnumImplementation, getEnumNames, getEnumUnionFromSchema, getExtension, getFileInfo, getFormDataFieldFileType, getFullRoute, getIsBodyVerb, getKey, getMockFileExtensionByTypeName, getNumberWord, getObject, getOperationId, getOrvalGeneratedTypes, getParameters, getParams, getParamsInPath, getPropertySafe, getProps, getQueryParams, getRefInfo, getResReqTypes, getResponse, getResponseTypeCategory, getRoute, getRouteAsArray, getScalar, getSuccessResponseType, getTypedResponse, getWarningCount, isBinaryContentType, isBoolean, isDirectory, isFunction, isModule, isNullish, isNumber, isNumeric, isObject, isReference, isSchema, isString, isStringLike, isSyntheticDefaultImportsAllow, isUrl, isVerb, isVerbose, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, logVerbose, logWarning, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resetWarnings, resolveDiscriminators, resolveExampleRefs, resolveInstalledVersion, resolveInstalledVersions, resolveObject, resolveRef, resolveValue, sanitize, setVerbose, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
5451
+ export { BODY_TYPE_NAME, DefaultTag, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NAMED_COMPONENT_SECTIONS, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, SupportedFormatter, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, addDependency, asyncReduce, buildAngularParamsFilterExpression, camel, collectReferencedComponents, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicImport, escape, escapeRegExp, filterByContentType, filteredVerbs, fixCrossDirectoryImports, fixRegularSchemaImports, generalJSTypes, generalJSTypesWithArray, generateAxiosOptions, generateBodyMutatorConfig, generateBodyOptions, generateComponentDefinition, generateDependencyImports, generateFormDataAndUrlEncodedFunction, generateImports, generateModelInline, generateModelsInline, generateMutator, generateMutatorConfig, generateMutatorImports, generateMutatorRequestOptions, generateOptions, generateParameterDefinition, generateQueryParamsAxiosConfig, generateSchemasDefinition, generateTarget, generateTargetForTags, generateVerbImports, generateVerbOptions, generateVerbsOptions, getAngularFilteredParamsCallExpression, getAngularFilteredParamsExpression, getAngularFilteredParamsHelperBody, getArray, getBaseUrlRuntimeImports, getBodiesByContentType, getBody, getCombinedEnumValue, getDefaultContentType, getEnum, getEnumDescriptions, getEnumImplementation, getEnumNames, getEnumUnionFromSchema, getExtension, getFileInfo, getFormDataFieldFileType, getFullRoute, getImportExtension, getIsBodyVerb, getKey, getMockFileExtensionByTypeName, getNumberWord, getObject, getOperationId, getOrvalGeneratedTypes, getParameters, getParams, getParamsInPath, getPropertySafe, getProps, getQueryParams, getRefInfo, getResReqTypes, getResponse, getResponseTypeCategory, getRoute, getRouteAsArray, getScalar, getSuccessResponseType, getTypedResponse, getWarningCount, isBinaryContentType, isBoolean, isComponentRef, isDirectory, isFakerMock, isFunction, isModule, isMswMock, isNullish, isNumber, isNumeric, isObject, isReference, isSchema, isString, isStringLike, isSyntheticDefaultImportsAllow, isUrl, isVerb, isVerbose, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, logVerbose, logWarning, makeRouteSafe, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resetWarnings, resolveDiscriminators, resolveExampleRefs, resolveInstalledVersion, resolveInstalledVersions, resolveObject, resolveRef, resolveValue, sanitize, setVerbose, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, wrapRouteParameters, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
5042
5452
 
5043
5453
  //# sourceMappingURL=index.mjs.map