@orval/core 8.1.0 → 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`;
@@ -813,9 +866,32 @@ function jsStringEscape(input) {
813
866
  /**
814
867
  * Deduplicates a TypeScript union type string.
815
868
  * Handles types like "A | B | B" → "A | B" and "null | null" → "null".
869
+ * Only splits on top-level | (not inside {} () [] <> or string literals).
816
870
  */
817
871
  function dedupeUnionType(unionType) {
818
- const parts = unionType.split("|").map((part) => part.trim());
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());
819
895
  return [...new Set(parts)].join(" | ");
820
896
  }
821
897
 
@@ -1060,6 +1136,10 @@ function getSchema$1(schema, context) {
1060
1136
  ...currentSchema,
1061
1137
  nullable: schema.nullable
1062
1138
  };
1139
+ if ("type" in schema && Array.isArray(schema.type)) currentSchema = {
1140
+ ...currentSchema,
1141
+ type: schema.type
1142
+ };
1063
1143
  return {
1064
1144
  currentSchema,
1065
1145
  refInfo
@@ -1085,7 +1165,7 @@ function resolveExampleRefs(examples, context) {
1085
1165
 
1086
1166
  //#endregion
1087
1167
  //#region src/resolvers/value.ts
1088
- function resolveValue({ schema, name, context }) {
1168
+ function resolveValue({ schema, name, context, formDataContext }) {
1089
1169
  if (isReference(schema)) {
1090
1170
  const { schema: schemaObject, imports } = resolveRef(schema, context);
1091
1171
  const resolvedImport = imports[0];
@@ -1119,7 +1199,8 @@ function resolveValue({ schema, name, context }) {
1119
1199
  ...getScalar({
1120
1200
  item: schema,
1121
1201
  name,
1122
- context
1202
+ context,
1203
+ formDataContext
1123
1204
  }),
1124
1205
  originalSchema: schema,
1125
1206
  isRef: false
@@ -1161,11 +1242,12 @@ function createTypeAliasIfNeeded({ resolvedValue, propName, context }) {
1161
1242
  dependencies: resolvedValue.dependencies
1162
1243
  };
1163
1244
  }
1164
- function resolveObjectOriginal({ schema, propName, combined = false, context }) {
1245
+ function resolveObjectOriginal({ schema, propName, combined = false, context, formDataContext }) {
1165
1246
  const resolvedValue = resolveValue({
1166
1247
  schema,
1167
1248
  name: propName,
1168
- context
1249
+ context,
1250
+ formDataContext
1169
1251
  });
1170
1252
  const aliased = createTypeAliasIfNeeded({
1171
1253
  resolvedValue,
@@ -1199,19 +1281,21 @@ function resolveObjectOriginal({ schema, propName, combined = false, context })
1199
1281
  return resolvedValue;
1200
1282
  }
1201
1283
  const resolveObjectCacheMap = /* @__PURE__ */ new Map();
1202
- function resolveObject({ schema, propName, combined = false, context }) {
1284
+ function resolveObject({ schema, propName, combined = false, context, formDataContext }) {
1203
1285
  const hashKey = JSON.stringify({
1204
1286
  schema,
1205
1287
  propName,
1206
1288
  combined,
1207
- projectName: context.projectName ?? context.output.target
1289
+ projectName: context.projectName ?? context.output.target,
1290
+ formDataContext
1208
1291
  });
1209
1292
  if (resolveObjectCacheMap.has(hashKey)) return resolveObjectCacheMap.get(hashKey);
1210
1293
  const result = resolveObjectOriginal({
1211
1294
  schema,
1212
1295
  propName,
1213
1296
  combined,
1214
- context
1297
+ context,
1298
+ formDataContext
1215
1299
  });
1216
1300
  resolveObjectCacheMap.set(hashKey, result);
1217
1301
  return result;
@@ -1224,18 +1308,19 @@ function resolveObject({ schema, propName, combined = false, context }) {
1224
1308
  *
1225
1309
  * @param item item with type === "array"
1226
1310
  */
1227
- function getArray({ schema, name, context }) {
1311
+ function getArray({ schema, name, context, formDataContext }) {
1228
1312
  const schema31 = schema;
1313
+ const itemSuffix = context.output.override.components.schemas.itemSuffix;
1229
1314
  if (schema31.prefixItems) {
1230
1315
  const resolvedObjects = schema31.prefixItems.map((item, index) => resolveObject({
1231
1316
  schema: item,
1232
- propName: name + context.output.override.components.schemas.itemSuffix + index,
1317
+ propName: name ? name + itemSuffix + index : void 0,
1233
1318
  context
1234
1319
  }));
1235
1320
  if (schema31.items) {
1236
1321
  const additional = resolveObject({
1237
1322
  schema: schema31.items,
1238
- propName: name + context.output.override.components.schemas.itemSuffix + "Additional",
1323
+ propName: name ? name + itemSuffix + "Additional" : void 0,
1239
1324
  context
1240
1325
  });
1241
1326
  resolvedObjects.push({
@@ -1259,8 +1344,9 @@ function getArray({ schema, name, context }) {
1259
1344
  if (schema.items) {
1260
1345
  const resolvedObject = resolveObject({
1261
1346
  schema: schema.items,
1262
- propName: name + context.output.override.components.schemas.itemSuffix,
1263
- context
1347
+ propName: name ? name + itemSuffix : void 0,
1348
+ context,
1349
+ formDataContext
1264
1350
  });
1265
1351
  return {
1266
1352
  value: `${schema.readOnly === true && !context.output.override.suppressReadonlyModifier ? "readonly " : ""}${resolvedObject.value.includes("|") ? `(${resolvedObject.value})[]` : `${resolvedObject.value}[]`}`,
@@ -1288,321 +1374,424 @@ function getArray({ schema, name, context }) {
1288
1374
  }
1289
1375
 
1290
1376
  //#endregion
1291
- //#region src/getters/imports.ts
1292
- function getAliasedImports({ name, resolvedValue, context }) {
1293
- return context.output.schemas && resolvedValue.isRef ? resolvedValue.imports.map((imp) => {
1294
- if (!needCreateImportAlias({
1295
- name,
1296
- imp
1297
- })) return imp;
1298
- return {
1299
- ...imp,
1300
- alias: `__${imp.name}`
1301
- };
1302
- }) : resolvedValue.imports;
1303
- }
1304
- function needCreateImportAlias({ imp, name }) {
1305
- return !imp.alias && imp.name === name;
1306
- }
1307
- function getImportAliasForRefOrValue({ context, imports, resolvedValue }) {
1308
- if (!context.output.schemas || !resolvedValue.isRef) return resolvedValue.value;
1309
- return imports.find((imp) => imp.name === resolvedValue.value)?.alias ?? resolvedValue.value;
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;
1310
1397
  }
1311
-
1312
- //#endregion
1313
- //#region src/getters/scalar.ts
1314
- /**
1315
- * Return the typescript equivalent of open-api data type
1316
- *
1317
- * @param item
1318
- * @ref https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.1.md#data-types
1319
- */
1320
- function getScalar({ item, name, context }) {
1321
- const nullable = isArray(item.type) && item.type.includes("null") || item.nullable === true ? " | null" : "";
1322
- const enumItems = item.enum?.filter((enumItem) => enumItem !== null);
1323
- let itemType = item.type;
1324
- if (!itemType && item.items) {
1325
- item.type = "array";
1326
- itemType = "array";
1327
- }
1328
- if (isArray(item.type) && item.type.includes("null")) {
1329
- const typesWithoutNull = item.type.filter((x) => x !== "null");
1330
- itemType = typesWithoutNull.length === 1 ? typesWithoutNull[0] : typesWithoutNull;
1331
- }
1332
- switch (itemType) {
1333
- case "number":
1334
- case "integer": {
1335
- let value = context.output.override.useBigInt && (item.format === "int64" || item.format === "uint64") ? "bigint" : "number";
1336
- let isEnum = false;
1337
- if (enumItems) {
1338
- value = enumItems.map((enumItem) => `${enumItem}`).join(" | ");
1339
- isEnum = true;
1340
- }
1341
- value += nullable;
1342
- const itemWithConst = item;
1343
- if (itemWithConst.const !== void 0) value = itemWithConst.const;
1344
- return {
1345
- value,
1346
- isEnum,
1347
- type: "number",
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
+ }],
1348
1411
  schemas: [],
1349
- imports: [],
1350
- isRef: false,
1351
- hasReadonlyProps: item.readOnly || false,
1352
- dependencies: [],
1353
- example: item.example,
1354
- examples: resolveExampleRefs(item.examples, context)
1355
- };
1356
- }
1357
- case "boolean": {
1358
- let value = "boolean" + nullable;
1359
- const itemWithConst = item;
1360
- if (itemWithConst.const !== void 0) value = itemWithConst.const;
1361
- return {
1362
- value,
1363
- type: "boolean",
1412
+ type: "unknown",
1364
1413
  isEnum: false,
1365
- schemas: [],
1366
- imports: [],
1367
- isRef: false,
1368
- hasReadonlyProps: item.readOnly || false,
1369
- dependencies: [],
1370
- example: item.example,
1371
- examples: resolveExampleRefs(item.examples, context)
1372
- };
1373
- }
1374
- case "array": {
1375
- const { value, ...rest } = getArray({
1376
- schema: item,
1377
- name,
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,
1378
1441
  context
1379
1442
  });
1380
- return {
1381
- value: value + nullable,
1382
- ...rest,
1383
- dependencies: rest.dependencies ?? []
1384
- };
1385
- }
1386
- case "string": {
1387
- let value = "string";
1388
- let isEnum = false;
1389
- if (enumItems) {
1390
- value = enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `);
1391
- isEnum = true;
1392
- }
1393
- if (item.format === "binary") value = "Blob";
1394
- if (context.output.override.useDates && (item.format === "date" || item.format === "date-time")) value = "Date";
1395
- value += nullable;
1396
- const itemWithConst = item;
1397
- if (itemWithConst.const) value = `'${itemWithConst.const}'`;
1398
- return {
1399
- value,
1400
- isEnum,
1401
- type: "string",
1402
- imports: [],
1443
+ return [{
1444
+ value: name$1,
1445
+ imports: [{
1446
+ name: name$1,
1447
+ schemaName
1448
+ }, ...additionalImports],
1403
1449
  schemas: [],
1404
- isRef: false,
1405
- hasReadonlyProps: item.readOnly || false,
1406
- dependencies: [],
1407
- example: item.example,
1408
- examples: resolveExampleRefs(item.examples, context)
1409
- };
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
+ }];
1410
1462
  }
