@orval/core 8.0.3 → 8.2.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
@@ -388,6 +388,57 @@ function conventionName(name, convention) {
388
388
  return nameConventionTransform(name);
389
389
  }
390
390
 
391
+ //#endregion
392
+ //#region src/utils/content-type.ts
393
+ /**
394
+ * Determine if a content type is binary (vs text-based).
395
+ */
396
+ function isBinaryContentType(contentType) {
397
+ if (contentType === "application/octet-stream") return true;
398
+ if (contentType.startsWith("image/")) return true;
399
+ if (contentType.startsWith("audio/")) return true;
400
+ if (contentType.startsWith("video/")) return true;
401
+ if (contentType.startsWith("font/")) return true;
402
+ if (contentType.startsWith("text/")) return false;
403
+ if ([
404
+ "+json",
405
+ "-json",
406
+ "+xml",
407
+ "-xml",
408
+ "+yaml",
409
+ "-yaml",
410
+ "+rss",
411
+ "-rss",
412
+ "+csv",
413
+ "-csv"
414
+ ].some((suffix) => contentType.includes(suffix))) return false;
415
+ return !new Set([
416
+ "application/json",
417
+ "application/xml",
418
+ "application/yaml",
419
+ "application/x-www-form-urlencoded",
420
+ "application/javascript",
421
+ "application/ecmascript",
422
+ "application/graphql"
423
+ ]).has(contentType);
424
+ }
425
+ /**
426
+ * Determine if a form-data field should be treated as a file (binary or text).
427
+ *
428
+ * Precedence (per OAS 3.1): encoding.contentType > schema.contentMediaType
429
+ *
430
+ * Returns:
431
+ * - 'binary': binary file (Blob)
432
+ * - 'text': text file (Blob | string)
433
+ * - undefined: not a file, use standard string resolution
434
+ */
435
+ function getFormDataFieldFileType(resolvedSchema, partContentType) {
436
+ if (resolvedSchema.type !== "string") return;
437
+ if (resolvedSchema.contentEncoding) return;
438
+ const effectiveContentType = partContentType ?? resolvedSchema.contentMediaType;
439
+ if (effectiveContentType) return isBinaryContentType(effectiveContentType) ? "binary" : "text";
440
+ }
441
+
391
442
  //#endregion
392
443
  //#region src/utils/compare-version.ts
