@orval/core 8.1.0 → 8.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  import { t as __export } from "./chunk-C6wwvPpM.mjs";
2
- import { entries, groupBy, isArray, isEmptyish, prop, unique, uniqueBy, uniqueWith } from "remeda";
2
+ 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
3
  import { keyword } from "esutils";
4
4
  import path from "node:path";
5
- import fs from "node:fs";
6
- import { globby } from "globby";
7
5
  import { compare } from "compare-versions";
8
6
  import debug from "debug";
9
7
  import { pathToFileURL } from "node:url";
8
+ import fs from "node:fs";
9
+ import { globby } from "globby";
10
10
  import readline from "node:readline";
11
11
  import chalk from "chalk";
12
12
  import { isDereferenced } from "@scalar/openapi-types/helpers";
@@ -108,124 +108,20 @@ const generalJSTypes = [
108
108
  "object",
109
109
  "blob"
110
110
  ];
111
- const generalJSTypesWithArray = generalJSTypes.reduce((acc, type) => {
112
- acc.push(type, `Array<${type}>`, `${type}[]`);
113
- return acc;
114
- }, []);
111
+ const generalJSTypesWithArray = generalJSTypes.flatMap((type) => [
112
+ type,
113
+ `Array<${type}>`,
114
+ `${type}[]`
115
+ ]);
115
116
  const VERBS_WITH_BODY = [
116
117
  Verbs.POST,
117
118
  Verbs.PUT,
118
119
  Verbs.PATCH,
119
120
  Verbs.DELETE
120
121
  ];
121
- const URL_REGEX = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/;
122
+ const URL_REGEX = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]+$/;
122
123
  const TEMPLATE_TAG_REGEX = /\${(.+?)}/g;
123
124
 
124
- //#endregion
125
- //#region src/utils/extension.ts
126
- function getExtension(path$2) {
127
- return path$2.toLowerCase().includes(".yaml") || path$2.toLowerCase().includes(".yml") ? "yaml" : "json";
128
- }
129
-
130
- //#endregion
131
- //#region src/utils/file.ts
132
- function getFileInfo(target = "", { backupFilename = "filename", extension = ".ts" } = {}) {
133
- const isDir = isDirectory(target);
134
- const filePath = isDir ? path.join(target, backupFilename + extension) : target;
135
- return {
136
- path: filePath,
137
- pathWithoutExtension: filePath.replace(/\.[^/.]+$/, ""),
138
- extension,
139
- isDirectory: isDir,
140
- dirname: path.dirname(filePath),
141
- filename: path.basename(filePath, extension.startsWith(".") ? extension : `.${extension}`)
142
- };
143
- }
144
- async function removeFilesAndEmptyFolders(patterns, dir) {
145
- const files = await globby(patterns, {
146
- cwd: dir,
147
- absolute: true
148
- });
149
- await Promise.all(files.map((file) => fs.promises.unlink(file)));
150
- const sortedDirectories = (await globby(["**/*"], {
151
- cwd: dir,
152
- absolute: true,
153
- onlyDirectories: true
154
- })).toSorted((a, b) => {
155
- const depthA = a.split("/").length;
156
- return b.split("/").length - depthA;
157
- });
158
- for (const directory of sortedDirectories) try {
159
- if ((await fs.promises.readdir(directory)).length === 0) await fs.promises.rmdir(directory);
160
- } catch {}
161
- }
162
-
163
- //#endregion
164
- //#region src/utils/path.ts
165
- var path_exports = /* @__PURE__ */ __export({
166
- basename: () => basename,
167
- dirname: () => dirname,
168
- extname: () => extname,
169
- getSchemaFileName: () => getSchemaFileName,
170
- isAbsolute: () => isAbsolute,
171
- join: () => join,
172
- joinSafe: () => joinSafe,
173
- normalizeSafe: () => normalizeSafe,
174
- relativeSafe: () => relativeSafe,
175
- resolve: () => resolve,
176
- separator: () => separator
177
- });
178
- const path$1 = {};
179
- const isFunction$1 = (val) => typeof val == "function";
180
- const isString$1 = (val) => {
181
- if (typeof val === "string") return true;
182
- if (typeof val === "object" && val !== null) return Object.toString.call(val) == "[object String]";
183
- return false;
184
- };
185
- for (const [propName, propValue] of Object.entries(path)) if (isFunction$1(propValue)) path$1[propName] = ((propName$1) => {
186
- return (...args) => {
187
- args = args.map((p) => {
188
- return isString$1(p) ? toUnix(p) : p;
189
- });
190
- const result = path[propName$1](...args);
191
- return isString$1(result) ? toUnix(result) : result;
192
- };
193
- })(propName);
194
- else path$1[propName] = propValue;
195
- const { join, resolve, extname, dirname, basename, isAbsolute } = path$1;
196
- /**
197
- * Behaves exactly like `path.relative(from, to)`, but keeps the first meaningful "./"
198
- */
199
- function relativeSafe(from, to) {
200
- return normalizeSafe(`.${separator}${path$1.relative(from, to)}`);
201
- }
202
- function getSchemaFileName(path$2) {
203
- return path$2.replace(`.${getExtension(path$2)}`, "").slice(path$2.lastIndexOf("/") + 1);
204
- }
205
- const separator = "/";
206
- const toUnix = function(value) {
207
- value = value.replaceAll("\\", "/");
208
- value = value.replaceAll(/(?<!^)\/+/g, "/");
209
- return value;
210
- };
211
- function normalizeSafe(value) {
212
- let result;
213
- value = toUnix(value);
214
- result = path$1.normalize(value);
215
- if (value.startsWith("./") && !result.startsWith("./") && !result.startsWith("..")) result = "./" + result;
216
- else if (value.startsWith("//") && !result.startsWith("//")) result = value.startsWith("//./") ? "//." + result : "/" + result;
217
- return result;
218
- }
219
- function joinSafe(...values) {
220
- let result = path$1.join(...values);
221
- if (values.length > 0) {
222
- const firstValue = toUnix(values[0]);
223
- if (firstValue.startsWith("./") && !result.startsWith("./") && !result.startsWith("..")) result = "./" + result;
224
- else if (firstValue.startsWith("//") && !result.startsWith("//")) result = firstValue.startsWith("//./") ? "//." + result : "/" + result;
225
- }
226
- return result;
227
- }
228
-
229
125
  //#endregion
230
126
  //#region src/utils/assertion.ts
231
127
  /**
@@ -234,42 +130,29 @@ function joinSafe(...values) {
234
130
  * @param property
235
131
  */
236
132
  function isReference(obj) {
237
- return !isNull(obj) && Object.hasOwn(obj, "$ref");
133
+ return !isNullish$1(obj) && Object.hasOwn(obj, "$ref");
238
134
  }