1411
- case "null": return {
1412
- value: "null",
1413
- isEnum: false,
1414
- type: "null",
1415
- imports: [],
1416
- schemas: [],
1417
- isRef: false,
1418
- hasReadonlyProps: item.readOnly || false,
1419
- dependencies: []
1420
- };
1421
- case "object":
1422
- default: {
1423
- if (isArray(itemType)) return combineSchemas({
1424
- schema: { anyOf: itemType.map((type) => ({
1425
- ...item,
1426
- type
1427
- })) },
1428
- name,
1429
- separator: "anyOf",
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;
1470
+ }
1471
+ const isFormData = formDataContentTypes.has(contentType);
1472
+ const resolvedValue = getResReqContentTypes({
1473
+ mediaType,
1474
+ propName: effectivePropName,
1430
1475
  context,
1431
- nullable
1476
+ isFormData,
1477
+ contentType
1432
1478
  });
1433
- if (enumItems) return {
1434
- value: enumItems.map((enumItem) => isString(enumItem) ? `'${escape(enumItem)}'` : `${enumItem}`).filter(Boolean).join(` | `) + nullable,
1435
- isEnum: true,
1436
- type: "string",
1437
- imports: [],
1438
- schemas: [],
1439
- isRef: false,
1440
- hasReadonlyProps: item.readOnly || false,
1441
- dependencies: [],
1442
- example: item.example,
1443
- examples: resolveExampleRefs(item.examples, context)
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)
1444
1500
  };
1445
- const { value, ...rest } = getObject({
1446
- item,
1447
- name,
1501
+ const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1502
+ name: effectivePropName,
1503
+ schemaObject: mediaType.schema,
1448
1504
  context,
1449
- 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
1450
1521
  });
1451
1522
  return {
1452
- value,
1453
- ...rest
1523
+ ...resolvedValue,
1524
+ imports: [...resolvedValue.imports, ...additionalImports],
1525
+ formData,
1526
+ formUrlEncoded,
1527
+ contentType,
1528
+ example: mediaType.example,
1529
+ examples: resolveExampleRefs(mediaType.examples, context)
1454
1530
  };
1455
- }
1456
- }
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);
1457
1557
  }