393
444
  function compareVersions(firstVersion, secondVersions, operator = ">=") {
@@ -797,11 +848,13 @@ function escape(str, char = "'") {
797
848
  * @param input String to escape
798
849
  */
799
850
  function jsStringEscape(input) {
800
- return input.replaceAll(/["'\\\n\r\u2028\u2029]/g, (character) => {
851
+ return input.replaceAll(/["'\\\n\r\u2028\u2029/*]/g, (character) => {
801
852
  switch (character) {
802
853
  case "\"":
803
854
  case "'":
804
- case "\\": return "\\" + character;
855
+ case "\\":
856
+ case "/":
857
+ case "*": return "\\" + character;
805
858
  case "\n": return String.raw`\n`;
806
859
  case "\r": return String.raw`\r`;
807
860
  case "\u2028": return String.raw`\u2028`;
@@ -810,6 +863,37 @@ function jsStringEscape(input) {
810
863
  }
811
864
  });
812
865
  }
866
+ /**
867
+ * Deduplicates a TypeScript union type string.
868
+ * Handles types like "A | B | B" → "A | B" and "null | null" → "null".
869
+ * Only splits on top-level | (not inside {} () [] <> or string literals).
870
+ */
871
+ function dedupeUnionType(unionType) {
872
+ const parts = [];
873
+ let current = "";
874
+ let depth = 0;
875
+ let quote = "";
876
+ let escaped = false;
877
+ for (const c of unionType) {
878
+ if (!escaped && (c === "'" || c === "\"")) {
879
+ if (!quote) quote = c;
880
+ else if (quote === c) quote = "";
881
+ }
882
+ if (!quote) {
883
+ if ("{([<".includes(c)) depth++;
884
+ if ("})]>".includes(c)) depth--;
885
+ if (c === "|" && depth === 0) {
886
+ parts.push(current.trim());
887
+ current = "";
888
+ continue;
889
+ }
890
+ }
891
+ current += c;
892
+ escaped = !!quote && !escaped && c === "\\";
893
+ }
894
+ if (current.trim()) parts.push(current.trim());
895
+ return [...new Set(parts)].join(" | ");
896
+ }
813
897
 
814
898
  //#endregion
815
899
  //#region src/utils/tsconfig.ts
@@ -895,6 +979,76 @@ const toNumberKey = (value) => {
895
979
  const getUnion = (value, enumName) => {
896
980
  return `export type ${enumName} = ${value};`;
897
981
  };
982
+ function getEnumUnionFromSchema(schema) {
983
+ if (!schema?.enum) return "";
984
+ return schema.enum.filter((val) => val !== null).map((val) => isString(val) ? `'${escape(val)}'` : `${val}`).join(" | ");
985
+ }
986
+ const stripNullUnion = (value) => value.replace(/\s*\|\s*null/g, "").trim();
987
+ const isSpreadableEnumRef = (schema, refName) => {
988
+ if (!schema?.enum || !refName) return false;
989
+ if (!getEnumUnionFromSchema(schema)) return false;
990
+ const type = schema.type;
991
+ if (type === "boolean" || Array.isArray(type) && type.includes("boolean")) return false;
992
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(refName);
993
+ };
994
+ const buildInlineEnum = (schema, enumValue) => {
995
+ const names = getEnumNames(schema);
996
+ const descriptions = getEnumDescriptions(schema);
997
+ return getEnumImplementation(enumValue ?? getEnumUnionFromSchema(schema), names, descriptions);
998
+ };
999
+ function getCombinedEnumValue(inputs) {
1000
+ const valueImports = [];
1001
+ const hasNull = inputs.some((input) => {
1002
+ if (input.value.includes("| null")) return true;
1003
+ const schema = input.schema;
1004
+ if (!schema) return false;
1005
+ if (schema.nullable === true) return true;
1006
+ if (Array.isArray(schema.type) && schema.type.includes("null")) return true;
1007
+ return schema.enum?.includes(null) ?? false;
1008
+ });
1009
+ const addValueImport = (name) => {
1010
+ if (!valueImports.includes(name)) valueImports.push(name);
1011
+ };
1012
+ if (inputs.length === 1) {
1013
+ const input = inputs[0];
1014
+ if (input.isRef) {
1015
+ const refName = stripNullUnion(input.value);
1016
+ if (isSpreadableEnumRef(input.schema, refName)) {
1017
+ addValueImport(refName);
1018
+ return {
1019
+ value: refName,
1020
+ valueImports,
1021
+ hasNull
1022
+ };
1023
+ }
1024
+ return {
1025
+ value: `{${buildInlineEnum(input.schema)}} as const`,
1026
+ valueImports,
1027
+ hasNull
1028
+ };
1029
+ }
1030
+ return {
1031
+ value: `{${buildInlineEnum(input.schema, stripNullUnion(input.value))}} as const`,
1032
+ valueImports,
1033
+ hasNull
1034
+ };
1035
+ }
1036
+ return {
1037
+ value: `{${inputs.map((input) => {
1038
+ if (input.isRef) {
1039
+ const refName = stripNullUnion(input.value);
1040
+ if (isSpreadableEnumRef(input.schema, refName)) {
1041
+ addValueImport(refName);
1042
+ return `...${refName},`;
1043
+ }
1044
+ return buildInlineEnum(input.schema);
1045
+ }
1046
+ return buildInlineEnum(input.schema, stripNullUnion(input.value));
1047
+ }).join("")}} as const`,
1048
+ valueImports,
1049
+ hasNull
1050
+ };
1051
+ }
898
1052
 
899
1053
  //#endregion
900
1054
  //#region src/getters/ref.ts
@@ -982,6 +1136,10 @@ function getSchema$1(schema, context) {
982
1136
  ...currentSchema,
983
1137
  nullable: schema.nullable
984
1138
  };
1139
+ if ("type" in schema && Array.isArray(schema.type)) currentSchema = {
1140
+ ...currentSchema,
1141
+ type: schema.type
1142
+ };
985
1143
  return {
986
1144
  currentSchema,
987
1145
  refInfo
@@ -1007,7 +1165,7 @@ function resolveExampleRefs(examples, context) {
1007
1165
 
1008
1166
  //#endregion
1009
1167
  //#region src/resolvers/value.ts
1010
- function resolveValue({ schema, name, context }) {
1168
+ function resolveValue({ schema, name, context, formDataContext }) {
1011
1169
  if (isReference(schema)) {
1012
1170
  const { schema: schemaObject, imports } = resolveRef(schema, context);
1013
1171
  const resolvedImport = imports[0];
@@ -1041,7 +1199,8 @@ function resolveValue({ schema, name, context }) {
1041
1199
  ...getScalar({
1042
1200
  item: schema,
1043
1201
  name,
1044
- context
1202
+ context,
1203
+ formDataContext
1045
1204
  }),
1046
1205
  originalSchema: schema,
1047
1206
  isRef: false
@@ -1083,11 +1242,12 @@ function createTypeAliasIfNeeded({ resolvedValue, propName, context }) {
1083
1242
  dependencies: resolvedValue.dependencies
1084
1243
  };
1085
1244
  }
1086
- function resolveObjectOriginal({ schema, propName, combined = false, context }) {
1245
+ function resolveObjectOriginal({ schema, propName, combined = false, context, formDataContext }) {
1087
1246
  const resolvedValue = resolveValue({
1088
1247
  schema,
1089
1248
  name: propName,
1090
- context
1249
+ context,
1250
+ formDataContext
1091
1251
  });
1092
1252
  const aliased = createTypeAliasIfNeeded({
1093
1253
  resolvedValue,
@@ -1121,19 +1281,21 @@ function resolveObjectOriginal({ schema, propName, combined = false, context })
1121
1281
  return resolvedValue;
1122
1282
  }
1123
1283
  const resolveObjectCacheMap = /* @__PURE__ */ new Map();
1124
- function resolveObject({ schema, propName, combined = false, context }) {
1284
+ function resolveObject({ schema, propName, combined = false, context, formDataContext }) {
1125
1285
  const hashKey = JSON.stringify({
1126
1286
  schema,
1127
1287
  propName,
1128
1288
  combined,
1129
- projectName: context.projectName ?? context.output.target
1289
+ projectName: context.projectName ?? context.output.target,
1290
+ formDataContext
1130
1291
  });
1131
1292
  if (resolveObjectCacheMap.has(hashKey)) return resolveObjectCacheMap.get(hashKey);
1132
1293
  const result = resolveObjectOriginal({
1133
1294
  schema,
1134
1295
  propName,
1135
1296
  combined,
1136
- context
1297
+ context,
1298
+ formDataContext
1137
1299
  });
1138
1300
  resolveObjectCacheMap.set(hashKey, result);
1139
1301
  return result;
@@ -1146,18 +1308,19 @@ function resolveObject({ schema, propName, combined = false, context }) {
1146
1308
  *
1147
1309
  * @param item item with type === "array"
1148
1310
  */
1149
- function getArray({ schema, name, context }) {
1311
+ function getArray({ schema, name, context, formDataContext }) {
1150
1312
  const schema31 = schema;
1313
+ const itemSuffix = context.output.override.components.schemas.itemSuffix;
1151
1314
  if (schema31.prefixItems) {
1152
1315
  const resolvedObjects = schema31.prefixItems.map((item, index) => resolveObject({
1153
1316
  schema: item,
1154
- propName: name + context.output.override.components.schemas.itemSuffix + index,
1317
+ propName: name ? name + itemSuffix + index : void 0,
1155
1318
  context
1156
1319
  }));
1157
1320
  if (schema31.items) {
1158
1321
  const additional = resolveObject({
1159
1322
  schema: schema31.items,
1160
- propName: name + context.output.override.components.schemas.itemSuffix + "Additional",
1323
+ propName: name ? name + itemSuffix + "Additional" : void 0,
1161
1324
  context
1162
1325
  });
1163
1326
  resolvedObjects.push({
@@ -1181,8 +1344,9 @@ function getArray({ schema, name, context }) {
1181
1344
  if (schema.items) {
1182
1345
  const resolvedObject = resolveObject({
1183
1346
  schema: schema.items,
1184
- propName: name + context.output.override.components.schemas.itemSuffix,
1185
- context
1347
+ propName: name ? name + itemSuffix : void 0,
1348
+ context,
1349
+ formDataContext
1186
1350
  });
1187
1351
  return {
1188
1352
  value: `${schema.readOnly === true && !context.output.override.suppressReadonlyModifier ? "readonly " : ""}${resolvedObject.value.includes("|") ? `(${resolvedObject.value})[]` : `${resolvedObject.value}[]`}`,
@@ -1210,323 +1374,423 @@ function getArray({ schema, name, context }) {
1210
1374
  }
1211
1375
 
1212
1376
  //#endregion
1213
- //#region src/getters/imports.ts
1214
- function getAliasedImports({ name, resolvedValue, context }) {
1215
- return context.output.schemas && resolvedValue.isRef ? resolvedValue.imports.map((imp) => {
1216
- if (!needCreateImportAlias({
1217
- name,
1218
- imp
1219
- })) return imp;
1220
- return {
1221
- ...imp,
1222
- alias: `__${imp.name}`
1223
- };
1224
- }) : resolvedValue.imports;
1225
- }
1226
- function needCreateImportAlias({ imp, name }) {
1227
- return !imp.alias && imp.name === name;
1228
- }
1229
- function getImportAliasForRefOrValue({ context, imports, resolvedValue }) {
1230
- if (!context.output.schemas || !resolvedValue.isRef) return resolvedValue.value;
1231
- return imports.find((imp) => imp.name === resolvedValue.value)?.alias ?? resolvedValue.value;
1377
+ //#region src/getters/res-req-types.ts
1378
+ const formDataContentTypes = new Set(["multipart/form-data"]);
1379
+ const formUrlEncodedContentTypes = new Set(["application/x-www-form-urlencoded"]);
1380
+ function getResReqContentTypes({ mediaType, propName, context, isFormData, contentType }) {
1381
+ if (!mediaType.schema) return;
1382
+ const formDataContext = isFormData ? {
1383
+ atPart: false,
1384
+ encoding: mediaType.encoding ?? {}
1385
+ } : void 0;
1386
+ const resolvedObject = resolveObject({
1387
+ schema: mediaType.schema,
1388
+ propName,
1389
+ context,
1390
+ formDataContext
1391
+ });
1392
+ if (!isFormData && isBinaryContentType(contentType)) return {
1393
+ ...resolvedObject,
1394
+ value: "Blob"
1395
+ };
1396
+ return resolvedObject;
1232
1397
  }
1233
-
1234
- //#endregion
1235
- //#region src/getters/scalar.ts
1236
- /**
1237
- * Return the typescript equivalent of open-api data type
1238
- *
1239
- * @param item
1240
- * @ref https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.1.md#data-types
1241
- */
1242
- function getScalar({ item, name, context }) {
1243
- const nullable = isArray(item.type) && item.type.includes("null") || item.nullable === true ? " | null" : "";
1244
- const enumItems = item.enum?.filter((enumItem) => enumItem !== null);
1245
- let itemType = item.type;
1246
- if (!itemType && item.items) {
1247
- item.type = "array";
1248
- itemType = "array";
1249
- }
1250
- if (isArray(item.type) && item.type.includes("null")) {
1251
- const typesWithoutNull = item.type.filter((x) => x !== "null");
1252
- itemType = typesWithoutNull.length === 1 ? typesWithoutNull[0] : typesWithoutNull;
1253
- }
1254
- switch (itemType) {
1255
- case "number":
1256
- case "integer": {
1257
- let value = context.output.override.useBigInt && (item.format === "int64" || item.format === "uint64") ? "bigint" : "number";
1258
- let isEnum = false;
1259
- if (enumItems) {
1260
- value = enumItems.map((enumItem) => `${enumItem}`).join(" | ");
1261
- isEnum = true;
1262
- }
1263
- value += nullable;
1264
- const itemWithConst = item;
1265
- if (itemWithConst.const !== void 0) value = itemWithConst.const;
1266
- return {
1267
- value,
1268
- isEnum,
1269
- type: "number",
1398
+ function getResReqTypes(responsesOrRequests, name, context, defaultType = "unknown", uniqueKey = (item) => item.value) {
1399
+ return uniqueBy(responsesOrRequests.filter(([_, res]) => Boolean(res)).map(([key, res]) => {
1400
+ if (isReference(res)) {
1401
+ const { schema: bodySchema, imports: [{ name: name$1, schemaName }] } = resolveRef(res, context);
1402
+ const [contentType, mediaType] = Object.entries(bodySchema.content ?? {})[0] ?? [];
1403
+ const isFormData = formDataContentTypes.has(contentType);
1404
+ const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1405
+ if (!isFormData && !isFormUrlEncoded || !mediaType.schema) return [{
1406
+ value: name$1,
1407
+ imports: [{
1408
+ name: name$1,
1409
+ schemaName
1410
+ }],
1270
1411
  schemas: [],
1271
- imports: [],
1272
- isRef: false,
1273
- hasReadonlyProps: item.readOnly || false,
1274
- dependencies: [],
1275
- example: item.example,
1276
- examples: resolveExampleRefs(item.examples, context)
1277
- };
1278
- }
1279
- case "boolean": {
1280
- let value = "boolean" + nullable;
1281
- const itemWithConst = item;
1282
- if (itemWithConst.const !== void 0) value = itemWithConst.const;
1283
- return {
1284
- value,
1285
- type: "boolean",
1412
+ type: "unknown",
1286
1413
  isEnum: false,
1287
- schemas: [],
1288
- imports: [],
1289
- isRef: false,
1290
- hasReadonlyProps: item.readOnly || false,
1291
- dependencies: [],
1292
- example: item.example,
1293
- examples: resolveExampleRefs(item.examples, context)
1294
- };
1295
- }
1296
- case "array": {
1297
- const { value, ...rest } = getArray({
1298
- schema: item,
1299
- name,
1414
+ isRef: true,
1415
+ hasReadonlyProps: false,
1416
+ originalSchema: mediaType?.schema,
1417
+ example: mediaType?.example,
1418
+ examples: resolveExampleRefs(mediaType?.examples, context),
1419
+ key,
1420
+ contentType
1421
+ }];
1422
+ const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1423
+ name: name$1,
1424
+ schemaObject: mediaType.schema,
1425
+ context,
1426
+ isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1427
+ isRef: true,
1428
+ encoding: mediaType.encoding
1429
+ }) : void 0;
1430
+ const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1431
+ name: name$1,
1432
+ schemaObject: mediaType.schema,
1433
+ context,
1434
+ isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1435
+ isUrlEncoded: true,
1436
+ isRef: true,
1437
+ encoding: mediaType.encoding
1438
+ }) : void 0;
1439
+ const additionalImports = getFormDataAdditionalImports({
1440
+ schemaObject: mediaType.schema,
1300
1441
  context
1301
1442
  });
1302
- return {
1303
- value: value + nullable,
1304
- ...rest,
1305
- dependencies: rest.dependencies ?? []
1306
- };
1443
+ return [{
1444
+ value: name$1,
1445
+ imports: [{
1446
+ name: name$1,
1447
+ schemaName
1448
+ }, ...additionalImports],
1449
+ schemas: [],
1450
+ type: "unknown",
1451
+ isEnum: false,
1452
+ hasReadonlyProps: false,
1453
+ formData,
1454
+ formUrlEncoded,
1455
+ isRef: true,
1456
+ originalSchema: mediaType.schema,
1457
+ example: mediaType.example,
1458
+ examples: resolveExampleRefs(mediaType.examples, context),
1459
+ key,
1460
+ contentType
1461
+ }];
1307
1462
  }
1308
- case "string": {
1309
- let value = "string";
1310
- let isEnum = false;
1311
- if (enumItems) {
1312
- value = enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `);
1313
- isEnum = true;
1463
+ if (res.content) return Object.entries(res.content).map(([contentType, mediaType], index, arr) => {
1464
+ let propName = key ? pascal(name) + pascal(key) : void 0;
1465
+ if (propName && arr.length > 1) propName = propName + pascal(getNumberWord(index + 1));
1466
+ let effectivePropName = propName;
1467
+ if (mediaType.schema && isReference(mediaType.schema)) {
1468
+ const { imports } = resolveRef(mediaType.schema, context);
1469
+ if (imports[0]?.name) effectivePropName = imports[0].name;
1314
1470
  }
1315
- if (item.format === "binary") value = "Blob";
1316
- if (context.output.override.useDates && (item.format === "date" || item.format === "date-time")) value = "Date";
1317
- value += nullable;
1318
- const itemWithConst = item;
1319
- if (itemWithConst.const) value = `'${itemWithConst.const}'`;
1320
- return {
1321
- value,
1322
- isEnum,
1323
- type: "string",
1324
- imports: [],
1325
- schemas: [],
1326
- isRef: false,
1327
- hasReadonlyProps: item.readOnly || false,
1328
- dependencies: [],
1329
- example: item.example,
1330
- examples: resolveExampleRefs(item.examples, context)
1331
- };
1332
- }
1333
- case "null": return {
1334
- value: "null",
1335
- isEnum: false,
1336
- type: "null",
1337
- imports: [],
1338
- schemas: [],
1339
- isRef: false,
1340
- hasReadonlyProps: item.readOnly || false,
1341
- dependencies: []
1342
- };
1343
- case "object":
1344
- default: {
1345
- if (isArray(itemType)) return combineSchemas({
1346
- schema: { anyOf: itemType.map((type) => ({
1347
- ...item,
1348
- type
1349
- })) },
1350
- name,
1351
- separator: "anyOf",
1471
+ const isFormData = formDataContentTypes.has(contentType);
1472
+ const resolvedValue = getResReqContentTypes({
1473
+ mediaType,
1474
+ propName: effectivePropName,
1352
1475
  context,
1353
- nullable
1476
+ isFormData,
1477
+ contentType
1354
1478
  });
1355
- if (enumItems) return {
1356
- value: enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `) + nullable,
1357
- isEnum: true,
1358
- type: "string",
1359
- imports: [],
1360
- schemas: [],
1361
- isRef: false,
1362
- hasReadonlyProps: item.readOnly || false,
1363
- dependencies: [],
1364
- example: item.example,
1365
- examples: resolveExampleRefs(item.examples, context)
1479
+ if (!resolvedValue) {
1480
+ if (isBinaryContentType(contentType)) return {
1481
+ value: "Blob",
1482
+ imports: [],
1483
+ schemas: [],
1484
+ type: "Blob",
1485
+ isEnum: false,
1486
+ key,
1487
+ isRef: false,
1488
+ hasReadonlyProps: false,
1489
+ contentType
1490
+ };
1491
+ return;
1492
+ }
1493
+ const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1494
+ if (!isFormData && !isFormUrlEncoded || !effectivePropName) return {
1495
+ ...resolvedValue,
1496
+ imports: resolvedValue.imports,
1497
+ contentType,
1498
+ example: mediaType.example,
1499
+ examples: resolveExampleRefs(mediaType.examples, context)
1366
1500
  };
1367
- const { value, ...rest } = getObject({
1368
- item,
1369
- name,
1501
+ const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1502
+ name: effectivePropName,
1503
+ schemaObject: mediaType.schema,
1370
1504
  context,
1371
- nullable
1505
+ isRequestBodyOptional: "required" in res && res.required === false,
1506
+ isRef: true,
1507
+ encoding: mediaType.encoding
1508
+ }) : void 0;
1509
+ const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1510
+ name: effectivePropName,
1511
+ schemaObject: mediaType.schema,
1512
+ context,
1513
+ isUrlEncoded: true,
1514
+ isRequestBodyOptional: "required" in res && res.required === false,
1515
+ isRef: true,
1516
+ encoding: mediaType.encoding
1517
+ }) : void 0;
1518
+ const additionalImports = getFormDataAdditionalImports({
1519
+ schemaObject: mediaType.schema,
1520
+ context
1372
1521
  });
1373
1522
  return {
1374
- value,
1375
- ...rest
1523
+ ...resolvedValue,
1524
+ imports: [...resolvedValue.imports, ...additionalImports],
1525
+ formData,
1526
+ formUrlEncoded,
1527
+ contentType,
1528
+ example: mediaType.example,
1529
+ examples: resolveExampleRefs(mediaType.examples, context)
1376
1530
  };
1377
- }
1378
- }
1531
+ }).filter(Boolean).map((x) => ({
1532
+ ...x,
1533
+ key
1534
+ }));
1535
+ const swaggerSchema = "schema" in res ? res.schema : void 0;
1536
+ if (swaggerSchema) return [{
1537
+ ...resolveObject({
1538
+ schema: swaggerSchema,
1539
+ propName: key ? pascal(name) + pascal(key) : void 0,
1540
+ context
1541
+ }),
1542
+ contentType: "application/json",
1543
+ key
1544
+ }];
1545
+ return [{
1546
+ value: defaultType,
1547
+ imports: [],
1548
+ schemas: [],
1549
+ type: defaultType,
1550
+ isEnum: false,
1551
+ key,
1552
+ isRef: false,
1553
+ hasReadonlyProps: false,
1554
+ contentType: "application/json"
1555
+ }];
1556
+ }).flat(), uniqueKey);
1379
1557
  }
1380
-
1381
- //#endregion
1382
- //#region src/getters/combine.ts
1383
- function combineValues({ resolvedData, resolvedValue, separator: separator$1, context }) {
1384
- if (resolvedData.isEnum.every(Boolean)) return `${resolvedData.values.join(` | `)}${resolvedValue ? ` | ${resolvedValue.value}` : ""}`;
1385
- if (separator$1 === "allOf") {
1386
- let resolvedDataValue = resolvedData.values.map((v) => v.includes(" | ") ? `(${v})` : v).join(` & `);
1387
- if (resolvedData.originalSchema.length > 0 && resolvedValue) {
1388
- const discriminatedPropertySchemas = resolvedData.originalSchema.filter((s) => s?.discriminator && resolvedValue.value.includes(` ${s.discriminator.propertyName}:`));
1389
- if (discriminatedPropertySchemas.length > 0) resolvedDataValue = `Omit<${resolvedDataValue}, '${discriminatedPropertySchemas.map((s) => s.discriminator?.propertyName).join("' | '")}'>`;
1558
+ /**
1559
+ * Determine the response type category for a given content type.
1560
+ * Used to set the correct responseType option in HTTP clients.
1561
+ *
1562
+ * @param contentType - The MIME content type (e.g., 'application/json', 'text/plain')
1563
+ * @returns The response type category to use for parsing
1564
+ */
1565
+ function getResponseTypeCategory(contentType) {
1566
+ if (isBinaryContentType(contentType)) return "blob";
1567
+ if (contentType === "application/json" || contentType.includes("+json") || contentType.includes("-json")) return "json";
1568
+ return "text";
1569
+ }
1570
+ /**
1571
+ * Get the default content type from a list of content types.
1572
+ * Priority: application/json > any JSON-like type > first in list
1573
+ *
1574
+ * @param contentTypes - Array of content types from OpenAPI spec
1575
+ * @returns The default content type to use
1576
+ */
1577
+ function getDefaultContentType(contentTypes) {
1578
+ if (contentTypes.length === 0) return "application/json";
1579
+ if (contentTypes.includes("application/json")) return "application/json";
1580
+ const jsonType = contentTypes.find((ct) => ct.includes("+json") || ct.includes("-json"));
1581
+ if (jsonType) return jsonType;
1582
+ return contentTypes[0];
1583
+ }
1584
+ function getFormDataAdditionalImports({ schemaObject, context }) {
1585
+ const { schema } = resolveRef(schemaObject, context);
1586
+ if (schema.type !== "object") return [];
1587
+ const combinedSchemas = schema.oneOf || schema.anyOf;
1588
+ if (!combinedSchemas) return [];
1589
+ return combinedSchemas.map((schema$1) => resolveRef(schema$1, context).imports[0]).filter(Boolean);
1590
+ }
1591
+ function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequestBodyOptional, isUrlEncoded, isRef, encoding }) {
1592
+ const { schema, imports } = resolveRef(schemaObject, context);
1593
+ const propName = camel(!isRef && isReference(schemaObject) ? imports[0].name : name);
1594
+ const additionalImports = [];
1595
+ const variableName = isUrlEncoded ? "formUrlEncoded" : "formData";
1596
+ let form = isUrlEncoded ? `const ${variableName} = new URLSearchParams();\n` : `const ${variableName} = new FormData();\n`;
1597
+ const combinedSchemas = schema.oneOf || schema.anyOf || schema.allOf;
1598
+ if (schema.type === "object" || schema.type === void 0 && combinedSchemas) {
1599
+ if (combinedSchemas) {
1600
+ const shouldCast = !!schema.oneOf || !!schema.anyOf;
1601
+ const combinedSchemasFormData = combinedSchemas.map((schema$1) => {
1602
+ const { schema: combinedSchema, imports: imports$1 } = resolveRef(schema$1, context);
1603
+ let newPropName = propName;
1604
+ let newPropDefinition = "";
1605
+ if (shouldCast && imports$1[0]) {
1606
+ additionalImports.push(imports$1[0]);
1607
+ newPropName = `${propName}${pascal(imports$1[0].name)}`;
1608
+ newPropDefinition = `const ${newPropName} = (${propName} as ${imports$1[0].name}${isRequestBodyOptional ? " | undefined" : ""});\n`;
1609
+ }
1610
+ return newPropDefinition + resolveSchemaPropertiesToFormData({
1611
+ schema: combinedSchema,
1612
+ variableName,
1613
+ propName: newPropName,
1614
+ context,
1615
+ isRequestBodyOptional,
1616
+ encoding
1617
+ });
1618
+ }).filter(Boolean).join("\n");
1619
+ form += combinedSchemasFormData;
1390
1620
  }
1391
- const resolvedValueStr = resolvedValue?.value.includes(" | ") ? `(${resolvedValue.value})` : resolvedValue?.value;
1392
- const joined = `${resolvedDataValue}${resolvedValue ? ` & ${resolvedValueStr}` : ""}`;
1393
- const overrideRequiredProperties = resolvedData.requiredProperties.filter((prop$1) => !resolvedData.originalSchema.some((schema) => schema?.properties?.[prop$1] && schema.required?.includes(prop$1)));
1394
- if (overrideRequiredProperties.length > 0) return `${joined} & Required<Pick<${joined}, '${overrideRequiredProperties.join("' | '")}'>>`;
1395
- return joined;
1621
+ if (schema.properties) {
1622
+ const formDataValues = resolveSchemaPropertiesToFormData({
1623
+ schema,
1624
+ variableName,
1625
+ propName,
1626
+ context,
1627
+ isRequestBodyOptional,
1628
+ encoding
1629
+ });
1630
+ form += formDataValues;
1631
+ }
1632
+ return form;
1396
1633
  }
1397
- let values = resolvedData.values;
1398
- if (resolvedData.allProperties.length && context.output.unionAddMissingProperties) {
1399
- values = [];
1400
- for (let i = 0; i < resolvedData.values.length; i += 1) {
1401
- const subSchema = resolvedData.originalSchema[i];
1402
- if (subSchema?.type !== "object") {
1403
- values.push(resolvedData.values[i]);
1404
- continue;
1405
- }
1406
- const missingProperties = unique(resolvedData.allProperties.filter((p) => !Object.keys(subSchema.properties).includes(p)));
1407
- values.push(`${resolvedData.values[i]}${missingProperties.length > 0 ? ` & {${missingProperties.map((p) => `${p}?: never`).join("; ")}}` : ""}`);
1634
+ if (schema.type === "array") {
1635
+ let valueStr = "value";
1636
+ if (schema.items) {
1637
+ const { schema: itemSchema } = resolveRef(schema.items, context);
1638
+ if (itemSchema.type === "object" || itemSchema.type === "array") valueStr = "JSON.stringify(value)";
1639
+ else if (itemSchema.type === "number" || itemSchema.type === "integer" || itemSchema.type === "boolean") valueStr = "value.toString()";
1408
1640
  }
1641
+ return `${form}${propName}.forEach(value => ${variableName}.append('data', ${valueStr}))\n`;
1409
1642
  }
1410
- if (resolvedValue) return `(${values.join(` & ${resolvedValue.value}) | (`)} & ${resolvedValue.value})`;
1411
- return values.join(" | ");
1643
+ if (schema.type === "number" || schema.type === "integer" || schema.type === "boolean") return `${form}${variableName}.append('data', ${propName}.toString())\n`;
1644
+ return `${form}${variableName}.append('data', ${propName})\n`;
1412
1645
  }
1413
- function combineSchemas({ name, schema, separator: separator$1, context, nullable }) {
1414
- const items = schema[separator$1] ?? [];
1415
- const resolvedData = items.reduce((acc, subSchema) => {
1416
- let propName;
1417
- if (context.output.override.aliasCombinedTypes) {
1418
- propName = name ? name + pascal(separator$1) : void 0;
1419
- if (propName && acc.schemas.length > 0) propName = propName + pascal(getNumberWord(acc.schemas.length + 1));
1420
- }
1421
- if (separator$1 === "allOf" && isSchema(subSchema) && subSchema.required) acc.requiredProperties.push(...subSchema.required);
1422
- const resolvedValue$1 = resolveObject({
1423
- schema: subSchema,
1424
- propName,
1425
- combined: true,
1426
- context
1427
- });
1428
- const aliasedImports = getAliasedImports({
1429
- context,
1430
- name,
1431
- resolvedValue: resolvedValue$1
1432
- });
1433
- const value = getImportAliasForRefOrValue({
1434
- context,
1435
- resolvedValue: resolvedValue$1,
1436
- imports: aliasedImports
1437
- });
1438
- acc.values.push(value);
1439
- acc.imports.push(...aliasedImports);
1440
- acc.schemas.push(...resolvedValue$1.schemas);
1441
- acc.dependencies.push(...resolvedValue$1.dependencies);
1442
- acc.isEnum.push(resolvedValue$1.isEnum);
1443
- acc.types.push(resolvedValue$1.type);
1444
- acc.isRef.push(resolvedValue$1.isRef);
1445
- acc.originalSchema.push(resolvedValue$1.originalSchema);
1446
- acc.hasReadonlyProps ||= resolvedValue$1.hasReadonlyProps;
1447
- if (resolvedValue$1.type === "object" && resolvedValue$1.originalSchema.properties) acc.allProperties.push(...Object.keys(resolvedValue$1.originalSchema.properties));
1448
- return acc;
1449
- }, {
1450
- values: [],
1451
- imports: [],
1452
- schemas: [],
1453
- isEnum: [],
1454
- isRef: [],
1455
- types: [],
1456
- dependencies: [],
1457
- originalSchema: [],
1458
- allProperties: [],
1459
- hasReadonlyProps: false,
1460
- example: schema.example,
1461
- examples: resolveExampleRefs(schema.examples, context),
1462
- requiredProperties: separator$1 === "allOf" ? schema.required ?? [] : []
1463
- });
1464
- if (resolvedData.isEnum.every(Boolean) && name && items.length > 1 && separator$1 !== "oneOf") {
1465
- const newEnum = `export const ${pascal(name)} = ${getCombineEnumValue(resolvedData)}`;
1466
- return {
1467
- value: `typeof ${pascal(name)}[keyof typeof ${pascal(name)}] ${nullable}`,
1468
- imports: [{ name: pascal(name) }],
1469
- schemas: [...resolvedData.schemas, {
1470
- imports: resolvedData.imports.map((toImport) => ({
1471
- ...toImport,
1472
- values: true
1473
- })),
1474
- model: newEnum,
1475
- name
1476
- }],
1477
- isEnum: false,
1478
- type: "object",
1479
- isRef: false,
1480
- hasReadonlyProps: resolvedData.hasReadonlyProps,
1481
- dependencies: resolvedData.dependencies,
1482
- example: schema.example,
1483
- examples: resolveExampleRefs(schema.examples, context)
1484
- };
1485
- }
1486
- let resolvedValue;
1487
- if (schema.properties) resolvedValue = getScalar({
1488
- item: Object.fromEntries(Object.entries(schema).filter(([key]) => key !== separator$1)),
1489
- name,
1490
- context
1491
- });
1492
- else if (separator$1 === "allOf" && (schema.oneOf || schema.anyOf)) {
1493
- const siblingCombiner = schema.oneOf ? "oneOf" : "anyOf";
1494
- resolvedValue = combineSchemas({
1495
- schema: { [siblingCombiner]: schema[siblingCombiner] },
1496
- name,
1497
- separator: siblingCombiner,
1646
+ function resolveSchemaPropertiesToFormData({ schema, variableName, propName, context, isRequestBodyOptional, keyPrefix = "", depth = 0, encoding }) {
1647
+ return Object.entries(schema.properties ?? {}).reduce((acc, [key, value]) => {
1648
+ const { schema: property } = resolveRef(value, context);
1649
+ if (property.readOnly) return acc;
1650
+ let formDataValue = "";
1651
+ const partContentType = (depth === 0 ? encoding?.[key] : void 0)?.contentType;
1652
+ const formattedKeyPrefix = isRequestBodyOptional ? keyword.isIdentifierNameES5(key) ? "?" : "?." : "";
1653
+ const formattedKey = keyword.isIdentifierNameES5(key) ? `.${key}` : `['${key}']`;
1654
+ const valueKey = `${propName}${formattedKeyPrefix}${formattedKey}`;
1655
+ const nonOptionalValueKey = `${propName}${formattedKey}`;
1656
+ const fileType = getFormDataFieldFileType(property, partContentType);
1657
+ const effectiveContentType = partContentType ?? property.contentMediaType;
1658
+ if (fileType === "binary" || property.format === "binary") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
1659
+ else if (fileType === "text") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey} instanceof Blob ? ${nonOptionalValueKey} : new Blob([${nonOptionalValueKey}], { type: '${effectiveContentType}' }));\n`;
1660
+ else if (property.type === "object") formDataValue = context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE ? resolveSchemaPropertiesToFormData({
1661
+ schema: property,
1662
+ variableName,
1663
+ propName: nonOptionalValueKey,
1498
1664
  context,
1499
- nullable: ""
1665
+ isRequestBodyOptional,
1666
+ keyPrefix: `${keyPrefix}${key}.`,
1667
+ depth: depth + 1,
1668
+ encoding
1669
+ }) : partContentType ? `${variableName}.append(\`${keyPrefix}${key}\`, new Blob([JSON.stringify(${nonOptionalValueKey})], { type: '${partContentType}' }));\n` : `${variableName}.append(\`${keyPrefix}${key}\`, JSON.stringify(${nonOptionalValueKey}));\n`;
1670
+ else if (property.type === "array") {
1671
+ let valueStr = "value";
1672
+ let hasNonPrimitiveChild = false;
1673
+ if (property.items) {
1674
+ const { schema: itemSchema } = resolveRef(property.items, context);
1675
+ if (itemSchema.type === "object" || itemSchema.type === "array") if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
1676
+ hasNonPrimitiveChild = true;
1677
+ const resolvedValue = resolveSchemaPropertiesToFormData({
1678
+ schema: itemSchema,
1679
+ variableName,
1680
+ propName: "value",
1681
+ context,
1682
+ isRequestBodyOptional,
1683
+ keyPrefix: `${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}].`,
1684
+ depth: depth + 1
1685
+ });
1686
+ formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => {
1687
+ ${resolvedValue}});\n`;
1688
+ } else valueStr = "JSON.stringify(value)";
1689
+ 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()";
1690
+ }
1691
+ if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
1692
+ if (!hasNonPrimitiveChild) formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => ${variableName}.append(\`${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}]\`, ${valueStr}));\n`;
1693
+ } else formDataValue = `${valueKey}.forEach(value => ${variableName}.append(\`${keyPrefix}${key}${context.output.override.formData.arrayHandling === FormDataArrayHandling.SERIALIZE_WITH_BRACKETS ? "[]" : ""}\`, ${valueStr}));\n`;
1694
+ } 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`;
1695
+ else formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
1696
+ let existSubSchemaNullable = false;
1697
+ if (property.allOf || property.anyOf || property.oneOf) {
1698
+ const subSchemas = (property.allOf || property.anyOf || property.oneOf)?.map((c) => resolveObject({
1699
+ schema: c,
1700
+ combined: true,
1701
+ context
1702
+ }));
1703
+ if (subSchemas?.some((subSchema) => {
1704
+ return [
1705
+ "number",
1706
+ "integer",
1707
+ "boolean"
1708
+ ].includes(subSchema.type);
1709
+ })) formDataValue = `${variableName}.append(\`${key}\`, ${nonOptionalValueKey}.toString())\n`;
1710
+ if (subSchemas?.some((subSchema) => {
1711
+ return subSchema.type === "null";
1712
+ })) existSubSchemaNullable = true;
1713
+ }
1714
+ const isRequired = schema.required?.includes(key) && !isRequestBodyOptional;
1715
+ if (property.nullable || property.type?.includes("null") || existSubSchemaNullable) {
1716
+ if (isRequired) return acc + `if(${valueKey} !== null) {\n ${formDataValue} }\n`;
1717
+ return acc + `if(${valueKey} !== undefined && ${nonOptionalValueKey} !== null) {\n ${formDataValue} }\n`;
1718
+ }
1719
+ if (isRequired) return acc + formDataValue;
1720
+ return acc + `if(${valueKey} !== undefined) {\n ${formDataValue} }\n`;
1721
+ }, "");
1722
+ }
1723
+
1724
+ //#endregion
1725
+ //#region src/getters/body.ts
1726
+ function getBody({ requestBody, operationName, context, contentType }) {
1727
+ const allBodyTypes = getResReqTypes([[context.output.override.components.requestBodies.suffix, requestBody]], operationName, context);
1728
+ const filteredBodyTypes = contentType ? allBodyTypes.filter((type) => {
1729
+ let include = true;
1730
+ let exclude = false;
1731
+ if (contentType.include) include = contentType.include.includes(type.contentType);
1732
+ if (contentType.exclude) exclude = contentType.exclude.includes(type.contentType);
1733
+ return include && !exclude;
1734
+ }) : allBodyTypes;
1735
+ const imports = filteredBodyTypes.flatMap(({ imports: imports$1 }) => imports$1);
1736
+ const schemas = filteredBodyTypes.flatMap(({ schemas: schemas$1 }) => schemas$1);
1737
+ const definition = filteredBodyTypes.map(({ value }) => value).join(" | ");
1738
+ const nonReadonlyDefinition = filteredBodyTypes.some((x) => x.hasReadonlyProps) && definition ? `NonReadonly<${definition}>` : definition;
1739
+ let implementation = generalJSTypesWithArray.includes(definition.toLowerCase()) || filteredBodyTypes.length > 1 ? camel(operationName) + context.output.override.components.requestBodies.suffix : camel(definition);
1740
+ let isOptional = false;
1741
+ if (implementation) {
1742
+ implementation = sanitize(implementation, {
1743
+ underscore: "_",
1744
+ whitespace: "_",
1745
+ dash: true,
1746
+ es5keyword: true,
1747
+ es5IdentifierName: true
1500
1748
  });
1749
+ if (isReference(requestBody)) {
1750
+ const { schema: bodySchema } = resolveRef(requestBody, context);
1751
+ if (bodySchema.required !== void 0) isOptional = !bodySchema.required;
1752
+ } else if (requestBody.required !== void 0) isOptional = !requestBody.required;
1501
1753
  }
1502
1754
  return {
1503
- value: combineValues({
1504
- resolvedData,
1505
- separator: separator$1,
1506
- resolvedValue,
1507
- context
1508
- }) + nullable,
1509
- imports: resolvedValue ? [...resolvedData.imports, ...resolvedValue.imports] : resolvedData.imports,
1510
- schemas: resolvedValue ? [...resolvedData.schemas, ...resolvedValue.schemas] : resolvedData.schemas,
1511
- dependencies: resolvedValue ? [...resolvedData.dependencies, ...resolvedValue.dependencies] : resolvedData.dependencies,
1512
- isEnum: false,
1513
- type: "object",
1514
- isRef: false,
1515
- hasReadonlyProps: resolvedData?.hasReadonlyProps || resolvedValue?.hasReadonlyProps || false,
1516
- example: schema.example,
1517
- examples: resolveExampleRefs(schema.examples, context)
1755
+ originalSchema: requestBody,
1756
+ definition: nonReadonlyDefinition,
1757
+ implementation,
1758
+ imports,
1759
+ schemas,
1760
+ isOptional,
1761
+ ...filteredBodyTypes.length === 1 ? {
1762
+ formData: filteredBodyTypes[0].formData,
1763
+ formUrlEncoded: filteredBodyTypes[0].formUrlEncoded,
1764
+ contentType: filteredBodyTypes[0].contentType
1765
+ } : {
1766
+ formData: "",
1767
+ formUrlEncoded: "",
1768
+ contentType: ""
1769
+ }
1518
1770
  };
1519
1771
  }
1520
- const getCombineEnumValue = ({ values, isRef, originalSchema }) => {
1521
- if (values.length === 1) {
1522
- if (isRef[0]) return values[0];
1523
- return `{${getEnumImplementation(values[0])}} as const`;
1524
- }
1525
- return `{${values.map((e, i) => {
1526
- if (isRef[i]) return `...${e},`;
1527
- return getEnumImplementation(e, getEnumNames(originalSchema[i]), getEnumDescriptions(originalSchema[i]));
1528
- }).join("")}} as const`;
1529
- };
1772
+
1773
+ //#endregion
1774
+ //#region src/getters/imports.ts
1775
+ function getAliasedImports({ name, resolvedValue, context }) {
1776
+ return context.output.schemas && resolvedValue.isRef ? resolvedValue.imports.map((imp) => {
1777
+ if (!needCreateImportAlias({
1778
+ name,
1779
+ imp
1780
+ })) return imp;
1781
+ return {
1782
+ ...imp,
1783
+ alias: `__${imp.name}`
1784
+ };
1785
+ }) : resolvedValue.imports;
1786
+ }
1787
+ function needCreateImportAlias({ imp, name }) {
1788
+ return !imp.alias && imp.name === name;
1789
+ }
1790
+ function getImportAliasForRefOrValue({ context, imports, resolvedValue }) {
1791
+ if (!context.output.schemas || !resolvedValue.isRef) return resolvedValue.value;
1792
+ return imports.find((imp) => imp.name === resolvedValue.value)?.alias ?? resolvedValue.value;
1793
+ }
1530
1794
 
1531
1795
  //#endregion
1532
1796
  //#region src/getters/keys.ts
@@ -1552,12 +1816,17 @@ function getIndexSignatureKey(item) {
1552
1816
  if (enumValues && enumValues.length > 0) return enumValues.map((val) => `'${val}'`).join(" | ");
1553
1817
  return "string";
1554
1818
  }
1819
+ function getPropertyNamesRecordType(item, valueType) {
1820
+ const enumValues = getPropertyNamesEnum(item);
1821
+ if (!enumValues || enumValues.length === 0) return;
1822
+ return `Partial<Record<${enumValues.map((val) => `'${val}'`).join(" | ")}, ${valueType}>>`;
1823
+ }
1555
1824
  /**
1556
1825
  * Return the output type from an object
1557
1826
  *
1558
1827
  * @param item item with type === "object"
1559
1828
  */
1560
- function getObject({ item, name, context, nullable, propertyOverrides }) {
1829
+ function getObject({ item, name, context, nullable, formDataContext }) {
1561
1830
  if (isReference(item)) {
1562
1831
  const { name: name$1 } = getRefInfo(item.$ref, context);
1563
1832
  return {
@@ -1578,7 +1847,8 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1578
1847
  name,
1579
1848
  separator: item.allOf ? "allOf" : item.oneOf ? "oneOf" : "anyOf",
1580
1849
  context,
1581
- nullable
1850
+ nullable,
1851
+ formDataContext
1582
1852
  });
1583
1853
  if (Array.isArray(item.type)) return combineSchemas({
1584
1854
  schema: { anyOf: item.type.map((type) => ({
@@ -1604,10 +1874,15 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1604
1874
  }
1605
1875
  const allSpecSchemas = context.spec.components?.schemas ?? {};
1606
1876
  if (Object.keys(allSpecSchemas).some((schemaName) => pascal(schemaName) === propName)) propName = propName + "Property";
1607
- const resolvedValue = propertyOverrides?.[key] ?? resolveObject({
1877
+ const propertyFormDataContext = formDataContext && !formDataContext.atPart ? {
1878
+ atPart: true,
1879
+ partContentType: formDataContext.encoding[key]?.contentType
1880
+ } : void 0;
1881
+ const resolvedValue = resolveObject({
1608
1882
  schema,
1609
1883
  propName,
1610
- context
1884
+ context,
1885
+ formDataContext: propertyFormDataContext
1611
1886
  });
1612
1887
  const isReadOnly = item.readOnly || schema.readOnly;
1613
1888
  if (!index) acc.value += "{";
@@ -1635,23 +1910,39 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1635
1910
  imports: aliasedImports
1636
1911
  });
1637
1912
  const propValue = needsValueImport ? alias : constLiteral ?? alias;
1638
- acc.value += `\n ${doc ? `${doc} ` : ""}${isReadOnly && !context.output.override.suppressReadonlyModifier ? "readonly " : ""}${getKey(key)}${isRequired ? "" : "?"}: ${propValue};`;
1913
+ const finalPropValue = isRequired ? propValue : context.output.override.useNullForOptional === true ? `${propValue} | null` : propValue;
1914
+ acc.value += `\n ${doc ? `${doc} ` : ""}${isReadOnly && !context.output.override.suppressReadonlyModifier ? "readonly " : ""}${getKey(key)}${isRequired ? "" : "?"}: ${finalPropValue};`;
1639
1915
  acc.schemas.push(...resolvedValue.schemas);
1640
1916
  acc.dependencies.push(...resolvedValue.dependencies);
1641
1917
  if (arr.length - 1 === index) {
1642
- if (item.additionalProperties) {
1643
- const keyType$1 = getIndexSignatureKey(item);
1644
- if (isBoolean(item.additionalProperties)) acc.value += `\n [key: ${keyType$1}]: unknown;\n }`;
1645
- else {
1646
- const resolvedValue$1 = resolveValue({
1647
- schema: item.additionalProperties,
1648
- name,
1649
- context
1650
- });
1918
+ if (item.additionalProperties) if (isBoolean(item.additionalProperties)) {
1919
+ const recordType$1 = getPropertyNamesRecordType(item, "unknown");
1920
+ if (recordType$1) {
1921
+ acc.value += "\n}";
1922
+ acc.value += ` & ${recordType$1}`;
1923
+ acc.useTypeAlias = true;
1924
+ } else {
1925
+ const keyType$1 = getIndexSignatureKey(item);
1926
+ acc.value += `\n [key: ${keyType$1}]: unknown;\n }`;
1927
+ }
1928
+ } else {
1929
+ const resolvedValue$1 = resolveValue({
1930
+ schema: item.additionalProperties,
1931
+ name,
1932
+ context
1933
+ });
1934
+ const recordType$1 = getPropertyNamesRecordType(item, resolvedValue$1.value);
1935
+ if (recordType$1) {
1936
+ acc.value += "\n}";
1937
+ acc.value += ` & ${recordType$1}`;
1938
+ acc.useTypeAlias = true;
1939
+ } else {
1940
+ const keyType$1 = getIndexSignatureKey(item);
1651
1941
  acc.value += `\n [key: ${keyType$1}]: ${resolvedValue$1.value};\n}`;
1652
- acc.dependencies.push(...resolvedValue$1.dependencies);
1653
1942
  }
1654
- } else acc.value += "\n}";
1943
+ acc.dependencies.push(...resolvedValue$1.dependencies);
1944
+ }
1945
+ else acc.value += "\n}";
1655
1946
  acc.value += nullable;
1656
1947
  }
1657
1948
  return acc;
@@ -1664,40 +1955,68 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1664
1955
  isRef: false,
1665
1956
  schema: {},
1666
1957
  hasReadonlyProps: false,
1958
+ useTypeAlias: false,
1667
1959
  dependencies: [],
1668
1960
  example: item.example,
1669
1961
  examples: resolveExampleRefs(item.examples, context)
1670
1962
  });
1671
1963
  }
1672
1964
  if (item.additionalProperties) {
1673
- const keyType$1 = getIndexSignatureKey(item);
1674
- if (isBoolean(item.additionalProperties)) return {
1675
- value: `{ [key: ${keyType$1}]: unknown }` + nullable,
1676
- imports: [],
1677
- schemas: [],
1678
- isEnum: false,
1679
- type: "object",
1680
- isRef: false,
1681
- hasReadonlyProps: item.readOnly || false,
1682
- dependencies: []
1683
- };
1965
+ if (isBoolean(item.additionalProperties)) {
1966
+ const recordType$2 = getPropertyNamesRecordType(item, "unknown");
1967
+ if (recordType$2) return {
1968
+ value: recordType$2 + nullable,
1969
+ imports: [],
1970
+ schemas: [],
1971
+ isEnum: false,
1972
+ type: "object",
1973
+ isRef: false,
1974
+ hasReadonlyProps: item.readOnly || false,
1975
+ useTypeAlias: true,
1976
+ dependencies: []
1977
+ };
1978
+ return {
1979
+ value: `{ [key: ${getIndexSignatureKey(item)}]: unknown }` + nullable,
1980
+ imports: [],
1981
+ schemas: [],
1982
+ isEnum: false,
1983
+ type: "object",
1984
+ isRef: false,
1985
+ hasReadonlyProps: item.readOnly || false,
1986
+ useTypeAlias: false,
1987
+ dependencies: []
1988
+ };
1989
+ }
1684
1990
  const resolvedValue = resolveValue({
1685
1991
  schema: item.additionalProperties,
1686
1992
  name,
1687
1993
  context
1688
1994
  });
1689
- return {
1690
- value: `{[key: ${keyType$1}]: ${resolvedValue.value}}` + nullable,
1995
+ const recordType$1 = getPropertyNamesRecordType(item, resolvedValue.value);
1996
+ if (recordType$1) return {
1997
+ value: recordType$1 + nullable,
1691
1998
  imports: resolvedValue.imports ?? [],
1692
1999
  schemas: resolvedValue.schemas ?? [],
1693
2000
  isEnum: false,
1694
2001
  type: "object",
1695
2002
  isRef: false,
1696
2003
  hasReadonlyProps: resolvedValue.hasReadonlyProps,
2004
+ useTypeAlias: true,
1697
2005
  dependencies: resolvedValue.dependencies
1698
2006
  };
1699
- }
1700
- const itemWithConst = item;
2007
+ return {
2008
+ value: `{[key: ${getIndexSignatureKey(item)}]: ${resolvedValue.value}}` + nullable,
2009
+ imports: resolvedValue.imports ?? [],
2010
+ schemas: resolvedValue.schemas ?? [],
2011
+ isEnum: false,
2012
+ type: "object",
2013
+ isRef: false,
2014
+ hasReadonlyProps: resolvedValue.hasReadonlyProps,
2015
+ useTypeAlias: false,
2016
+ dependencies: resolvedValue.dependencies
2017
+ };
2018
+ }
2019
+ const itemWithConst = item;
1701
2020
  if (itemWithConst.const) return {
1702
2021
  value: `'${itemWithConst.const}'`,
1703
2022
  imports: [],
@@ -1709,6 +2028,18 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1709
2028
  dependencies: []
1710
2029
  };
1711
2030
  const keyType = item.type === "object" ? getIndexSignatureKey(item) : "string";
2031
+ const recordType = getPropertyNamesRecordType(item, "unknown");
2032
+ if (item.type === "object" && recordType) return {
2033
+ value: recordType + nullable,
2034
+ imports: [],
2035
+ schemas: [],
2036
+ isEnum: false,
2037
+ type: "object",
2038
+ isRef: false,
2039
+ hasReadonlyProps: item.readOnly || false,
2040
+ useTypeAlias: true,
2041
+ dependencies: []
2042
+ };
1712
2043
  return {
1713
2044
  value: (item.type === "object" ? `{ [key: ${keyType}]: unknown }` : "unknown") + nullable,
1714
2045
  imports: [],
@@ -1717,490 +2048,310 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1717
2048
  type: "object",
1718
2049
  isRef: false,
1719
2050
  hasReadonlyProps: item.readOnly || false,
2051
+ useTypeAlias: false,
1720
2052
  dependencies: []
1721
2053
  };
1722
2054
  }
1723
2055
 
1724
2056
  //#endregion
1725
- //#region src/getters/res-req-types.ts
1726
- const formDataContentTypes = new Set(["multipart/form-data"]);
1727
- const formUrlEncodedContentTypes = new Set(["application/x-www-form-urlencoded"]);
1728
- function getResReqContentTypes({ mediaType, propName, context, isFormData, contentType }) {
1729
- if (!mediaType.schema) return;
1730
- if (isFormData) {
1731
- const formDataResult = resolveFormDataRootObject({
1732
- schemaOrRef: mediaType.schema,
1733
- propName,
1734
- context,
1735
- encoding: mediaType.encoding
1736
- });
1737
- if (formDataResult) return formDataResult;
2057
+ //#region src/getters/scalar.ts
2058
+ /**
2059
+ * Return the typescript equivalent of open-api data type
2060
+ *
2061
+ * @param item
2062
+ * @ref https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.1.md#data-types
2063
+ */
2064
+ function getScalar({ item, name, context, formDataContext }) {
2065
+ const nullable = isArray(item.type) && item.type.includes("null") || item.nullable === true ? " | null" : "";
2066
+ const enumItems = item.enum?.filter((enumItem) => enumItem !== null);
2067
+ let itemType = item.type;
2068
+ if (!itemType && item.items) {
2069
+ item.type = "array";
2070
+ itemType = "array";
1738
2071
  }
1739
- const resolvedObject = resolveObject({
1740
- schema: mediaType.schema,
1741
- propName,
1742
- context
1743
- });
1744
- if (!isFormData && isBinaryContentType(contentType)) return {
1745
- ...resolvedObject,
1746
- value: "Blob"
1747
- };
1748
- return resolvedObject;
1749
- }
1750
- function getResReqTypes(responsesOrRequests, name, context, defaultType = "unknown", uniqueKey = (item) => item.value) {
1751
- return uniqueBy(responsesOrRequests.filter(([_, res]) => Boolean(res)).map(([key, res]) => {
1752
- if (isReference(res)) {
1753
- const { schema: bodySchema, imports: [{ name: name$1, schemaName }] } = resolveRef(res, context);
1754
- const [contentType, mediaType] = Object.entries(bodySchema.content ?? {})[0] ?? [];
1755
- const isFormData = formDataContentTypes.has(contentType);
1756
- const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1757
- if (!isFormData && !isFormUrlEncoded || !mediaType.schema) return [{
1758
- value: name$1,
1759
- imports: [{
1760
- name: name$1,
1761
- schemaName
1762
- }],
2072
+ if (isArray(item.type) && item.type.includes("null")) {
2073
+ const typesWithoutNull = item.type.filter((x) => x !== "null");
2074
+ itemType = typesWithoutNull.length === 1 ? typesWithoutNull[0] : typesWithoutNull;
2075
+ }
2076
+ switch (itemType) {
2077
+ case "number":
2078
+ case "integer": {
2079
+ let value = context.output.override.useBigInt && (item.format === "int64" || item.format === "uint64") ? "bigint" : "number";
2080
+ let isEnum = false;
2081
+ if (enumItems) {
2082
+ value = enumItems.map((enumItem) => `${enumItem}`).join(" | ");
2083
+ isEnum = true;
2084
+ }
2085
+ value += nullable;
2086
+ const itemWithConst = item;
2087
+ if (itemWithConst.const !== void 0) value = itemWithConst.const;
2088
+ return {
2089
+ value,
2090
+ isEnum,
2091
+ type: "number",
1763
2092
  schemas: [],
1764
- type: "unknown",
2093
+ imports: [],
2094
+ isRef: false,
2095
+ hasReadonlyProps: item.readOnly || false,
2096
+ dependencies: [],
2097
+ example: item.example,
2098
+ examples: resolveExampleRefs(item.examples, context)
2099
+ };
2100
+ }
2101
+ case "boolean": {
2102
+ let value = "boolean" + nullable;
2103
+ const itemWithConst = item;
2104
+ if (itemWithConst.const !== void 0) value = itemWithConst.const;
2105
+ return {
2106
+ value,
2107
+ type: "boolean",
1765
2108
  isEnum: false,
1766
- isRef: true,
1767
- hasReadonlyProps: false,
1768
- originalSchema: mediaType?.schema,
1769
- example: mediaType?.example,
1770
- examples: resolveExampleRefs(mediaType?.examples, context),
1771
- key,
1772
- contentType
1773
- }];
1774
- const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1775
- name: name$1,
1776
- schemaObject: mediaType.schema,
1777
- context,
1778
- isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1779
- isRef: true,
1780
- encoding: mediaType.encoding
1781
- }) : void 0;
1782
- const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1783
- name: name$1,
1784
- schemaObject: mediaType.schema,
1785
- context,
1786
- isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1787
- isUrlEncoded: true,
1788
- isRef: true,
1789
- encoding: mediaType.encoding
1790
- }) : void 0;
1791
- const additionalImports = getFormDataAdditionalImports({
1792
- schemaObject: mediaType.schema,
1793
- context
1794
- });
1795
- return [{
1796
- value: name$1,
1797
- imports: [{
1798
- name: name$1,
1799
- schemaName
1800
- }, ...additionalImports],
1801
2109
  schemas: [],
1802
- type: "unknown",
1803
- isEnum: false,
1804
- hasReadonlyProps: false,
1805
- formData,
1806
- formUrlEncoded,
1807
- isRef: true,
1808
- originalSchema: mediaType.schema,
1809
- example: mediaType.example,
1810
- examples: resolveExampleRefs(mediaType.examples, context),
1811
- key,
1812
- contentType
1813
- }];
2110
+ imports: [],
2111
+ isRef: false,
2112
+ hasReadonlyProps: item.readOnly || false,
2113
+ dependencies: [],
2114
+ example: item.example,
2115
+ examples: resolveExampleRefs(item.examples, context)
2116
+ };
1814
2117
  }
1815
- if (res.content) return Object.entries(res.content).map(([contentType, mediaType], index, arr) => {
1816
- let propName = key ? pascal(name) + pascal(key) : void 0;
1817
- if (propName && arr.length > 1) propName = propName + pascal(getNumberWord(index + 1));
1818
- let effectivePropName = propName;
1819
- if (mediaType.schema && isReference(mediaType.schema)) {
1820
- const { imports } = resolveRef(mediaType.schema, context);
1821
- if (imports[0]?.name) effectivePropName = imports[0].name;
1822
- }
1823
- const isFormData = formDataContentTypes.has(contentType);
1824
- const resolvedValue = getResReqContentTypes({
1825
- mediaType,
1826
- propName: effectivePropName,
2118
+ case "array": {
2119
+ const { value, ...rest } = getArray({
2120
+ schema: item,
2121
+ name,
1827
2122
  context,
1828
- isFormData,
1829
- contentType
2123
+ formDataContext
1830
2124
  });
1831
- if (!resolvedValue) {
1832
- if (isBinaryContentType(contentType)) return {
1833
- value: "Blob",
1834
- imports: [],
1835
- schemas: [],
1836
- type: "Blob",
1837
- isEnum: false,
1838
- key,
1839
- isRef: false,
1840
- hasReadonlyProps: false,
1841
- contentType
1842
- };
1843
- return;
1844
- }
1845
- const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1846
- if (!isFormData && !isFormUrlEncoded || !effectivePropName) return {
1847
- ...resolvedValue,
1848
- imports: resolvedValue.imports,
1849
- contentType,
1850
- example: mediaType.example,
1851
- examples: resolveExampleRefs(mediaType.examples, context)
2125
+ return {
2126
+ value: value + nullable,
2127
+ ...rest,
2128
+ dependencies: rest.dependencies ?? []
1852
2129
  };
1853
- const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1854
- name: effectivePropName,
1855
- schemaObject: mediaType.schema,
1856
- context,
1857
- isRequestBodyOptional: "required" in res && res.required === false,
1858
- isRef: true,
1859
- encoding: mediaType.encoding
1860
- }) : void 0;
1861
- const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1862
- name: effectivePropName,
1863
- schemaObject: mediaType.schema,
1864
- context,
1865
- isUrlEncoded: true,
1866
- isRequestBodyOptional: "required" in res && res.required === false,
1867
- isRef: true,
1868
- encoding: mediaType.encoding
1869
- }) : void 0;
1870
- const additionalImports = getFormDataAdditionalImports({
1871
- schemaObject: mediaType.schema,
1872
- context
1873
- });
2130
+ }
2131
+ case "string": {
2132
+ let value = "string";
2133
+ let isEnum = false;
2134
+ if (enumItems) {
2135
+ value = enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `);
2136
+ isEnum = true;
2137
+ }
2138
+ if (item.format === "binary") value = "Blob";
2139
+ else if (formDataContext?.atPart) {
2140
+ const fileType = getFormDataFieldFileType(item, formDataContext.partContentType);
2141
+ if (fileType) value = fileType === "binary" ? "Blob" : "Blob | string";
2142
+ }
2143
+ if (context.output.override.useDates && (item.format === "date" || item.format === "date-time")) value = "Date";
2144
+ value += nullable;
2145
+ const itemWithConst = item;
2146
+ if (itemWithConst.const) value = `'${itemWithConst.const}'`;
1874
2147
  return {
1875
- ...resolvedValue,
1876
- imports: [...resolvedValue.imports, ...additionalImports],
1877
- formData,
1878
- formUrlEncoded,
1879
- contentType,
1880
- example: mediaType.example,
1881
- examples: resolveExampleRefs(mediaType.examples, context)
2148
+ value,
2149
+ isEnum,
2150
+ type: "string",
2151
+ imports: [],
2152
+ schemas: [],
2153
+ isRef: false,
2154
+ hasReadonlyProps: item.readOnly || false,
2155
+ dependencies: [],
2156
+ example: item.example,
2157
+ examples: resolveExampleRefs(item.examples, context)
1882
2158
  };
1883
- }).filter(Boolean).map((x) => ({
1884
- ...x,
1885
- key
1886
- }));
1887
- const swaggerSchema = "schema" in res ? res.schema : void 0;
1888
- if (swaggerSchema) return [{
1889
- ...resolveObject({
1890
- schema: swaggerSchema,
1891
- propName: key ? pascal(name) + pascal(key) : void 0,
1892
- context
1893
- }),
1894
- contentType: "application/json",
1895
- key
1896
- }];
1897
- return [{
1898
- value: defaultType,
2159
+ }
2160
+ case "null": return {
2161
+ value: "null",
2162
+ isEnum: false,
2163
+ type: "null",
1899
2164
  imports: [],
1900
2165
  schemas: [],
1901
- type: defaultType,
1902
- isEnum: false,
1903
- key,
1904
2166
  isRef: false,
1905
- hasReadonlyProps: false,
1906
- contentType: "application/json"
1907
- }];
1908
- }).flat(), uniqueKey);
1909
- }
1910
- function isBinaryContentType(contentType) {
1911
- if (contentType === "application/octet-stream") return true;
1912
- if (contentType.startsWith("image/")) return true;
1913
- if (contentType.startsWith("audio/")) return true;
1914
- if (contentType.startsWith("video/")) return true;
1915
- if (contentType.startsWith("font/")) return true;
1916
- if (contentType.startsWith("text/")) return false;
1917
- if ([
1918
- "+json",
1919
- "-json",
1920
- "+xml",
1921
- "-xml",
1922
- "+yaml",
1923
- "-yaml",
1924
- "+rss",
1925
- "-rss",
1926
- "+csv",
1927
- "-csv"
1928
- ].some((suffix) => contentType.includes(suffix))) return false;
1929
- return !new Set([
1930
- "application/json",
1931
- "application/xml",
1932
- "application/yaml",
1933
- "application/x-www-form-urlencoded",
1934
- "application/javascript",
1935
- "application/ecmascript",
1936
- "application/graphql"
1937
- ]).has(contentType);
1938
- }
1939
- /**
1940
- * Determine the response type category for a given content type.
1941
- * Used to set the correct responseType option in HTTP clients.
1942
- *
1943
- * @param contentType - The MIME content type (e.g., 'application/json', 'text/plain')
1944
- * @returns The response type category to use for parsing
1945
- */
1946
- function getResponseTypeCategory(contentType) {
1947
- if (isBinaryContentType(contentType)) return "blob";
1948
- if (contentType === "application/json" || contentType.includes("+json") || contentType.includes("-json")) return "json";
1949
- return "text";
1950
- }
1951
- /**
1952
- * Get the default content type from a list of content types.
1953
- * Priority: application/json > any JSON-like type > first in list
1954
- *
1955
- * @param contentTypes - Array of content types from OpenAPI spec
1956
- * @returns The default content type to use
1957
- */
1958
- function getDefaultContentType(contentTypes) {
1959
- if (contentTypes.length === 0) return "application/json";
1960
- if (contentTypes.includes("application/json")) return "application/json";
1961
- const jsonType = contentTypes.find((ct) => ct.includes("+json") || ct.includes("-json"));
1962
- if (jsonType) return jsonType;
1963
- return contentTypes[0];
1964
- }
1965
- /**
1966
- * Determine if a form-data root field should be treated as binary or text file
1967
- * based on encoding.contentType or contentMediaType.
1968
- *
1969
- * Returns:
1970
- * - 'binary': field is a binary file (Blob in types, File in zod)
1971
- * - 'text': field is a text file that can accept string (Blob | string in types, File | string in zod)
1972
- * - undefined: no override, use standard resolution
1973
- */
1974
- function getFormDataFieldFileType(resolvedSchema, encodingContentType) {
1975
- if (resolvedSchema.type !== "string") return;
1976
- if (resolvedSchema.contentEncoding) return;
1977
- const effectiveContentType = encodingContentType ?? resolvedSchema.contentMediaType;
1978
- if (effectiveContentType) return isBinaryContentType(effectiveContentType) ? "binary" : "text";
1979
- }
1980
- /**
1981
- * Resolve form-data root object with file type overrides.
1982
- * Returns undefined if no file type overrides needed (caller should use normal resolution).
1983
- */
1984
- function resolveFormDataRootObject({ schemaOrRef, propName, context, encoding }) {
1985
- const { schema } = resolveRef(schemaOrRef, context);
1986
- if (!schema.properties) return;
1987
- const propertyOverrides = {};
1988
- for (const key of Object.keys(schema.properties)) {
1989
- const propSchema = schema.properties[key];
1990
- const { schema: resolvedSchema } = resolveRef(propSchema, context);
1991
- const fileType = getFormDataFieldFileType(resolvedSchema, encoding?.[key]?.contentType);
1992
- if (fileType) propertyOverrides[key] = {
1993
- ...getScalar({
1994
- item: resolvedSchema,
1995
- name: propName,
1996
- context
1997
- }),
1998
- value: fileType === "binary" ? "Blob" : "Blob | string"
2167
+ hasReadonlyProps: item.readOnly || false,
2168
+ dependencies: []
1999
2169
  };
2000
- }
2001
- if (Object.keys(propertyOverrides).length === 0) return;
2002
- const result = getObject({
2003
- item: schema,
2004
- name: propName,
2005
- context,
2006
- nullable: "",
2007
- propertyOverrides
2008
- });
2009
- return createTypeAliasIfNeeded({
2010
- resolvedValue: {
2011
- ...result,
2012
- originalSchema: schema
2013
- },
2014
- propName,
2015
- context
2016
- }) ?? result;
2017
- }
2018
- function getFormDataAdditionalImports({ schemaObject, context }) {
2019
- const { schema } = resolveRef(schemaObject, context);
2020
- if (schema.type !== "object") return [];
2021
- const combinedSchemas = schema.oneOf || schema.anyOf;
2022
- if (!combinedSchemas) return [];
2023
- return combinedSchemas.map((schema$1) => resolveRef(schema$1, context).imports[0]).filter(Boolean);
2024
- }
2025
- function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequestBodyOptional, isUrlEncoded, isRef, encoding }) {
2026
- const { schema, imports } = resolveRef(schemaObject, context);
2027
- const propName = camel(!isRef && isReference(schemaObject) ? imports[0].name : name);
2028
- const additionalImports = [];
2029
- const variableName = isUrlEncoded ? "formUrlEncoded" : "formData";
2030
- let form = isUrlEncoded ? `const ${variableName} = new URLSearchParams();\n` : `const ${variableName} = new FormData();\n`;
2031
- const combinedSchemas = schema.oneOf || schema.anyOf || schema.allOf;
2032
- if (schema.type === "object" || schema.type === void 0 && combinedSchemas) {
2033
- if (combinedSchemas) {
2034
- const shouldCast = !!schema.oneOf || !!schema.anyOf;
2035
- const combinedSchemasFormData = combinedSchemas.map((schema$1) => {
2036
- const { schema: combinedSchema, imports: imports$1 } = resolveRef(schema$1, context);
2037
- let newPropName = propName;
2038
- let newPropDefinition = "";
2039
- if (shouldCast && imports$1[0]) {
2040
- additionalImports.push(imports$1[0]);
2041
- newPropName = `${propName}${pascal(imports$1[0].name)}`;
2042
- newPropDefinition = `const ${newPropName} = (${propName} as ${imports$1[0].name}${isRequestBodyOptional ? " | undefined" : ""});\n`;
2043
- }
2044
- return newPropDefinition + resolveSchemaPropertiesToFormData({
2045
- schema: combinedSchema,
2046
- variableName,
2047
- propName: newPropName,
2048
- context,
2049
- isRequestBodyOptional,
2050
- encoding
2051
- });
2052
- }).filter(Boolean).join("\n");
2053
- form += combinedSchemasFormData;
2054
- }
2055
- if (schema.properties) {
2056
- const formDataValues = resolveSchemaPropertiesToFormData({
2057
- schema,
2058
- variableName,
2059
- propName,
2170
+ case "object":
2171
+ default: {
2172
+ if (isArray(itemType)) return combineSchemas({
2173
+ schema: { anyOf: itemType.map((type) => ({
2174
+ ...item,
2175
+ type
2176
+ })) },
2177
+ name,
2178
+ separator: "anyOf",
2060
2179
  context,
2061
- isRequestBodyOptional,
2062
- encoding
2180
+ nullable
2063
2181
  });
2064
- form += formDataValues;
2182
+ if (enumItems) return {
2183
+ value: enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `) + nullable,
2184
+ isEnum: true,
2185
+ type: "string",
2186
+ imports: [],
2187
+ schemas: [],
2188
+ isRef: false,
2189
+ hasReadonlyProps: item.readOnly || false,
2190
+ dependencies: [],
2191
+ example: item.example,
2192
+ examples: resolveExampleRefs(item.examples, context)
2193
+ };
2194
+ const hasCombiners = item.allOf || item.anyOf || item.oneOf;
2195
+ const { value, ...rest } = getObject({
2196
+ item,
2197
+ name,
2198
+ context,
2199
+ nullable,
2200
+ formDataContext: formDataContext?.atPart === false || formDataContext?.atPart && hasCombiners ? formDataContext : void 0
2201
+ });
2202
+ return {
2203
+ value,
2204
+ ...rest
2205
+ };
2065
2206
  }
2066
- return form;
2067
2207
  }
2068
- if (schema.type === "array") {
2069
- let valueStr = "value";
2070
- if (schema.items) {
2071
- const { schema: itemSchema } = resolveRef(schema.items, context);
2072
- if (itemSchema.type === "object" || itemSchema.type === "array") valueStr = "JSON.stringify(value)";
2073
- else if (itemSchema.type === "number" || itemSchema.type === "integer" || itemSchema.type === "boolean") valueStr = "value.toString()";
2208
+ }
2209
+
2210
+ //#endregion
2211
+ //#region src/getters/combine.ts
2212
+ function combineValues({ resolvedData, resolvedValue, separator: separator$1, context }) {
2213
+ if (resolvedData.isEnum.every(Boolean)) return `${resolvedData.values.join(` | `)}${resolvedValue ? ` | ${resolvedValue.value}` : ""}`;
2214
+ if (separator$1 === "allOf") {
2215
+ let resolvedDataValue = resolvedData.values.map((v) => v.includes(" | ") ? `(${v})` : v).join(` & `);
2216
+ if (resolvedData.originalSchema.length > 0 && resolvedValue) {
2217
+ const discriminatedPropertySchemas = resolvedData.originalSchema.filter((s) => s?.discriminator && resolvedValue.value.includes(` ${s.discriminator.propertyName}:`));
2218
+ if (discriminatedPropertySchemas.length > 0) resolvedDataValue = `Omit<${resolvedDataValue}, '${discriminatedPropertySchemas.map((s) => s.discriminator?.propertyName).join("' | '")}'>`;
2074
2219
  }
2075
- return `${form}${propName}.forEach(value => ${variableName}.append('data', ${valueStr}))\n`;
2220
+ const resolvedValueStr = resolvedValue?.value.includes(" | ") ? `(${resolvedValue.value})` : resolvedValue?.value;
2221
+ const joined = `${resolvedDataValue}${resolvedValue ? ` & ${resolvedValueStr}` : ""}`;
2222
+ const overrideRequiredProperties = resolvedData.requiredProperties.filter((prop$1) => !resolvedData.originalSchema.some((schema) => schema?.properties?.[prop$1] && schema.required?.includes(prop$1)));
2223
+ if (overrideRequiredProperties.length > 0) return `${joined} & Required<Pick<${joined}, '${overrideRequiredProperties.join("' | '")}'>>`;
2224
+ return joined;
2076
2225
  }
2077
- if (schema.type === "number" || schema.type === "integer" || schema.type === "boolean") return `${form}${variableName}.append('data', ${propName}.toString())\n`;
2078
- return `${form}${variableName}.append('data', ${propName})\n`;
2079
- }
2080
- function resolveSchemaPropertiesToFormData({ schema, variableName, propName, context, isRequestBodyOptional, keyPrefix = "", depth = 0, encoding }) {
2081
- return Object.entries(schema.properties ?? {}).reduce((acc, [key, value]) => {
2082
- const { schema: property } = resolveRef(value, context);
2083
- if (property.readOnly) return acc;
2084
- let formDataValue = "";
2085
- const encodingContentType = (depth === 0 ? encoding?.[key] : void 0)?.contentType;
2086
- const formattedKeyPrefix = isRequestBodyOptional ? keyword.isIdentifierNameES5(key) ? "?" : "?." : "";
2087
- const formattedKey = keyword.isIdentifierNameES5(key) ? `.${key}` : `['${key}']`;
2088
- const valueKey = `${propName}${formattedKeyPrefix}${formattedKey}`;
2089
- const nonOptionalValueKey = `${propName}${formattedKey}`;
2090
- const fileType = getFormDataFieldFileType(property, encodingContentType);
2091
- const effectiveContentType = encodingContentType ?? property.contentMediaType;
2092
- if (fileType === "binary" || property.format === "binary") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
2093
- else if (fileType === "text") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey} instanceof Blob ? ${nonOptionalValueKey} : new Blob([${nonOptionalValueKey}], { type: '${effectiveContentType}' }));\n`;
2094
- else if (property.type === "object") formDataValue = context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE ? resolveSchemaPropertiesToFormData({
2095
- schema: property,
2096
- variableName,
2097
- propName: nonOptionalValueKey,
2098
- context,
2099
- isRequestBodyOptional,
2100
- keyPrefix: `${keyPrefix}${key}.`,
2101
- depth: depth + 1,
2102
- encoding
2103
- }) : encodingContentType ? `${variableName}.append(\`${keyPrefix}${key}\`, new Blob([JSON.stringify(${nonOptionalValueKey})], { type: '${encodingContentType}' }));\n` : `${variableName}.append(\`${keyPrefix}${key}\`, JSON.stringify(${nonOptionalValueKey}));\n`;
2104
- else if (property.type === "array") {
2105
- let valueStr = "value";
2106
- let hasNonPrimitiveChild = false;
2107
- if (property.items) {
2108
- const { schema: itemSchema } = resolveRef(property.items, context);
2109
- if (itemSchema.type === "object" || itemSchema.type === "array") if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
2110
- hasNonPrimitiveChild = true;
2111
- const resolvedValue = resolveSchemaPropertiesToFormData({
2112
- schema: itemSchema,
2113
- variableName,
2114
- propName: "value",
2115
- context,
2116
- isRequestBodyOptional,
2117
- keyPrefix: `${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}].`,
2118
- depth: depth + 1
2119
- });
2120
- formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => {
2121
- ${resolvedValue}});\n`;
2122
- } else valueStr = "JSON.stringify(value)";
2123
- 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()";
2226
+ let values = resolvedData.values;
2227
+ if (resolvedData.allProperties.length && context.output.unionAddMissingProperties) {
2228
+ values = [];
2229
+ for (let i = 0; i < resolvedData.values.length; i += 1) {
2230
+ const subSchema = resolvedData.originalSchema[i];
2231
+ if (subSchema?.type !== "object") {
2232
+ values.push(resolvedData.values[i]);
2233
+ continue;
2124
2234
  }
2125
- if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
2126
- if (!hasNonPrimitiveChild) formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => ${variableName}.append(\`${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}]\`, ${valueStr}));\n`;
2127
- } else formDataValue = `${valueKey}.forEach(value => ${variableName}.append(\`${keyPrefix}${key}${context.output.override.formData.arrayHandling === FormDataArrayHandling.SERIALIZE_WITH_BRACKETS ? "[]" : ""}\`, ${valueStr}));\n`;
2128
- } 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`;
2129
- else formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
2130
- let existSubSchemaNullable = false;
2131
- if (property.allOf || property.anyOf || property.oneOf) {
2132
- const subSchemas = (property.allOf || property.anyOf || property.oneOf)?.map((c) => resolveObject({
2133
- schema: c,
2134
- combined: true,
2135
- context
2136
- }));
2137
- if (subSchemas?.some((subSchema) => {
2138
- return [
2139
- "number",
2140
- "integer",
2141
- "boolean"
2142
- ].includes(subSchema.type);
2143
- })) formDataValue = `${variableName}.append(\`${key}\`, ${nonOptionalValueKey}.toString())\n`;
2144
- if (subSchemas?.some((subSchema) => {
2145
- return subSchema.type === "null";
2146
- })) existSubSchemaNullable = true;
2147
- }
2148
- const isRequired = schema.required?.includes(key) && !isRequestBodyOptional;
2149
- if (property.nullable || property.type?.includes("null") || existSubSchemaNullable) {
2150
- if (isRequired) return acc + `if(${valueKey} !== null) {\n ${formDataValue} }\n`;
2151
- return acc + `if(${valueKey} !== undefined && ${nonOptionalValueKey} !== null) {\n ${formDataValue} }\n`;
2235
+ const missingProperties = unique(resolvedData.allProperties.filter((p) => !Object.keys(subSchema.properties).includes(p)));
2236
+ values.push(`${resolvedData.values[i]}${missingProperties.length > 0 ? ` & {${missingProperties.map((p) => `${p}?: never`).join("; ")}}` : ""}`);
2152
2237
  }
2153
- if (isRequired) return acc + formDataValue;
2154
- return acc + `if(${valueKey} !== undefined) {\n ${formDataValue} }\n`;
2155
- }, "");
2238
+ }
2239
+ if (resolvedValue) return `(${values.join(` & ${resolvedValue.value}) | (`)} & ${resolvedValue.value})`;
2240
+ return values.join(" | ");
2156
2241
  }
2157
-
2158
- //#endregion
2159
- //#region src/getters/body.ts
2160
- function getBody({ requestBody, operationName, context, contentType }) {
2161
- const allBodyTypes = getResReqTypes([[context.output.override.components.requestBodies.suffix, requestBody]], operationName, context);
2162
- const filteredBodyTypes = contentType ? allBodyTypes.filter((type) => {
2163
- let include = true;
2164
- let exclude = false;
2165
- if (contentType.include) include = contentType.include.includes(type.contentType);
2166
- if (contentType.exclude) exclude = contentType.exclude.includes(type.contentType);
2167
- return include && !exclude;
2168
- }) : allBodyTypes;
2169
- const imports = filteredBodyTypes.flatMap(({ imports: imports$1 }) => imports$1);
2170
- const schemas = filteredBodyTypes.flatMap(({ schemas: schemas$1 }) => schemas$1);
2171
- const definition = filteredBodyTypes.map(({ value }) => value).join(" | ");
2172
- const nonReadonlyDefinition = filteredBodyTypes.some((x) => x.hasReadonlyProps) && definition ? `NonReadonly<${definition}>` : definition;
2173
- let implementation = generalJSTypesWithArray.includes(definition.toLowerCase()) || filteredBodyTypes.length > 1 ? camel(operationName) + context.output.override.components.requestBodies.suffix : camel(definition);
2174
- let isOptional = false;
2175
- if (implementation) {
2176
- implementation = sanitize(implementation, {
2177
- underscore: "_",
2178
- whitespace: "_",
2179
- dash: true,
2180
- es5keyword: true,
2181
- es5IdentifierName: true
2242
+ function combineSchemas({ name, schema, separator: separator$1, context, nullable, formDataContext }) {
2243
+ const items = schema[separator$1] ?? [];
2244
+ const resolvedData = items.reduce((acc, subSchema) => {
2245
+ let propName;
2246
+ if (context.output.override.aliasCombinedTypes) {
2247
+ propName = name ? name + pascal(separator$1) : void 0;
2248
+ if (propName && acc.schemas.length > 0) propName = propName + pascal(getNumberWord(acc.schemas.length + 1));
2249
+ }
2250
+ if (separator$1 === "allOf" && isSchema(subSchema) && subSchema.required) acc.requiredProperties.push(...subSchema.required);
2251
+ const resolvedValue$1 = resolveObject({
2252
+ schema: subSchema,
2253
+ propName,
2254
+ combined: true,
2255
+ context,
2256
+ formDataContext
2257
+ });
2258
+ const aliasedImports = getAliasedImports({
2259
+ context,
2260
+ name,
2261
+ resolvedValue: resolvedValue$1
2262
+ });
2263
+ const value = getImportAliasForRefOrValue({
2264
+ context,
2265
+ resolvedValue: resolvedValue$1,
2266
+ imports: aliasedImports
2267
+ });
2268
+ acc.values.push(value);
2269
+ acc.imports.push(...aliasedImports);
2270
+ acc.schemas.push(...resolvedValue$1.schemas);
2271
+ acc.dependencies.push(...resolvedValue$1.dependencies);
2272
+ acc.isEnum.push(resolvedValue$1.isEnum);
2273
+ acc.types.push(resolvedValue$1.type);
2274
+ acc.isRef.push(resolvedValue$1.isRef);
2275
+ acc.originalSchema.push(resolvedValue$1.originalSchema);
2276
+ acc.hasReadonlyProps ||= resolvedValue$1.hasReadonlyProps;
2277
+ if (resolvedValue$1.type === "object" && resolvedValue$1.originalSchema.properties) acc.allProperties.push(...Object.keys(resolvedValue$1.originalSchema.properties));
2278
+ return acc;
2279
+ }, {
2280
+ values: [],
2281
+ imports: [],
2282
+ schemas: [],
2283
+ isEnum: [],
2284
+ isRef: [],
2285
+ types: [],
2286
+ dependencies: [],
2287
+ originalSchema: [],
2288
+ allProperties: [],
2289
+ hasReadonlyProps: false,
2290
+ example: schema.example,
2291
+ examples: resolveExampleRefs(schema.examples, context),
2292
+ requiredProperties: separator$1 === "allOf" ? schema.required ?? [] : []
2293
+ });
2294
+ if (resolvedData.isEnum.every(Boolean) && name && items.length > 1 && context.output.override.enumGenerationType !== EnumGeneration.UNION) {
2295
+ const { value: combinedEnumValue, valueImports, hasNull } = getCombinedEnumValue(resolvedData.values.map((value, index) => ({
2296
+ value,
2297
+ isRef: resolvedData.isRef[index],
2298
+ schema: resolvedData.originalSchema[index]
2299
+ })));
2300
+ const newEnum = `export const ${pascal(name)} = ${combinedEnumValue}`;
2301
+ const valueImportSet = new Set(valueImports);
2302
+ const typeSuffix = `${nullable}${hasNull && !nullable.includes("null") ? " | null" : ""}`;
2303
+ return {
2304
+ value: `typeof ${pascal(name)}[keyof typeof ${pascal(name)}]${typeSuffix}`,
2305
+ imports: [{ name: pascal(name) }],
2306
+ schemas: [...resolvedData.schemas, {
2307
+ imports: resolvedData.imports.filter((toImport) => valueImportSet.has(toImport.alias ?? toImport.name)).map((toImport) => ({
2308
+ ...toImport,
2309
+ values: true
2310
+ })),
2311
+ model: newEnum,
2312
+ name
2313
+ }],
2314
+ isEnum: false,
2315
+ type: "object",
2316
+ isRef: false,
2317
+ hasReadonlyProps: resolvedData.hasReadonlyProps,
2318
+ dependencies: resolvedData.dependencies,
2319
+ example: schema.example,
2320
+ examples: resolveExampleRefs(schema.examples, context)
2321
+ };
2322
+ }
2323
+ let resolvedValue;
2324
+ if (schema.properties) resolvedValue = getScalar({
2325
+ item: Object.fromEntries(Object.entries(schema).filter(([key]) => key !== separator$1)),
2326
+ name,
2327
+ context
2328
+ });
2329
+ else if (separator$1 === "allOf" && (schema.oneOf || schema.anyOf)) {
2330
+ const siblingCombiner = schema.oneOf ? "oneOf" : "anyOf";
2331
+ resolvedValue = combineSchemas({
2332
+ schema: { [siblingCombiner]: schema[siblingCombiner] },
2333
+ name,
2334
+ separator: siblingCombiner,
2335
+ context,
2336
+ nullable: ""
2182
2337
  });
2183
- if (isReference(requestBody)) {
2184
- const { schema: bodySchema } = resolveRef(requestBody, context);
2185
- if (bodySchema.required !== void 0) isOptional = !bodySchema.required;
2186
- } else if (requestBody.required !== void 0) isOptional = !requestBody.required;
2187
2338
  }
2188
2339
  return {
2189
- originalSchema: requestBody,
2190
- definition: nonReadonlyDefinition,
2191
- implementation,
2192
- imports,
2193
- schemas,
2194
- isOptional,
2195
- ...filteredBodyTypes.length === 1 ? {
2196
- formData: filteredBodyTypes[0].formData,
2197
- formUrlEncoded: filteredBodyTypes[0].formUrlEncoded,
2198
- contentType: filteredBodyTypes[0].contentType
2199
- } : {
2200
- formData: "",
2201
- formUrlEncoded: "",
2202
- contentType: ""
2203
- }
2340
+ value: dedupeUnionType(combineValues({
2341
+ resolvedData,
2342
+ separator: separator$1,
2343
+ resolvedValue,
2344
+ context
2345
+ }) + nullable),
2346
+ imports: resolvedValue ? [...resolvedData.imports, ...resolvedValue.imports] : resolvedData.imports,
2347
+ schemas: resolvedValue ? [...resolvedData.schemas, ...resolvedValue.schemas] : resolvedData.schemas,
2348
+ dependencies: resolvedValue ? [...resolvedData.dependencies, ...resolvedValue.dependencies] : resolvedData.dependencies,
2349
+ isEnum: false,
2350
+ type: "object",
2351
+ isRef: false,
2352
+ hasReadonlyProps: resolvedData?.hasReadonlyProps || resolvedValue?.hasReadonlyProps || false,
2353
+ example: schema.example,
2354
+ examples: resolveExampleRefs(schema.examples, context)
2204
2355
  };
2205
2356
  }
2206
2357
 
@@ -2240,7 +2391,7 @@ function resolveDiscriminators(schemas, context) {
2240
2391
  //#endregion
2241
2392
  //#region src/getters/operation.ts
2242
2393
  function getOperationId(operation, route, verb) {
2243
- if (operation.operationId) return operation.operationId;
2394
+ if (isString(operation.operationId)) return operation.operationId;
2244
2395
  return pascal([verb, ...route.split("/").map((p) => sanitize(p, {
2245
2396
  dash: true,
2246
2397
  underscore: "-",
@@ -2331,10 +2482,10 @@ function getParams({ route, pathParams = [], operationId, context, output }) {
2331
2482
  function getProps({ body, queryParams, params, operationName, headers, context }) {
2332
2483
  const bodyProp = {
2333
2484
  name: body.implementation,
2334
- definition: `${body.implementation}${body.isOptional ? "?" : ""}: ${body.definition}`,
2335
- implementation: `${body.implementation}${body.isOptional ? "?" : ""}: ${body.definition}`,
2485
+ definition: `${body.implementation}${body.isOptional && !context.output.optionsParamRequired ? "?" : ""}: ${body.definition}`,
2486
+ implementation: `${body.implementation}${body.isOptional && !context.output.optionsParamRequired ? "?" : ""}: ${body.definition}`,
2336
2487
  default: false,
2337
- required: !body.isOptional,
2488
+ required: !body.isOptional || context.output.optionsParamRequired,
2338
2489
  type: GetterPropType.BODY
2339
2490
  };
2340
2491
  const queryParamsProp = {
@@ -2342,15 +2493,15 @@ function getProps({ body, queryParams, params, operationName, headers, context }
2342
2493
  definition: getQueryParamDefinition(queryParams, context),
2343
2494
  implementation: getQueryParamDefinition(queryParams, context),
2344
2495
  default: false,
2345
- required: isUndefined(queryParams?.isOptional) ? !context.output.allParamsOptional : !queryParams?.isOptional && !context.output.allParamsOptional,
2496
+ required: isUndefined(queryParams?.isOptional) ? !context.output.allParamsOptional || context.output.optionsParamRequired : !queryParams?.isOptional && !context.output.allParamsOptional || context.output.optionsParamRequired,
2346
2497
  type: GetterPropType.QUERY_PARAM
2347
2498
  };
2348
2499
  const headersProp = {
2349
2500
  name: "headers",
2350
- definition: `headers${headers?.isOptional ? "?" : ""}: ${headers?.schema.name}`,
2351
- implementation: `headers${headers?.isOptional ? "?" : ""}: ${headers?.schema.name}`,
2501
+ definition: `headers${headers?.isOptional && !context.output.optionsParamRequired ? "?" : ""}: ${headers?.schema.name}`,
2502
+ implementation: `headers${headers?.isOptional && !context.output.optionsParamRequired ? "?" : ""}: ${headers?.schema.name}`,
2352
2503
  default: false,
2353
- required: isUndefined(headers?.isOptional) ? false : !headers?.isOptional,
2504
+ required: isUndefined(headers?.isOptional) ? false : !headers?.isOptional || context.output.optionsParamRequired,
2354
2505
  type: GetterPropType.HEADER
2355
2506
  };
2356
2507
  let paramGetterProps;
@@ -2358,7 +2509,7 @@ function getProps({ body, queryParams, params, operationName, headers, context }
2358
2509
  const parameterTypeName = `${pascal(operationName)}PathParameters`;
2359
2510
  const name = "pathParams";
2360
2511
  const namedParametersTypeDefinition = `export type ${parameterTypeName} = {\n ${params.map((property) => property.definition).join(",\n ")},\n }`;
2361
- const isOptional = params.every((param) => param.default);
2512
+ const isOptional = context.output.optionsParamRequired || params.every((param) => param.default);
2362
2513
  const implementation = `{ ${params.map((property) => property.default ? `${property.name} = ${property.default}` : property.name).join(", ")} }: ${parameterTypeName}${isOptional ? " = {}" : ""}`;
2363
2514
  const destructured = `{ ${params.map((property) => property.name).join(", ")} }`;
2364
2515
  paramGetterProps = [{
@@ -2389,7 +2540,7 @@ function getProps({ body, queryParams, params, operationName, headers, context }
2389
2540
  function getQueryParamDefinition(queryParams, context) {
2390
2541
  let paramType = queryParams?.schema.name;
2391
2542
  if (OutputClient.ANGULAR === context.output.client) paramType = `DeepNonNullable<${paramType}>`;
2392
- return `params${queryParams?.isOptional || context.output.allParamsOptional ? "?" : ""}: ${paramType}`;
2543
+ return `params${(queryParams?.isOptional || context.output.allParamsOptional) && !context.output.optionsParamRequired ? "?" : ""}: ${paramType}`;
2393
2544
  }
2394
2545
 
2395
2546
  //#endregion
@@ -2498,8 +2649,8 @@ function getResponse({ responses, operationName, context, contentType }) {
2498
2649
  success: [],
2499
2650
  errors: []
2500
2651
  });
2501
- const success = groupedByStatus.success.map(({ value, formData }) => formData ? "Blob" : value).join(" | ");
2502
- const errors = groupedByStatus.errors.map(({ value }) => value).join(" | ");
2652
+ const success = dedupeUnionType(groupedByStatus.success.map(({ value, formData }) => formData ? "Blob" : value).join(" | "));
2653
+ const errors = dedupeUnionType(groupedByStatus.errors.map(({ value }) => value).join(" | "));
2503
2654
  const defaultType = filteredTypes.find(({ key }) => key === "default")?.value;
2504
2655
  return {
2505
2656
  imports,
@@ -3043,10 +3194,11 @@ function generateInterface({ name, schema, context }) {
3043
3194
  context
3044
3195
  });
3045
3196
  const isEmptyObject = scalar.value === "{}";
3197
+ const shouldUseTypeAlias = context?.output.override?.useTypeOverInterfaces || scalar.useTypeAlias;
3046
3198
  let model = "";
3047
3199
  model += jsDoc(schema);
3048
3200
  if (isEmptyObject) model += "// eslint-disable-next-line @typescript-eslint/no-empty-interface\n";
3049
- if (scalar.type === "object" && !context?.output.override?.useTypeOverInterfaces) if (scalar.type === "object" && schema.properties && Object.values(schema.properties).length > 0 && Object.values(schema.properties).every((item) => "const" in item)) {
3201
+ 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)) {
3050
3202
  const mappedScalarValue = scalar.value.replaceAll(";", ",").replaceAll("?:", ":");
3051
3203
  model += `export const ${name}Value = ${mappedScalarValue} as const;\nexport type ${name} = typeof ${name}Value;\n`;
3052
3204
  } else {
@@ -3198,7 +3350,7 @@ function generateSchemaDefinitions(schemaName, schema, context, suffix) {
3198
3350
  async function generateVerbOptions({ verb, output, operation, route, pathRoute, verbParameters = [], context }) {
3199
3351
  const { responses, requestBody, parameters: operationParameters, tags = [], deprecated, description, summary } = operation;
3200
3352
  const operationId = getOperationId(operation, route, verb);
3201
- const overrideOperation = output.override.operations[operation.operationId];
3353
+ const overrideOperation = output.override.operations[operationId];
3202
3354
  const overrideTag = Object.entries(output.override.tags).reduce((acc, [tag, options]) => tags.includes(tag) && options ? mergeDeep(acc, options) : acc, {});
3203
3355
  const override = mergeDeep(mergeDeep(output.override, overrideTag), overrideOperation);
3204
3356
  const overrideOperationName = overrideOperation?.operationName ?? output.override.operationName;
@@ -3518,7 +3670,13 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
3518
3670
  const ext = fileExtension.endsWith(".ts") ? fileExtension.slice(0, -3) : fileExtension;
3519
3671
  const conventionNamesSet = new Set(Object.values(schemaGroups).map((group) => conventionName(group[0].name, namingConvention)));
3520
3672
  try {
3521
- const fileContent = `${header}\n${[...conventionNamesSet].map((schemaName) => `export * from './${schemaName}${ext}';`).toSorted((a, b) => a.localeCompare(b)).join("\n")}`;
3673
+ const currentExports = [...conventionNamesSet].map((schemaName) => `export * from './${schemaName}${ext}';`).toSorted((a, b) => a.localeCompare(b));
3674
+ const existingExports = (await fs$1.readFile(schemaFilePath, "utf8")).match(/export\s+\*\s+from\s+['"][^'"]+['"]/g)?.map((statement) => {
3675
+ const match = statement.match(/export\s+\*\s+from\s+['"]([^'"]+)['"]/);
3676
+ if (!match) return void 0;
3677
+ return `export * from '${match[1]}';`;
3678
+ }).filter((statement) => Boolean(statement)) ?? [];
3679
+ const fileContent = `${header}\n${[...new Set([...existingExports, ...currentExports])].toSorted((a, b) => a.localeCompare(b)).join("\n")}`;
3522
3680
  await fs$1.writeFile(schemaFilePath, fileContent, { encoding: "utf8" });
3523
3681
  } catch (error) {
3524
3682
  throw new Error(`Oups... 🍻. An Error occurred while writing schema index file ${schemaFilePath} => ${error}`);
@@ -4104,5 +4262,5 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4104
4262
  }
4105
4263
 
4106
4264
  //#endregion
4107
- 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, 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, getDefaultContentType, getEnum, getEnumDescriptions, getEnumImplementation, getEnumNames, 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 };
4265
+ 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 };
4108
4266
  //# sourceMappingURL=index.mjs.map