239
- function isDirectory(path$2) {
240
- return !extname(path$2);
135
+ function isDirectory(pathValue) {
136
+ return !path.extname(pathValue);
241
137
  }
242
138
  function isObject(x) {
243
139
  return Object.prototype.toString.call(x) === "[object Object]";
244
140
  }
141
+ function isStringLike(val) {
142
+ if (isString$1(val)) return true;
143
+ return Object.prototype.toString.call(val) === "[object String]";
144
+ }
245
145
  function isModule(x) {
246
146
  return Object.prototype.toString.call(x) === "[object Module]";
247
147
  }
248
- function isString(x) {
249
- return typeof x === "string";
250
- }
251
- function isNumber(x) {
252
- return typeof x === "number";
253
- }
254
148
  function isNumeric(x) {
255
- return /^-?\d+$/.test(x);
256
- }
257
- function isBoolean(x) {
258
- return typeof x === "boolean";
259
- }
260
- function isFunction(x) {
261
- return typeof x === "function";
262
- }
263
- function isUndefined(x) {
264
- return x === void 0;
265
- }
266
- function isNull(x) {
267
- return x === null;
149
+ if (typeof x === "number") return Number.isInteger(x);
150
+ return isString$1(x) && /^-?\d+$/.test(x);
268
151
  }
269
152
  function isSchema(x) {
270
153
  if (!isObject(x)) return false;
271
- if (isString(x.type) && Object.values(SchemaType).includes(x.type)) return true;
272
- const combine = x.allOf || x.anyOf || x.oneOf;
154
+ if (isString$1(x.type) && Object.values(SchemaType).includes(x.type)) return true;
155
+ const combine = x.allOf ?? x.anyOf ?? x.oneOf;
273
156
  if (Array.isArray(combine)) return true;
274
157
  if (isObject(x.properties)) return true;
275
158
  return false;
@@ -278,27 +161,26 @@ function isVerb(verb) {
278
161
  return Object.values(Verbs).includes(verb);
279
162
  }
280
163
  function isUrl(str) {
281
- let givenURL;
164
+ if (!str.trim()) return false;
282
165
  try {
283
- givenURL = new URL(str);
166
+ const url = new URL(str);
167
+ return ["http:", "https:"].includes(url.protocol);
284
168
  } catch {
285
169
  return false;
286
170
  }
287
- return givenURL.protocol === "http:" || givenURL.protocol === "https:";
288
171
  }
289
172
 
290
173
  //#endregion
291
174
  //#region src/utils/async-reduce.ts
292
175
  async function asyncReduce(array, reducer, initValue) {
293
- let accumulate = typeof initValue === "object" ? Object.create(initValue) : initValue;
176
+ let accumulate = initValue === null || initValue === Object(initValue) && !isFunction(initValue) ? Object.create(initValue) : initValue;
294
177
  for (const item of array) accumulate = await reducer(accumulate, item);
295
178
  return accumulate;
296
179
  }
297
180
 
298
181
  //#endregion
299
182
  //#region src/utils/case.ts
300
- const unicodes = function(s, prefix) {
301
- prefix = prefix || "";
183
+ const unicodes = function(s, prefix = "") {
302
184
  return s.replaceAll(/(^|-)/g, String.raw`$1\u` + prefix).replaceAll(",", String.raw`\u` + prefix);
303
185
  };
304
186
  const symbols = unicodes("20-26,28-2F,3A-40,5B-60,7B-7E,A0-BF,D7,F7", "00");
@@ -320,8 +202,8 @@ const regexps = {
320
202
  const deapostrophe = (s) => {
321
203
  return s.replace(regexps.apostrophe, "");
322
204
  };
323
- const up = String.prototype.toUpperCase;
324
- const low = String.prototype.toLowerCase;
205
+ const up = (s) => s.toUpperCase();
206
+ const low = (s) => s.toLowerCase();
325
207
  const fill = (s, fillWith, isDeapostrophe = false) => {
326
208
  s = s.replace(regexps.fill, function(m, next) {
327
209
  return next ? fillWith + next : "";
@@ -330,14 +212,13 @@ const fill = (s, fillWith, isDeapostrophe = false) => {
330
212
  return s;
331
213
  };
332
214
  const decap = (s, char = 0) => {
333
- return low.call(s.charAt(char)) + s.slice(char + 1);
215
+ return low(s.charAt(char)) + s.slice(char + 1);
334
216
  };
335
217
  const relax = (m, before, acronym, caps) => {
336
218
  return before + " " + (acronym ? acronym + " " : "") + caps;
337
219
  };
338
220
  const prep = (s, isFill = false, isPascal = false, isUpper = false) => {
339
- s = s == void 0 ? "" : s + "";
340
- if (!isUpper && regexps.upper.test(s)) s = low.call(s);
221
+ if (!isUpper && regexps.upper.test(s)) s = low(s);
341
222
  if (!isFill && !regexps.hole.test(s)) {
342
223
  var holey = fill(s, " ");
343
224
  if (regexps.hole.test(holey)) s = holey;
@@ -346,20 +227,20 @@ const prep = (s, isFill = false, isPascal = false, isUpper = false) => {
346
227
  return s;
347
228
  };
348
229
  const lower = (s, fillWith, isDeapostrophe) => {
349
- return fill(low.call(prep(s, !!fillWith)), fillWith, isDeapostrophe);
230
+ return fill(low(prep(s, !!fillWith)), fillWith, isDeapostrophe);
350
231
  };
351
232
  const pascalMemory = {};
352
- function pascal(s) {
233
+ function pascal(s = "") {
353
234
  if (pascalMemory[s]) return pascalMemory[s];
354
- const isStartWithUnderscore = s?.startsWith("_");
355
- if (regexps.upper.test(s)) s = low.call(s);
356
- const pascalString = (s?.match(/[a-zA-Z0-9\u00C0-\u017F]+/g) || []).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
235
+ const isStartWithUnderscore = s.startsWith("_");
236
+ if (regexps.upper.test(s)) s = low(s);
237
+ const pascalString = (s.match(/[a-zA-Z0-9\u00C0-\u017F]+/g) ?? []).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
357
238
  const pascalWithUnderscore = isStartWithUnderscore ? `_${pascalString}` : pascalString;
358
239
  pascalMemory[s] = pascalWithUnderscore;
359
240
  return pascalWithUnderscore;
360
241
  }
361
- function camel(s) {
362
- const isStartWithUnderscore = s?.startsWith("_");
242
+ function camel(s = "") {
243
+ const isStartWithUnderscore = s.startsWith("_");
363
244
  const camelString = decap(pascal(s), isStartWithUnderscore ? 1 : 0);
364
245
  return isStartWithUnderscore ? `_${camelString}` : camelString;
365
246
  }
@@ -370,7 +251,7 @@ function kebab(s) {
370
251
  return lower(s, "-", true);
371
252
  }
372
253
  function upper(s, fillWith, isDeapostrophe) {
373
- return fill(up.call(prep(s, !!fillWith, false, true)), fillWith, isDeapostrophe);
254
+ return fill(up(prep(s, !!fillWith, false, true)), fillWith, isDeapostrophe);
374
255
  }
375
256
  function conventionName(name, convention) {
376
257
  let nameConventionTransform = camel;
@@ -396,6 +277,87 @@ function compareVersions(firstVersion, secondVersions, operator = ">=") {
396
277
  return compare(firstVersion.replace(/(\s(.*))/, ""), secondVersions, operator);
397
278
  }
398
279
 
280
+ //#endregion
281
+ //#region src/utils/content-type.ts
282
+ /**
283
+ * Determine if a content type is binary (vs text-based).
284
+ */
285
+ function isBinaryContentType(contentType) {
286
+ if (contentType === "application/octet-stream") return true;
287
+ if (contentType.startsWith("image/")) return true;
288
+ if (contentType.startsWith("audio/")) return true;
289
+ if (contentType.startsWith("video/")) return true;
290
+ if (contentType.startsWith("font/")) return true;
291
+ if (contentType.startsWith("text/")) return false;
292
+ if ([
293
+ "+json",
294
+ "-json",
295
+ "+xml",
296
+ "-xml",
297
+ "+yaml",
298
+ "-yaml",
299
+ "+rss",
300
+ "-rss",
301
+ "+csv",
302
+ "-csv"
303
+ ].some((suffix) => contentType.includes(suffix))) return false;
304
+ return !new Set([
305
+ "application/json",
306
+ "application/xml",
307
+ "application/yaml",
308
+ "application/x-www-form-urlencoded",
309
+ "application/javascript",
310
+ "application/ecmascript",
311
+ "application/graphql"
312
+ ]).has(contentType);
313
+ }
314
+ /**
315
+ * Determine if a form-data field should be treated as a file (binary or text).
316
+ *
317
+ * Precedence (per OAS 3.1): encoding.contentType > schema.contentMediaType
318
+ *
319
+ * Returns:
320
+ * - 'binary': binary file (Blob)
321
+ * - 'text': text file (Blob | string)
322
+ * - undefined: not a file, use standard string resolution
323
+ */
324
+ function getFormDataFieldFileType(resolvedSchema, partContentType) {
325
+ if (resolvedSchema.type !== "string") return;
326
+ if (resolvedSchema.contentEncoding) return;
327
+ const contentMediaType = resolvedSchema.contentMediaType;
328
+ const effectiveContentType = partContentType ?? contentMediaType;
329
+ if (effectiveContentType) return isBinaryContentType(effectiveContentType) ? "binary" : "text";
330
+ }
331
+ /**
332
+ * Filters items by content type based on include/exclude rules
333
+ *
334
+ * @param items - Array of items with contentType property
335
+ * @param filter - Optional filter configuration
336
+ * @returns Filtered array
337
+ *
338
+ * @example
339
+ * ```ts
340
+ * const types = [
341
+ * { contentType: 'application/json', value: '...' },
342
+ * { contentType: 'text/xml', value: '...' }
343
+ * ];
344
+ *
345
+ * // Include only JSON
346
+ * filterByContentType(types, { include: ['application/json'] });
347
+ *
348
+ * // Exclude XML
349
+ * filterByContentType(types, { exclude: ['text/xml'] });
350
+ * ```
351
+ */
352
+ function filterByContentType(items, filter$1) {
353
+ if (!filter$1) return items;
354
+ return items.filter((item) => {
355
+ const shouldInclude = !filter$1.include || filter$1.include.includes(item.contentType);
356
+ const shouldExclude = filter$1.exclude?.includes(item.contentType) ?? false;
357
+ return shouldInclude && !shouldExclude;
358
+ });
359
+ }
360
+
399
361
  //#endregion
400
362
  //#region src/utils/debug.ts
401
363
  const filter = process.env.ORVAL_DEBUG_FILTER;
@@ -403,7 +365,7 @@ const DEBUG = process.env.DEBUG;
403
365
  function createDebugger(ns, options = {}) {
404
366
  const log$1 = debug(ns);
405
367
  const { onlyWhenFocused } = options;
406
- const focus = typeof onlyWhenFocused === "string" ? onlyWhenFocused : ns;
368
+ const focus = isString(onlyWhenFocused) ? onlyWhenFocused : ns;
407
369
  return (msg, ...args) => {
408
370
  if (filter && !msg.includes(filter)) return;
409
371
  if (onlyWhenFocused && !DEBUG?.includes(focus)) return;
@@ -417,13 +379,13 @@ const search = String.raw`\*/`;
417
379
  const replacement = String.raw`*\/`;
418
380
  const regex$1 = new RegExp(search, "g");
419
381
  function jsDoc(schema, tryOneLine = false, context) {
420
- if (context?.output?.override?.jsDoc) {
382
+ if (context?.output.override.jsDoc) {
421
383
  const { filter: filter$1 } = context.output.override.jsDoc;
422
384
  if (filter$1) return keyValuePairsToJsDoc(filter$1(schema));
423
385
  }
424
386
  const { description, deprecated, summary, minLength, maxLength, minimum, maximum, exclusiveMinimum, exclusiveMaximum, minItems, maxItems, pattern } = schema;
425
387
  const isNullable = schema.type === "null" || Array.isArray(schema.type) && schema.type.includes("null");
426
- const lines = (Array.isArray(description) ? description.filter((d) => !d.includes("eslint-disable")) : [description || ""]).map((line) => line.replaceAll(regex$1, replacement));
388
+ const lines = (Array.isArray(description) ? description.filter((d) => !d.includes("eslint-disable")) : [description ?? ""]).map((line) => line.replaceAll(regex$1, replacement));
427
389
  const count$1 = [
428
390
  description,
429
391
  deprecated,
@@ -438,7 +400,7 @@ function jsDoc(schema, tryOneLine = false, context) {
438
400
  maxItems?.toString(),
439
401
  isNullable ? "null" : "",
440
402
  pattern
441
- ].reduce((acc, it) => it ? acc + 1 : acc, 0);
403
+ ].filter(Boolean).length;
442
404
  if (!count$1) return "";
443
405
  const oneLine = count$1 === 1 && tryOneLine;
444
406
  const eslintDisable = Array.isArray(description) ? description.find((d) => d.includes("eslint-disable"))?.replaceAll(regex$1, replacement) : void 0;
@@ -505,10 +467,49 @@ async function dynamicImport(toImport, from = process.cwd(), takeDefault = true)
505
467
  }
506
468
  return toImport;
507
469
  } catch (error) {
508
- throw new Error(`Oups... 🍻. Path: ${toImport} => ${error}`);
470
+ throw new Error(`Oups... 🍻. Path: ${String(toImport)} => ${String(error)}`);
509
471
  }
510
472
  }
511
473
 
474
+ //#endregion
475
+ //#region src/utils/extension.ts
476
+ function getExtension(path$2) {
477
+ return path$2.toLowerCase().includes(".yaml") || path$2.toLowerCase().includes(".yml") ? "yaml" : "json";
478
+ }
479
+
480
+ //#endregion
481
+ //#region src/utils/file.ts
482
+ function getFileInfo(target = "", { backupFilename = "filename", extension = ".ts" } = {}) {
483
+ const isDir = isDirectory(target);
484
+ const filePath = isDir ? path.join(target, backupFilename + extension) : target;
485
+ return {
486
+ path: filePath,
487
+ pathWithoutExtension: filePath.replace(/\.[^/.]+$/, ""),
488
+ extension,
489
+ isDirectory: isDir,
490
+ dirname: path.dirname(filePath),
491
+ filename: path.basename(filePath, extension.startsWith(".") ? extension : `.${extension}`)
492
+ };
493
+ }
494
+ async function removeFilesAndEmptyFolders(patterns, dir) {
495
+ const files = await globby(patterns, {
496
+ cwd: dir,
497
+ absolute: true
498
+ });
499
+ await Promise.all(files.map((file) => fs.promises.unlink(file)));
500
+ const sortedDirectories = (await globby(["**/*"], {
501
+ cwd: dir,
502
+ absolute: true,
503
+ onlyDirectories: true
504
+ })).toSorted((a, b) => {
505
+ const depthA = a.split("/").length;
506
+ return b.split("/").length - depthA;
507
+ });
508
+ for (const directory of sortedDirectories) try {
509
+ if ((await fs.promises.readdir(directory)).length === 0) await fs.promises.rmdir(directory);
510
+ } catch {}
511
+ }
512
+
512
513
  //#endregion
513
514
  //#region src/utils/file-extensions.ts
514
515
  function getMockFileExtensionByTypeName(mock) {
@@ -557,7 +558,7 @@ function logError(err, tag) {
557
558
  if (err instanceof Error) {
558
559
  message = (err.message || err.stack) ?? "Unknown error";
559
560
  if (err.cause) {
560
- const causeMsg = err.cause instanceof Error ? err.cause.message : typeof err.cause === "string" ? err.cause : JSON.stringify(err.cause, void 0, 2);
561
+ const causeMsg = err.cause instanceof Error ? err.cause.message : isString(err.cause) ? err.cause : JSON.stringify(err.cause, void 0, 2);
561
562
  message += `\n Cause: ${causeMsg}`;
562
563
  }
563
564
  } else message = String(err);
@@ -644,16 +645,16 @@ function createLogger(level = "info", options = {}) {
644
645
 
645
646
  //#endregion
646
647
  //#region src/utils/merge-deep.ts
647
- const isObject$1 = (obj) => obj && typeof obj === "object";
648
648
  function mergeDeep(source, target) {
649
- if (!isObject$1(target) || !isObject$1(source)) return source;
650
- return Object.entries(target).reduce((acc, [key, value]) => {
649
+ if (!isObject(target) || !isObject(source)) return source;
650
+ const acc = Object.assign({}, source);
651
+ for (const [key, value] of Object.entries(target)) {
651
652
  const sourceValue = acc[key];
652
653
  if (Array.isArray(sourceValue) && Array.isArray(value)) acc[key] = [...sourceValue, ...value];
653
- else if (isObject$1(sourceValue) && isObject$1(value)) acc[key] = mergeDeep(sourceValue, value);
654
+ else if (isObject(sourceValue) && isObject(value)) acc[key] = mergeDeep(sourceValue, value);
654
655
  else acc[key] = value;
655
- return acc;
656
- }, Object.assign({}, source));
656
+ }
657
+ return acc;
657
658
  }
658
659
 
659
660
  //#endregion
@@ -663,9 +664,65 @@ function count(str = "", key) {
663
664
  return (str.match(new RegExp(key, "g")) ?? []).length;
664
665
  }
665
666
 
667
+ //#endregion
668
+ //#region src/utils/path.ts
669
+ var path_exports = /* @__PURE__ */ __export({
670
+ basename: () => basename,
671
+ dirname: () => dirname,
672
+ extname: () => extname,
673
+ getSchemaFileName: () => getSchemaFileName,
674
+ isAbsolute: () => isAbsolute,
675
+ join: () => join,
676
+ joinSafe: () => joinSafe,
677
+ normalizeSafe: () => normalizeSafe,
678
+ relativeSafe: () => relativeSafe,
679
+ resolve: () => resolve,
680
+ separator: () => separator
681
+ });
682
+ function wrapPathFn(fn) {
683
+ return (...args) => {
684
+ const result = fn(...args.map((p) => isStringLike(p) ? toUnix(p) : p));
685
+ return isStringLike(result) ? toUnix(result) : result;
686
+ };
687
+ }
688
+ const path$1 = Object.fromEntries(Object.entries(path).map(([key, value]) => [key, isFunction(value) ? wrapPathFn(value) : value]));
689
+ const { join, resolve, extname, dirname, basename, isAbsolute } = path$1;
690
+ /**
691
+ * Behaves exactly like `path.relative(from, to)`, but keeps the first meaningful "./"
692
+ */
693
+ function relativeSafe(from, to) {
694
+ return normalizeSafe(`.${separator}${path$1.relative(from, to)}`);
695
+ }
696
+ function getSchemaFileName(path$2) {
697
+ return path$2.replace(`.${getExtension(path$2)}`, "").slice(path$2.lastIndexOf("/") + 1);
698
+ }
699
+ const separator = "/";
700
+ const toUnix = function(value) {
701
+ value = value.replaceAll("\\", "/");
702
+ value = value.replaceAll(/(?<!^)\/+/g, "/");
703
+ return value;
704
+ };
705
+ function normalizeSafe(value) {
706
+ let result;
707
+ value = toUnix(value);
708
+ result = path$1.normalize(value);
709
+ if (value.startsWith("./") && !result.startsWith("./") && !result.startsWith("..")) result = "./" + result;
710
+ else if (value.startsWith("//") && !result.startsWith("//")) result = value.startsWith("//./") ? "//." + result : "/" + result;
711
+ return result;
712
+ }
713
+ function joinSafe(...values) {
714
+ let result = path$1.join(...values);
715
+ if (values.length > 0) {
716
+ const firstValue = toUnix(values[0]);
717
+ if (firstValue.startsWith("./") && !result.startsWith("./") && !result.startsWith("..")) result = "./" + result;
718
+ else if (firstValue.startsWith("//") && !result.startsWith("//")) result = firstValue.startsWith("//./") ? "//." + result : "/" + result;
719
+ }
720
+ return result;
721
+ }
722
+
666
723
  //#endregion
667
724
  //#region src/utils/sort.ts
668
- const sortByPriority = (arr) => arr.sort((a, b) => {
725
+ const sortByPriority = (arr) => arr.toSorted((a, b) => {
669
726
  if (a.default) return 1;
670
727
  if (b.default) return -1;
671
728
  if (a.required && b.required) return 0;
@@ -689,17 +746,20 @@ const sortByPriority = (arr) => arr.sort((a, b) => {
689
746
  * stringify({ a: 1, b: 'test' }) // returns "{ a: 1, b: 'test', }"
690
747
  */
691
748
  function stringify(data) {
692
- if (isUndefined(data) || isNull(data)) return;
749
+ if (isNullish$1(data)) return;
693
750
  if (isString(data)) return `'${data.replaceAll("'", String.raw`\'`)}'`;
694
- if (isNumber(data) || isBoolean(data) || isFunction(data)) return `${data}`;
695
- if (Array.isArray(data)) return `[${data.map(stringify).join(", ")}]`;
696
- return Object.entries(data).reduce((acc, [key, value], index, arr) => {
751
+ if (isNumber(data) || isBoolean(data) || isFunction(data)) return String(data);
752
+ if (Array.isArray(data)) return `[${data.map((item) => stringify(item)).join(", ")}]`;
753
+ const entries$1 = Object.entries(data);
754
+ let result = "";
755
+ for (const [index, [key, value]] of entries$1.entries()) {
697
756
  const strValue = stringify(value);
698
- if (arr.length === 1) return `{ ${key}: ${strValue}, }`;
699
- if (!index) return `{ ${key}: ${strValue}, `;
700
- if (arr.length - 1 === index) return acc + `${key}: ${strValue}, }`;
701
- return acc + `${key}: ${strValue}, `;
702
- }, "");
757
+ if (entries$1.length === 1) result = `{ ${key}: ${strValue}, }`;
758
+ else if (!index) result = `{ ${key}: ${strValue}, `;
759
+ else if (entries$1.length - 1 === index) result += `${key}: ${strValue}, }`;
760
+ else result += `${key}: ${strValue}, `;
761
+ }
762
+ return result;
703
763
  }
704
764
  /**
705
765
  * Sanitizes a string value by removing or replacing special characters and ensuring
@@ -723,7 +783,7 @@ function stringify(data) {
723
783
  function sanitize(value, options) {
724
784
  const { whitespace = "", underscore = "", dot = "", dash = "", es5keyword = false, es5IdentifierName = false, special = false } = options ?? {};
725
785
  let newValue = value;
726
- if (!special) newValue = newValue.replaceAll(/[!"`'#%&,:;<>=@{}~\$\(\)\*\+\/\\\?\[\]\^\|]/g, "");
786
+ if (!special) newValue = newValue.replaceAll(/[!"`'#%&,:;<>=@{}~$()*+/\\?[\]^|]/g, "");
727
787
  if (whitespace !== true) newValue = newValue.replaceAll(/[\s]/g, whitespace);
728
788
  if (underscore !== true) newValue = newValue.replaceAll(/['_']/g, underscore);
729
789
  if (dot !== true) newValue = newValue.replaceAll(/[.]/g, dot);
@@ -748,7 +808,11 @@ function sanitize(value, options) {
748
808
  */
749
809
  function toObjectString(props, path$2) {
750
810
  if (props.length === 0) return "";
751
- return (typeof path$2 === "string" ? props.map((prop$1) => path$2.split(".").reduce((obj, key) => obj && typeof obj === "object" ? obj[key] : void 0, prop$1)) : props).join(",\n ") + ",";
811
+ return (isString(path$2) ? props.map((prop$1) => {
812
+ let obj = prop$1;
813
+ for (const key of path$2.split(".")) obj = obj && (isObject(obj) || Array.isArray(obj)) ? obj[key] : void 0;
814
+ return obj;
815
+ }) : props).join(",\n ") + ",";
752
816
  }
753
817
  const NUMBERS = {
754
818
  "0": "zero",
@@ -772,7 +836,7 @@ const NUMBERS = {
772
836
  * getNumberWord(42) // returns "fourtwo"
773
837
  */
774
838
  function getNumberWord(num) {
775
- return num.toString().split("").reduce((acc, n) => acc + NUMBERS[n], "");
839
+ return [...num.toString()].reduce((acc, n) => acc + NUMBERS[n], "");
776
840
  }
777
841
  /**
778
842
  * Escapes a specific character in a string by prefixing it with a backslash.
@@ -797,11 +861,13 @@ function escape(str, char = "'") {
797
861
  * @param input String to escape
798
862
  */
799
863
  function jsStringEscape(input) {
800
- return input.replaceAll(/["'\\\n\r\u2028\u2029]/g, (character) => {
864
+ return input.replaceAll(/["'\\\n\r\u2028\u2029/*]/g, (character) => {
801
865
  switch (character) {
802
866
  case "\"":
803
867
  case "'":
804
- case "\\": return "\\" + character;
868
+ case "\\":
869
+ case "/":
870
+ case "*": return "\\" + character;
805
871
  case "\n": return String.raw`\n`;
806
872
  case "\r": return String.raw`\r`;
807
873
  case "\u2028": return String.raw`\u2028`;
@@ -813,12 +879,35 @@ function jsStringEscape(input) {
813
879
  /**
814
880
  * Deduplicates a TypeScript union type string.
815
881
  * Handles types like "A | B | B" → "A | B" and "null | null" → "null".
882
+ * Only splits on top-level | (not inside {} () [] <> or string literals).
816
883
  */
817
884
  function dedupeUnionType(unionType) {
818
- const parts = unionType.split("|").map((part) => part.trim());
819
- return [...new Set(parts)].join(" | ");
820
- }
821
-
885
+ const parts = [];
886
+ let current = "";
887
+ let depth = 0;
888
+ let quote = "";
889
+ let escaped = false;
890
+ for (const c of unionType) {
891
+ if (!escaped && (c === "'" || c === "\"")) {
892
+ if (!quote) quote = c;
893
+ else if (quote === c) quote = "";
894
+ }
895
+ if (!quote) {
896
+ if ("{([<".includes(c)) depth++;
897
+ if ("})]>".includes(c)) depth--;
898
+ if (c === "|" && depth === 0) {
899
+ parts.push(current.trim());
900
+ current = "";
901
+ continue;
902
+ }
903
+ }
904
+ current += c;
905
+ escaped = !!quote && !escaped && c === "\\";
906
+ }
907
+ if (current.trim()) parts.push(current.trim());
908
+ return [...new Set(parts)].join(" | ");
909
+ }
910
+
822
911
  //#endregion
823
912
  //#region src/utils/tsconfig.ts
824
913
  function isSyntheticDefaultImportsAllow(config) {
@@ -841,8 +930,7 @@ function getEnumDescriptions(schemaObject) {
841
930
  function getEnum(value, enumName, names, enumGenerationType, descriptions, enumNamingConvention) {
842
931
  if (enumGenerationType === EnumGeneration.CONST) return getTypeConstEnum(value, enumName, names, descriptions, enumNamingConvention);
843
932
  if (enumGenerationType === EnumGeneration.ENUM) return getNativeEnum(value, enumName, names, enumNamingConvention);
844
- if (enumGenerationType === EnumGeneration.UNION) return getUnion(value, enumName);
845
- throw new Error(`Invalid enumGenerationType: ${enumGenerationType}`);
933
+ return getUnion(value, enumName);
846
934
  }
847
935
  const getTypeConstEnum = (value, enumName, names, descriptions, enumNamingConvention) => {
848
936
  let enumValue = `export type ${enumName} = typeof ${enumName}[keyof typeof ${enumName}]`;
@@ -858,11 +946,16 @@ const getTypeConstEnum = (value, enumName, names, descriptions, enumNamingConven
858
946
  };
859
947
  function getEnumImplementation(value, names, descriptions, enumNamingConvention) {
860
948
  if (value === "") return "";
861
- return [...new Set(value.split(" | "))].reduce((acc, val, index) => {
949
+ const uniqueValues = [...new Set(value.split(" | "))];
950
+ let result = "";
951
+ for (const [index, val] of uniqueValues.entries()) {
862
952
  const name = names?.[index];
863
953
  const description = descriptions?.[index];
864
954
  const comment = description ? ` /** ${description} */\n` : "";
865
- if (name) return acc + comment + ` ${keyword.isIdentifierNameES5(name) ? name : `'${name}'`}: ${val},\n`;
955
+ if (name) {
956
+ result += comment + ` ${keyword.isIdentifierNameES5(name) ? name : `'${name}'`}: ${val},\n`;
957
+ continue;
958
+ }
866
959
  let key = val.startsWith("'") ? val.slice(1, -1) : val;
867
960
  if (isNumeric(key)) key = toNumberKey(key);
868
961
  if (key.length > 1) key = sanitize(key, {
@@ -872,17 +965,23 @@ function getEnumImplementation(value, names, descriptions, enumNamingConvention)
872
965
  special: true
873
966
  });
874
967
  if (enumNamingConvention) key = conventionName(key, enumNamingConvention);
875
- return acc + comment + ` ${keyword.isIdentifierNameES5(key) ? key : `'${key}'`}: ${val},\n`;
876
- }, "");
968
+ result += comment + ` ${keyword.isIdentifierNameES5(key) ? key : `'${key}'`}: ${val},\n`;
969
+ }
970
+ return result;
877
971
  }
878
972
  const getNativeEnum = (value, enumName, names, enumNamingConvention) => {
879
973
  return `export enum ${enumName} {\n${getNativeEnumItems(value, names, enumNamingConvention)}\n}`;
880
974
  };
881
975
  const getNativeEnumItems = (value, names, enumNamingConvention) => {
882
976
  if (value === "") return "";
883
- return [...new Set(value.split(" | "))].reduce((acc, val, index) => {
977
+ const uniqueValues = [...new Set(value.split(" | "))];
978
+ let result = "";
979
+ for (const [index, val] of uniqueValues.entries()) {
884
980
  const name = names?.[index];
885
- if (name) return acc + ` ${keyword.isIdentifierNameES5(name) ? name : `'${name}'`}= ${val},\n`;
981
+ if (name) {
982
+ result += ` ${keyword.isIdentifierNameES5(name) ? name : `'${name}'`}= ${val},\n`;
983
+ continue;
984
+ }
886
985
  let key = val.startsWith("'") ? val.slice(1, -1) : val;
887
986
  if (isNumeric(key)) key = toNumberKey(key);
888
987
  if (key.length > 1) key = sanitize(key, {
@@ -892,8 +991,9 @@ const getNativeEnumItems = (value, names, enumNamingConvention) => {
892
991
  special: true
893
992
  });
894
993
  if (enumNamingConvention) key = conventionName(key, enumNamingConvention);
895
- return acc + ` ${keyword.isIdentifierNameES5(key) ? key : `'${key}'`}= ${val},\n`;
896
- }, "");
994
+ result += ` ${keyword.isIdentifierNameES5(key) ? key : `'${key}'`}= ${val},\n`;
995
+ }
996
+ return result;
897
997
  };
898
998
  const toNumberKey = (value) => {
899
999
  if (value.startsWith("-")) return `NUMBER_MINUS_${value.slice(1)}`;
@@ -905,9 +1005,9 @@ const getUnion = (value, enumName) => {
905
1005
  };
906
1006
  function getEnumUnionFromSchema(schema) {
907
1007
  if (!schema?.enum) return "";
908
- return schema.enum.filter((val) => val !== null).map((val) => isString(val) ? `'${escape(val)}'` : `${val}`).join(" | ");
1008
+ return schema.enum.filter((val) => val !== null).map((val) => isString(val) ? `'${escape(val)}'` : String(val)).join(" | ");
909
1009
  }
910
- const stripNullUnion = (value) => value.replace(/\s*\|\s*null/g, "").trim();
1010
+ const stripNullUnion = (value) => value.replaceAll(/\s*\|\s*null/g, "").trim();
911
1011
  const isSpreadableEnumRef = (schema, refName) => {
912
1012
  if (!schema?.enum || !refName) return false;
913
1013
  if (!getEnumUnionFromSchema(schema)) return false;
@@ -1053,13 +1153,23 @@ function getSchema$1(schema, context) {
1053
1153
  const refInfo = getRefInfo(schema.$ref, context);
1054
1154
  const { refPaths } = refInfo;
1055
1155
  let schemaByRefPaths = Array.isArray(refPaths) ? prop(context.spec, ...refPaths) : void 0;
1056
- if (!schemaByRefPaths) schemaByRefPaths = context.spec;
1156
+ schemaByRefPaths ??= context.spec;
1057
1157
  if (isReference(schemaByRefPaths)) return getSchema$1(schemaByRefPaths, context);
1058
- let currentSchema = schemaByRefPaths ? schemaByRefPaths : context.spec;
1059
- if ("nullable" in schema) currentSchema = {
1060
- ...currentSchema,
1061
- nullable: schema.nullable
1062
- };
1158
+ let currentSchema = schemaByRefPaths || context.spec;
1159
+ if ("nullable" in schema) {
1160
+ const nullable = schema.nullable;
1161
+ currentSchema = {
1162
+ ...currentSchema,
1163
+ nullable
1164
+ };
1165
+ }
1166
+ if ("type" in schema && Array.isArray(schema.type)) {
1167
+ const type = schema.type;
1168
+ currentSchema = {
1169
+ ...currentSchema,
1170
+ type
1171
+ };
1172
+ }
1063
1173
  return {
1064
1174
  currentSchema,
1065
1175
  refInfo
@@ -1073,19 +1183,16 @@ function resolveExampleRefs(examples, context) {
1073
1183
  return schema.value;
1074
1184
  }
1075
1185
  return example;
1076
- }) : Object.entries(examples).reduce((acc, [key, example]) => {
1077
- let schema = example;
1078
- if (isReference(example)) schema = resolveRef(example, context).schema.value;
1079
- return {
1080
- ...acc,
1081
- [key]: schema
1082
- };
1083
- }, {});
1186
+ }) : (() => {
1187
+ const result = {};
1188
+ for (const [key, example] of Object.entries(examples)) result[key] = isReference(example) ? resolveRef(example, context).schema.value : example;
1189
+ return result;
1190
+ })();
1084
1191
  }
1085
1192
 
1086
1193
  //#endregion
1087
1194
  //#region src/resolvers/value.ts
1088
- function resolveValue({ schema, name, context }) {
1195
+ function resolveValue({ schema, name, context, formDataContext }) {
1089
1196
  if (isReference(schema)) {
1090
1197
  const { schema: schemaObject, imports } = resolveRef(schema, context);
1091
1198
  const resolvedImport = imports[0];
@@ -1099,7 +1206,8 @@ function resolveValue({ schema, name, context }) {
1099
1206
  parents: [...context.parents ?? [], refName]
1100
1207
  }
1101
1208
  }).hasReadonlyProps;
1102
- const nullable = Array.isArray(schemaObject.type) && schemaObject.type.includes("null") || schemaObject.nullable === true ? " | null" : "";
1209
+ const isAnyOfNullable = schemaObject.anyOf?.some((anyOfItem) => !isReference(anyOfItem) && (anyOfItem.type === "null" || Array.isArray(anyOfItem.type) && anyOfItem.type.includes("null")));
1210
+ const nullable = Array.isArray(schemaObject.type) && schemaObject.type.includes("null") || schemaObject.nullable === true || isAnyOfNullable ? " | null" : "";
1103
1211
  return {
1104
1212
  value: resolvedImport.name + nullable,
1105
1213
  imports: [{
@@ -1119,7 +1227,8 @@ function resolveValue({ schema, name, context }) {
1119
1227
  ...getScalar({
1120
1228
  item: schema,
1121
1229
  name,
1122
- context
1230
+ context,
1231
+ formDataContext
1123
1232
  }),
1124
1233
  originalSchema: schema,
1125
1234
  isRef: false
@@ -1135,7 +1244,7 @@ function resolveValue({ schema, name, context }) {
1135
1244
  function createTypeAliasIfNeeded({ resolvedValue, propName, context }) {
1136
1245
  if (!propName) return;
1137
1246
  if (resolvedValue.isEnum || resolvedValue.type !== "object") return;
1138
- const aliasPattern = context.output.override.aliasCombinedTypes ? "{|&|\\|" : "{";
1247
+ const aliasPattern = context.output.override.aliasCombinedTypes ? String.raw`{|&|\|` : "{";
1139
1248
  if (!new RegExp(aliasPattern).test(resolvedValue.value)) return;
1140
1249
  const { originalSchema } = resolvedValue;
1141
1250
  const doc = jsDoc(originalSchema);
@@ -1161,11 +1270,12 @@ function createTypeAliasIfNeeded({ resolvedValue, propName, context }) {
1161
1270
  dependencies: resolvedValue.dependencies
1162
1271
  };
1163
1272
  }
1164
- function resolveObjectOriginal({ schema, propName, combined = false, context }) {
1273
+ function resolveObjectOriginal({ schema, propName, combined = false, context, formDataContext }) {
1165
1274
  const resolvedValue = resolveValue({
1166
1275
  schema,
1167
1276
  name: propName,
1168
- context
1277
+ context,
1278
+ formDataContext
1169
1279
  });
1170
1280
  const aliased = createTypeAliasIfNeeded({
1171
1281
  resolvedValue,
@@ -1177,8 +1287,8 @@ function resolveObjectOriginal({ schema, propName, combined = false, context })
1177
1287
  originalSchema: resolvedValue.originalSchema
1178
1288
  };
1179
1289
  if (propName && resolvedValue.isEnum && !combined && !resolvedValue.isRef) {
1180
- const doc = jsDoc(resolvedValue.originalSchema ?? {});
1181
- const enumValue = getEnum(resolvedValue.value, propName, getEnumNames(resolvedValue.originalSchema), context.output.override.enumGenerationType, getEnumDescriptions(resolvedValue.originalSchema), context.output.override.namingConvention?.enum);
1290
+ const doc = jsDoc(resolvedValue.originalSchema);
1291
+ const enumValue = getEnum(resolvedValue.value, propName, getEnumNames(resolvedValue.originalSchema), context.output.override.enumGenerationType, getEnumDescriptions(resolvedValue.originalSchema), context.output.override.namingConvention.enum);
1182
1292
  return {
1183
1293
  value: propName,
1184
1294
  imports: [{ name: propName }],
@@ -1199,19 +1309,21 @@ function resolveObjectOriginal({ schema, propName, combined = false, context })
1199
1309
  return resolvedValue;
1200
1310
  }
1201
1311
  const resolveObjectCacheMap = /* @__PURE__ */ new Map();
1202
- function resolveObject({ schema, propName, combined = false, context }) {
1312
+ function resolveObject({ schema, propName, combined = false, context, formDataContext }) {
1203
1313
  const hashKey = JSON.stringify({
1204
1314
  schema,
1205
1315
  propName,
1206
1316
  combined,
1207
- projectName: context.projectName ?? context.output.target
1317
+ projectName: context.projectName ?? context.output.target,
1318
+ formDataContext
1208
1319
  });
1209
1320
  if (resolveObjectCacheMap.has(hashKey)) return resolveObjectCacheMap.get(hashKey);
1210
1321
  const result = resolveObjectOriginal({
1211
1322
  schema,
1212
1323
  propName,
1213
1324
  combined,
1214
- context
1325
+ context,
1326
+ formDataContext
1215
1327
  });
1216
1328
  resolveObjectCacheMap.set(hashKey, result);
1217
1329
  return result;
@@ -1224,18 +1336,22 @@ function resolveObject({ schema, propName, combined = false, context }) {
1224
1336
  *
1225
1337
  * @param item item with type === "array"
1226
1338
  */
1227
- function getArray({ schema, name, context }) {
1228
- const schema31 = schema;
1229
- if (schema31.prefixItems) {
1230
- const resolvedObjects = schema31.prefixItems.map((item, index) => resolveObject({
1339
+ function getArray({ schema, name, context, formDataContext }) {
1340
+ const schemaPrefixItems = schema.prefixItems;
1341
+ const schemaItems = schema.items;
1342
+ const schemaExample = schema.example;
1343
+ const schemaExamples = schema.examples;
1344
+ const itemSuffix = context.output.override.components.schemas.itemSuffix;
1345
+ if (schemaPrefixItems) {
1346
+ const resolvedObjects = schemaPrefixItems.map((item, index) => resolveObject({
1231
1347
  schema: item,
1232
- propName: name + context.output.override.components.schemas.itemSuffix + index,
1348
+ propName: name ? name + itemSuffix + String(index) : void 0,
1233
1349
  context
1234
1350
  }));
1235
- if (schema31.items) {
1351
+ if (schemaItems) {
1236
1352
  const additional = resolveObject({
1237
- schema: schema31.items,
1238
- propName: name + context.output.override.components.schemas.itemSuffix + "Additional",
1353
+ schema: schemaItems,
1354
+ propName: name ? name + itemSuffix + "Additional" : void 0,
1239
1355
  context
1240
1356
  });
1241
1357
  resolvedObjects.push({
@@ -1252,15 +1368,16 @@ function getArray({ schema, name, context }) {
1252
1368
  schemas: resolvedObjects.flatMap((o) => o.schemas),
1253
1369
  dependencies: resolvedObjects.flatMap((o) => o.dependencies),
1254
1370
  hasReadonlyProps: resolvedObjects.some((o) => o.hasReadonlyProps),
1255
- example: schema.example,
1256
- examples: resolveExampleRefs(schema.examples, context)
1371
+ example: schemaExample,
1372
+ examples: resolveExampleRefs(schemaExamples, context)
1257
1373
  };
1258
1374
  }
1259
- if (schema.items) {
1375
+ if (schemaItems) {
1260
1376
  const resolvedObject = resolveObject({
1261
- schema: schema.items,
1262
- propName: name + context.output.override.components.schemas.itemSuffix,
1263
- context
1377
+ schema: schemaItems,
1378
+ propName: name ? name + itemSuffix : void 0,
1379
+ context,
1380
+ formDataContext
1264
1381
  });
1265
1382
  return {
1266
1383
  value: `${schema.readOnly === true && !context.output.override.suppressReadonlyModifier ? "readonly " : ""}${resolvedObject.value.includes("|") ? `(${resolvedObject.value})[]` : `${resolvedObject.value}[]`}`,
@@ -1271,8 +1388,8 @@ function getArray({ schema, name, context }) {
1271
1388
  type: "array",
1272
1389
  isRef: false,
1273
1390
  hasReadonlyProps: resolvedObject.hasReadonlyProps,
1274
- example: schema.example,
1275
- examples: resolveExampleRefs(schema.examples, context)
1391
+ example: schemaExample,
1392
+ examples: resolveExampleRefs(schemaExamples, context)
1276
1393
  };
1277
1394
  } else if (compareVersions(context.spec.openapi, "3.1", ">=")) return {
1278
1395
  value: "unknown[]",
@@ -1288,321 +1405,452 @@ function getArray({ schema, name, context }) {
1288
1405
  }
1289
1406
 
1290
1407
  //#endregion
1291
- //#region src/getters/imports.ts
1292
- function getAliasedImports({ name, resolvedValue, context }) {
1293
- return context.output.schemas && resolvedValue.isRef ? resolvedValue.imports.map((imp) => {
1294
- if (!needCreateImportAlias({
1295
- name,
1296
- imp
1297
- })) return imp;
1298
- return {
1299
- ...imp,
1300
- alias: `__${imp.name}`
1301
- };
1302
- }) : resolvedValue.imports;
1303
- }
1304
- function needCreateImportAlias({ imp, name }) {
1305
- return !imp.alias && imp.name === name;
1306
- }
1307
- function getImportAliasForRefOrValue({ context, imports, resolvedValue }) {
1308
- if (!context.output.schemas || !resolvedValue.isRef) return resolvedValue.value;
1309
- return imports.find((imp) => imp.name === resolvedValue.value)?.alias ?? resolvedValue.value;
1408
+ //#region src/getters/res-req-types.ts
1409
+ const getSchemaType = (s) => s.type;
1410
+ const getSchemaCombined = (s) => s.oneOf ?? s.anyOf ?? s.allOf;
1411
+ const getSchemaOneOf = (s) => s.oneOf;
1412
+ const getSchemaAnyOf = (s) => s.anyOf;
1413
+ const getSchemaItems = (s) => s.items;
1414
+ const getSchemaRequired = (s) => s.required;
1415
+ const getSchemaProperties = (s) => s.properties;
1416
+ const formDataContentTypes = new Set(["multipart/form-data"]);
1417
+ const formUrlEncodedContentTypes = new Set(["application/x-www-form-urlencoded"]);
1418
+ function getResReqContentTypes({ mediaType, propName, context, isFormData, contentType }) {
1419
+ if (!mediaType.schema) return;
1420
+ const formDataContext = isFormData ? {
1421
+ atPart: false,
1422
+ encoding: mediaType.encoding ?? {}
1423
+ } : void 0;
1424
+ const resolvedObject = resolveObject({
1425
+ schema: mediaType.schema,
1426
+ propName,
1427
+ context,
1428
+ formDataContext
1429
+ });
1430
+ if (!isFormData && isBinaryContentType(contentType)) return {
1431
+ ...resolvedObject,
1432
+ value: "Blob"
1433
+ };
1434
+ return resolvedObject;
1310
1435
  }
1311
-
1312
- //#endregion
1313
- //#region src/getters/scalar.ts
1314
- /**
1315
- * Return the typescript equivalent of open-api data type
1316
- *
1317
- * @param item
1318
- * @ref https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.1.md#data-types
1319
- */
1320
- function getScalar({ item, name, context }) {
1321
- const nullable = isArray(item.type) && item.type.includes("null") || item.nullable === true ? " | null" : "";
1322
- const enumItems = item.enum?.filter((enumItem) => enumItem !== null);
1323
- let itemType = item.type;
1324
- if (!itemType && item.items) {
1325
- item.type = "array";
1326
- itemType = "array";
1327
- }
1328
- if (isArray(item.type) && item.type.includes("null")) {
1329
- const typesWithoutNull = item.type.filter((x) => x !== "null");
1330
- itemType = typesWithoutNull.length === 1 ? typesWithoutNull[0] : typesWithoutNull;
1331
- }
1332
- switch (itemType) {
1333
- case "number":
1334
- case "integer": {
1335
- let value = context.output.override.useBigInt && (item.format === "int64" || item.format === "uint64") ? "bigint" : "number";
1336
- let isEnum = false;
1337
- if (enumItems) {
1338
- value = enumItems.map((enumItem) => `${enumItem}`).join(" | ");
1339
- isEnum = true;
1340
- }
1341
- value += nullable;
1342
- const itemWithConst = item;
1343
- if (itemWithConst.const !== void 0) value = itemWithConst.const;
1344
- return {
1345
- value,
1346
- isEnum,
1347
- type: "number",
1436
+ function getResReqTypes(responsesOrRequests, name, context, defaultType = "unknown", uniqueKey = (item) => item.value) {
1437
+ return uniqueBy(responsesOrRequests.filter(([, res]) => Boolean(res)).map(([key, res]) => {
1438
+ if (isReference(res)) {
1439
+ const { schema: bodySchema, imports: [{ name: name$1, schemaName }] } = resolveRef(res, context);
1440
+ const [contentType, mediaType] = Object.entries(bodySchema.content ?? {})[0] ?? [];
1441
+ const isFormData = formDataContentTypes.has(contentType);
1442
+ const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1443
+ if (!isFormData && !isFormUrlEncoded || !mediaType.schema) return [{
1444
+ value: name$1,
1445
+ imports: [{
1446
+ name: name$1,
1447
+ schemaName
1448
+ }],
1348
1449
  schemas: [],
1349
- imports: [],
1350
- isRef: false,
1351
- hasReadonlyProps: item.readOnly || false,
1352
- dependencies: [],
1353
- example: item.example,
1354
- examples: resolveExampleRefs(item.examples, context)
1355
- };
1356
- }
1357
- case "boolean": {
1358
- let value = "boolean" + nullable;
1359
- const itemWithConst = item;
1360
- if (itemWithConst.const !== void 0) value = itemWithConst.const;
1361
- return {
1362
- value,
1363
- type: "boolean",
1450
+ type: "unknown",
1364
1451
  isEnum: false,
1365
- schemas: [],
1366
- imports: [],
1367
- isRef: false,
1368
- hasReadonlyProps: item.readOnly || false,
1369
- dependencies: [],
1370
- example: item.example,
1371
- examples: resolveExampleRefs(item.examples, context)
1372
- };
1373
- }
1374
- case "array": {
1375
- const { value, ...rest } = getArray({
1376
- schema: item,
1377
- name,
1452
+ isRef: true,
1453
+ hasReadonlyProps: false,
1454
+ originalSchema: mediaType.schema,
1455
+ example: mediaType.example,
1456
+ examples: resolveExampleRefs(mediaType.examples, context),
1457
+ key,
1458
+ contentType
1459
+ }];
1460
+ const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1461
+ name: name$1,
1462
+ schemaObject: mediaType.schema,
1463
+ context,
1464
+ isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1465
+ isRef: true,
1466
+ encoding: mediaType.encoding
1467
+ }) : void 0;
1468
+ const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1469
+ name: name$1,
1470
+ schemaObject: mediaType.schema,
1471
+ context,
1472
+ isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1473
+ isUrlEncoded: true,
1474
+ isRef: true,
1475
+ encoding: mediaType.encoding
1476
+ }) : void 0;
1477
+ const additionalImports = getFormDataAdditionalImports({
1478
+ schemaObject: mediaType.schema,
1378
1479
  context
1379
1480
  });
1380
- return {
1381
- value: value + nullable,
1382
- ...rest,
1383
- dependencies: rest.dependencies ?? []
1384
- };
1385
- }
1386
- case "string": {
1387
- let value = "string";
1388
- let isEnum = false;
1389
- if (enumItems) {
1390
- value = enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `);
1391
- isEnum = true;
1392
- }
1393
- if (item.format === "binary") value = "Blob";
1394
- if (context.output.override.useDates && (item.format === "date" || item.format === "date-time")) value = "Date";
1395
- value += nullable;
1396
- const itemWithConst = item;
1397
- if (itemWithConst.const) value = `'${itemWithConst.const}'`;
1398
- return {
1399
- value,
1400
- isEnum,
1401
- type: "string",
1402
- imports: [],
1481
+ return [{
1482
+ value: name$1,
1483
+ imports: [{
1484
+ name: name$1,
1485
+ schemaName
1486
+ }, ...additionalImports],
1403
1487
  schemas: [],
1404
- isRef: false,
1405
- hasReadonlyProps: item.readOnly || false,
1406
- dependencies: [],
1407
- example: item.example,
1408
- examples: resolveExampleRefs(item.examples, context)
1409
- };
1488
+ type: "unknown",
1489
+ isEnum: false,
1490
+ hasReadonlyProps: false,
1491
+ formData,
1492
+ formUrlEncoded,
1493
+ isRef: true,
1494
+ originalSchema: mediaType.schema,
1495
+ example: mediaType.example,
1496
+ examples: resolveExampleRefs(mediaType.examples, context),
1497
+ key,
1498
+ contentType
1499
+ }];
1410
1500
  }
1411
- case "null": return {
1412
- value: "null",
1413
- isEnum: false,
1414
- type: "null",
1415
- imports: [],
1416
- schemas: [],
1417
- isRef: false,
1418
- hasReadonlyProps: item.readOnly || false,
1419
- dependencies: []
1420
- };
1421
- case "object":
1422
- default: {
1423
- if (isArray(itemType)) return combineSchemas({
1424
- schema: { anyOf: itemType.map((type) => ({
1425
- ...item,
1426
- type
1427
- })) },
1428
- name,
1429
- separator: "anyOf",
1501
+ if (res.content) return Object.entries(res.content).map(([contentType, mediaType], index, arr) => {
1502
+ let propName = key ? pascal(name) + pascal(key) : void 0;
1503
+ if (propName && arr.length > 1) propName = propName + pascal(getNumberWord(index + 1));
1504
+ let effectivePropName = propName;
1505
+ if (mediaType.schema && isReference(mediaType.schema)) {
1506
+ const { imports } = resolveRef(mediaType.schema, context);
1507
+ if (imports[0]?.name) effectivePropName = imports[0].name;
1508
+ }
1509
+ const isFormData = formDataContentTypes.has(contentType);
1510
+ const resolvedValue = getResReqContentTypes({
1511
+ mediaType,
1512
+ propName: effectivePropName,
1430
1513
  context,
1431
- nullable
1514
+ isFormData,
1515
+ contentType
1432
1516
  });
1433
- if (enumItems) return {
1434
- value: enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `) + nullable,
1435
- isEnum: true,
1436
- type: "string",
1437
- imports: [],
1438
- schemas: [],
1439
- isRef: false,
1440
- hasReadonlyProps: item.readOnly || false,
1441
- dependencies: [],
1442
- example: item.example,
1443
- examples: resolveExampleRefs(item.examples, context)
1517
+ if (!resolvedValue) {
1518
+ if (isBinaryContentType(contentType)) return {
1519
+ value: "Blob",
1520
+ imports: [],
1521
+ schemas: [],
1522
+ type: "Blob",
1523
+ isEnum: false,
1524
+ key,
1525
+ isRef: false,
1526
+ hasReadonlyProps: false,
1527
+ contentType
1528
+ };
1529
+ return;
1530
+ }
1531
+ const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1532
+ if (!isFormData && !isFormUrlEncoded || !effectivePropName || !mediaType.schema) return {
1533
+ ...resolvedValue,
1534
+ imports: resolvedValue.imports,
1535
+ contentType,
1536
+ example: mediaType.example,
1537
+ examples: resolveExampleRefs(mediaType.examples, context)
1444
1538
  };
1445
- const { value, ...rest } = getObject({
1446
- item,
1447
- name,
1448
- context,
1449
- nullable
1539
+ const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1540
+ name: effectivePropName,
1541
+ schemaObject: mediaType.schema,
1542
+ context,
1543
+ isRequestBodyOptional: "required" in res && res.required === false,
1544
+ isRef: true,
1545
+ encoding: mediaType.encoding
1546
+ }) : void 0;
1547
+ const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1548
+ name: effectivePropName,
1549
+ schemaObject: mediaType.schema,
1550
+ context,
1551
+ isUrlEncoded: true,
1552
+ isRequestBodyOptional: "required" in res && res.required === false,
1553
+ isRef: true,
1554
+ encoding: mediaType.encoding
1555
+ }) : void 0;
1556
+ const additionalImports = getFormDataAdditionalImports({
1557
+ schemaObject: mediaType.schema,
1558
+ context
1450
1559
  });
1451
1560
  return {
1452
- value,
1453
- ...rest
1561
+ ...resolvedValue,
1562
+ imports: [...resolvedValue.imports, ...additionalImports],
1563
+ formData,
1564
+ formUrlEncoded,
1565
+ contentType,
1566
+ example: mediaType.example,
1567
+ examples: resolveExampleRefs(mediaType.examples, context)
1454
1568
  };
1569
+ }).filter(Boolean).map((x) => ({
1570
+ ...x,
1571
+ key
1572
+ }));
1573
+ const swaggerSchema = "schema" in res ? res.schema : void 0;
1574
+ if (swaggerSchema) return [{
1575
+ ...resolveObject({
1576
+ schema: swaggerSchema,
1577
+ propName: key ? pascal(name) + pascal(key) : void 0,
1578
+ context
1579
+ }),
1580
+ contentType: "application/json",
1581
+ key
1582
+ }];
1583
+ return [{
1584
+ value: defaultType,
1585
+ imports: [],
1586
+ schemas: [],
1587
+ type: defaultType,
1588
+ isEnum: false,
1589
+ key,
1590
+ isRef: false,
1591
+ hasReadonlyProps: false,
1592
+ contentType: "application/json"
1593
+ }];
1594
+ }).flat(), uniqueKey);
1595
+ }
1596
+ /**
1597
+ * Determine the response type category for a given content type.
1598
+ * Used to set the correct responseType option in HTTP clients.
1599
+ *
1600
+ * @param contentType - The MIME content type (e.g., 'application/json', 'text/plain')
1601
+ * @returns The response type category to use for parsing
1602
+ */
1603
+ function getResponseTypeCategory(contentType) {
1604
+ if (isBinaryContentType(contentType)) return "blob";
1605
+ if (contentType === "application/json" || contentType.includes("+json") || contentType.includes("-json")) return "json";
1606
+ return "text";
1607
+ }
1608
+ /**
1609
+ * Get the default content type from a list of content types.
1610
+ * Priority: application/json > any JSON-like type > first in list
1611
+ *
1612
+ * @param contentTypes - Array of content types from OpenAPI spec
1613
+ * @returns The default content type to use
1614
+ */
1615
+ function getDefaultContentType(contentTypes) {
1616
+ if (contentTypes.length === 0) return "application/json";
1617
+ if (contentTypes.includes("application/json")) return "application/json";
1618
+ const jsonType = contentTypes.find((ct) => ct.includes("+json") || ct.includes("-json"));
1619
+ if (jsonType) return jsonType;
1620
+ return contentTypes[0];
1621
+ }
1622
+ function getFormDataAdditionalImports({ schemaObject, context }) {
1623
+ const { schema } = resolveRef(schemaObject, context);
1624
+ if (schema.type !== "object") return [];
1625
+ const combinedSchemas = getSchemaOneOf(schema) ?? getSchemaAnyOf(schema);
1626
+ if (!combinedSchemas) return [];
1627
+ return combinedSchemas.map((subSchema) => resolveRef(subSchema, context).imports[0]).filter(Boolean);
1628
+ }
1629
+ function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequestBodyOptional, isUrlEncoded, isRef, encoding }) {
1630
+ const { schema, imports } = resolveRef(schemaObject, context);
1631
+ const propName = camel(!isRef && isReference(schemaObject) ? imports[0].name : name);
1632
+ const additionalImports = [];
1633
+ const variableName = isUrlEncoded ? "formUrlEncoded" : "formData";
1634
+ let form = isUrlEncoded ? `const ${variableName} = new URLSearchParams();\n` : `const ${variableName} = new FormData();\n`;
1635
+ const combinedSchemas = getSchemaCombined(schema);
1636
+ if (schema.type === "object" || schema.type === void 0 && combinedSchemas) {
1637
+ if (combinedSchemas) {
1638
+ const shouldCast = !!getSchemaOneOf(schema) || !!getSchemaAnyOf(schema);
1639
+ const combinedSchemasFormData = combinedSchemas.map((subSchema) => {
1640
+ const { schema: combinedSchema, imports: imports$1 } = resolveRef(subSchema, context);
1641
+ let newPropName = propName;
1642
+ let newPropDefinition = "";
1643
+ if (shouldCast && imports$1[0]) {
1644
+ additionalImports.push(imports$1[0]);
1645
+ newPropName = `${propName}${pascal(imports$1[0].name)}`;
1646
+ newPropDefinition = `const ${newPropName} = (${propName} as ${imports$1[0].name}${isRequestBodyOptional ? " | undefined" : ""});\n`;
1647
+ }
1648
+ return newPropDefinition + resolveSchemaPropertiesToFormData({
1649
+ schema: combinedSchema,
1650
+ variableName,
1651
+ propName: newPropName,
1652
+ context,
1653
+ isRequestBodyOptional,
1654
+ encoding
1655
+ });
1656
+ }).filter(Boolean).join("\n");
1657
+ form += combinedSchemasFormData;
1658
+ }
1659
+ if (schema.properties) {
1660
+ const formDataValues = resolveSchemaPropertiesToFormData({
1661
+ schema,
1662
+ variableName,
1663
+ propName,
1664
+ context,
1665
+ isRequestBodyOptional,
1666
+ encoding
1667
+ });
1668
+ form += formDataValues;
1455
1669
  }
1670
+ return form;
1456
1671
  }
1457
- }
1458
-
1459
- //#endregion
1460
- //#region src/getters/combine.ts
1461
- function combineValues({ resolvedData, resolvedValue, separator: separator$1, context }) {
1462
- if (resolvedData.isEnum.every(Boolean)) return `${resolvedData.values.join(` | `)}${resolvedValue ? ` | ${resolvedValue.value}` : ""}`;
1463
- if (separator$1 === "allOf") {
1464
- let resolvedDataValue = resolvedData.values.map((v) => v.includes(" | ") ? `(${v})` : v).join(` & `);
1465
- if (resolvedData.originalSchema.length > 0 && resolvedValue) {
1466
- const discriminatedPropertySchemas = resolvedData.originalSchema.filter((s) => s?.discriminator && resolvedValue.value.includes(` ${s.discriminator.propertyName}:`));
1467
- if (discriminatedPropertySchemas.length > 0) resolvedDataValue = `Omit<${resolvedDataValue}, '${discriminatedPropertySchemas.map((s) => s.discriminator?.propertyName).join("' | '")}'>`;
1672
+ if (schema.type === "array") {
1673
+ let valueStr = "value";
1674
+ const schemaItems = getSchemaItems(schema);
1675
+ if (schemaItems) {
1676
+ const { schema: itemSchema } = resolveRef(schemaItems, context);
1677
+ if (itemSchema.type === "object" || itemSchema.type === "array") valueStr = "JSON.stringify(value)";
1678
+ else if (itemSchema.type === "number" || itemSchema.type === "integer" || itemSchema.type === "boolean") valueStr = "value.toString()";
1468
1679
  }
1469
- const resolvedValueStr = resolvedValue?.value.includes(" | ") ? `(${resolvedValue.value})` : resolvedValue?.value;
1470
- const joined = `${resolvedDataValue}${resolvedValue ? ` & ${resolvedValueStr}` : ""}`;
1471
- const overrideRequiredProperties = resolvedData.requiredProperties.filter((prop$1) => !resolvedData.originalSchema.some((schema) => schema?.properties?.[prop$1] && schema.required?.includes(prop$1)));
1472
- if (overrideRequiredProperties.length > 0) return `${joined} & Required<Pick<${joined}, '${overrideRequiredProperties.join("' | '")}'>>`;
1473
- return joined;
1680
+ return `${form}${propName}.forEach(value => ${variableName}.append('data', ${valueStr}))\n`;
1474
1681
  }
1475
- let values = resolvedData.values;
1476
- if (resolvedData.allProperties.length && context.output.unionAddMissingProperties) {
1477
- values = [];
1478
- for (let i = 0; i < resolvedData.values.length; i += 1) {
1479
- const subSchema = resolvedData.originalSchema[i];
1480
- if (subSchema?.type !== "object") {
1481
- values.push(resolvedData.values[i]);
1682
+ if (schema.type === "number" || schema.type === "integer" || schema.type === "boolean") return `${form}${variableName}.append('data', ${propName}.toString())\n`;
1683
+ return `${form}${variableName}.append('data', ${propName})\n`;
1684
+ }
1685
+ function resolveSchemaPropertiesToFormData({ schema, variableName, propName, context, isRequestBodyOptional, keyPrefix = "", depth = 0, encoding }) {
1686
+ let formDataValues = "";
1687
+ const schemaProps = getSchemaProperties(schema) ?? {};
1688
+ for (const [key, value] of Object.entries(schemaProps)) {
1689
+ const { schema: property } = resolveRef(value, context);
1690
+ if (property.readOnly) continue;
1691
+ let formDataValue = "";
1692
+ const partContentType = (depth === 0 ? encoding?.[key] : void 0)?.contentType;
1693
+ const formattedKeyPrefix = isRequestBodyOptional ? keyword.isIdentifierNameES5(key) ? "?" : "?." : "";
1694
+ const formattedKey = keyword.isIdentifierNameES5(key) ? `.${key}` : `['${key}']`;
1695
+ const valueKey = `${propName}${formattedKeyPrefix}${formattedKey}`;
1696
+ const nonOptionalValueKey = `${propName}${formattedKey}`;
1697
+ const fileType = getFormDataFieldFileType(property, partContentType);
1698
+ const effectiveContentType = partContentType ?? property.contentMediaType;
1699
+ if (fileType === "binary" || property.format === "binary") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
1700
+ else if (fileType === "text") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey} instanceof Blob ? ${nonOptionalValueKey} : new Blob([${nonOptionalValueKey}], { type: '${effectiveContentType}' }));\n`;
1701
+ else if (property.type === "object") formDataValue = context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE ? resolveSchemaPropertiesToFormData({
1702
+ schema: property,
1703
+ variableName,
1704
+ propName: nonOptionalValueKey,
1705
+ context,
1706
+ isRequestBodyOptional,
1707
+ keyPrefix: `${keyPrefix}${key}.`,
1708
+ depth: depth + 1,
1709
+ encoding
1710
+ }) : partContentType ? `${variableName}.append(\`${keyPrefix}${key}\`, new Blob([JSON.stringify(${nonOptionalValueKey})], { type: '${partContentType}' }));\n` : `${variableName}.append(\`${keyPrefix}${key}\`, JSON.stringify(${nonOptionalValueKey}));\n`;
1711
+ else if (property.type === "array") {
1712
+ let valueStr = "value";
1713
+ let hasNonPrimitiveChild = false;
1714
+ const propertyItems = getSchemaItems(property);
1715
+ if (propertyItems) {
1716
+ const { schema: itemSchema } = resolveRef(propertyItems, context);
1717
+ if (itemSchema.type === "object" || itemSchema.type === "array") if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
1718
+ hasNonPrimitiveChild = true;
1719
+ const resolvedValue = resolveSchemaPropertiesToFormData({
1720
+ schema: itemSchema,
1721
+ variableName,
1722
+ propName: "value",
1723
+ context,
1724
+ isRequestBodyOptional,
1725
+ keyPrefix: `${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}].`,
1726
+ depth: depth + 1
1727
+ });
1728
+ formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => {
1729
+ ${resolvedValue}});\n`;
1730
+ } else valueStr = "JSON.stringify(value)";
1731
+ else {
1732
+ const itemType = getSchemaType(itemSchema);
1733
+ 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()";
1734
+ }
1735
+ }
1736
+ if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
1737
+ if (!hasNonPrimitiveChild) formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => ${variableName}.append(\`${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}]\`, ${valueStr}));\n`;
1738
+ } else formDataValue = `${valueKey}.forEach(value => ${variableName}.append(\`${keyPrefix}${key}${context.output.override.formData.arrayHandling === FormDataArrayHandling.SERIALIZE_WITH_BRACKETS ? "[]" : ""}\`, ${valueStr}));\n`;
1739
+ } else if ((() => {
1740
+ const propType$1 = getSchemaType(property);
1741
+ return propType$1 === "number" || Array.isArray(propType$1) && propType$1.includes("number") || propType$1 === "integer" || Array.isArray(propType$1) && propType$1.includes("integer") || propType$1 === "boolean" || Array.isArray(propType$1) && propType$1.includes("boolean");
1742
+ })()) formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey}.toString())\n`;
1743
+ else formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
1744
+ let existSubSchemaNullable = false;
1745
+ const combine = getSchemaCombined(property);
1746
+ if (combine) {
1747
+ const subSchemas = combine.map((c) => resolveObject({
1748
+ schema: c,
1749
+ combined: true,
1750
+ context
1751
+ }));
1752
+ if (subSchemas.some((subSchema) => {
1753
+ return [
1754
+ "number",
1755
+ "integer",
1756
+ "boolean"
1757
+ ].includes(subSchema.type);
1758
+ })) formDataValue = `${variableName}.append(\`${key}\`, ${nonOptionalValueKey}.toString())\n`;
1759
+ if (subSchemas.some((subSchema) => {
1760
+ return subSchema.type === "null";
1761
+ })) existSubSchemaNullable = true;
1762
+ }
1763
+ const isRequired = getSchemaRequired(schema)?.includes(key) && !isRequestBodyOptional;
1764
+ const propType = getSchemaType(property);
1765
+ if (property.nullable || Array.isArray(propType) && propType.includes("null") || existSubSchemaNullable) {
1766
+ if (isRequired) {
1767
+ formDataValues += `if(${valueKey} !== null) {\n ${formDataValue} }\n`;
1482
1768
  continue;
1483
1769
  }
1484
- const missingProperties = unique(resolvedData.allProperties.filter((p) => !Object.keys(subSchema.properties).includes(p)));
1485
- values.push(`${resolvedData.values[i]}${missingProperties.length > 0 ? ` & {${missingProperties.map((p) => `${p}?: never`).join("; ")}}` : ""}`);
1770
+ formDataValues += `if(${valueKey} !== undefined && ${nonOptionalValueKey} !== null) {\n ${formDataValue} }\n`;
1771
+ continue;
1486
1772
  }
1487
- }
1488
- if (resolvedValue) return `(${values.join(` & ${resolvedValue.value}) | (`)} & ${resolvedValue.value})`;
1489
- return values.join(" | ");
1490
- }
1491
- function combineSchemas({ name, schema, separator: separator$1, context, nullable }) {
1492
- const items = schema[separator$1] ?? [];
1493
- const resolvedData = items.reduce((acc, subSchema) => {
1494
- let propName;
1495
- if (context.output.override.aliasCombinedTypes) {
1496
- propName = name ? name + pascal(separator$1) : void 0;
1497
- if (propName && acc.schemas.length > 0) propName = propName + pascal(getNumberWord(acc.schemas.length + 1));
1773
+ if (isRequired) {
1774
+ formDataValues += formDataValue;
1775
+ continue;
1498
1776
  }
1499
- if (separator$1 === "allOf" && isSchema(subSchema) && subSchema.required) acc.requiredProperties.push(...subSchema.required);
1500
- const resolvedValue$1 = resolveObject({
1501
- schema: subSchema,
1502
- propName,
1503
- combined: true,
1504
- context
1505
- });
1506
- const aliasedImports = getAliasedImports({
1507
- context,
1508
- name,
1509
- resolvedValue: resolvedValue$1
1510
- });
1511
- const value = getImportAliasForRefOrValue({
1512
- context,
1513
- resolvedValue: resolvedValue$1,
1514
- imports: aliasedImports
1515
- });
1516
- acc.values.push(value);
1517
- acc.imports.push(...aliasedImports);
1518
- acc.schemas.push(...resolvedValue$1.schemas);
1519
- acc.dependencies.push(...resolvedValue$1.dependencies);
1520
- acc.isEnum.push(resolvedValue$1.isEnum);
1521
- acc.types.push(resolvedValue$1.type);
1522
- acc.isRef.push(resolvedValue$1.isRef);
1523
- acc.originalSchema.push(resolvedValue$1.originalSchema);
1524
- acc.hasReadonlyProps ||= resolvedValue$1.hasReadonlyProps;
1525
- if (resolvedValue$1.type === "object" && resolvedValue$1.originalSchema.properties) acc.allProperties.push(...Object.keys(resolvedValue$1.originalSchema.properties));
1526
- return acc;
1527
- }, {
1528
- values: [],
1529
- imports: [],
1530
- schemas: [],
1531
- isEnum: [],
1532
- isRef: [],
1533
- types: [],
1534
- dependencies: [],
1535
- originalSchema: [],
1536
- allProperties: [],
1537
- hasReadonlyProps: false,
1538
- example: schema.example,
1539
- examples: resolveExampleRefs(schema.examples, context),
1540
- requiredProperties: separator$1 === "allOf" ? schema.required ?? [] : []
1541
- });
1542
- if (resolvedData.isEnum.every(Boolean) && name && items.length > 1 && context.output.override.enumGenerationType !== EnumGeneration.UNION) {
1543
- const { value: combinedEnumValue, valueImports, hasNull } = getCombinedEnumValue(resolvedData.values.map((value, index) => ({
1544
- value,
1545
- isRef: resolvedData.isRef[index],
1546
- schema: resolvedData.originalSchema[index]
1547
- })));
1548
- const newEnum = `export const ${pascal(name)} = ${combinedEnumValue}`;
1549
- const valueImportSet = new Set(valueImports);
1550
- const typeSuffix = `${nullable}${hasNull && !nullable.includes("null") ? " | null" : ""}`;
1551
- return {
1552
- value: `typeof ${pascal(name)}[keyof typeof ${pascal(name)}]${typeSuffix}`,
1553
- imports: [{ name: pascal(name) }],
1554
- schemas: [...resolvedData.schemas, {
1555
- imports: resolvedData.imports.filter((toImport) => valueImportSet.has(toImport.alias ?? toImport.name)).map((toImport) => ({
1556
- ...toImport,
1557
- values: true
1558
- })),
1559
- model: newEnum,
1560
- name
1561
- }],
1562
- isEnum: false,
1563
- type: "object",
1564
- isRef: false,
1565
- hasReadonlyProps: resolvedData.hasReadonlyProps,
1566
- dependencies: resolvedData.dependencies,
1567
- example: schema.example,
1568
- examples: resolveExampleRefs(schema.examples, context)
1569
- };
1777
+ formDataValues += `if(${valueKey} !== undefined) {\n ${formDataValue} }\n`;
1570
1778
  }
1571
- let resolvedValue;
1572
- if (schema.properties) resolvedValue = getScalar({
1573
- item: Object.fromEntries(Object.entries(schema).filter(([key]) => key !== separator$1)),
1574
- name,
1575
- context
1576
- });
1577
- else if (separator$1 === "allOf" && (schema.oneOf || schema.anyOf)) {
1578
- const siblingCombiner = schema.oneOf ? "oneOf" : "anyOf";
1579
- resolvedValue = combineSchemas({
1580
- schema: { [siblingCombiner]: schema[siblingCombiner] },
1581
- name,
1582
- separator: siblingCombiner,
1583
- context,
1584
- nullable: ""
1779
+ return formDataValues;
1780
+ }
1781
+
1782
+ //#endregion
1783
+ //#region src/getters/body.ts
1784
+ /**
1785
+ * Extract all content types from a requestBody (#2812)
1786
+ */
1787
+ function getRequestBodyContentTypes(requestBody, context) {
1788
+ if (!requestBody) return [];
1789
+ const content = (isReference(requestBody) ? resolveRef(requestBody, context).schema : requestBody).content;
1790
+ return content ? Object.keys(content) : [];
1791
+ }
1792
+ function getBody({ requestBody, operationName, context, contentType }) {
1793
+ const filteredBodyTypes = filterByContentType(getResReqTypes([[context.output.override.components.requestBodies.suffix, requestBody]], operationName, context), contentType);
1794
+ const imports = filteredBodyTypes.flatMap(({ imports: imports$1 }) => imports$1);
1795
+ const schemas = filteredBodyTypes.flatMap(({ schemas: schemas$1 }) => schemas$1);
1796
+ const definition = filteredBodyTypes.map(({ value }) => value).join(" | ");
1797
+ const nonReadonlyDefinition = filteredBodyTypes.some((x) => x.hasReadonlyProps) && definition ? `NonReadonly<${definition}>` : definition;
1798
+ let implementation = generalJSTypesWithArray.includes(definition.toLowerCase()) || filteredBodyTypes.length > 1 ? camel(operationName) + context.output.override.components.requestBodies.suffix : camel(definition);
1799
+ let isOptional = false;
1800
+ if (implementation) {
1801
+ implementation = sanitize(implementation, {
1802
+ underscore: "_",
1803
+ whitespace: "_",
1804
+ dash: true,
1805
+ es5keyword: true,
1806
+ es5IdentifierName: true
1585
1807
  });
1808
+ if (isReference(requestBody)) {
1809
+ const { schema: bodySchema } = resolveRef(requestBody, context);
1810
+ if (bodySchema.required !== void 0) isOptional = !bodySchema.required;
1811
+ } else if (requestBody.required !== void 0) isOptional = !requestBody.required;
1586
1812
  }
1587
1813
  return {
1588
- value: dedupeUnionType(combineValues({
1589
- resolvedData,
1590
- separator: separator$1,
1591
- resolvedValue,
1592
- context
1593
- }) + nullable),
1594
- imports: resolvedValue ? [...resolvedData.imports, ...resolvedValue.imports] : resolvedData.imports,
1595
- schemas: resolvedValue ? [...resolvedData.schemas, ...resolvedValue.schemas] : resolvedData.schemas,
1596
- dependencies: resolvedValue ? [...resolvedData.dependencies, ...resolvedValue.dependencies] : resolvedData.dependencies,
1597
- isEnum: false,
1598
- type: "object",
1599
- isRef: false,
1600
- hasReadonlyProps: resolvedData?.hasReadonlyProps || resolvedValue?.hasReadonlyProps || false,
1601
- example: schema.example,
1602
- examples: resolveExampleRefs(schema.examples, context)
1814
+ originalSchema: requestBody,
1815
+ definition: nonReadonlyDefinition,
1816
+ implementation,
1817
+ imports,
1818
+ schemas,
1819
+ isOptional,
1820
+ ...filteredBodyTypes.length === 1 ? {
1821
+ formData: filteredBodyTypes[0].formData,
1822
+ formUrlEncoded: filteredBodyTypes[0].formUrlEncoded,
1823
+ contentType: filteredBodyTypes[0].contentType
1824
+ } : {
1825
+ formData: "",
1826
+ formUrlEncoded: "",
1827
+ contentType: ""
1828
+ }
1603
1829
  };
1604
1830
  }
1605
1831
 
1832
+ //#endregion
1833
+ //#region src/getters/imports.ts
1834
+ function getAliasedImports({ name, resolvedValue, context }) {
1835
+ return context.output.schemas && resolvedValue.isRef ? resolvedValue.imports.map((imp) => {
1836
+ if (!needCreateImportAlias({
1837
+ name,
1838
+ imp
1839
+ })) return imp;
1840
+ return {
1841
+ ...imp,
1842
+ alias: `__${imp.name}`
1843
+ };
1844
+ }) : resolvedValue.imports;
1845
+ }
1846
+ function needCreateImportAlias({ imp, name }) {
1847
+ return !imp.alias && imp.name === name;
1848
+ }
1849
+ function getImportAliasForRefOrValue({ context, imports, resolvedValue }) {
1850
+ if (!context.output.schemas || !resolvedValue.isRef) return resolvedValue.value;
1851
+ return imports.find((imp) => imp.name === resolvedValue.value)?.alias ?? resolvedValue.value;
1852
+ }
1853
+
1606
1854
  //#endregion
1607
1855
  //#region src/getters/keys.ts
1608
1856
  function getKey(key) {
@@ -1616,7 +1864,10 @@ function getKey(key) {
1616
1864
  * Returns undefined if propertyNames doesn't have an enum
1617
1865
  */
1618
1866
  function getPropertyNamesEnum(item) {
1619
- if ("propertyNames" in item && item.propertyNames && "enum" in item.propertyNames && Array.isArray(item.propertyNames.enum)) return item.propertyNames.enum.filter((val) => typeof val === "string");
1867
+ if ("propertyNames" in item && item.propertyNames && "enum" in item.propertyNames) {
1868
+ const propertyNames = item.propertyNames;
1869
+ if (Array.isArray(propertyNames.enum)) return propertyNames.enum.filter((val) => isString(val));
1870
+ }
1620
1871
  }
1621
1872
  /**
1622
1873
  * Generate index signature key type based on propertyNames enum
@@ -1637,7 +1888,7 @@ function getPropertyNamesRecordType(item, valueType) {
1637
1888
  *
1638
1889
  * @param item item with type === "object"
1639
1890
  */
1640
- function getObject({ item, name, context, nullable, propertyOverrides }) {
1891
+ function getObject({ item, name, context, nullable, formDataContext }) {
1641
1892
  if (isReference(item)) {
1642
1893
  const { name: name$1 } = getRefInfo(item.$ref, context);
1643
1894
  return {
@@ -1658,25 +1909,46 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1658
1909
  name,
1659
1910
  separator: item.allOf ? "allOf" : item.oneOf ? "oneOf" : "anyOf",
1660
1911
  context,
1661
- nullable
1662
- });
1663
- if (Array.isArray(item.type)) return combineSchemas({
1664
- schema: { anyOf: item.type.map((type) => ({
1665
- ...item,
1666
- type
1667
- })) },
1668
- name,
1669
- separator: "anyOf",
1670
- context,
1671
- nullable
1912
+ nullable,
1913
+ formDataContext
1672
1914
  });
1673
- if (item.properties && Object.entries(item.properties).length > 0) {
1674
- const entries$1 = Object.entries(item.properties);
1915
+ if (Array.isArray(item.type)) {
1916
+ const typeArray = item.type;
1917
+ const baseItem = item;
1918
+ return combineSchemas({
1919
+ schema: { anyOf: typeArray.map((type) => ({
1920
+ ...baseItem,
1921
+ type
1922
+ })) },
1923
+ name,
1924
+ separator: "anyOf",
1925
+ context,
1926
+ nullable
1927
+ });
1928
+ }
1929
+ const itemProperties = item.properties;
1930
+ if (itemProperties && Object.entries(itemProperties).length > 0) {
1931
+ const entries$1 = Object.entries(itemProperties);
1675
1932
  if (context.output.propertySortOrder === PropertySortOrder.ALPHABETICAL) entries$1.sort((a, b) => {
1676
1933
  return a[0].localeCompare(b[0]);
1677
1934
  });
1678
- return entries$1.reduce((acc, [key, schema], index, arr) => {
1679
- const isRequired = (Array.isArray(item.required) ? item.required : []).includes(key);
1935
+ const acc = {
1936
+ imports: [],
1937
+ schemas: [],
1938
+ value: "",
1939
+ isEnum: false,
1940
+ type: "object",
1941
+ isRef: false,
1942
+ schema: {},
1943
+ hasReadonlyProps: false,
1944
+ useTypeAlias: false,
1945
+ dependencies: [],
1946
+ example: item.example,
1947
+ examples: resolveExampleRefs(item.examples, context)
1948
+ };
1949
+ const itemRequired = item.required;
1950
+ for (const [index, [key, schema]] of entries$1.entries()) {
1951
+ const isRequired = (Array.isArray(itemRequired) ? itemRequired : []).includes(key);
1680
1952
  let propName = "";
1681
1953
  if (name) {
1682
1954
  const isKeyStartWithUnderscore = key.startsWith("_");
@@ -1684,21 +1956,26 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1684
1956
  }
1685
1957
  const allSpecSchemas = context.spec.components?.schemas ?? {};
1686
1958
  if (Object.keys(allSpecSchemas).some((schemaName) => pascal(schemaName) === propName)) propName = propName + "Property";
1687
- const resolvedValue = propertyOverrides?.[key] ?? resolveObject({
1959
+ const propertyFormDataContext = formDataContext && !formDataContext.atPart ? {
1960
+ atPart: true,
1961
+ partContentType: formDataContext.encoding[key]?.contentType
1962
+ } : void 0;
1963
+ const resolvedValue = resolveObject({
1688
1964
  schema,
1689
1965
  propName,
1690
- context
1966
+ context,
1967
+ formDataContext: propertyFormDataContext
1691
1968
  });
1692
- const isReadOnly = item.readOnly || schema.readOnly;
1969
+ const isReadOnly = item.readOnly ?? schema.readOnly;
1693
1970
  if (!index) acc.value += "{";
1694
1971
  const doc = jsDoc(schema, true, context);
1695
- acc.hasReadonlyProps ||= isReadOnly || false;
1696
- const constValue = "const" in schema ? schema.const : void 0;
1697
- const hasConst = constValue !== void 0;
1972
+ if (isReadOnly ?? false) acc.hasReadonlyProps = true;
1973
+ const constValue$1 = "const" in schema ? schema.const : void 0;
1974
+ const hasConst = constValue$1 !== void 0;
1698
1975
  let constLiteral;
1699
1976
  if (!hasConst) constLiteral = void 0;
1700
- else if (typeof constValue === "string") constLiteral = `'${escape(constValue)}'`;
1701
- else constLiteral = JSON.stringify(constValue);
1977
+ else if (isString(constValue$1)) constLiteral = `'${escape(constValue$1)}'`;
1978
+ else constLiteral = JSON.stringify(constValue$1);
1702
1979
  const needsValueImport = hasConst && (resolvedValue.isEnum || resolvedValue.type === "enum");
1703
1980
  const aliasedImports = needsValueImport ? resolvedValue.imports.map((imp) => ({
1704
1981
  ...imp,
@@ -1719,8 +1996,9 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1719
1996
  acc.value += `\n ${doc ? `${doc} ` : ""}${isReadOnly && !context.output.override.suppressReadonlyModifier ? "readonly " : ""}${getKey(key)}${isRequired ? "" : "?"}: ${finalPropValue};`;
1720
1997
  acc.schemas.push(...resolvedValue.schemas);
1721
1998
  acc.dependencies.push(...resolvedValue.dependencies);
1722
- if (arr.length - 1 === index) {
1723
- if (item.additionalProperties) if (isBoolean(item.additionalProperties)) {
1999
+ if (entries$1.length - 1 === index) {
2000
+ const additionalProps = item.additionalProperties;
2001
+ if (additionalProps) if (isBoolean(additionalProps)) {
1724
2002
  const recordType$1 = getPropertyNamesRecordType(item, "unknown");
1725
2003
  if (recordType$1) {
1726
2004
  acc.value += "\n}";
@@ -1732,42 +2010,31 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1732
2010
  }
1733
2011
  } else {
1734
2012
  const resolvedValue$1 = resolveValue({
1735
- schema: item.additionalProperties,
2013
+ schema: additionalProps,
1736
2014
  name,
1737
2015
  context
1738
2016
  });
1739
2017
  const recordType$1 = getPropertyNamesRecordType(item, resolvedValue$1.value);
1740
2018
  if (recordType$1) {
1741
- acc.value += "\n}";
1742
- acc.value += ` & ${recordType$1}`;
1743
- acc.useTypeAlias = true;
1744
- } else {
1745
- const keyType$1 = getIndexSignatureKey(item);
1746
- acc.value += `\n [key: ${keyType$1}]: ${resolvedValue$1.value};\n}`;
1747
- }
1748
- acc.dependencies.push(...resolvedValue$1.dependencies);
1749
- }
1750
- else acc.value += "\n}";
1751
- acc.value += nullable;
1752
- }
1753
- return acc;
1754
- }, {
1755
- imports: [],
1756
- schemas: [],
1757
- value: "",
1758
- isEnum: false,
1759
- type: "object",
1760
- isRef: false,
1761
- schema: {},
1762
- hasReadonlyProps: false,
1763
- useTypeAlias: false,
1764
- dependencies: [],
1765
- example: item.example,
1766
- examples: resolveExampleRefs(item.examples, context)
1767
- });
2019
+ acc.value += "\n}";
2020
+ acc.value += ` & ${recordType$1}`;
2021
+ acc.useTypeAlias = true;
2022
+ } else {
2023
+ const keyType$1 = getIndexSignatureKey(item);
2024
+ acc.value += `\n [key: ${keyType$1}]: ${resolvedValue$1.value};\n}`;
2025
+ }
2026
+ acc.dependencies.push(...resolvedValue$1.dependencies);
2027
+ }
2028
+ else acc.value += "\n}";
2029
+ acc.value += nullable;
2030
+ }
2031
+ }
2032
+ return acc;
1768
2033
  }
1769
- if (item.additionalProperties) {
1770
- if (isBoolean(item.additionalProperties)) {
2034
+ const outerAdditionalProps = item.additionalProperties;
2035
+ const readOnlyFlag = item.readOnly;
2036
+ if (outerAdditionalProps) {
2037
+ if (isBoolean(outerAdditionalProps)) {
1771
2038
  const recordType$2 = getPropertyNamesRecordType(item, "unknown");
1772
2039
  if (recordType$2) return {
1773
2040
  value: recordType$2 + nullable,
@@ -1776,7 +2043,7 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1776
2043
  isEnum: false,
1777
2044
  type: "object",
1778
2045
  isRef: false,
1779
- hasReadonlyProps: item.readOnly || false,
2046
+ hasReadonlyProps: readOnlyFlag ?? false,
1780
2047
  useTypeAlias: true,
1781
2048
  dependencies: []
1782
2049
  };
@@ -1787,21 +2054,21 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1787
2054
  isEnum: false,
1788
2055
  type: "object",
1789
2056
  isRef: false,
1790
- hasReadonlyProps: item.readOnly || false,
2057
+ hasReadonlyProps: readOnlyFlag ?? false,
1791
2058
  useTypeAlias: false,
1792
2059
  dependencies: []
1793
2060
  };
1794
2061
  }
1795
2062
  const resolvedValue = resolveValue({
1796
- schema: item.additionalProperties,
2063
+ schema: outerAdditionalProps,
1797
2064
  name,
1798
2065
  context
1799
2066
  });
1800
2067
  const recordType$1 = getPropertyNamesRecordType(item, resolvedValue.value);
1801
2068
  if (recordType$1) return {
1802
2069
  value: recordType$1 + nullable,
1803
- imports: resolvedValue.imports ?? [],
1804
- schemas: resolvedValue.schemas ?? [],
2070
+ imports: resolvedValue.imports,
2071
+ schemas: resolvedValue.schemas,
1805
2072
  isEnum: false,
1806
2073
  type: "object",
1807
2074
  isRef: false,
@@ -1811,8 +2078,8 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1811
2078
  };
1812
2079
  return {
1813
2080
  value: `{[key: ${getIndexSignatureKey(item)}]: ${resolvedValue.value}}` + nullable,
1814
- imports: resolvedValue.imports ?? [],
1815
- schemas: resolvedValue.schemas ?? [],
2081
+ imports: resolvedValue.imports,
2082
+ schemas: resolvedValue.schemas,
1816
2083
  isEnum: false,
1817
2084
  type: "object",
1818
2085
  isRef: false,
@@ -1821,15 +2088,15 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1821
2088
  dependencies: resolvedValue.dependencies
1822
2089
  };
1823
2090
  }
1824
- const itemWithConst = item;
1825
- if (itemWithConst.const) return {
1826
- value: `'${itemWithConst.const}'`,
2091
+ const constValue = item.const;
2092
+ if (constValue) return {
2093
+ value: `'${constValue}'`,
1827
2094
  imports: [],
1828
2095
  schemas: [],
1829
2096
  isEnum: false,
1830
2097
  type: "string",
1831
2098
  isRef: false,
1832
- hasReadonlyProps: item.readOnly || false,
2099
+ hasReadonlyProps: readOnlyFlag ?? false,
1833
2100
  dependencies: []
1834
2101
  };
1835
2102
  const keyType = item.type === "object" ? getIndexSignatureKey(item) : "string";
@@ -1841,7 +2108,7 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1841
2108
  isEnum: false,
1842
2109
  type: "object",
1843
2110
  isRef: false,
1844
- hasReadonlyProps: item.readOnly || false,
2111
+ hasReadonlyProps: readOnlyFlag ?? false,
1845
2112
  useTypeAlias: true,
1846
2113
  dependencies: []
1847
2114
  };
@@ -1852,492 +2119,367 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1852
2119
  isEnum: false,
1853
2120
  type: "object",
1854
2121
  isRef: false,
1855
- hasReadonlyProps: item.readOnly || false,
2122
+ hasReadonlyProps: readOnlyFlag ?? false,
1856
2123
  useTypeAlias: false,
1857
2124
  dependencies: []
1858
2125
  };
1859
2126
  }
1860
2127
 
1861
2128
  //#endregion
1862
- //#region src/getters/res-req-types.ts
1863
- const formDataContentTypes = new Set(["multipart/form-data"]);
1864
- const formUrlEncodedContentTypes = new Set(["application/x-www-form-urlencoded"]);
1865
- function getResReqContentTypes({ mediaType, propName, context, isFormData, contentType }) {
1866
- if (!mediaType.schema) return;
1867
- if (isFormData) {
1868
- const formDataResult = resolveFormDataRootObject({
1869
- schemaOrRef: mediaType.schema,
1870
- propName,
1871
- context,
1872
- encoding: mediaType.encoding
1873
- });
1874
- if (formDataResult) return formDataResult;
1875
- }
1876
- const resolvedObject = resolveObject({
1877
- schema: mediaType.schema,
1878
- propName,
1879
- context
1880
- });
1881
- if (!isFormData && isBinaryContentType(contentType)) return {
1882
- ...resolvedObject,
1883
- value: "Blob"
1884
- };
1885
- return resolvedObject;
1886
- }
1887
- function getResReqTypes(responsesOrRequests, name, context, defaultType = "unknown", uniqueKey = (item) => item.value) {
1888
- return uniqueBy(responsesOrRequests.filter(([_, res]) => Boolean(res)).map(([key, res]) => {
1889
- if (isReference(res)) {
1890
- const { schema: bodySchema, imports: [{ name: name$1, schemaName }] } = resolveRef(res, context);
1891
- const [contentType, mediaType] = Object.entries(bodySchema.content ?? {})[0] ?? [];
1892
- const isFormData = formDataContentTypes.has(contentType);
1893
- const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1894
- if (!isFormData && !isFormUrlEncoded || !mediaType.schema) return [{
1895
- value: name$1,
1896
- imports: [{
1897
- name: name$1,
1898
- schemaName
1899
- }],
1900
- schemas: [],
1901
- type: "unknown",
1902
- isEnum: false,
1903
- isRef: true,
1904
- hasReadonlyProps: false,
1905
- originalSchema: mediaType?.schema,
1906
- example: mediaType?.example,
1907
- examples: resolveExampleRefs(mediaType?.examples, context),
1908
- key,
1909
- contentType
1910
- }];
1911
- const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1912
- name: name$1,
1913
- schemaObject: mediaType.schema,
1914
- context,
1915
- isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1916
- isRef: true,
1917
- encoding: mediaType.encoding
1918
- }) : void 0;
1919
- const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1920
- name: name$1,
1921
- schemaObject: mediaType.schema,
1922
- context,
1923
- isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1924
- isUrlEncoded: true,
1925
- isRef: true,
1926
- encoding: mediaType.encoding
1927
- }) : void 0;
1928
- const additionalImports = getFormDataAdditionalImports({
1929
- schemaObject: mediaType.schema,
1930
- context
1931
- });
1932
- return [{
1933
- value: name$1,
1934
- imports: [{
1935
- name: name$1,
1936
- schemaName
1937
- }, ...additionalImports],
1938
- schemas: [],
1939
- type: "unknown",
1940
- isEnum: false,
1941
- hasReadonlyProps: false,
1942
- formData,
1943
- formUrlEncoded,
1944
- isRef: true,
1945
- originalSchema: mediaType.schema,
1946
- example: mediaType.example,
1947
- examples: resolveExampleRefs(mediaType.examples, context),
1948
- key,
1949
- contentType
1950
- }];
1951
- }
1952
- if (res.content) return Object.entries(res.content).map(([contentType, mediaType], index, arr) => {
1953
- let propName = key ? pascal(name) + pascal(key) : void 0;
1954
- if (propName && arr.length > 1) propName = propName + pascal(getNumberWord(index + 1));
1955
- let effectivePropName = propName;
1956
- if (mediaType.schema && isReference(mediaType.schema)) {
1957
- const { imports } = resolveRef(mediaType.schema, context);
1958
- if (imports[0]?.name) effectivePropName = imports[0].name;
1959
- }
1960
- const isFormData = formDataContentTypes.has(contentType);
1961
- const resolvedValue = getResReqContentTypes({
1962
- mediaType,
1963
- propName: effectivePropName,
1964
- context,
1965
- isFormData,
1966
- contentType
1967
- });
1968
- if (!resolvedValue) {
1969
- if (isBinaryContentType(contentType)) return {
1970
- value: "Blob",
1971
- imports: [],
1972
- schemas: [],
1973
- type: "Blob",
1974
- isEnum: false,
1975
- key,
1976
- isRef: false,
1977
- hasReadonlyProps: false,
1978
- contentType
1979
- };
1980
- return;
1981
- }
1982
- const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1983
- if (!isFormData && !isFormUrlEncoded || !effectivePropName) return {
1984
- ...resolvedValue,
1985
- imports: resolvedValue.imports,
1986
- contentType,
1987
- example: mediaType.example,
1988
- examples: resolveExampleRefs(mediaType.examples, context)
1989
- };
1990
- const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1991
- name: effectivePropName,
1992
- schemaObject: mediaType.schema,
1993
- context,
1994
- isRequestBodyOptional: "required" in res && res.required === false,
1995
- isRef: true,
1996
- encoding: mediaType.encoding
1997
- }) : void 0;
1998
- const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1999
- name: effectivePropName,
2000
- schemaObject: mediaType.schema,
2001
- context,
2002
- isUrlEncoded: true,
2003
- isRequestBodyOptional: "required" in res && res.required === false,
2004
- isRef: true,
2005
- encoding: mediaType.encoding
2006
- }) : void 0;
2007
- const additionalImports = getFormDataAdditionalImports({
2008
- schemaObject: mediaType.schema,
2009
- context
2010
- });
2011
- return {
2012
- ...resolvedValue,
2013
- imports: [...resolvedValue.imports, ...additionalImports],
2014
- formData,
2015
- formUrlEncoded,
2016
- contentType,
2017
- example: mediaType.example,
2018
- examples: resolveExampleRefs(mediaType.examples, context)
2019
- };
2020
- }).filter(Boolean).map((x) => ({
2021
- ...x,
2022
- key
2023
- }));
2024
- const swaggerSchema = "schema" in res ? res.schema : void 0;
2025
- if (swaggerSchema) return [{
2026
- ...resolveObject({
2027
- schema: swaggerSchema,
2028
- propName: key ? pascal(name) + pascal(key) : void 0,
2029
- context
2030
- }),
2031
- contentType: "application/json",
2032
- key
2033
- }];
2034
- return [{
2035
- value: defaultType,
2036
- imports: [],
2037
- schemas: [],
2038
- type: defaultType,
2039
- isEnum: false,
2040
- key,
2041
- isRef: false,
2042
- hasReadonlyProps: false,
2043
- contentType: "application/json"
2044
- }];
2045
- }).flat(), uniqueKey);
2046
- }
2047
- function isBinaryContentType(contentType) {
2048
- if (contentType === "application/octet-stream") return true;
2049
- if (contentType.startsWith("image/")) return true;
2050
- if (contentType.startsWith("audio/")) return true;
2051
- if (contentType.startsWith("video/")) return true;
2052
- if (contentType.startsWith("font/")) return true;
2053
- if (contentType.startsWith("text/")) return false;
2054
- if ([
2055
- "+json",
2056
- "-json",
2057
- "+xml",
2058
- "-xml",
2059
- "+yaml",
2060
- "-yaml",
2061
- "+rss",
2062
- "-rss",
2063
- "+csv",
2064
- "-csv"
2065
- ].some((suffix) => contentType.includes(suffix))) return false;
2066
- return !new Set([
2067
- "application/json",
2068
- "application/xml",
2069
- "application/yaml",
2070
- "application/x-www-form-urlencoded",
2071
- "application/javascript",
2072
- "application/ecmascript",
2073
- "application/graphql"
2074
- ]).has(contentType);
2075
- }
2076
- /**
2077
- * Determine the response type category for a given content type.
2078
- * Used to set the correct responseType option in HTTP clients.
2079
- *
2080
- * @param contentType - The MIME content type (e.g., 'application/json', 'text/plain')
2081
- * @returns The response type category to use for parsing
2082
- */
2083
- function getResponseTypeCategory(contentType) {
2084
- if (isBinaryContentType(contentType)) return "blob";
2085
- if (contentType === "application/json" || contentType.includes("+json") || contentType.includes("-json")) return "json";
2086
- return "text";
2087
- }
2088
- /**
2089
- * Get the default content type from a list of content types.
2090
- * Priority: application/json > any JSON-like type > first in list
2091
- *
2092
- * @param contentTypes - Array of content types from OpenAPI spec
2093
- * @returns The default content type to use
2094
- */
2095
- function getDefaultContentType(contentTypes) {
2096
- if (contentTypes.length === 0) return "application/json";
2097
- if (contentTypes.includes("application/json")) return "application/json";
2098
- const jsonType = contentTypes.find((ct) => ct.includes("+json") || ct.includes("-json"));
2099
- if (jsonType) return jsonType;
2100
- return contentTypes[0];
2101
- }
2102
- /**
2103
- * Determine if a form-data root field should be treated as binary or text file
2104
- * based on encoding.contentType or contentMediaType.
2105
- *
2106
- * Returns:
2107
- * - 'binary': field is a binary file (Blob in types, File in zod)
2108
- * - 'text': field is a text file that can accept string (Blob | string in types, File | string in zod)
2109
- * - undefined: no override, use standard resolution
2110
- */
2111
- function getFormDataFieldFileType(resolvedSchema, encodingContentType) {
2112
- if (resolvedSchema.type !== "string") return;
2113
- if (resolvedSchema.contentEncoding) return;
2114
- const effectiveContentType = encodingContentType ?? resolvedSchema.contentMediaType;
2115
- if (effectiveContentType) return isBinaryContentType(effectiveContentType) ? "binary" : "text";
2116
- }
2129
+ //#region src/getters/scalar.ts
2117
2130
  /**
2118
- * Resolve form-data root object with file type overrides.
2119
- * Returns undefined if no file type overrides needed (caller should use normal resolution).
2131
+ * Return the typescript equivalent of open-api data type
2132
+ *
2133
+ * @param item
2134
+ * @ref https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.1.md#data-types
2120
2135
  */
2121
- function resolveFormDataRootObject({ schemaOrRef, propName, context, encoding }) {
2122
- const { schema } = resolveRef(schemaOrRef, context);
2123
- if (!schema.properties) return;
2124
- const propertyOverrides = {};
2125
- for (const key of Object.keys(schema.properties)) {
2126
- const propSchema = schema.properties[key];
2127
- const { schema: resolvedSchema } = resolveRef(propSchema, context);
2128
- const fileType = getFormDataFieldFileType(resolvedSchema, encoding?.[key]?.contentType);
2129
- if (fileType) propertyOverrides[key] = {
2130
- ...getScalar({
2131
- item: resolvedSchema,
2132
- name: propName,
2133
- context
2134
- }),
2135
- value: fileType === "binary" ? "Blob" : "Blob | string"
2136
- };
2136
+ function getScalar({ item, name, context, formDataContext }) {
2137
+ const schemaEnum = item.enum;
2138
+ const schemaType = item.type;
2139
+ const schemaReadOnly = item.readOnly;
2140
+ const schemaExample = item.example;
2141
+ const schemaExamples = item.examples;
2142
+ const schemaConst = item.const;
2143
+ const schemaFormat = item.format;
2144
+ const schemaNullable = item.nullable;
2145
+ const nullable = isArray(schemaType) && schemaType.includes("null") || schemaNullable === true ? " | null" : "";
2146
+ const enumItems = schemaEnum?.filter((enumItem) => enumItem !== null);
2147
+ let itemType = schemaType;
2148
+ if (!itemType && item.items) {
2149
+ item.type = "array";
2150
+ itemType = "array";
2137
2151
  }
2138
- if (Object.keys(propertyOverrides).length === 0) return;
2139
- const result = getObject({
2140
- item: schema,
2141
- name: propName,
2142
- context,
2143
- nullable: "",
2144
- propertyOverrides
2145
- });
2146
- return createTypeAliasIfNeeded({
2147
- resolvedValue: {
2148
- ...result,
2149
- originalSchema: schema
2150
- },
2151
- propName,
2152
- context
2153
- }) ?? result;
2154
- }
2155
- function getFormDataAdditionalImports({ schemaObject, context }) {
2156
- const { schema } = resolveRef(schemaObject, context);
2157
- if (schema.type !== "object") return [];
2158
- const combinedSchemas = schema.oneOf || schema.anyOf;
2159
- if (!combinedSchemas) return [];
2160
- return combinedSchemas.map((schema$1) => resolveRef(schema$1, context).imports[0]).filter(Boolean);
2161
- }
2162
- function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequestBodyOptional, isUrlEncoded, isRef, encoding }) {
2163
- const { schema, imports } = resolveRef(schemaObject, context);
2164
- const propName = camel(!isRef && isReference(schemaObject) ? imports[0].name : name);
2165
- const additionalImports = [];
2166
- const variableName = isUrlEncoded ? "formUrlEncoded" : "formData";
2167
- let form = isUrlEncoded ? `const ${variableName} = new URLSearchParams();\n` : `const ${variableName} = new FormData();\n`;
2168
- const combinedSchemas = schema.oneOf || schema.anyOf || schema.allOf;
2169
- if (schema.type === "object" || schema.type === void 0 && combinedSchemas) {
2170
- if (combinedSchemas) {
2171
- const shouldCast = !!schema.oneOf || !!schema.anyOf;
2172
- const combinedSchemasFormData = combinedSchemas.map((schema$1) => {
2173
- const { schema: combinedSchema, imports: imports$1 } = resolveRef(schema$1, context);
2174
- let newPropName = propName;
2175
- let newPropDefinition = "";
2176
- if (shouldCast && imports$1[0]) {
2177
- additionalImports.push(imports$1[0]);
2178
- newPropName = `${propName}${pascal(imports$1[0].name)}`;
2179
- newPropDefinition = `const ${newPropName} = (${propName} as ${imports$1[0].name}${isRequestBodyOptional ? " | undefined" : ""});\n`;
2180
- }
2181
- return newPropDefinition + resolveSchemaPropertiesToFormData({
2182
- schema: combinedSchema,
2183
- variableName,
2184
- propName: newPropName,
2185
- context,
2186
- isRequestBodyOptional,
2187
- encoding
2188
- });
2189
- }).filter(Boolean).join("\n");
2190
- form += combinedSchemasFormData;
2152
+ if (isArray(schemaType) && schemaType.includes("null")) {
2153
+ const typesWithoutNull = schemaType.filter((x) => x !== "null");
2154
+ itemType = typesWithoutNull.length === 1 ? typesWithoutNull[0] : typesWithoutNull;
2155
+ }
2156
+ switch (itemType) {
2157
+ case "number":
2158
+ case "integer": {
2159
+ let value = context.output.override.useBigInt && (schemaFormat === "int64" || schemaFormat === "uint64") ? "bigint" : "number";
2160
+ let isEnum = false;
2161
+ if (enumItems) {
2162
+ value = enumItems.map((enumItem) => `${enumItem}`).join(" | ");
2163
+ isEnum = true;
2164
+ }
2165
+ value += nullable;
2166
+ if (schemaConst !== void 0) value = schemaConst;
2167
+ return {
2168
+ value,
2169
+ isEnum,
2170
+ type: "number",
2171
+ schemas: [],
2172
+ imports: [],
2173
+ isRef: false,
2174
+ hasReadonlyProps: schemaReadOnly ?? false,
2175
+ dependencies: [],
2176
+ example: schemaExample,
2177
+ examples: resolveExampleRefs(schemaExamples, context)
2178
+ };
2191
2179
  }
2192
- if (schema.properties) {
2193
- const formDataValues = resolveSchemaPropertiesToFormData({
2194
- schema,
2195
- variableName,
2196
- propName,
2180
+ case "boolean": {
2181
+ let value = "boolean" + nullable;
2182
+ if (schemaConst !== void 0) value = schemaConst;
2183
+ return {
2184
+ value,
2185
+ type: "boolean",
2186
+ isEnum: false,
2187
+ schemas: [],
2188
+ imports: [],
2189
+ isRef: false,
2190
+ hasReadonlyProps: schemaReadOnly ?? false,
2191
+ dependencies: [],
2192
+ example: schemaExample,
2193
+ examples: resolveExampleRefs(schemaExamples, context)
2194
+ };
2195
+ }
2196
+ case "array": {
2197
+ const { value, ...rest } = getArray({
2198
+ schema: item,
2199
+ name,
2197
2200
  context,
2198
- isRequestBodyOptional,
2199
- encoding
2201
+ formDataContext
2200
2202
  });
2201
- form += formDataValues;
2203
+ return {
2204
+ value: value + nullable,
2205
+ ...rest,
2206
+ dependencies: rest.dependencies
2207
+ };
2208
+ }
2209
+ case "string": {
2210
+ let value = "string";
2211
+ let isEnum = false;
2212
+ if (enumItems) {
2213
+ value = enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `);
2214
+ isEnum = true;
2215
+ }
2216
+ if (schemaFormat === "binary") value = "Blob";
2217
+ else if (formDataContext?.atPart) {
2218
+ const fileType = getFormDataFieldFileType(item, formDataContext.partContentType);
2219
+ if (fileType) value = fileType === "binary" ? "Blob" : "Blob | string";
2220
+ }
2221
+ if (context.output.override.useDates && (schemaFormat === "date" || schemaFormat === "date-time")) value = "Date";
2222
+ value += nullable;
2223
+ if (schemaConst) value = `'${schemaConst}'`;
2224
+ return {
2225
+ value,
2226
+ isEnum,
2227
+ type: "string",
2228
+ imports: [],
2229
+ schemas: [],
2230
+ isRef: false,
2231
+ hasReadonlyProps: schemaReadOnly ?? false,
2232
+ dependencies: [],
2233
+ example: schemaExample,
2234
+ examples: resolveExampleRefs(schemaExamples, context)
2235
+ };
2236
+ }
2237
+ case "null": return {
2238
+ value: "null",
2239
+ isEnum: false,
2240
+ type: "null",
2241
+ imports: [],
2242
+ schemas: [],
2243
+ isRef: false,
2244
+ hasReadonlyProps: schemaReadOnly ?? false,
2245
+ dependencies: []
2246
+ };
2247
+ default: {
2248
+ if (isArray(itemType)) return combineSchemas({
2249
+ schema: { anyOf: itemType.map((type) => Object.assign({}, item, { type })) },
2250
+ name,
2251
+ separator: "anyOf",
2252
+ context,
2253
+ nullable
2254
+ });
2255
+ if (enumItems) return {
2256
+ value: enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : String(enumItem)).filter(Boolean).join(` | `) + nullable,
2257
+ isEnum: true,
2258
+ type: "string",
2259
+ imports: [],
2260
+ schemas: [],
2261
+ isRef: false,
2262
+ hasReadonlyProps: schemaReadOnly ?? false,
2263
+ dependencies: [],
2264
+ example: schemaExample,
2265
+ examples: resolveExampleRefs(schemaExamples, context)
2266
+ };
2267
+ const hasCombiners = item.allOf ?? item.anyOf ?? item.oneOf;
2268
+ const { value, ...rest } = getObject({
2269
+ item,
2270
+ name,
2271
+ context,
2272
+ nullable,
2273
+ formDataContext: formDataContext?.atPart === false || formDataContext?.atPart && hasCombiners ? formDataContext : void 0
2274
+ });
2275
+ return {
2276
+ value,
2277
+ ...rest
2278
+ };
2202
2279
  }
2203
- return form;
2204
2280
  }
2205
- if (schema.type === "array") {
2206
- let valueStr = "value";
2207
- if (schema.items) {
2208
- const { schema: itemSchema } = resolveRef(schema.items, context);
2209
- if (itemSchema.type === "object" || itemSchema.type === "array") valueStr = "JSON.stringify(value)";
2210
- else if (itemSchema.type === "number" || itemSchema.type === "integer" || itemSchema.type === "boolean") valueStr = "value.toString()";
2281
+ }
2282
+
2283
+ //#endregion
2284
+ //#region src/getters/combine.ts
2285
+ const mergeableAllOfKeys = new Set([
2286
+ "type",
2287
+ "properties",
2288
+ "required"
2289
+ ]);
2290
+ function isMergeableAllOfObject(schema) {
2291
+ if (isNullish$1(schema.properties)) return false;
2292
+ if (schema.allOf || schema.anyOf || schema.oneOf) return false;
2293
+ if (!isNullish$1(schema.type) && schema.type !== "object") return false;
2294
+ return Object.keys(schema).every((key) => mergeableAllOfKeys.has(key));
2295
+ }
2296
+ function normalizeAllOfSchema(schema) {
2297
+ const schemaAllOf = schema.allOf;
2298
+ if (!schemaAllOf) return schema;
2299
+ let didMerge = false;
2300
+ const schemaProperties = schema.properties;
2301
+ const schemaRequired = schema.required;
2302
+ const mergedProperties = { ...schemaProperties };
2303
+ const mergedRequired = new Set(schemaRequired);
2304
+ const remainingAllOf = [];
2305
+ for (const subSchema of schemaAllOf) {
2306
+ if (isSchema(subSchema) && isMergeableAllOfObject(subSchema)) {
2307
+ didMerge = true;
2308
+ if (subSchema.properties) Object.assign(mergedProperties, subSchema.properties);
2309
+ const subRequired = subSchema.required;
2310
+ if (subRequired) for (const prop$1 of subRequired) mergedRequired.add(prop$1);
2311
+ continue;
2211
2312
  }
2212
- return `${form}${propName}.forEach(value => ${variableName}.append('data', ${valueStr}))\n`;
2313
+ remainingAllOf.push(subSchema);
2213
2314
  }
2214
- if (schema.type === "number" || schema.type === "integer" || schema.type === "boolean") return `${form}${variableName}.append('data', ${propName}.toString())\n`;
2215
- return `${form}${variableName}.append('data', ${propName})\n`;
2315
+ if (!didMerge || remainingAllOf.length === 0) return schema;
2316
+ return {
2317
+ ...schema,
2318
+ ...Object.keys(mergedProperties).length > 0 && { properties: mergedProperties },
2319
+ ...mergedRequired.size > 0 && { required: [...mergedRequired] },
2320
+ ...remainingAllOf.length > 0 && { allOf: remainingAllOf }
2321
+ };
2216
2322
  }
2217
- function resolveSchemaPropertiesToFormData({ schema, variableName, propName, context, isRequestBodyOptional, keyPrefix = "", depth = 0, encoding }) {
2218
- return Object.entries(schema.properties ?? {}).reduce((acc, [key, value]) => {
2219
- const { schema: property } = resolveRef(value, context);
2220
- if (property.readOnly) return acc;
2221
- let formDataValue = "";
2222
- const encodingContentType = (depth === 0 ? encoding?.[key] : void 0)?.contentType;
2223
- const formattedKeyPrefix = isRequestBodyOptional ? keyword.isIdentifierNameES5(key) ? "?" : "?." : "";
2224
- const formattedKey = keyword.isIdentifierNameES5(key) ? `.${key}` : `['${key}']`;
2225
- const valueKey = `${propName}${formattedKeyPrefix}${formattedKey}`;
2226
- const nonOptionalValueKey = `${propName}${formattedKey}`;
2227
- const fileType = getFormDataFieldFileType(property, encodingContentType);
2228
- const effectiveContentType = encodingContentType ?? property.contentMediaType;
2229
- if (fileType === "binary" || property.format === "binary") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
2230
- else if (fileType === "text") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey} instanceof Blob ? ${nonOptionalValueKey} : new Blob([${nonOptionalValueKey}], { type: '${effectiveContentType}' }));\n`;
2231
- else if (property.type === "object") formDataValue = context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE ? resolveSchemaPropertiesToFormData({
2232
- schema: property,
2233
- variableName,
2234
- propName: nonOptionalValueKey,
2235
- context,
2236
- isRequestBodyOptional,
2237
- keyPrefix: `${keyPrefix}${key}.`,
2238
- depth: depth + 1,
2239
- encoding
2240
- }) : encodingContentType ? `${variableName}.append(\`${keyPrefix}${key}\`, new Blob([JSON.stringify(${nonOptionalValueKey})], { type: '${encodingContentType}' }));\n` : `${variableName}.append(\`${keyPrefix}${key}\`, JSON.stringify(${nonOptionalValueKey}));\n`;
2241
- else if (property.type === "array") {
2242
- let valueStr = "value";
2243
- let hasNonPrimitiveChild = false;
2244
- if (property.items) {
2245
- const { schema: itemSchema } = resolveRef(property.items, context);
2246
- if (itemSchema.type === "object" || itemSchema.type === "array") if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
2247
- hasNonPrimitiveChild = true;
2248
- const resolvedValue = resolveSchemaPropertiesToFormData({
2249
- schema: itemSchema,
2250
- variableName,
2251
- propName: "value",
2252
- context,
2253
- isRequestBodyOptional,
2254
- keyPrefix: `${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}].`,
2255
- depth: depth + 1
2256
- });
2257
- formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => {
2258
- ${resolvedValue}});\n`;
2259
- } else valueStr = "JSON.stringify(value)";
2260
- else if (itemSchema.type === "number" || itemSchema.type?.includes("number") || itemSchema.type === "integer" || itemSchema.type?.includes("integer") || itemSchema.type === "boolean" || itemSchema.type?.includes("boolean")) valueStr = "value.toString()";
2323
+ function combineValues({ resolvedData, resolvedValue, separator: separator$1, context, parentSchema }) {
2324
+ if (resolvedData.isEnum.every(Boolean)) return `${resolvedData.values.join(` | `)}${resolvedValue ? ` | ${resolvedValue.value}` : ""}`;
2325
+ if (separator$1 === "allOf") {
2326
+ let resolvedDataValue = resolvedData.values.map((v) => v.includes(" | ") ? `(${v})` : v).join(` & `);
2327
+ if (resolvedData.originalSchema.length > 0 && resolvedValue) {
2328
+ const discriminatedPropertySchemas = resolvedData.originalSchema.filter((s) => {
2329
+ const disc = s?.discriminator;
2330
+ return disc && resolvedValue.value.includes(` ${disc.propertyName}:`);
2331
+ });
2332
+ if (discriminatedPropertySchemas.length > 0) resolvedDataValue = `Omit<${resolvedDataValue}, '${discriminatedPropertySchemas.map((s) => s.discriminator?.propertyName).join("' | '")}'>`;
2333
+ }
2334
+ const resolvedValueStr = resolvedValue?.value.includes(" | ") ? `(${resolvedValue.value})` : resolvedValue?.value;
2335
+ const joined = `${resolvedDataValue}${resolvedValue ? ` & ${resolvedValueStr}` : ""}`;
2336
+ const overrideRequiredProperties = resolvedData.requiredProperties.filter((prop$1) => !resolvedData.originalSchema.some((schema) => {
2337
+ const props = schema?.properties;
2338
+ const req = schema?.required;
2339
+ return props?.[prop$1] && req?.includes(prop$1);
2340
+ }) && !(() => {
2341
+ const parentProps = parentSchema?.properties;
2342
+ const parentReq = parentSchema?.required;
2343
+ return !!(parentProps?.[prop$1] && parentReq?.includes(prop$1));
2344
+ })());
2345
+ if (overrideRequiredProperties.length > 0) return `${joined} & Required<Pick<${joined}, '${overrideRequiredProperties.join("' | '")}'>>`;
2346
+ return joined;
2347
+ }
2348
+ let values = resolvedData.values;
2349
+ if (resolvedData.allProperties.length && context.output.unionAddMissingProperties) {
2350
+ values = [];
2351
+ for (let i = 0; i < resolvedData.values.length; i += 1) {
2352
+ const subSchema = resolvedData.originalSchema[i];
2353
+ if (subSchema?.type !== "object" || !subSchema.properties) {
2354
+ values.push(resolvedData.values[i]);
2355
+ continue;
2261
2356
  }
2262
- if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
2263
- if (!hasNonPrimitiveChild) formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => ${variableName}.append(\`${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}]\`, ${valueStr}));\n`;
2264
- } else formDataValue = `${valueKey}.forEach(value => ${variableName}.append(\`${keyPrefix}${key}${context.output.override.formData.arrayHandling === FormDataArrayHandling.SERIALIZE_WITH_BRACKETS ? "[]" : ""}\`, ${valueStr}));\n`;
2265
- } else if (property.type === "number" || property.type?.includes("number") || property.type === "integer" || property.type?.includes("integer") || property.type === "boolean" || property.type?.includes("boolean")) formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey}.toString())\n`;
2266
- else formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
2267
- let existSubSchemaNullable = false;
2268
- if (property.allOf || property.anyOf || property.oneOf) {
2269
- const subSchemas = (property.allOf || property.anyOf || property.oneOf)?.map((c) => resolveObject({
2270
- schema: c,
2271
- combined: true,
2272
- context
2273
- }));
2274
- if (subSchemas?.some((subSchema) => {
2275
- return [
2276
- "number",
2277
- "integer",
2278
- "boolean"
2279
- ].includes(subSchema.type);
2280
- })) formDataValue = `${variableName}.append(\`${key}\`, ${nonOptionalValueKey}.toString())\n`;
2281
- if (subSchemas?.some((subSchema) => {
2282
- return subSchema.type === "null";
2283
- })) existSubSchemaNullable = true;
2357
+ const subSchemaProps = subSchema.properties;
2358
+ const missingProperties = unique(resolvedData.allProperties.filter((p) => !Object.keys(subSchemaProps).includes(p)));
2359
+ values.push(`${resolvedData.values[i]}${missingProperties.length > 0 ? ` & {${missingProperties.map((p) => `${p}?: never`).join("; ")}}` : ""}`);
2284
2360
  }
2285
- const isRequired = schema.required?.includes(key) && !isRequestBodyOptional;
2286
- if (property.nullable || property.type?.includes("null") || existSubSchemaNullable) {
2287
- if (isRequired) return acc + `if(${valueKey} !== null) {\n ${formDataValue} }\n`;
2288
- return acc + `if(${valueKey} !== undefined && ${nonOptionalValueKey} !== null) {\n ${formDataValue} }\n`;
2361
+ }
2362
+ if (resolvedValue) return `(${values.join(` & ${resolvedValue.value}) | (`)} & ${resolvedValue.value})`;
2363
+ return values.join(" | ");
2364
+ }
2365
+ function combineSchemas({ name, schema, separator: separator$1, context, nullable, formDataContext }) {
2366
+ const normalizedSchema = separator$1 === "allOf" && !context.output.override.aliasCombinedTypes && !schema.oneOf && !schema.anyOf ? normalizeAllOfSchema(schema) : schema;
2367
+ const items = normalizedSchema[separator$1] ?? [];
2368
+ const resolvedData = {
2369
+ values: [],
2370
+ imports: [],
2371
+ schemas: [],
2372
+ isEnum: [],
2373
+ isRef: [],
2374
+ types: [],
2375
+ dependencies: [],
2376
+ originalSchema: [],
2377
+ allProperties: [],
2378
+ hasReadonlyProps: false,
2379
+ example: schema.example,
2380
+ examples: resolveExampleRefs(schema.examples, context),
2381
+ requiredProperties: separator$1 === "allOf" ? schema.required ?? [] : []
2382
+ };
2383
+ for (const subSchema of items) {
2384
+ let propName;
2385
+ if (context.output.override.aliasCombinedTypes) {
2386
+ propName = name ? name + pascal(separator$1) : void 0;
2387
+ if (propName && resolvedData.schemas.length > 0) propName = propName + pascal(getNumberWord(resolvedData.schemas.length + 1));
2289
2388
  }
2290
- if (isRequired) return acc + formDataValue;
2291
- return acc + `if(${valueKey} !== undefined) {\n ${formDataValue} }\n`;
2292
- }, "");
2293
- }
2294
-
2295
- //#endregion
2296
- //#region src/getters/body.ts
2297
- function getBody({ requestBody, operationName, context, contentType }) {
2298
- const allBodyTypes = getResReqTypes([[context.output.override.components.requestBodies.suffix, requestBody]], operationName, context);
2299
- const filteredBodyTypes = contentType ? allBodyTypes.filter((type) => {
2300
- let include = true;
2301
- let exclude = false;
2302
- if (contentType.include) include = contentType.include.includes(type.contentType);
2303
- if (contentType.exclude) exclude = contentType.exclude.includes(type.contentType);
2304
- return include && !exclude;
2305
- }) : allBodyTypes;
2306
- const imports = filteredBodyTypes.flatMap(({ imports: imports$1 }) => imports$1);
2307
- const schemas = filteredBodyTypes.flatMap(({ schemas: schemas$1 }) => schemas$1);
2308
- const definition = filteredBodyTypes.map(({ value }) => value).join(" | ");
2309
- const nonReadonlyDefinition = filteredBodyTypes.some((x) => x.hasReadonlyProps) && definition ? `NonReadonly<${definition}>` : definition;
2310
- let implementation = generalJSTypesWithArray.includes(definition.toLowerCase()) || filteredBodyTypes.length > 1 ? camel(operationName) + context.output.override.components.requestBodies.suffix : camel(definition);
2311
- let isOptional = false;
2312
- if (implementation) {
2313
- implementation = sanitize(implementation, {
2314
- underscore: "_",
2315
- whitespace: "_",
2316
- dash: true,
2317
- es5keyword: true,
2318
- es5IdentifierName: true
2389
+ if (separator$1 === "allOf" && isSchema(subSchema) && subSchema.required) resolvedData.requiredProperties.push(...subSchema.required);
2390
+ const resolvedValue$1 = resolveObject({
2391
+ schema: subSchema,
2392
+ propName,
2393
+ combined: true,
2394
+ context,
2395
+ formDataContext
2396
+ });
2397
+ const aliasedImports = getAliasedImports({
2398
+ context,
2399
+ name,
2400
+ resolvedValue: resolvedValue$1
2401
+ });
2402
+ const value = getImportAliasForRefOrValue({
2403
+ context,
2404
+ resolvedValue: resolvedValue$1,
2405
+ imports: aliasedImports
2406
+ });
2407
+ resolvedData.values.push(value);
2408
+ resolvedData.imports.push(...aliasedImports);
2409
+ resolvedData.schemas.push(...resolvedValue$1.schemas);
2410
+ resolvedData.dependencies.push(...resolvedValue$1.dependencies);
2411
+ resolvedData.isEnum.push(resolvedValue$1.isEnum);
2412
+ resolvedData.types.push(resolvedValue$1.type);
2413
+ resolvedData.isRef.push(resolvedValue$1.isRef);
2414
+ resolvedData.originalSchema.push(resolvedValue$1.originalSchema);
2415
+ if (resolvedValue$1.hasReadonlyProps) resolvedData.hasReadonlyProps = true;
2416
+ const originalProps = resolvedValue$1.originalSchema.properties;
2417
+ if (resolvedValue$1.type === "object" && originalProps) resolvedData.allProperties.push(...Object.keys(originalProps));
2418
+ }
2419
+ if (resolvedData.isEnum.every(Boolean) && name && items.length > 1 && context.output.override.enumGenerationType !== EnumGeneration.UNION) {
2420
+ const { value: combinedEnumValue, valueImports, hasNull } = getCombinedEnumValue(resolvedData.values.map((value, index) => ({
2421
+ value,
2422
+ isRef: resolvedData.isRef[index],
2423
+ schema: resolvedData.originalSchema[index]
2424
+ })));
2425
+ const newEnum = `export const ${pascal(name)} = ${combinedEnumValue}`;
2426
+ const valueImportSet = new Set(valueImports);
2427
+ const typeSuffix = `${nullable}${hasNull && !nullable.includes("null") ? " | null" : ""}`;
2428
+ return {
2429
+ value: `typeof ${pascal(name)}[keyof typeof ${pascal(name)}]${typeSuffix}`,
2430
+ imports: [{ name: pascal(name) }],
2431
+ schemas: [...resolvedData.schemas, {
2432
+ imports: resolvedData.imports.filter((toImport) => valueImportSet.has(toImport.alias ?? toImport.name)).map((toImport) => ({
2433
+ ...toImport,
2434
+ values: true
2435
+ })),
2436
+ model: newEnum,
2437
+ name
2438
+ }],
2439
+ isEnum: false,
2440
+ type: "object",
2441
+ isRef: false,
2442
+ hasReadonlyProps: resolvedData.hasReadonlyProps,
2443
+ dependencies: resolvedData.dependencies,
2444
+ example: schema.example,
2445
+ examples: resolveExampleRefs(schema.examples, context)
2446
+ };
2447
+ }
2448
+ let resolvedValue;
2449
+ if (normalizedSchema.properties) resolvedValue = getScalar({
2450
+ item: Object.fromEntries(Object.entries(normalizedSchema).filter(([key]) => key !== separator$1)),
2451
+ name,
2452
+ context,
2453
+ formDataContext
2454
+ });
2455
+ else if (separator$1 === "allOf" && (schema.oneOf || schema.anyOf)) {
2456
+ const siblingCombiner = schema.oneOf ? "oneOf" : "anyOf";
2457
+ const siblingSchemas = schema[siblingCombiner];
2458
+ resolvedValue = combineSchemas({
2459
+ schema: { [siblingCombiner]: siblingSchemas },
2460
+ name,
2461
+ separator: siblingCombiner,
2462
+ context,
2463
+ nullable: ""
2319
2464
  });
2320
- if (isReference(requestBody)) {
2321
- const { schema: bodySchema } = resolveRef(requestBody, context);
2322
- if (bodySchema.required !== void 0) isOptional = !bodySchema.required;
2323
- } else if (requestBody.required !== void 0) isOptional = !requestBody.required;
2324
2465
  }
2325
2466
  return {
2326
- originalSchema: requestBody,
2327
- definition: nonReadonlyDefinition,
2328
- implementation,
2329
- imports,
2330
- schemas,
2331
- isOptional,
2332
- ...filteredBodyTypes.length === 1 ? {
2333
- formData: filteredBodyTypes[0].formData,
2334
- formUrlEncoded: filteredBodyTypes[0].formUrlEncoded,
2335
- contentType: filteredBodyTypes[0].contentType
2336
- } : {
2337
- formData: "",
2338
- formUrlEncoded: "",
2339
- contentType: ""
2340
- }
2467
+ value: dedupeUnionType(combineValues({
2468
+ resolvedData,
2469
+ separator: separator$1,
2470
+ resolvedValue,
2471
+ context,
2472
+ parentSchema: normalizedSchema
2473
+ }) + nullable),
2474
+ imports: resolvedValue ? [...resolvedData.imports, ...resolvedValue.imports] : resolvedData.imports,
2475
+ schemas: resolvedValue ? [...resolvedData.schemas, ...resolvedValue.schemas] : resolvedData.schemas,
2476
+ dependencies: resolvedValue ? [...resolvedData.dependencies, ...resolvedValue.dependencies] : resolvedData.dependencies,
2477
+ isEnum: false,
2478
+ type: "object",
2479
+ isRef: false,
2480
+ hasReadonlyProps: resolvedData.hasReadonlyProps || (resolvedValue?.hasReadonlyProps ?? false),
2481
+ example: schema.example,
2482
+ examples: resolveExampleRefs(schema.examples, context)
2341
2483
  };
2342
2484
  }
2343
2485
 
@@ -2346,7 +2488,9 @@ function getBody({ requestBody, operationName, context, contentType }) {
2346
2488
  function resolveDiscriminators(schemas, context) {
2347
2489
  const transformedSchemas = schemas;
2348
2490
  for (const schema of Object.values(transformedSchemas)) {
2349
- if (typeof schema === "boolean") continue;
2491
+ if (isBoolean$1(schema)) continue;
2492
+ const discriminator = schema.discriminator;
2493
+ if (!schema.oneOf && isArray(discriminator?.oneOf)) schema.oneOf = discriminator.oneOf;
2350
2494
  if (schema.discriminator?.mapping) {
2351
2495
  const { mapping, propertyName } = schema.discriminator;
2352
2496
  for (const [mappingKey, mappingValue] of Object.entries(mapping)) {
@@ -2357,17 +2501,25 @@ function resolveDiscriminators(schemas, context) {
2357
2501
  } catch {
2358
2502
  subTypeSchema = transformedSchemas[mappingValue];
2359
2503
  }
2360
- if (typeof subTypeSchema === "boolean" || propertyName === void 0) continue;
2504
+ if (isBoolean$1(subTypeSchema) || propertyName === void 0) continue;
2361
2505
  const property = subTypeSchema.properties?.[propertyName];
2362
- if (typeof property === "boolean" || property === void 0) continue;
2506
+ if (isBoolean$1(property)) continue;
2507
+ const schemaProperty = property && !isReference(property) ? property : void 0;
2508
+ const enumProperty = schemaProperty ? getPropertySafe(schemaProperty, "enum") : {
2509
+ hasProperty: false,
2510
+ value: void 0
2511
+ };
2512
+ const mergedEnumValues = [...((enumProperty.hasProperty && Array.isArray(enumProperty.value) ? enumProperty.value : void 0) ?? []).filter((value) => value !== mappingKey), mappingKey];
2513
+ const mergedProperty = {
2514
+ ...schemaProperty,
2515
+ type: "string",
2516
+ enum: mergedEnumValues
2517
+ };
2363
2518
  subTypeSchema.properties = {
2364
2519
  ...subTypeSchema.properties,
2365
- [propertyName]: {
2366
- type: "string",
2367
- enum: [...property?.enum?.filter((value) => value !== mappingKey) ?? [], mappingKey]
2368
- }
2520
+ [propertyName]: mergedProperty
2369
2521
  };
2370
- subTypeSchema.required = [...subTypeSchema.required ?? [], propertyName];
2522
+ subTypeSchema.required = [...new Set([...subTypeSchema.required ?? [], propertyName])];
2371
2523
  }
2372
2524
  }
2373
2525
  }
@@ -2389,23 +2541,22 @@ function getOperationId(operation, route, verb) {
2389
2541
  //#endregion
2390
2542
  //#region src/getters/parameters.ts
2391
2543
  function getParameters({ parameters, context }) {
2392
- return parameters.reduce((acc, p) => {
2393
- if (isReference(p)) {
2394
- const { schema: parameter, imports } = resolveRef(p, context);
2395
- if (parameter.in === "path" || parameter.in === "query" || parameter.in === "header") acc[parameter.in].push({
2396
- parameter,
2397
- imports
2398
- });
2399
- } else if (p.in === "query" || p.in === "path" || p.in === "header") acc[p.in].push({
2400
- parameter: p,
2401
- imports: []
2402
- });
2403
- return acc;
2404
- }, {
2544
+ const result = {
2405
2545
  path: [],
2406
2546
  query: [],
2407
2547
  header: []
2548
+ };
2549
+ for (const p of parameters) if (isReference(p)) {
2550
+ const { schema: parameter, imports } = resolveRef(p, context);
2551
+ if (parameter.in === "path" || parameter.in === "query" || parameter.in === "header") result[parameter.in].push({
2552
+ parameter,
2553
+ imports
2554
+ });
2555
+ } else if (p.in === "query" || p.in === "path" || p.in === "header") result[p.in].push({
2556
+ parameter: p,
2557
+ imports: []
2408
2558
  });
2559
+ return result;
2409
2560
  }
2410
2561
 
2411
2562
  //#endregion
@@ -2449,16 +2600,18 @@ function getParams({ route, pathParams = [], operationId, context, output }) {
2449
2600
  schema,
2450
2601
  context
2451
2602
  });
2603
+ const originalSchema = resolvedValue.originalSchema;
2604
+ const schemaDefault = originalSchema.default;
2452
2605
  let paramType = resolvedValue.value;
2453
2606
  if (output.allParamsOptional) paramType = `${paramType} | undefined | null`;
2454
2607
  return {
2455
2608
  name,
2456
- definition: `${name}${!required || resolvedValue.originalSchema.default ? "?" : ""}: ${paramType}`,
2457
- implementation: `${name}${!required && !resolvedValue.originalSchema.default ? "?" : ""}${resolvedValue.originalSchema.default ? `: ${paramType} = ${stringify(resolvedValue.originalSchema.default)}` : `: ${paramType}`}`,
2458
- default: resolvedValue.originalSchema.default,
2609
+ definition: `${name}${!required || schemaDefault ? "?" : ""}: ${paramType}`,
2610
+ implementation: `${name}${!required && !schemaDefault ? "?" : ""}${schemaDefault ? `: ${paramType} = ${stringify(schemaDefault)}` : `: ${paramType}`}`,
2611
+ default: schemaDefault,
2459
2612
  required,
2460
2613
  imports: resolvedValue.imports,
2461
- originalSchema: resolvedValue.originalSchema
2614
+ originalSchema
2462
2615
  };
2463
2616
  });
2464
2617
  }
@@ -2479,7 +2632,7 @@ function getProps({ body, queryParams, params, operationName, headers, context }
2479
2632
  definition: getQueryParamDefinition(queryParams, context),
2480
2633
  implementation: getQueryParamDefinition(queryParams, context),
2481
2634
  default: false,
2482
- required: isUndefined(queryParams?.isOptional) ? !context.output.allParamsOptional || context.output.optionsParamRequired : !queryParams?.isOptional && !context.output.allParamsOptional || context.output.optionsParamRequired,
2635
+ required: isNullish(queryParams?.isOptional) ? !context.output.allParamsOptional || context.output.optionsParamRequired : !queryParams.isOptional && !context.output.allParamsOptional || context.output.optionsParamRequired,
2483
2636
  type: GetterPropType.QUERY_PARAM
2484
2637
  };
2485
2638
  const headersProp = {
@@ -2487,7 +2640,7 @@ function getProps({ body, queryParams, params, operationName, headers, context }
2487
2640
  definition: `headers${headers?.isOptional && !context.output.optionsParamRequired ? "?" : ""}: ${headers?.schema.name}`,
2488
2641
  implementation: `headers${headers?.isOptional && !context.output.optionsParamRequired ? "?" : ""}: ${headers?.schema.name}`,
2489
2642
  default: false,
2490
- required: isUndefined(headers?.isOptional) ? false : !headers?.isOptional || context.output.optionsParamRequired,
2643
+ required: isNullish(headers?.isOptional) ? false : !headers.isOptional || context.output.optionsParamRequired,
2491
2644
  type: GetterPropType.HEADER
2492
2645
  };
2493
2646
  let paramGetterProps;
@@ -2541,16 +2694,18 @@ function getQueryParamsTypes(queryParams, operationName, context) {
2541
2694
  es5keyword: true,
2542
2695
  es5IdentifierName: true
2543
2696
  });
2544
- const schema = schemaParam || content["application/json"].schema;
2697
+ const schema = schemaParam ?? content?.["application/json"]?.schema;
2698
+ if (!schema) throw new Error(`Query parameter "${name}" has no schema or content definition`);
2545
2699
  const resolvedValue = resolveValue({
2546
2700
  schema,
2547
2701
  context,
2548
2702
  name: queryName
2549
2703
  });
2550
2704
  const key = getKey(name);
2705
+ const schemaForDoc = schema;
2551
2706
  const doc = jsDoc({
2552
2707
  description: parameter.description,
2553
- ...schema
2708
+ ...schemaForDoc
2554
2709
  }, void 0, context);
2555
2710
  if (parameterImports.length > 0) return {
2556
2711
  definition: `${doc}${key}${!required || schema.default ? "?" : ""}: ${parameterImports[0].name};`,
@@ -2560,7 +2715,7 @@ function getQueryParamsTypes(queryParams, operationName, context) {
2560
2715
  };
2561
2716
  if (resolvedValue.isEnum && !resolvedValue.isRef) {
2562
2717
  const enumName = queryName;
2563
- const enumValue = getEnum(resolvedValue.value, enumName, getEnumNames(resolvedValue.originalSchema), context.output.override.enumGenerationType, getEnumDescriptions(resolvedValue.originalSchema), context.output.override.namingConvention?.enum);
2718
+ const enumValue = getEnum(resolvedValue.value, enumName, getEnumNames(resolvedValue.originalSchema), context.output.override.enumGenerationType, getEnumDescriptions(resolvedValue.originalSchema), context.output.override.namingConvention.enum);
2564
2719
  return {
2565
2720
  definition: `${doc}${key}${!required || schema.default ? "?" : ""}: ${enumName};`,
2566
2721
  imports: [{ name: enumName }],
@@ -2602,39 +2757,16 @@ function getQueryParams({ queryParams, operationName, context, suffix = "params"
2602
2757
  //#endregion
2603
2758
  //#region src/getters/response.ts
2604
2759
  function getResponse({ responses, operationName, context, contentType }) {
2605
- if (!responses) return {
2606
- imports: [],
2607
- definition: {
2608
- success: "",
2609
- errors: ""
2610
- },
2611
- isBlob: false,
2612
- types: {
2613
- success: [],
2614
- errors: []
2615
- },
2616
- schemas: [],
2617
- contentTypes: []
2618
- };
2619
- const types = getResReqTypes(Object.entries(responses), operationName, context, "void", (type) => `${type.key}-${type.value}`);
2620
- const filteredTypes = contentType ? types.filter((type) => {
2621
- let include = true;
2622
- let exclude = false;
2623
- if (contentType.include) include = contentType.include.includes(type.contentType);
2624
- if (contentType.exclude) exclude = contentType.exclude.includes(type.contentType);
2625
- return include && !exclude;
2626
- }) : types;
2760
+ const filteredTypes = filterByContentType(getResReqTypes(Object.entries(responses), operationName, context, "void", (type) => `${type.key}-${type.value}`), contentType);
2627
2761
  const imports = filteredTypes.flatMap(({ imports: imports$1 }) => imports$1);
2628
2762
  const schemas = filteredTypes.flatMap(({ schemas: schemas$1 }) => schemas$1);
2629
2763
  const contentTypes = [...new Set(filteredTypes.map(({ contentType: contentType$1 }) => contentType$1))];
2630
- const groupedByStatus = filteredTypes.reduce((acc, type) => {
2631
- if (type.key.startsWith("2")) acc.success.push(type);
2632
- else acc.errors.push(type);
2633
- return acc;
2634
- }, {
2764
+ const groupedByStatus = {
2635
2765
  success: [],
2636
2766
  errors: []
2637
- });
2767
+ };
2768
+ for (const type of filteredTypes) if (type.key.startsWith("2")) groupedByStatus.success.push(type);
2769
+ else groupedByStatus.errors.push(type);
2638
2770
  const success = dedupeUnionType(groupedByStatus.success.map(({ value, formData }) => formData ? "Blob" : value).join(" | "));
2639
2771
  const errors = dedupeUnionType(groupedByStatus.errors.map(({ value }) => value).join(" | "));
2640
2772
  const defaultType = filteredTypes.find(({ key }) => key === "default")?.value;
@@ -2670,16 +2802,18 @@ const getRoutePath = (path$2) => {
2670
2802
  return hasParam(path$2) ? `${prev}\${${param}}${next}` : `${prev}${param}${next}`;
2671
2803
  };
2672
2804
  function getRoute(route) {
2673
- return route.split("/").reduce((acc, path$2, i) => {
2674
- if (!path$2 && !i) return acc;
2675
- if (!path$2.includes("{")) return `${acc}/${path$2}`;
2676
- return `${acc}/${getRoutePath(path$2)}`;
2677
- }, "");
2805
+ const splittedRoute = route.split("/");
2806
+ let result = "";
2807
+ for (const [i, path$2] of splittedRoute.entries()) {
2808
+ if (!path$2 && !i) continue;
2809
+ result += path$2.includes("{") ? `/${getRoutePath(path$2)}` : `/${path$2}`;
2810
+ }
2811
+ return result;
2678
2812
  }
2679
2813
  function getFullRoute(route, servers, baseUrl) {
2680
2814
  const getBaseUrl = () => {
2681
2815
  if (!baseUrl) return "";
2682
- if (typeof baseUrl === "string") return baseUrl;
2816
+ if (isString(baseUrl)) return baseUrl;
2683
2817
  if (baseUrl.getBaseUrlFromSpecification) {
2684
2818
  if (!servers) throw new Error("Orval is configured to use baseUrl from the specifications 'servers' field, but there exist no servers in the specification.");
2685
2819
  const server = servers.at(Math.min(baseUrl.index ?? 0, servers.length - 1));
@@ -2754,25 +2888,26 @@ function generateImports({ imports, namingConvention = NamingConvention.CAMEL_CA
2754
2888
  }).join("\n");
2755
2889
  }
2756
2890
  function generateMutatorImports({ mutators, implementation, oneMore }) {
2757
- return uniqueWith(mutators, (a, b) => a.name === b.name && a.default === b.default).reduce((acc, mutator) => {
2891
+ let imports = "";
2892
+ for (const mutator of uniqueWith(mutators, (a, b) => a.name === b.name && a.default === b.default)) {
2758
2893
  const path$2 = `${oneMore ? "../" : ""}${mutator.path}`;
2759
2894
  const importDefault = mutator.default ? mutator.name : `{ ${mutator.name} }`;
2760
- acc += `import ${importDefault} from '${path$2}';`;
2761
- acc += "\n";
2895
+ imports += `import ${importDefault} from '${path$2}';`;
2896
+ imports += "\n";
2762
2897
  if (implementation && (mutator.hasErrorType || mutator.bodyTypeName)) {
2763
2898
  let errorImportName = "";
2764
2899
  const targetErrorImportName = mutator.default ? `ErrorType as ${mutator.errorTypeName}` : mutator.errorTypeName;
2765
- if (mutator.hasErrorType && implementation.includes(mutator.errorTypeName) && !acc.includes(`{ ${targetErrorImportName} `)) errorImportName = targetErrorImportName;
2900
+ if (mutator.hasErrorType && implementation.includes(mutator.errorTypeName) && !imports.includes(`{ ${targetErrorImportName} `)) errorImportName = targetErrorImportName;
2766
2901
  let bodyImportName = "";
2767
2902
  const targetBodyImportName = mutator.default ? `BodyType as ${mutator.bodyTypeName}` : mutator.bodyTypeName;
2768
- if (mutator.bodyTypeName && implementation.includes(mutator.bodyTypeName) && !acc.includes(` ${targetBodyImportName} }`)) bodyImportName = targetBodyImportName;
2903
+ if (mutator.bodyTypeName && implementation.includes(mutator.bodyTypeName) && !imports.includes(` ${targetBodyImportName} }`)) bodyImportName = targetBodyImportName ?? "";
2769
2904
  if (bodyImportName || errorImportName) {
2770
- acc += `import type { ${errorImportName}${errorImportName && bodyImportName ? " , " : ""}${bodyImportName} } from '${path$2}';`;
2771
- acc += "\n";
2905
+ imports += `import type { ${errorImportName}${errorImportName && bodyImportName ? " , " : ""}${bodyImportName} } from '${path$2}';`;
2906
+ imports += "\n";
2772
2907
  }
2773
2908
  }
2774
- return acc;
2775
- }, "");
2909
+ }
2910
+ return imports;
2776
2911
  }
2777
2912
  function generateDependency({ deps, isAllowSyntheticDefaultImports, dependency, projectName, key, onlyTypes }) {
2778
2913
  const defaultDep = deps.find((e) => e.default && (isAllowSyntheticDefaultImports || !e.syntheticDefaultImport));
@@ -2787,31 +2922,25 @@ function generateDependency({ deps, isAllowSyntheticDefaultImports, dependency,
2787
2922
  importString += `import ${onlyTypes ? "type " : ""}${defaultDep ? `${defaultDep.name}${depsString ? "," : ""}` : ""}${depsString ? `{\n ${depsString}\n}` : ""} from '${dependency}${key !== "default" && projectName ? `/${projectName}` : ""}';`;
2788
2923
  return importString;
2789
2924
  }
2790
- function addDependency({ implementation, exports, dependency, projectName, hasSchemaDir, isAllowSyntheticDefaultImports }) {
2925
+ function addDependency({ implementation, exports, dependency, projectName, isAllowSyntheticDefaultImports }) {
2791
2926
  const toAdds = exports.filter((e) => {
2792
2927
  const searchWords = [e.alias, e.name].filter((p) => p?.length).join("|");
2793
2928
  const pattern = new RegExp(String.raw`\b(${searchWords})\b`, "g");
2794
2929
  return implementation.match(pattern);
2795
2930
  });
2796
2931
  if (toAdds.length === 0) return;
2797
- const groupedBySpecKey = toAdds.reduce((acc, dep) => {
2932
+ const groupedBySpecKey = { default: {
2933
+ types: [],
2934
+ values: []
2935
+ } };
2936
+ for (const dep of toAdds) {
2798
2937
  const key = "default";
2799
- if (dep.values && (isAllowSyntheticDefaultImports || !dep.syntheticDefaultImport)) {
2800
- acc[key] = {
2801
- ...acc[key],
2802
- values: [...acc[key]?.values ?? [], dep]
2803
- };
2804
- return acc;
2805
- }
2806
- acc[key] = {
2807
- ...acc[key],
2808
- types: [...acc[key]?.types ?? [], dep]
2809
- };
2810
- return acc;
2811
- }, {});
2938
+ if (dep.values && (isAllowSyntheticDefaultImports || !dep.syntheticDefaultImport)) groupedBySpecKey[key].values.push(dep);
2939
+ else groupedBySpecKey[key].types.push(dep);
2940
+ }
2812
2941
  return Object.entries(groupedBySpecKey).map(([key, { values, types }]) => {
2813
2942
  let dep = "";
2814
- if (values) dep += generateDependency({
2943
+ if (values.length > 0) dep += generateDependency({
2815
2944
  deps: values,
2816
2945
  isAllowSyntheticDefaultImports,
2817
2946
  dependency,
@@ -2819,9 +2948,9 @@ function addDependency({ implementation, exports, dependency, projectName, hasSc
2819
2948
  key,
2820
2949
  onlyTypes: false
2821
2950
  });
2822
- if (types) {
2951
+ if (types.length > 0) {
2823
2952
  let uniqueTypes = types;
2824
- if (values) {
2953
+ if (values.length > 0) {
2825
2954
  uniqueTypes = types.filter((t) => !values.some((v) => v.name === t.name));
2826
2955
  dep += "\n";
2827
2956
  }
@@ -2848,7 +2977,7 @@ function generateDependencyImports(implementation, imports, projectName, hasSche
2848
2977
  projectName,
2849
2978
  hasSchemaDir,
2850
2979
  isAllowSyntheticDefaultImports
2851
- })).filter(Boolean).sort((a, b) => {
2980
+ })).filter((x) => Boolean(x)).toSorted((a, b) => {
2852
2981
  const aLib = getLibName(a);
2853
2982
  const bLib = getLibName(b);
2854
2983
  if (aLib === bLib) return 0;
@@ -2874,7 +3003,10 @@ function generateModelInline(acc, model) {
2874
3003
  return acc + `${model}\n`;
2875
3004
  }
2876
3005
  function generateModelsInline(obj) {
2877
- return Object.values(obj).flat().reduce((acc, { model }) => generateModelInline(acc, model), "");
3006
+ const schemas = Object.values(obj).flat();
3007
+ let result = "";
3008
+ for (const { model } of schemas) result = generateModelInline(result, model);
3009
+ return result;
2878
3010
  }
2879
3011
 
2880
3012
  //#endregion
@@ -2900,7 +3032,7 @@ async function bundleFile(root, fileName, alias, external, compilerOptions) {
2900
3032
  treeShaking: false,
2901
3033
  keepNames: false,
2902
3034
  alias,
2903
- external: external || ["*"]
3035
+ external: external ?? ["*"]
2904
3036
  })).outputFiles[0];
2905
3037
  return text;
2906
3038
  }
@@ -3045,7 +3177,7 @@ function generateAxiosOptions({ response, isExactOptionalPropertyTypes, queryPar
3045
3177
  if (headers) value += "\n headers,";
3046
3178
  if (hasSignal) value += isExactOptionalPropertyTypes ? `\n ...(${signalVar} ? { ${signalProp} } : {}),` : `\n ${signalProp},`;
3047
3179
  }
3048
- if (!isObject(requestOptions) || !requestOptions.hasOwnProperty("responseType")) {
3180
+ if (!isObject(requestOptions) || !Object.hasOwn(requestOptions, "responseType")) {
3049
3181
  if (response.isBlob) value += `\n responseType: 'blob',`;
3050
3182
  else if (response.contentTypes.at(0) === "text/plain") value += `\n responseType: 'text',`;
3051
3183
  }
@@ -3057,7 +3189,7 @@ function generateAxiosOptions({ response, isExactOptionalPropertyTypes, queryPar
3057
3189
  else value += "\n params: {...params, ...options?.params},";
3058
3190
  if (headers) value += "\n headers: {...headers, ...options?.headers},";
3059
3191
  }
3060
- if (!isAngular && queryParams && (paramsSerializer || paramsSerializerOptions?.qs)) value += paramsSerializer ? `\n paramsSerializer: ${paramsSerializer.name},` : `\n paramsSerializer: (params) => qs.stringify(params, ${JSON.stringify(paramsSerializerOptions.qs)}),`;
3192
+ if (!isAngular && queryParams && (paramsSerializer || paramsSerializerOptions?.qs)) value += paramsSerializer ? `\n paramsSerializer: ${paramsSerializer.name},` : `\n paramsSerializer: (params) => qs.stringify(params, ${JSON.stringify(paramsSerializerOptions?.qs)}),`;
3061
3193
  return value;
3062
3194
  }
3063
3195
  function generateOptions({ route, body, headers, queryParams, response, verb, requestOptions, isFormData, isFormUrlEncoded, isAngular, isExactOptionalPropertyTypes, hasSignal, hasSignalParam, isVue, paramsSerializer, paramsSerializerOptions }) {
@@ -3098,7 +3230,7 @@ function generateQueryParamsAxiosConfig(response, isVue, queryParams) {
3098
3230
  function generateMutatorConfig({ route, body, headers, queryParams, response, verb, isFormData, isFormUrlEncoded, hasSignal, hasSignalParam = false, isExactOptionalPropertyTypes, isVue }) {
3099
3231
  const bodyOptions = getIsBodyVerb(verb) ? generateBodyMutatorConfig(body, isFormData, isFormUrlEncoded) : "";
3100
3232
  const queryParamsOptions = generateQueryParamsAxiosConfig(response, isVue ?? false, queryParams);
3101
- const headerOptions = body.contentType ? `,\n headers: {'Content-Type': '${body.contentType}', ${headers ? "...headers" : ""}}` : headers ? ",\n headers" : "";
3233
+ const headerOptions = body.contentType && !["multipart/form-data"].includes(body.contentType) ? `,\n headers: {'Content-Type': '${body.contentType}', ${headers ? "...headers" : ""}}` : headers ? ",\n headers" : "";
3102
3234
  const signalVar = hasSignalParam ? "querySignal" : "signal";
3103
3235
  const signalProp = hasSignalParam ? `signal: ${signalVar}` : "signal";
3104
3236
  return `{url: \`${route}\`, method: '${verb.toUpperCase()}'${headerOptions}${bodyOptions}${queryParamsOptions}${hasSignal ? `, ${isExactOptionalPropertyTypes ? `...(${signalVar} ? { ${signalProp} }: {})` : signalProp}` : ""}\n }`;
@@ -3180,18 +3312,20 @@ function generateInterface({ name, schema, context }) {
3180
3312
  context
3181
3313
  });
3182
3314
  const isEmptyObject = scalar.value === "{}";
3183
- const shouldUseTypeAlias = context?.output.override?.useTypeOverInterfaces || scalar.useTypeAlias;
3315
+ const shouldUseTypeAlias = context.output.override.useTypeOverInterfaces ?? scalar.useTypeAlias;
3184
3316
  let model = "";
3185
3317
  model += jsDoc(schema);
3186
3318
  if (isEmptyObject) model += "// eslint-disable-next-line @typescript-eslint/no-empty-interface\n";
3187
- if (scalar.type === "object" && !shouldUseTypeAlias) if (scalar.type === "object" && schema.properties && Object.values(schema.properties).length > 0 && Object.values(schema.properties).every((item) => "const" in item)) {
3188
- const mappedScalarValue = scalar.value.replaceAll(";", ",").replaceAll("?:", ":");
3189
- model += `export const ${name}Value = ${mappedScalarValue} as const;\nexport type ${name} = typeof ${name}Value;\n`;
3190
- } else {
3191
- const blankInterfaceValue = scalar.value === "unknown" ? "{}" : scalar.value;
3192
- model += `export interface ${name} ${blankInterfaceValue}\n`;
3193
- }
3194
- else model += `export type ${name} = ${scalar.value};\n`;
3319
+ if (scalar.type === "object" && !shouldUseTypeAlias) {
3320
+ const properties = schema.properties;
3321
+ if (properties && Object.values(properties).length > 0 && Object.values(properties).every((item) => "const" in item)) {
3322
+ const mappedScalarValue = scalar.value.replaceAll(";", ",").replaceAll("?:", ":");
3323
+ model += `export const ${name}Value = ${mappedScalarValue} as const;\nexport type ${name} = typeof ${name}Value;\n`;
3324
+ } else {
3325
+ const blankInterfaceValue = scalar.value === "unknown" ? "{}" : scalar.value;
3326
+ model += `export interface ${name} ${blankInterfaceValue}\n`;
3327
+ }
3328
+ } else model += `export type ${name} = ${scalar.value};\n`;
3195
3329
  const externalModulesImportsOnly = scalar.imports.filter((importName) => importName.alias ? importName.alias !== name : importName.name !== name);
3196
3330
  return [...scalar.schemas, {
3197
3331
  name,
@@ -3241,7 +3375,7 @@ function sortSchemasByDependencies(schemas) {
3241
3375
  for (const dependencyName of schema.dependencies) if (dependencyName && schemaNames.has(dependencyName)) dependencies.add(dependencyName);
3242
3376
  }
3243
3377
  for (const imp of schema.imports) {
3244
- const dependencyName = imp.alias || imp.name;
3378
+ const dependencyName = imp.alias ?? imp.name;
3245
3379
  if (dependencyName && schemaNames.has(dependencyName)) dependencies.add(dependencyName);
3246
3380
  }
3247
3381
  dependencyMap.set(schema.name, dependencies);
@@ -3278,7 +3412,7 @@ function generateSchemaDefinitions(schemaName, schema, context, suffix) {
3278
3412
  es5keyword: true,
3279
3413
  es5IdentifierName: true
3280
3414
  });
3281
- if (typeof schema === "boolean") return [{
3415
+ if (isBoolean(schema)) return [{
3282
3416
  name: sanitizedSchemaName,
3283
3417
  model: `export type ${sanitizedSchemaName} = ${schema ? "any" : "never"};\n`,
3284
3418
  imports: [],
@@ -3297,7 +3431,7 @@ function generateSchemaDefinitions(schemaName, schema, context, suffix) {
3297
3431
  let output = "";
3298
3432
  let imports = resolvedValue.imports;
3299
3433
  output += jsDoc(schema);
3300
- if (resolvedValue.isEnum && !resolvedValue.isRef) output += getEnum(resolvedValue.value, sanitizedSchemaName, getEnumNames(resolvedValue.originalSchema), context.output.override.enumGenerationType, getEnumDescriptions(resolvedValue.originalSchema), context.output.override.namingConvention?.enum);
3434
+ if (resolvedValue.isEnum && !resolvedValue.isRef) output += getEnum(resolvedValue.value, sanitizedSchemaName, getEnumNames(resolvedValue.originalSchema), context.output.override.enumGenerationType, getEnumDescriptions(resolvedValue.originalSchema), context.output.override.namingConvention.enum);
3301
3435
  else if (sanitizedSchemaName === resolvedValue.value && resolvedValue.isRef) {
3302
3436
  const { schema: referredSchema } = resolveRef(schema, context);
3303
3437
  if (!shouldCreateInterface(referredSchema)) {
@@ -3316,7 +3450,7 @@ function generateSchemaDefinitions(schemaName, schema, context, suffix) {
3316
3450
  resolvedValue.schemas = resolvedValue.schemas.filter((schema$1) => {
3317
3451
  if (schema$1.name !== sanitizedSchemaName) return true;
3318
3452
  output += `${schema$1.model}\n`;
3319
- imports = imports.concat(schema$1.imports);
3453
+ imports = [...imports, ...schema$1.imports];
3320
3454
  resolvedValue.dependencies.push(...schema$1.dependencies ?? []);
3321
3455
  return false;
3322
3456
  });
@@ -3333,26 +3467,56 @@ function generateSchemaDefinitions(schemaName, schema, context, suffix) {
3333
3467
 
3334
3468
  //#endregion
3335
3469
  //#region src/generators/verbs-options.ts
3336
- async function generateVerbOptions({ verb, output, operation, route, pathRoute, verbParameters = [], context }) {
3337
- const { responses, requestBody, parameters: operationParameters, tags = [], deprecated, description, summary } = operation;
3470
+ /**
3471
+ * Get a content-type specific suffix for operation names (#2812)
3472
+ */
3473
+ function getContentTypeSuffix(contentType) {
3474
+ return {
3475
+ "application/json": "Json",
3476
+ "multipart/form-data": "FormData",
3477
+ "application/x-www-form-urlencoded": "UrlEncoded",
3478
+ "application/xml": "Xml",
3479
+ "text/plain": "Text"
3480
+ }[contentType] || pascal(contentType.replaceAll(/[^a-zA-Z0-9]/g, "_"));
3481
+ }
3482
+ async function generateVerbOptions({ verb, output, operation, route, pathRoute, verbParameters = [], context, contentType }) {
3483
+ const { responses, requestBody, parameters: operationParameters, tags: rawTags, deprecated: rawDeprecated, description: rawDescription, summary: rawSummary } = operation;
3484
+ const tags = rawTags ?? [];
3485
+ const deprecated = rawDeprecated;
3486
+ const description = rawDescription;
3487
+ const summary = rawSummary;
3338
3488
  const operationId = getOperationId(operation, route, verb);
3339
3489
  const overrideOperation = output.override.operations[operationId];
3340
- const overrideTag = Object.entries(output.override.tags).reduce((acc, [tag, options]) => tags.includes(tag) && options ? mergeDeep(acc, options) : acc, {});
3341
- const override = mergeDeep(mergeDeep(output.override, overrideTag), overrideOperation);
3490
+ let overrideTag = {};
3491
+ for (const [tag, options] of Object.entries(output.override.tags)) if (tags.includes(tag) && options) overrideTag = mergeDeep(overrideTag, options);
3492
+ const override = mergeDeep(mergeDeep(output.override, overrideTag), overrideOperation ?? {});
3493
+ const originalContentTypeFilter = override.contentType;
3494
+ const requestBodyContentTypeFilter = contentType ? { include: [contentType] } : override.contentType;
3342
3495
  const overrideOperationName = overrideOperation?.operationName ?? output.override.operationName;
3343
- const operationName = overrideOperationName ? overrideOperationName(operation, route, verb) : sanitize(camel(operationId), { es5keyword: true });
3496
+ let operationName = overrideOperationName ? overrideOperationName(operation, route, verb) : sanitize(camel(operationId), { es5keyword: true });
3497
+ if (contentType) operationName = operationName + "With" + getContentTypeSuffix(contentType);
3344
3498
  const response = getResponse({
3345
- responses,
3499
+ responses: responses ?? {},
3346
3500
  operationName,
3347
3501
  context,
3348
- contentType: override.contentType
3502
+ contentType: originalContentTypeFilter
3349
3503
  });
3350
- const body = getBody({
3504
+ const body = requestBody ? getBody({
3351
3505
  requestBody,
3352
3506
  operationName,
3353
3507
  context,
3354
- contentType: override.contentType
3355
- });
3508
+ contentType: requestBodyContentTypeFilter
3509
+ }) : {
3510
+ originalSchema: {},
3511
+ definition: "",
3512
+ implementation: "",
3513
+ imports: [],
3514
+ schemas: [],
3515
+ formData: "",
3516
+ formUrlEncoded: "",
3517
+ contentType: "",
3518
+ isOptional: false
3519
+ };
3356
3520
  const parameters = getParameters({
3357
3521
  parameters: [...verbParameters, ...operationParameters ?? []],
3358
3522
  context
@@ -3428,7 +3592,7 @@ async function generateVerbOptions({ verb, output, operation, route, pathRoute,
3428
3592
  tags,
3429
3593
  route,
3430
3594
  pathRoute,
3431
- summary: operation.summary,
3595
+ summary,
3432
3596
  operationId,
3433
3597
  operationName,
3434
3598
  response,
@@ -3453,23 +3617,39 @@ async function generateVerbOptions({ verb, output, operation, route, pathRoute,
3453
3617
  function generateVerbsOptions({ verbs, input, output, route, pathRoute, context }) {
3454
3618
  return asyncReduce(_filteredVerbs(verbs, input.filters), async (acc, [verb, operation]) => {
3455
3619
  if (isVerb(verb)) {
3456
- const verbOptions = await generateVerbOptions({
3457
- verb,
3458
- output,
3459
- verbParameters: verbs.parameters,
3460
- route,
3461
- pathRoute,
3462
- operation,
3463
- context
3464
- });
3465
- acc.push(verbOptions);
3620
+ const contentTypes = getRequestBodyContentTypes(operation.requestBody, context);
3621
+ if (contentTypes.length > 1) for (const contentType of contentTypes) {
3622
+ const verbOptions = await generateVerbOptions({
3623
+ verb,
3624
+ output,
3625
+ verbParameters: verbs.parameters,
3626
+ route,
3627
+ pathRoute,
3628
+ operation,
3629
+ context,
3630
+ contentType
3631
+ });
3632
+ acc.push(verbOptions);
3633
+ }
3634
+ else {
3635
+ const verbOptions = await generateVerbOptions({
3636
+ verb,
3637
+ output,
3638
+ verbParameters: verbs.parameters,
3639
+ route,
3640
+ pathRoute,
3641
+ operation,
3642
+ context
3643
+ });
3644
+ acc.push(verbOptions);
3645
+ }
3466
3646
  }
3467
3647
  return acc;
3468
3648
  }, []);
3469
3649
  }
3470
3650
  function _filteredVerbs(verbs, filters) {
3471
3651
  if (filters?.tags === void 0) return Object.entries(verbs);
3472
- const filterTags = filters.tags || [];
3652
+ const filterTags = filters.tags;
3473
3653
  const filterMode = filters.mode ?? "include";
3474
3654
  return Object.entries(verbs).filter(([, operation]) => {
3475
3655
  const isMatch = (operation.tags ?? []).some((tag) => filterTags.some((filterTag) => filterTag instanceof RegExp ? filterTag.test(tag) : filterTag === tag));
@@ -3515,17 +3695,25 @@ function splitSchemasByType(schemas) {
3515
3695
  };
3516
3696
  }
3517
3697
  /**
3698
+ * Get the import extension from a file extension.
3699
+ * Removes `.ts` suffix since TypeScript doesn't need it in imports.
3700
+ */
3701
+ function getImportExtension(fileExtension) {
3702
+ return fileExtension.replace(/\.ts$/, "") || "";
3703
+ }
3704
+ /**
3518
3705
  * Fix cross-directory imports when schemas reference other schemas in a different directory.
3519
3706
  * Updates import paths to use correct relative paths between directories.
3520
3707
  */
3521
- function fixSchemaImports(schemas, targetSchemaNames, fromPath, toPath, namingConvention) {
3708
+ function fixSchemaImports(schemas, targetSchemaNames, fromPath, toPath, namingConvention, fileExtension) {
3522
3709
  const relativePath = relativeSafe(fromPath, toPath);
3710
+ const importExtension = getImportExtension(fileExtension);
3523
3711
  for (const schema of schemas) schema.imports = schema.imports.map((imp) => {
3524
3712
  if (targetSchemaNames.has(imp.name)) {
3525
3713
  const fileName = conventionName(imp.name, namingConvention);
3526
3714
  return {
3527
3715
  ...imp,
3528
- importPath: joinSafe(relativePath, fileName)
3716
+ importPath: joinSafe(relativePath, fileName) + importExtension
3529
3717
  };
3530
3718
  }
3531
3719
  return imp;
@@ -3534,14 +3722,14 @@ function fixSchemaImports(schemas, targetSchemaNames, fromPath, toPath, namingCo
3534
3722
  /**
3535
3723
  * Fix imports in operation schemas that reference regular schemas.
3536
3724
  */
3537
- function fixCrossDirectoryImports(operationSchemas, regularSchemaNames, schemaPath, operationSchemaPath, namingConvention) {
3538
- fixSchemaImports(operationSchemas, regularSchemaNames, operationSchemaPath, schemaPath, namingConvention);
3725
+ function fixCrossDirectoryImports(operationSchemas, regularSchemaNames, schemaPath, operationSchemaPath, namingConvention, fileExtension) {
3726
+ fixSchemaImports(operationSchemas, regularSchemaNames, operationSchemaPath, schemaPath, namingConvention, fileExtension);
3539
3727
  }
3540
3728
  /**
3541
3729
  * Fix imports in regular schemas that reference operation schemas.
3542
3730
  */
3543
- function fixRegularSchemaImports(regularSchemas, operationSchemaNames, schemaPath, operationSchemaPath, namingConvention) {
3544
- fixSchemaImports(regularSchemas, operationSchemaNames, schemaPath, operationSchemaPath, namingConvention);
3731
+ function fixRegularSchemaImports(regularSchemas, operationSchemaNames, schemaPath, operationSchemaPath, namingConvention, fileExtension) {
3732
+ fixSchemaImports(regularSchemas, operationSchemaNames, schemaPath, operationSchemaPath, namingConvention, fileExtension);
3545
3733
  }
3546
3734
  function getSchemaKey(schemaPath, schemaName, namingConvention, fileExtension) {
3547
3735
  return getPath(schemaPath, conventionName(schemaName, namingConvention), fileExtension).toLowerCase().replaceAll("\\", "/");
@@ -3594,7 +3782,7 @@ function removeFileExtension(path$2, fileExtension) {
3594
3782
  function getSchema({ schema: { imports, model }, target, header, namingConvention = NamingConvention.CAMEL_CASE }) {
3595
3783
  let file = header;
3596
3784
  file += generateImports({
3597
- imports: imports.filter((imp) => !model.includes(`type ${imp.alias || imp.name} =`) && !model.includes(`interface ${imp.alias || imp.name} {`)),
3785
+ imports: imports.filter((imp) => !model.includes(`type ${imp.alias ?? imp.name} =`) && !model.includes(`interface ${imp.alias ?? imp.name} {`)),
3598
3786
  target,
3599
3787
  namingConvention
3600
3788
  });
@@ -3623,7 +3811,7 @@ async function writeSchema({ path: path$2, schema, target, namingConvention, fil
3623
3811
  namingConvention
3624
3812
  }));
3625
3813
  } catch (error) {
3626
- throw new Error(`Oups... 🍻. An Error occurred while writing schema ${name} => ${error}`);
3814
+ throw new Error(`Oups... 🍻. An Error occurred while writing schema ${name} => ${String(error)}`);
3627
3815
  }
3628
3816
  }
3629
3817
  async function writeSchemas({ schemaPath, schemas, target, namingConvention, fileExtension, header, indexFiles }) {
@@ -3658,14 +3846,14 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
3658
3846
  try {
3659
3847
  const currentExports = [...conventionNamesSet].map((schemaName) => `export * from './${schemaName}${ext}';`).toSorted((a, b) => a.localeCompare(b));
3660
3848
  const existingExports = (await fs$1.readFile(schemaFilePath, "utf8")).match(/export\s+\*\s+from\s+['"][^'"]+['"]/g)?.map((statement) => {
3661
- const match = statement.match(/export\s+\*\s+from\s+['"]([^'"]+)['"]/);
3662
- if (!match) return void 0;
3849
+ const match = /export\s+\*\s+from\s+['"]([^'"]+)['"]/.exec(statement);
3850
+ if (!match) return;
3663
3851
  return `export * from '${match[1]}';`;
3664
- }).filter((statement) => Boolean(statement)) ?? [];
3852
+ }).filter(Boolean) ?? [];
3665
3853
  const fileContent = `${header}\n${[...new Set([...existingExports, ...currentExports])].toSorted((a, b) => a.localeCompare(b)).join("\n")}`;
3666
3854
  await fs$1.writeFile(schemaFilePath, fileContent, { encoding: "utf8" });
3667
3855
  } catch (error) {
3668
- throw new Error(`Oups... 🍻. An Error occurred while writing schema index file ${schemaFilePath} => ${error}`);
3856
+ throw new Error(`Oups... 🍻. An Error occurred while writing schema index file ${schemaFilePath} => ${String(error)}`);
3669
3857
  }
3670
3858
  }
3671
3859
  }
@@ -3674,10 +3862,20 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
3674
3862
  //#region src/writers/generate-imports-for-builder.ts
3675
3863
  function generateImportsForBuilder(output, imports, relativeSchemasPath) {
3676
3864
  const isZodSchemaOutput = isObject(output.schemas) && output.schemas.type === "zod";
3677
- if (!output.indexFiles) return uniqueBy(imports, (x) => x.name).map((i) => {
3678
- const name = conventionName(i.schemaName || i.name, output.namingConvention);
3865
+ if (output.indexFiles) return isZodSchemaOutput ? [{
3866
+ exports: imports.map((i) => ({
3867
+ ...i,
3868
+ values: true
3869
+ })),
3870
+ dependency: joinSafe(relativeSchemasPath, "index.zod")
3871
+ }] : [{
3872
+ exports: imports,
3873
+ dependency: relativeSchemasPath
3874
+ }];
3875
+ else return uniqueBy(imports, (x) => x.name).map((i) => {
3876
+ const name = conventionName(i.schemaName ?? i.name, output.namingConvention);
3679
3877
  const suffix = isZodSchemaOutput ? ".zod" : "";
3680
- const importExtension = output.fileExtension?.replace(/\.ts$/, "") || "";
3878
+ const importExtension = output.fileExtension.replace(/\.ts$/, "") || "";
3681
3879
  return {
3682
3880
  exports: isZodSchemaOutput ? [{
3683
3881
  ...i,
@@ -3686,46 +3884,52 @@ function generateImportsForBuilder(output, imports, relativeSchemasPath) {
3686
3884
  dependency: joinSafe(relativeSchemasPath, `${name}${suffix}${importExtension}`)
3687
3885
  };
3688
3886
  });
3689
- else if (isZodSchemaOutput) return [{
3690
- exports: imports.map((i) => ({
3691
- ...i,
3692
- values: true
3693
- })),
3694
- dependency: joinSafe(relativeSchemasPath, "index.zod")
3695
- }];
3696
- else return [{
3697
- exports: imports,
3698
- dependency: relativeSchemasPath
3699
- }];
3700
3887
  }
3701
3888
 
3702
3889
  //#endregion
3703
3890
  //#region src/writers/target.ts
3704
3891
  function generateTarget(builder, options) {
3705
3892
  const operationNames = Object.values(builder.operations).map(({ operationName }) => operationName);
3706
- const isAngularClient = options?.client === OutputClient.ANGULAR;
3893
+ const isAngularClient = options.client === OutputClient.ANGULAR;
3707
3894
  const titles = builder.title({
3708
3895
  outputClient: options.client,
3709
3896
  title: pascal(builder.info.title),
3710
3897
  customTitleFunc: options.override.title,
3711
3898
  output: options
3712
3899
  });
3713
- const target = Object.values(builder.operations).reduce((acc, operation, index, arr) => {
3714
- acc.imports.push(...operation.imports);
3715
- acc.importsMock.push(...operation.importsMock);
3716
- acc.implementation += operation.implementation + "\n";
3717
- acc.implementationMock.function += operation.implementationMock.function;
3718
- acc.implementationMock.handler += operation.implementationMock.handler;
3719
- const handlerNameSeparator = acc.implementationMock.handlerName.length > 0 ? ",\n " : " ";
3720
- acc.implementationMock.handlerName += handlerNameSeparator + operation.implementationMock.handlerName + "()";
3721
- if (operation.mutator) acc.mutators.push(operation.mutator);
3722
- if (operation.formData) acc.formData.push(operation.formData);
3723
- if (operation.formUrlEncoded) acc.formUrlEncoded.push(operation.formUrlEncoded);
3724
- if (operation.paramsSerializer) acc.paramsSerializer.push(operation.paramsSerializer);
3725
- if (operation.clientMutators) acc.clientMutators.push(...operation.clientMutators);
3726
- if (operation.fetchReviver) acc.fetchReviver.push(operation.fetchReviver);
3727
- if (index === arr.length - 1) {
3728
- const isMutator = acc.mutators.some((mutator) => isAngularClient ? mutator.hasThirdArg : mutator.hasSecondArg);
3900
+ const target = {
3901
+ imports: [],
3902
+ implementation: "",
3903
+ implementationMock: {
3904
+ function: "",
3905
+ handler: "",
3906
+ handlerName: ""
3907
+ },
3908
+ importsMock: [],
3909
+ mutators: [],
3910
+ clientMutators: [],
3911
+ formData: [],
3912
+ formUrlEncoded: [],
3913
+ paramsSerializer: [],
3914
+ fetchReviver: []
3915
+ };
3916
+ const operations = Object.values(builder.operations);
3917
+ for (const [index, operation] of operations.entries()) {
3918
+ target.imports.push(...operation.imports);
3919
+ target.importsMock.push(...operation.importsMock);
3920
+ target.implementation += operation.implementation + "\n";
3921
+ target.implementationMock.function += operation.implementationMock.function;
3922
+ target.implementationMock.handler += operation.implementationMock.handler;
3923
+ const handlerNameSeparator = target.implementationMock.handlerName.length > 0 ? ",\n " : " ";
3924
+ target.implementationMock.handlerName += handlerNameSeparator + operation.implementationMock.handlerName + "()";
3925
+ if (operation.mutator) target.mutators.push(operation.mutator);
3926
+ if (operation.formData) target.formData.push(operation.formData);
3927
+ if (operation.formUrlEncoded) target.formUrlEncoded.push(operation.formUrlEncoded);
3928
+ if (operation.paramsSerializer) target.paramsSerializer.push(operation.paramsSerializer);
3929
+ if (operation.clientMutators) target.clientMutators.push(...operation.clientMutators);
3930
+ if (operation.fetchReviver) target.fetchReviver.push(operation.fetchReviver);
3931
+ if (index === operations.length - 1) {
3932
+ const isMutator = target.mutators.some((mutator) => isAngularClient ? mutator.hasThirdArg : mutator.hasSecondArg);
3729
3933
  const hasAwaitedType = compareVersions(options.packageJson?.dependencies?.typescript ?? options.packageJson?.devDependencies?.typescript ?? "4.4.0", "4.5.0");
3730
3934
  const header = builder.header({
3731
3935
  outputClient: options.client,
@@ -3737,38 +3941,22 @@ function generateTarget(builder, options) {
3737
3941
  titles,
3738
3942
  output: options,
3739
3943
  verbOptions: builder.verbOptions,
3740
- clientImplementation: acc.implementation
3944
+ clientImplementation: target.implementation
3741
3945
  });
3742
- acc.implementation = header.implementation + acc.implementation;
3743
- acc.implementationMock.handler = acc.implementationMock.handler + header.implementationMock + acc.implementationMock.handlerName;
3946
+ target.implementation = header.implementation + target.implementation;
3947
+ target.implementationMock.handler = target.implementationMock.handler + header.implementationMock + target.implementationMock.handlerName;
3744
3948
  const footer = builder.footer({
3745
- outputClient: options?.client,
3949
+ outputClient: options.client,
3746
3950
  operationNames,
3747
- hasMutator: acc.mutators.length > 0,
3951
+ hasMutator: target.mutators.length > 0,
3748
3952
  hasAwaitedType,
3749
3953
  titles,
3750
3954
  output: options
3751
3955
  });
3752
- acc.implementation += footer.implementation;
3753
- acc.implementationMock.handler += footer.implementationMock;
3956
+ target.implementation += footer.implementation;
3957
+ target.implementationMock.handler += footer.implementationMock;
3754
3958
  }
3755
- return acc;
3756
- }, {
3757
- imports: [],
3758
- implementation: "",
3759
- implementationMock: {
3760
- function: "",
3761
- handler: "",
3762
- handlerName: ""
3763
- },
3764
- importsMock: [],
3765
- mutators: [],
3766
- clientMutators: [],
3767
- formData: [],
3768
- formUrlEncoded: [],
3769
- paramsSerializer: [],
3770
- fetchReviver: []
3771
- });
3959
+ }
3772
3960
  return {
3773
3961
  ...target,
3774
3962
  implementationMock: target.implementationMock.function + target.implementationMock.handler
@@ -3824,7 +4012,7 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
3824
4012
  });
3825
4013
  const { imports, importsMock, implementation, implementationMock, mutators, clientMutators, formData, formUrlEncoded, paramsSerializer, fetchReviver } = generateTarget(builder, output);
3826
4014
  let data = header;
3827
- const schemasPath = output.schemas ? relativeSafe(dirname$1, getFileInfo(typeof output.schemas === "string" ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : void 0;
4015
+ const schemasPath = output.schemas ? relativeSafe(dirname$1, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : void 0;
3828
4016
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
3829
4017
  const importsForBuilder = schemasPath ? generateImportsForBuilder(output, imports.filter((imp) => !importsMock.some((impMock) => imp.name === impMock.name)), schemasPath) : [];
3830
4018
  data += builder.imports({
@@ -3893,7 +4081,7 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
3893
4081
  const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, formUrlEncoded, paramsSerializer, fetchReviver } = generateTarget(builder, output);
3894
4082
  let implementationData = header;
3895
4083
  let mockData = header;
3896
- const relativeSchemasPath = output.schemas ? relativeSafe(dirname$1, getFileInfo(typeof output.schemas === "string" ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas";
4084
+ const relativeSchemasPath = output.schemas ? relativeSafe(dirname$1, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas";
3897
4085
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
3898
4086
  const importsForBuilder = generateImportsForBuilder(output, imports, relativeSchemasPath);
3899
4087
  implementationData += builder.imports({
@@ -3953,7 +4141,7 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
3953
4141
  ...mockPath ? [mockPath] : []
3954
4142
  ];
3955
4143
  } catch (error) {
3956
- throw new Error(`Oups... 🍻. An Error occurred while splitting => ${error}`);
4144
+ throw new Error(`Oups... 🍻. An Error occurred while splitting => ${String(error)}`);
3957
4145
  }
3958
4146
  }
3959
4147
 
@@ -3967,8 +4155,7 @@ function addDefaultTagIfEmpty(operation) {
3967
4155
  }
3968
4156
  function generateTargetTags(currentAcc, operation) {
3969
4157
  const tag = kebab(operation.tags[0]);
3970
- const currentOperation = currentAcc[tag];
3971
- if (!currentOperation) {
4158
+ if (!(tag in currentAcc)) {
3972
4159
  currentAcc[tag] = {
3973
4160
  imports: operation.imports,
3974
4161
  importsMock: operation.importsMock,
@@ -3987,6 +4174,7 @@ function generateTargetTags(currentAcc, operation) {
3987
4174
  };
3988
4175
  return currentAcc;
3989
4176
  }
4177
+ const currentOperation = currentAcc[tag];
3990
4178
  currentAcc[tag] = {
3991
4179
  implementation: currentOperation.implementation + operation.implementation,
3992
4180
  imports: [...currentOperation.imports, ...operation.imports],
@@ -4007,66 +4195,69 @@ function generateTargetTags(currentAcc, operation) {
4007
4195
  }
4008
4196
  function generateTargetForTags(builder, options) {
4009
4197
  const isAngularClient = options.client === OutputClient.ANGULAR;
4010
- const allTargetTags = Object.values(builder.operations).map((operation) => addDefaultTagIfEmpty(operation)).reduce((acc, operation, index, arr) => {
4011
- const targetTags = generateTargetTags(acc, operation);
4012
- if (index === arr.length - 1) return Object.entries(targetTags).reduce((acc$1, [tag, target]) => {
4013
- const isMutator = !!target.mutators?.some((mutator) => isAngularClient ? mutator.hasThirdArg : mutator.hasSecondArg);
4014
- const operationNames = Object.values(builder.operations).filter(({ tags }) => tags.map((tag$1) => kebab(tag$1)).indexOf(kebab(tag)) === 0).map(({ operationName }) => operationName);
4015
- const hasAwaitedType = compareVersions(options.packageJson?.dependencies?.typescript ?? options.packageJson?.devDependencies?.typescript ?? "4.4.0", "4.5.0");
4016
- const titles = builder.title({
4017
- outputClient: options.client,
4018
- title: pascal(tag),
4019
- customTitleFunc: options.override.title,
4020
- output: options
4021
- });
4022
- const footer = builder.footer({
4023
- outputClient: options?.client,
4024
- operationNames,
4025
- hasMutator: !!target.mutators?.length,
4026
- hasAwaitedType,
4027
- titles,
4028
- output: options
4029
- });
4030
- const header = builder.header({
4031
- outputClient: options.client,
4032
- isRequestOptions: options.override.requestOptions !== false,
4033
- isMutator,
4034
- isGlobalMutator: !!options.override.mutator,
4035
- provideIn: options.override.angular.provideIn,
4036
- hasAwaitedType,
4037
- titles,
4038
- output: options,
4039
- verbOptions: builder.verbOptions,
4040
- tag,
4041
- clientImplementation: target.implementation
4042
- });
4043
- acc$1[tag] = {
4044
- implementation: header.implementation + target.implementation + footer.implementation,
4045
- implementationMock: {
4046
- function: target.implementationMock.function,
4047
- handler: target.implementationMock.handler + header.implementationMock + target.implementationMock.handlerName + footer.implementationMock,
4048
- handlerName: target.implementationMock.handlerName
4049
- },
4050
- imports: target.imports,
4051
- importsMock: target.importsMock,
4052
- mutators: target.mutators,
4053
- clientMutators: target.clientMutators,
4054
- formData: target.formData,
4055
- formUrlEncoded: target.formUrlEncoded,
4056
- paramsSerializer: target.paramsSerializer,
4057
- fetchReviver: target.fetchReviver
4058
- };
4059
- return acc$1;
4060
- }, {});
4061
- return targetTags;
4062
- }, {});
4063
- return Object.entries(allTargetTags).reduce((acc, [tag, target]) => {
4064
- acc[tag] = {
4065
- ...target,
4066
- implementationMock: target.implementationMock.function + target.implementationMock.handler
4067
- };
4068
- return acc;
4069
- }, {});
4198
+ const operations = Object.values(builder.operations).map((operation) => addDefaultTagIfEmpty(operation));
4199
+ let allTargetTags = {};
4200
+ for (const [index, operation] of operations.entries()) {
4201
+ allTargetTags = generateTargetTags(allTargetTags, operation);
4202
+ if (index === operations.length - 1) {
4203
+ const transformed = {};
4204
+ for (const [tag, target] of Object.entries(allTargetTags)) {
4205
+ const isMutator = !!target.mutators?.some((mutator) => isAngularClient ? mutator.hasThirdArg : mutator.hasSecondArg);
4206
+ const operationNames = Object.values(builder.operations).filter(({ tags }) => tags.map((tag$1) => kebab(tag$1)).indexOf(kebab(tag)) === 0).map(({ operationName }) => operationName);
4207
+ const hasAwaitedType = compareVersions(options.packageJson?.dependencies?.typescript ?? options.packageJson?.devDependencies?.typescript ?? "4.4.0", "4.5.0");
4208
+ const titles = builder.title({
4209
+ outputClient: options.client,
4210
+ title: pascal(tag),
4211
+ customTitleFunc: options.override.title,
4212
+ output: options
4213
+ });
4214
+ const footer = builder.footer({
4215
+ outputClient: options.client,
4216
+ operationNames,
4217
+ hasMutator: !!target.mutators?.length,
4218
+ hasAwaitedType,
4219
+ titles,
4220
+ output: options
4221
+ });
4222
+ const header = builder.header({
4223
+ outputClient: options.client,
4224
+ isRequestOptions: options.override.requestOptions !== false,
4225
+ isMutator,
4226
+ isGlobalMutator: !!options.override.mutator,
4227
+ provideIn: options.override.angular.provideIn,
4228
+ hasAwaitedType,
4229
+ titles,
4230
+ output: options,
4231
+ verbOptions: builder.verbOptions,
4232
+ tag,
4233
+ clientImplementation: target.implementation
4234
+ });
4235
+ transformed[tag] = {
4236
+ implementation: header.implementation + target.implementation + footer.implementation,
4237
+ implementationMock: {
4238
+ function: target.implementationMock.function,
4239
+ handler: target.implementationMock.handler + header.implementationMock + target.implementationMock.handlerName + footer.implementationMock,
4240
+ handlerName: target.implementationMock.handlerName
4241
+ },
4242
+ imports: target.imports,
4243
+ importsMock: target.importsMock,
4244
+ mutators: target.mutators,
4245
+ clientMutators: target.clientMutators,
4246
+ formData: target.formData,
4247
+ formUrlEncoded: target.formUrlEncoded,
4248
+ paramsSerializer: target.paramsSerializer,
4249
+ fetchReviver: target.fetchReviver
4250
+ };
4251
+ }
4252
+ allTargetTags = transformed;
4253
+ }
4254
+ }
4255
+ const result = {};
4256
+ for (const [tag, target] of Object.entries(allTargetTags)) result[tag] = {
4257
+ ...target,
4258
+ implementationMock: target.implementationMock.function + target.implementationMock.handler
4259
+ };
4260
+ return result;
4070
4261
  }
4071
4262
 
4072
4263
  //#endregion
@@ -4078,14 +4269,15 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4078
4269
  });
4079
4270
  const target = generateTargetForTags(builder, output);
4080
4271
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
4081
- const indexFilePath = output.mock && !isFunction(output.mock) && output.mock.indexMockFiles ? join(dirname$1, "index." + getMockFileExtensionByTypeName(output.mock) + extension) : void 0;
4272
+ const mockOption = output.mock && !isFunction(output.mock) ? output.mock : void 0;
4273
+ const indexFilePath = mockOption?.indexMockFiles ? join(dirname$1, "index." + getMockFileExtensionByTypeName(mockOption) + extension) : void 0;
4082
4274
  if (indexFilePath) await fs$1.outputFile(indexFilePath, "");
4083
4275
  return (await Promise.all(Object.entries(target).map(async ([tag, target$1]) => {
4084
4276
  try {
4085
4277
  const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, fetchReviver, formUrlEncoded, paramsSerializer } = target$1;
4086
4278
  let implementationData = header;
4087
4279
  let mockData = header;
4088
- const relativeSchemasPath = output.schemas ? "../" + relativeSafe(dirname$1, getFileInfo(typeof output.schemas === "string" ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "../" + filename + ".schemas";
4280
+ const relativeSchemasPath = output.schemas ? "../" + relativeSafe(dirname$1, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "../" + filename + ".schemas";
4089
4281
  const importsForBuilder = generateImportsForBuilder(output, imports, relativeSchemasPath);
4090
4282
  implementationData += builder.imports({
4091
4283
  client: output.client,
@@ -4155,9 +4347,9 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4155
4347
  const mockPath = output.mock ? join(dirname$1, tag, tag + "." + getMockFileExtensionByTypeName(output.mock) + extension) : void 0;
4156
4348
  if (mockPath) {
4157
4349
  await fs$1.outputFile(mockPath, mockData);
4158
- if (indexFilePath) {
4159
- const localMockPath = joinSafe("./", tag, tag + "." + getMockFileExtensionByTypeName(output.mock));
4160
- fs$1.appendFile(indexFilePath, `export { get${pascal(tag)}Mock } from '${localMockPath}'\n`);
4350
+ if (indexFilePath && mockOption) {
4351
+ const localMockPath = joinSafe("./", tag, tag + "." + getMockFileExtensionByTypeName(mockOption));
4352
+ await fs$1.appendFile(indexFilePath, `export { get${pascal(tag)}Mock } from '${localMockPath}'\n`);
4161
4353
  }
4162
4354
  }
4163
4355
  return [
@@ -4166,7 +4358,7 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4166
4358
  ...mockPath ? [mockPath] : []
4167
4359
  ];
4168
4360
  } catch (error) {
4169
- throw new Error(`Oups... 🍻. An Error occurred while splitting tag ${tag} => ${error}`);
4361
+ throw new Error(`Oups... 🍻. An Error occurred while splitting tag ${tag} => ${String(error)}`);
4170
4362
  }
4171
4363
  }))).flat();
4172
4364
  }
@@ -4184,7 +4376,7 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4184
4376
  try {
4185
4377
  const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, formUrlEncoded, fetchReviver, paramsSerializer } = target$1;
4186
4378
  let data = header;
4187
- const schemasPathRelative = output.schemas ? relativeSafe(dirname$1, getFileInfo(typeof output.schemas === "string" ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas";
4379
+ const schemasPathRelative = output.schemas ? relativeSafe(dirname$1, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas";
4188
4380
  const importsForBuilder = generateImportsForBuilder(output, imports.filter((imp) => !importsMock.some((impMock) => imp.name === impMock.name)), schemasPathRelative);
4189
4381
  data += builder.imports({
4190
4382
  client: output.client,
@@ -4242,11 +4434,11 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4242
4434
  await fs$1.outputFile(implementationPath, data);
4243
4435
  return [implementationPath, ...schemasPath ? [schemasPath] : []];
4244
4436
  } catch (error) {
4245
- throw new Error(`Oups... 🍻. An Error occurred while writing tag ${tag} => ${error}`);
4437
+ throw new Error(`Oups... 🍻. An Error occurred while writing tag ${tag} => ${String(error)}`);
4246
4438
  }
4247
4439
  }))).flat();
4248
4440
  }
4249
4441
 
4250
4442
  //#endregion
4251
- export { BODY_TYPE_NAME, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, _filteredVerbs, addDependency, asyncReduce, camel, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicImport, escape, 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, getArray, 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, getTypedResponse, isBinaryContentType, isBoolean, isDirectory, isFunction, isModule, isNull, isNumber, isNumeric, isObject, isReference, isSchema, isString, isSyntheticDefaultImportsAllow, isUndefined, isUrl, isVerb, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resolveDiscriminators, resolveExampleRefs, resolveObject, resolveRef, resolveValue, sanitize, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
4443
+ export { BODY_TYPE_NAME, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, _filteredVerbs, addDependency, asyncReduce, camel, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicImport, escape, filterByContentType, 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, getArray, 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, getRequestBodyContentTypes, getResReqTypes, getResponse, getResponseTypeCategory, getRoute, getRouteAsArray, getScalar, getTypedResponse, isBinaryContentType, isBoolean, isDirectory, isFunction, isModule, isNullish, isNumber, isNumeric, isObject, isReference, isSchema, isString, isStringLike, isSyntheticDefaultImportsAllow, isUrl, isVerb, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resolveDiscriminators, resolveExampleRefs, resolveObject, resolveRef, resolveValue, sanitize, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
4252
4444
  //# sourceMappingURL=index.mjs.map