1458
-
1459
- //#endregion
1460
- //#region src/getters/combine.ts
1461
- function combineValues({ resolvedData, resolvedValue, separator: separator$1, context }) {
1462
- if (resolvedData.isEnum.every(Boolean)) return `${resolvedData.values.join(` | `)}${resolvedValue ? ` | ${resolvedValue.value}` : ""}`;
1463
- if (separator$1 === "allOf") {
1464
- let resolvedDataValue = resolvedData.values.map((v) => v.includes(" | ") ? `(${v})` : v).join(` & `);
1465
- if (resolvedData.originalSchema.length > 0 && resolvedValue) {
1466
- const discriminatedPropertySchemas = resolvedData.originalSchema.filter((s) => s?.discriminator && resolvedValue.value.includes(` ${s.discriminator.propertyName}:`));
1467
- if (discriminatedPropertySchemas.length > 0) resolvedDataValue = `Omit<${resolvedDataValue}, '${discriminatedPropertySchemas.map((s) => s.discriminator?.propertyName).join("' | '")}'>`;
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;
1468
1620
  }
1469
- const resolvedValueStr = resolvedValue?.value.includes(" | ") ? `(${resolvedValue.value})` : resolvedValue?.value;
1470
- const joined = `${resolvedDataValue}${resolvedValue ? ` & ${resolvedValueStr}` : ""}`;
1471
- const overrideRequiredProperties = resolvedData.requiredProperties.filter((prop$1) => !resolvedData.originalSchema.some((schema) => schema?.properties?.[prop$1] && schema.required?.includes(prop$1)));
1472
- if (overrideRequiredProperties.length > 0) return `${joined} & Required<Pick<${joined}, '${overrideRequiredProperties.join("' | '")}'>>`;
1473
- return joined;
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;
1474
1633
  }
1475
- let values = resolvedData.values;
1476
- if (resolvedData.allProperties.length && context.output.unionAddMissingProperties) {
1477
- values = [];
1478
- for (let i = 0; i < resolvedData.values.length; i += 1) {
1479
- const subSchema = resolvedData.originalSchema[i];
1480
- if (subSchema?.type !== "object") {
1481
- values.push(resolvedData.values[i]);
1482
- continue;
1483
- }
1484
- const missingProperties = unique(resolvedData.allProperties.filter((p) => !Object.keys(subSchema.properties).includes(p)));
1485
- values.push(`${resolvedData.values[i]}${missingProperties.length > 0 ? ` & {${missingProperties.map((p) => `${p}?: never`).join("; ")}}` : ""}`);
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()";
1486
1640
  }
1641
+ return `${form}${propName}.forEach(value => ${variableName}.append('data', ${valueStr}))\n`;
1487
1642
  }
1488
- if (resolvedValue) return `(${values.join(` & ${resolvedValue.value}) | (`)} & ${resolvedValue.value})`;
1489
- 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`;
1490
1645
  }
1491
- function combineSchemas({ name, schema, separator: separator$1, context, nullable }) {
1492
- const items = schema[separator$1] ?? [];
1493
- const resolvedData = items.reduce((acc, subSchema) => {
1494
- let propName;
1495
- if (context.output.override.aliasCombinedTypes) {
1496
- propName = name ? name + pascal(separator$1) : void 0;
1497
- if (propName && acc.schemas.length > 0) propName = propName + pascal(getNumberWord(acc.schemas.length + 1));
1498
- }
1499
- if (separator$1 === "allOf" && isSchema(subSchema) && subSchema.required) acc.requiredProperties.push(...subSchema.required);
1500
- const resolvedValue$1 = resolveObject({
1501
- schema: subSchema,
1502
- propName,
1503
- combined: true,
1504
- context
1505
- });
1506
- const aliasedImports = getAliasedImports({
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,
1507
1664
  context,
1508
- name,
1509
- resolvedValue: resolvedValue$1
1510
- });
1511
- const value = getImportAliasForRefOrValue({
1512
- context,
1513
- resolvedValue: resolvedValue$1,
1514
- imports: aliasedImports
1515
- });
1516
- acc.values.push(value);
1517
- acc.imports.push(...aliasedImports);
1518
- acc.schemas.push(...resolvedValue$1.schemas);
1519
- acc.dependencies.push(...resolvedValue$1.dependencies);
1520
- acc.isEnum.push(resolvedValue$1.isEnum);
1521
- acc.types.push(resolvedValue$1.type);
1522
- acc.isRef.push(resolvedValue$1.isRef);
1523
- acc.originalSchema.push(resolvedValue$1.originalSchema);
1524
- acc.hasReadonlyProps ||= resolvedValue$1.hasReadonlyProps;
1525
- if (resolvedValue$1.type === "object" && resolvedValue$1.originalSchema.properties) acc.allProperties.push(...Object.keys(resolvedValue$1.originalSchema.properties));
1526
- return acc;
1527
- }, {
1528
- values: [],
1529
- imports: [],
1530
- schemas: [],
1531
- isEnum: [],
1532
- isRef: [],
1533
- types: [],
1534
- dependencies: [],
1535
- originalSchema: [],
1536
- allProperties: [],
1537
- hasReadonlyProps: false,
1538
- example: schema.example,
1539
- examples: resolveExampleRefs(schema.examples, context),
1540
- requiredProperties: separator$1 === "allOf" ? schema.required ?? [] : []
1541
- });
1542
- if (resolvedData.isEnum.every(Boolean) && name && items.length > 1 && context.output.override.enumGenerationType !== EnumGeneration.UNION) {
1543
- const { value: combinedEnumValue, valueImports, hasNull } = getCombinedEnumValue(resolvedData.values.map((value, index) => ({
1544
- value,
1545
- isRef: resolvedData.isRef[index],
1546
- schema: resolvedData.originalSchema[index]
1547
- })));
1548
- const newEnum = `export const ${pascal(name)} = ${combinedEnumValue}`;
1549
- const valueImportSet = new Set(valueImports);
1550
- const typeSuffix = `${nullable}${hasNull && !nullable.includes("null") ? " | null" : ""}`;
1551
- return {
1552
- value: `typeof ${pascal(name)}[keyof typeof ${pascal(name)}]${typeSuffix}`,
1553
- imports: [{ name: pascal(name) }],
1554
- schemas: [...resolvedData.schemas, {
1555
- imports: resolvedData.imports.filter((toImport) => valueImportSet.has(toImport.alias ?? toImport.name)).map((toImport) => ({
1556
- ...toImport,
1557
- values: true
1558
- })),
1559
- model: newEnum,
1560
- name
1561
- }],
1562
- isEnum: false,
1563
- type: "object",
1564
- isRef: false,
1565
- hasReadonlyProps: resolvedData.hasReadonlyProps,
1566
- dependencies: resolvedData.dependencies,
1567
- example: schema.example,
1568
- examples: resolveExampleRefs(schema.examples, context)
1569
- };
1570
- }
1571
- let resolvedValue;
1572
- if (schema.properties) resolvedValue = getScalar({
1573
- item: Object.fromEntries(Object.entries(schema).filter(([key]) => key !== separator$1)),
1574
- name,
1575
- context
1576
- });
1577
- else if (separator$1 === "allOf" && (schema.oneOf || schema.anyOf)) {
1578
- const siblingCombiner = schema.oneOf ? "oneOf" : "anyOf";
1579
- resolvedValue = combineSchemas({
1580
- schema: { [siblingCombiner]: schema[siblingCombiner] },
1581
- name,
1582
- separator: siblingCombiner,
1583
- context,
1584
- nullable: ""
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
1585
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;
1586
1753
  }
1587
1754
  return {
1588
- value: dedupeUnionType(combineValues({
1589
- resolvedData,
1590
- separator: separator$1,
1591
- resolvedValue,
1592
- context
1593
- }) + nullable),
1594
- imports: resolvedValue ? [...resolvedData.imports, ...resolvedValue.imports] : resolvedData.imports,
1595
- schemas: resolvedValue ? [...resolvedData.schemas, ...resolvedValue.schemas] : resolvedData.schemas,
1596
- dependencies: resolvedValue ? [...resolvedData.dependencies, ...resolvedValue.dependencies] : resolvedData.dependencies,
1597
- isEnum: false,
1598
- type: "object",
1599
- isRef: false,
1600
- hasReadonlyProps: resolvedData?.hasReadonlyProps || resolvedValue?.hasReadonlyProps || false,
1601
- example: schema.example,
1602
- examples: resolveExampleRefs(schema.examples, context)
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
+ }
1603
1770
  };
1604
1771
  }
1605
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
+ }
1794
+
1606
1795
  //#endregion
1607
1796
  //#region src/getters/keys.ts
1608
1797
  function getKey(key) {
@@ -1637,7 +1826,7 @@ function getPropertyNamesRecordType(item, valueType) {
1637
1826
  *
1638
1827
  * @param item item with type === "object"
1639
1828
  */
1640
- function getObject({ item, name, context, nullable, propertyOverrides }) {
1829
+ function getObject({ item, name, context, nullable, formDataContext }) {
1641
1830
  if (isReference(item)) {
1642
1831
  const { name: name$1 } = getRefInfo(item.$ref, context);
1643
1832
  return {
@@ -1658,7 +1847,8 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1658
1847
  name,
1659
1848
  separator: item.allOf ? "allOf" : item.oneOf ? "oneOf" : "anyOf",
1660
1849
  context,
1661
- nullable
1850
+ nullable,
1851
+ formDataContext
1662
1852
  });
1663
1853
  if (Array.isArray(item.type)) return combineSchemas({
1664
1854
  schema: { anyOf: item.type.map((type) => ({
@@ -1684,10 +1874,15 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1684
1874
  }
1685
1875
  const allSpecSchemas = context.spec.components?.schemas ?? {};
1686
1876
  if (Object.keys(allSpecSchemas).some((schemaName) => pascal(schemaName) === propName)) propName = propName + "Property";
1687
- 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({
1688
1882
  schema,
1689
1883
  propName,
1690
- context
1884
+ context,
1885
+ formDataContext: propertyFormDataContext
1691
1886
  });
1692
1887
  const isReadOnly = item.readOnly || schema.readOnly;
1693
1888
  if (!index) acc.value += "{";
@@ -1859,485 +2054,304 @@ function getObject({ item, name, context, nullable, propertyOverrides }) {
1859
2054
  }
1860
2055
 
1861
2056
  //#endregion
1862
- //#region src/getters/res-req-types.ts
1863
- const formDataContentTypes = new Set(["multipart/form-data"]);
1864
- const formUrlEncodedContentTypes = new Set(["application/x-www-form-urlencoded"]);
1865
- function getResReqContentTypes({ mediaType, propName, context, isFormData, contentType }) {
1866
- if (!mediaType.schema) return;
1867
- if (isFormData) {
1868
- const formDataResult = resolveFormDataRootObject({
1869
- schemaOrRef: mediaType.schema,
1870
- propName,
1871
- context,
1872
- encoding: mediaType.encoding
1873
- });
1874
- if (formDataResult) return formDataResult;
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";
1875
2071
  }
1876
- const resolvedObject = resolveObject({
1877
- schema: mediaType.schema,
1878
- propName,
1879
- context
1880
- });
1881
- if (!isFormData && isBinaryContentType(contentType)) return {
1882
- ...resolvedObject,
1883
- value: "Blob"
1884
- };
1885
- return resolvedObject;
1886
- }
1887
- function getResReqTypes(responsesOrRequests, name, context, defaultType = "unknown", uniqueKey = (item) => item.value) {
1888
- return uniqueBy(responsesOrRequests.filter(([_, res]) => Boolean(res)).map(([key, res]) => {
1889
- if (isReference(res)) {
1890
- const { schema: bodySchema, imports: [{ name: name$1, schemaName }] } = resolveRef(res, context);
1891
- const [contentType, mediaType] = Object.entries(bodySchema.content ?? {})[0] ?? [];
1892
- const isFormData = formDataContentTypes.has(contentType);
1893
- const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1894
- if (!isFormData && !isFormUrlEncoded || !mediaType.schema) return [{
1895
- value: name$1,
1896
- imports: [{
1897
- name: name$1,
1898
- schemaName
1899
- }],
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",
1900
2092
  schemas: [],
1901
- 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",
1902
2108
  isEnum: false,
1903
- isRef: true,
1904
- hasReadonlyProps: false,
1905
- originalSchema: mediaType?.schema,
1906
- example: mediaType?.example,
1907
- examples: resolveExampleRefs(mediaType?.examples, context),
1908
- key,
1909
- contentType
1910
- }];
1911
- const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1912
- name: name$1,
1913
- schemaObject: mediaType.schema,
1914
- context,
1915
- isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1916
- isRef: true,
1917
- encoding: mediaType.encoding
1918
- }) : void 0;
1919
- const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1920
- name: name$1,
1921
- schemaObject: mediaType.schema,
1922
- context,
1923
- isRequestBodyOptional: "required" in bodySchema && bodySchema.required === false,
1924
- isUrlEncoded: true,
1925
- isRef: true,
1926
- encoding: mediaType.encoding
1927
- }) : void 0;
1928
- const additionalImports = getFormDataAdditionalImports({
1929
- schemaObject: mediaType.schema,
1930
- context
1931
- });
1932
- return [{
1933
- value: name$1,
1934
- imports: [{
1935
- name: name$1,
1936
- schemaName
1937
- }, ...additionalImports],
1938
2109
  schemas: [],
1939
- type: "unknown",
1940
- isEnum: false,
1941
- hasReadonlyProps: false,
1942
- formData,
1943
- formUrlEncoded,
1944
- isRef: true,
1945
- originalSchema: mediaType.schema,
1946
- example: mediaType.example,
1947
- examples: resolveExampleRefs(mediaType.examples, context),
1948
- key,
1949
- contentType
1950
- }];
2110
+ imports: [],
2111
+ isRef: false,
2112
+ hasReadonlyProps: item.readOnly || false,
2113
+ dependencies: [],
2114
+ example: item.example,
2115
+ examples: resolveExampleRefs(item.examples, context)
2116
+ };
1951
2117
  }
1952
- if (res.content) return Object.entries(res.content).map(([contentType, mediaType], index, arr) => {
1953
- let propName = key ? pascal(name) + pascal(key) : void 0;
1954
- if (propName && arr.length > 1) propName = propName + pascal(getNumberWord(index + 1));
1955
- let effectivePropName = propName;
1956
- if (mediaType.schema && isReference(mediaType.schema)) {
1957
- const { imports } = resolveRef(mediaType.schema, context);
1958
- if (imports[0]?.name) effectivePropName = imports[0].name;
1959
- }
1960
- const isFormData = formDataContentTypes.has(contentType);
1961
- const resolvedValue = getResReqContentTypes({
1962
- mediaType,
1963
- propName: effectivePropName,
2118
+ case "array": {
2119
+ const { value, ...rest } = getArray({
2120
+ schema: item,
2121
+ name,
1964
2122
  context,
1965
- isFormData,
1966
- contentType
2123
+ formDataContext
1967
2124
  });
1968
- if (!resolvedValue) {
1969
- if (isBinaryContentType(contentType)) return {
1970
- value: "Blob",
1971
- imports: [],
1972
- schemas: [],
1973
- type: "Blob",
1974
- isEnum: false,
1975
- key,
1976
- isRef: false,
1977
- hasReadonlyProps: false,
1978
- contentType
1979
- };
1980
- return;
2125
+ return {
2126
+ value: value + nullable,
2127
+ ...rest,
2128
+ dependencies: rest.dependencies ?? []
2129
+ };
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;
1981
2137
  }
1982
- const isFormUrlEncoded = formUrlEncodedContentTypes.has(contentType);
1983
- if (!isFormData && !isFormUrlEncoded || !effectivePropName) return {
1984
- ...resolvedValue,
1985
- imports: resolvedValue.imports,
1986
- contentType,
1987
- example: mediaType.example,
1988
- examples: resolveExampleRefs(mediaType.examples, context)
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}'`;
2147
+ return {
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)
1989
2158
  };
1990
- const formData = isFormData ? getSchemaFormDataAndUrlEncoded({
1991
- name: effectivePropName,
1992
- schemaObject: mediaType.schema,
2159
+ }
2160
+ case "null": return {
2161
+ value: "null",
2162
+ isEnum: false,
2163
+ type: "null",
2164
+ imports: [],
2165
+ schemas: [],
2166
+ isRef: false,
2167
+ hasReadonlyProps: item.readOnly || false,
2168
+ dependencies: []
2169
+ };
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",
1993
2179
  context,
1994
- isRequestBodyOptional: "required" in res && res.required === false,
1995
- isRef: true,
1996
- encoding: mediaType.encoding
1997
- }) : void 0;
1998
- const formUrlEncoded = isFormUrlEncoded ? getSchemaFormDataAndUrlEncoded({
1999
- name: effectivePropName,
2000
- schemaObject: mediaType.schema,
2180
+ nullable
2181
+ });
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,
2001
2198
  context,
2002
- isUrlEncoded: true,
2003
- isRequestBodyOptional: "required" in res && res.required === false,
2004
- isRef: true,
2005
- encoding: mediaType.encoding
2006
- }) : void 0;
2007
- const additionalImports = getFormDataAdditionalImports({
2008
- schemaObject: mediaType.schema,
2009
- context
2199
+ nullable,
2200
+ formDataContext: formDataContext?.atPart === false || formDataContext?.atPart && hasCombiners ? formDataContext : void 0
2010
2201
  });
2011
2202
  return {
2012
- ...resolvedValue,
2013
- imports: [...resolvedValue.imports, ...additionalImports],
2014
- formData,
2015
- formUrlEncoded,
2016
- contentType,
2017
- example: mediaType.example,
2018
- examples: resolveExampleRefs(mediaType.examples, context)
2203
+ value,
2204
+ ...rest
2019
2205
  };
2020
- }).filter(Boolean).map((x) => ({
2021
- ...x,
2022
- key
2023
- }));
2024
- const swaggerSchema = "schema" in res ? res.schema : void 0;
2025
- if (swaggerSchema) return [{
2026
- ...resolveObject({
2027
- schema: swaggerSchema,
2028
- propName: key ? pascal(name) + pascal(key) : void 0,
2029
- context
2030
- }),
2031
- contentType: "application/json",
2032
- key
2033
- }];
2034
- return [{
2035
- value: defaultType,
2036
- imports: [],
2037
- schemas: [],
2038
- type: defaultType,
2039
- isEnum: false,
2040
- key,
2041
- isRef: false,
2042
- hasReadonlyProps: false,
2043
- contentType: "application/json"
2044
- }];
2045
- }).flat(), uniqueKey);
2206
+ }
2207
+ }
2046
2208
  }
2047
- function isBinaryContentType(contentType) {
2048
- if (contentType === "application/octet-stream") return true;
2049
- if (contentType.startsWith("image/")) return true;
2050
- if (contentType.startsWith("audio/")) return true;
2051
- if (contentType.startsWith("video/")) return true;
2052
- if (contentType.startsWith("font/")) return true;
2053
- if (contentType.startsWith("text/")) return false;
2054
- if ([
2055
- "+json",
2056
- "-json",
2057
- "+xml",
2058
- "-xml",
2059
- "+yaml",
2060
- "-yaml",
2061
- "+rss",
2062
- "-rss",
2063
- "+csv",
2064
- "-csv"
2065
- ].some((suffix) => contentType.includes(suffix))) return false;
2066
- return !new Set([
2067
- "application/json",
2068
- "application/xml",
2069
- "application/yaml",
2070
- "application/x-www-form-urlencoded",
2071
- "application/javascript",
2072
- "application/ecmascript",
2073
- "application/graphql"
2074
- ]).has(contentType);
2075
- }
2076
- /**
2077
- * Determine the response type category for a given content type.
2078
- * Used to set the correct responseType option in HTTP clients.
2079
- *
2080
- * @param contentType - The MIME content type (e.g., 'application/json', 'text/plain')
2081
- * @returns The response type category to use for parsing
2082
- */
2083
- function getResponseTypeCategory(contentType) {
2084
- if (isBinaryContentType(contentType)) return "blob";
2085
- if (contentType === "application/json" || contentType.includes("+json") || contentType.includes("-json")) return "json";
2086
- return "text";
2087
- }
2088
- /**
2089
- * Get the default content type from a list of content types.
2090
- * Priority: application/json > any JSON-like type > first in list
2091
- *
2092
- * @param contentTypes - Array of content types from OpenAPI spec
2093
- * @returns The default content type to use
2094
- */
2095
- function getDefaultContentType(contentTypes) {
2096
- if (contentTypes.length === 0) return "application/json";
2097
- if (contentTypes.includes("application/json")) return "application/json";
2098
- const jsonType = contentTypes.find((ct) => ct.includes("+json") || ct.includes("-json"));
2099
- if (jsonType) return jsonType;
2100
- return contentTypes[0];
2101
- }
2102
- /**
2103
- * Determine if a form-data root field should be treated as binary or text file
2104
- * based on encoding.contentType or contentMediaType.
2105
- *
2106
- * Returns:
2107
- * - 'binary': field is a binary file (Blob in types, File in zod)
2108
- * - 'text': field is a text file that can accept string (Blob | string in types, File | string in zod)
2109
- * - undefined: no override, use standard resolution
2110
- */
2111
- function getFormDataFieldFileType(resolvedSchema, encodingContentType) {
2112
- if (resolvedSchema.type !== "string") return;
2113
- if (resolvedSchema.contentEncoding) return;
2114
- const effectiveContentType = encodingContentType ?? resolvedSchema.contentMediaType;
2115
- if (effectiveContentType) return isBinaryContentType(effectiveContentType) ? "binary" : "text";
2116
- }
2117
- /**
2118
- * Resolve form-data root object with file type overrides.
2119
- * Returns undefined if no file type overrides needed (caller should use normal resolution).
2120
- */
2121
- function resolveFormDataRootObject({ schemaOrRef, propName, context, encoding }) {
2122
- const { schema } = resolveRef(schemaOrRef, context);
2123
- if (!schema.properties) return;
2124
- const propertyOverrides = {};
2125
- for (const key of Object.keys(schema.properties)) {
2126
- const propSchema = schema.properties[key];
2127
- const { schema: resolvedSchema } = resolveRef(propSchema, context);
2128
- const fileType = getFormDataFieldFileType(resolvedSchema, encoding?.[key]?.contentType);
2129
- if (fileType) propertyOverrides[key] = {
2130
- ...getScalar({
2131
- item: resolvedSchema,
2132
- name: propName,
2133
- context
2134
- }),
2135
- value: fileType === "binary" ? "Blob" : "Blob | string"
2136
- };
2137
- }
2138
- if (Object.keys(propertyOverrides).length === 0) return;
2139
- const result = getObject({
2140
- item: schema,
2141
- name: propName,
2142
- context,
2143
- nullable: "",
2144
- propertyOverrides
2145
- });
2146
- return createTypeAliasIfNeeded({
2147
- resolvedValue: {
2148
- ...result,
2149
- originalSchema: schema
2150
- },
2151
- propName,
2152
- context
2153
- }) ?? result;
2154
- }
2155
- function getFormDataAdditionalImports({ schemaObject, context }) {
2156
- const { schema } = resolveRef(schemaObject, context);
2157
- if (schema.type !== "object") return [];
2158
- const combinedSchemas = schema.oneOf || schema.anyOf;
2159
- if (!combinedSchemas) return [];
2160
- return combinedSchemas.map((schema$1) => resolveRef(schema$1, context).imports[0]).filter(Boolean);
2161
- }
2162
- function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequestBodyOptional, isUrlEncoded, isRef, encoding }) {
2163
- const { schema, imports } = resolveRef(schemaObject, context);
2164
- const propName = camel(!isRef && isReference(schemaObject) ? imports[0].name : name);
2165
- const additionalImports = [];
2166
- const variableName = isUrlEncoded ? "formUrlEncoded" : "formData";
2167
- let form = isUrlEncoded ? `const ${variableName} = new URLSearchParams();\n` : `const ${variableName} = new FormData();\n`;
2168
- const combinedSchemas = schema.oneOf || schema.anyOf || schema.allOf;
2169
- if (schema.type === "object" || schema.type === void 0 && combinedSchemas) {
2170
- if (combinedSchemas) {
2171
- const shouldCast = !!schema.oneOf || !!schema.anyOf;
2172
- const combinedSchemasFormData = combinedSchemas.map((schema$1) => {
2173
- const { schema: combinedSchema, imports: imports$1 } = resolveRef(schema$1, context);
2174
- let newPropName = propName;
2175
- let newPropDefinition = "";
2176
- if (shouldCast && imports$1[0]) {
2177
- additionalImports.push(imports$1[0]);
2178
- newPropName = `${propName}${pascal(imports$1[0].name)}`;
2179
- newPropDefinition = `const ${newPropName} = (${propName} as ${imports$1[0].name}${isRequestBodyOptional ? " | undefined" : ""});\n`;
2180
- }
2181
- return newPropDefinition + resolveSchemaPropertiesToFormData({
2182
- schema: combinedSchema,
2183
- variableName,
2184
- propName: newPropName,
2185
- context,
2186
- isRequestBodyOptional,
2187
- encoding
2188
- });
2189
- }).filter(Boolean).join("\n");
2190
- form += combinedSchemasFormData;
2191
- }
2192
- if (schema.properties) {
2193
- const formDataValues = resolveSchemaPropertiesToFormData({
2194
- schema,
2195
- variableName,
2196
- propName,
2197
- context,
2198
- isRequestBodyOptional,
2199
- encoding
2200
- });
2201
- form += formDataValues;
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("' | '")}'>`;
2202
2219
  }
2203
- return form;
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;
2204
2225
  }
2205
- if (schema.type === "array") {
2206
- let valueStr = "value";
2207
- if (schema.items) {
2208
- const { schema: itemSchema } = resolveRef(schema.items, context);
2209
- if (itemSchema.type === "object" || itemSchema.type === "array") valueStr = "JSON.stringify(value)";
2210
- else if (itemSchema.type === "number" || itemSchema.type === "integer" || itemSchema.type === "boolean") valueStr = "value.toString()";
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;
2234
+ }
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("; ")}}` : ""}`);
2211
2237
  }
2212
- return `${form}${propName}.forEach(value => ${variableName}.append('data', ${valueStr}))\n`;
2213
2238
  }
2214
- if (schema.type === "number" || schema.type === "integer" || schema.type === "boolean") return `${form}${variableName}.append('data', ${propName}.toString())\n`;
2215
- return `${form}${variableName}.append('data', ${propName})\n`;
2239
+ if (resolvedValue) return `(${values.join(` & ${resolvedValue.value}) | (`)} & ${resolvedValue.value})`;
2240
+ return values.join(" | ");
2216
2241
  }
2217
- function resolveSchemaPropertiesToFormData({ schema, variableName, propName, context, isRequestBodyOptional, keyPrefix = "", depth = 0, encoding }) {
2218
- return Object.entries(schema.properties ?? {}).reduce((acc, [key, value]) => {
2219
- const { schema: property } = resolveRef(value, context);
2220
- if (property.readOnly) return acc;
2221
- let formDataValue = "";
2222
- const encodingContentType = (depth === 0 ? encoding?.[key] : void 0)?.contentType;
2223
- const formattedKeyPrefix = isRequestBodyOptional ? keyword.isIdentifierNameES5(key) ? "?" : "?." : "";
2224
- const formattedKey = keyword.isIdentifierNameES5(key) ? `.${key}` : `['${key}']`;
2225
- const valueKey = `${propName}${formattedKeyPrefix}${formattedKey}`;
2226
- const nonOptionalValueKey = `${propName}${formattedKey}`;
2227
- const fileType = getFormDataFieldFileType(property, encodingContentType);
2228
- const effectiveContentType = encodingContentType ?? property.contentMediaType;
2229
- if (fileType === "binary" || property.format === "binary") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
2230
- else if (fileType === "text") formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey} instanceof Blob ? ${nonOptionalValueKey} : new Blob([${nonOptionalValueKey}], { type: '${effectiveContentType}' }));\n`;
2231
- else if (property.type === "object") formDataValue = context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE ? resolveSchemaPropertiesToFormData({
2232
- schema: property,
2233
- variableName,
2234
- propName: nonOptionalValueKey,
2235
- context,
2236
- isRequestBodyOptional,
2237
- keyPrefix: `${keyPrefix}${key}.`,
2238
- depth: depth + 1,
2239
- encoding
2240
- }) : encodingContentType ? `${variableName}.append(\`${keyPrefix}${key}\`, new Blob([JSON.stringify(${nonOptionalValueKey})], { type: '${encodingContentType}' }));\n` : `${variableName}.append(\`${keyPrefix}${key}\`, JSON.stringify(${nonOptionalValueKey}));\n`;
2241
- else if (property.type === "array") {
2242
- let valueStr = "value";
2243
- let hasNonPrimitiveChild = false;
2244
- if (property.items) {
2245
- const { schema: itemSchema } = resolveRef(property.items, context);
2246
- if (itemSchema.type === "object" || itemSchema.type === "array") if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
2247
- hasNonPrimitiveChild = true;
2248
- const resolvedValue = resolveSchemaPropertiesToFormData({
2249
- schema: itemSchema,
2250
- variableName,
2251
- propName: "value",
2252
- context,
2253
- isRequestBodyOptional,
2254
- keyPrefix: `${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}].`,
2255
- depth: depth + 1
2256
- });
2257
- formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => {
2258
- ${resolvedValue}});\n`;
2259
- } else valueStr = "JSON.stringify(value)";
2260
- else if (itemSchema.type === "number" || itemSchema.type?.includes("number") || itemSchema.type === "integer" || itemSchema.type?.includes("integer") || itemSchema.type === "boolean" || itemSchema.type?.includes("boolean")) valueStr = "value.toString()";
2261
- }
2262
- if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
2263
- if (!hasNonPrimitiveChild) formDataValue = `${valueKey}.forEach((value, index${depth > 0 ? depth : ""}) => ${variableName}.append(\`${keyPrefix}${key}[\${index${depth > 0 ? depth : ""}}]\`, ${valueStr}));\n`;
2264
- } else formDataValue = `${valueKey}.forEach(value => ${variableName}.append(\`${keyPrefix}${key}${context.output.override.formData.arrayHandling === FormDataArrayHandling.SERIALIZE_WITH_BRACKETS ? "[]" : ""}\`, ${valueStr}));\n`;
2265
- } else if (property.type === "number" || property.type?.includes("number") || property.type === "integer" || property.type?.includes("integer") || property.type === "boolean" || property.type?.includes("boolean")) formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey}.toString())\n`;
2266
- else formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey});\n`;
2267
- let existSubSchemaNullable = false;
2268
- if (property.allOf || property.anyOf || property.oneOf) {
2269
- const subSchemas = (property.allOf || property.anyOf || property.oneOf)?.map((c) => resolveObject({
2270
- schema: c,
2271
- combined: true,
2272
- context
2273
- }));
2274
- if (subSchemas?.some((subSchema) => {
2275
- return [
2276
- "number",
2277
- "integer",
2278
- "boolean"
2279
- ].includes(subSchema.type);
2280
- })) formDataValue = `${variableName}.append(\`${key}\`, ${nonOptionalValueKey}.toString())\n`;
2281
- if (subSchemas?.some((subSchema) => {
2282
- return subSchema.type === "null";
2283
- })) existSubSchemaNullable = true;
2284
- }
2285
- const isRequired = schema.required?.includes(key) && !isRequestBodyOptional;
2286
- if (property.nullable || property.type?.includes("null") || existSubSchemaNullable) {
2287
- if (isRequired) return acc + `if(${valueKey} !== null) {\n ${formDataValue} }\n`;
2288
- return acc + `if(${valueKey} !== undefined && ${nonOptionalValueKey} !== null) {\n ${formDataValue} }\n`;
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));
2289
2249
  }
2290
- if (isRequired) return acc + formDataValue;
2291
- return acc + `if(${valueKey} !== undefined) {\n ${formDataValue} }\n`;
2292
- }, "");
2293
- }
2294
-
2295
- //#endregion
2296
- //#region src/getters/body.ts
2297
- function getBody({ requestBody, operationName, context, contentType }) {
2298
- const allBodyTypes = getResReqTypes([[context.output.override.components.requestBodies.suffix, requestBody]], operationName, context);
2299
- const filteredBodyTypes = contentType ? allBodyTypes.filter((type) => {
2300
- let include = true;
2301
- let exclude = false;
2302
- if (contentType.include) include = contentType.include.includes(type.contentType);
2303
- if (contentType.exclude) exclude = contentType.exclude.includes(type.contentType);
2304
- return include && !exclude;
2305
- }) : allBodyTypes;
2306
- const imports = filteredBodyTypes.flatMap(({ imports: imports$1 }) => imports$1);
2307
- const schemas = filteredBodyTypes.flatMap(({ schemas: schemas$1 }) => schemas$1);
2308
- const definition = filteredBodyTypes.map(({ value }) => value).join(" | ");
2309
- const nonReadonlyDefinition = filteredBodyTypes.some((x) => x.hasReadonlyProps) && definition ? `NonReadonly<${definition}>` : definition;
2310
- let implementation = generalJSTypesWithArray.includes(definition.toLowerCase()) || filteredBodyTypes.length > 1 ? camel(operationName) + context.output.override.components.requestBodies.suffix : camel(definition);
2311
- let isOptional = false;
2312
- if (implementation) {
2313
- implementation = sanitize(implementation, {
2314
- underscore: "_",
2315
- whitespace: "_",
2316
- dash: true,
2317
- es5keyword: true,
2318
- es5IdentifierName: true
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: ""
2319
2337
  });
2320
- if (isReference(requestBody)) {
2321
- const { schema: bodySchema } = resolveRef(requestBody, context);
2322
- if (bodySchema.required !== void 0) isOptional = !bodySchema.required;
2323
- } else if (requestBody.required !== void 0) isOptional = !requestBody.required;
2324
2338
  }
2325
2339
  return {
2326
- originalSchema: requestBody,
2327
- definition: nonReadonlyDefinition,
2328
- implementation,
2329
- imports,
2330
- schemas,
2331
- isOptional,
2332
- ...filteredBodyTypes.length === 1 ? {
2333
- formData: filteredBodyTypes[0].formData,
2334
- formUrlEncoded: filteredBodyTypes[0].formUrlEncoded,
2335
- contentType: filteredBodyTypes[0].contentType
2336
- } : {
2337
- formData: "",
2338
- formUrlEncoded: "",
2339
- contentType: ""
2340
- }
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)
2341
2355
  };
2342
2356
  }
2343
2357