@kubb/adapter-oas 5.16.0 → 5.17.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.js CHANGED
@@ -19,7 +19,7 @@ var compatibility = {
19
19
  "node": ">=22.22"
20
20
  };
21
21
  // package.json
22
- var version = "5.16.0";
22
+ var version = "5.17.0";
23
23
  // src/constants.ts
24
24
  /**
25
25
  * Default parser options applied when no explicit options are provided.
@@ -1204,6 +1204,30 @@ function resolveServerUrl(server, overrides) {
1204
1204
  }
1205
1205
  return url;
1206
1206
  }
1207
+ /**
1208
+ * Maps OpenAPI servers to `InputServer` entries: drops entries without a `url` and fills `{variable}` defaults.
1209
+ */
1210
+ function toInputServers(servers) {
1211
+ return servers.filter((entry) => entry.url).map(({ url, description, variables }) => {
1212
+ const declared = variables && Object.keys(variables).length > 0 ? { variables } : {};
1213
+ try {
1214
+ return {
1215
+ url: resolveServerUrl({
1216
+ url,
1217
+ variables
1218
+ }),
1219
+ description,
1220
+ ...declared
1221
+ };
1222
+ } catch {
1223
+ return {
1224
+ url,
1225
+ description,
1226
+ ...declared
1227
+ };
1228
+ }
1229
+ });
1230
+ }
1207
1231
  // src/model/mediaType.ts
1208
1232
  /**
1209
1233
  * The media type without its parameters, so `text/event-stream; charset=utf-8` matches the bare name.
@@ -1378,7 +1402,7 @@ function flattenSchema(schema) {
1378
1402
  /**
1379
1403
  * Links one freshly converted schema and its nested schemas to their RFC 6901 pointers
1380
1404
  * (`#/components/schemas/Pet/properties/owner/properties/name`), and reports the advisory
1381
- * diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) against them.
1405
+ * diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`, `KUBB_DUPLICATE_DISCRIMINATOR`) against them.
1382
1406
  * Refs are not followed, so the resolved schema is handled under its own walk.
1383
1407
  * Reports land in the active build run, are a no-op outside one, and repeats are deduped by the build.
1384
1408
  */
@@ -1423,11 +1447,55 @@ function visit(node, pointer) {
1423
1447
  for (const member of node.members ?? []) visit(member, `${pointer}/not`);
1424
1448
  return;
1425
1449
  }
1450
+ if (node.type === "union" && node.discriminatorPropertyName) reportDuplicateDiscriminators(node, node.discriminatorPropertyName, pointer);
1426
1451
  if (node.type === "union" || node.type === "intersection") {
1427
1452
  const keyword = node.type === "intersection" ? "allOf" : node.strategy === "any" ? "anyOf" : "oneOf";
1428
1453
  for (const [index, member] of (node.members ?? []).entries()) visit(member, `${pointer}/${keyword}/${index}`);
1429
1454
  }
1430
1455
  }
1456
+ /**
1457
+ * Reports a declared discriminator whose value pins more than one member. The spec cannot tell
1458
+ * those members apart, and Zod's `z.discriminatedUnion` throws on the first parse.
1459
+ * One diagnostic per union, since the build dedupes by code and pointer.
1460
+ */
1461
+ function reportDuplicateDiscriminators(node, propertyName, pointer) {
1462
+ const keyword = node.strategy === "any" ? "anyOf" : "oneOf";
1463
+ const membersByValue = /* @__PURE__ */ new Map();
1464
+ for (const [index, member] of (node.members ?? []).entries()) {
1465
+ const label = memberName(member) ?? `${keyword}/${index}`;
1466
+ for (const value of memberValues(member, propertyName)) membersByValue.set(value, [...membersByValue.get(value) ?? [], label]);
1467
+ }
1468
+ const duplicates = [...membersByValue].filter(([, members]) => members.length > 1);
1469
+ if (!duplicates.length) return;
1470
+ Diagnostics.report({
1471
+ code: Diagnostics.code.duplicateDiscriminator,
1472
+ severity: "warning",
1473
+ message: duplicates.map(([value, members]) => `Discriminator "${propertyName}" value ${JSON.stringify(value)} matches more than one member (${members.join(", ")}).`).join(" "),
1474
+ help: `Give each member its own "${propertyName}" value, or drop the discriminator. Zod's z.discriminatedUnion throws on the first parse of this union.`,
1475
+ location: {
1476
+ kind: "schema",
1477
+ pointer
1478
+ }
1479
+ });
1480
+ }
1481
+ function memberValues(node, propertyName, seen = /* @__PURE__ */ new Set()) {
1482
+ if (seen.has(node)) return [];
1483
+ seen.add(node);
1484
+ const target = node.type === "ref" && node.schema ? node.schema : node;
1485
+ if (target.type === "union") return [...new Set(target.members?.flatMap((member) => memberValues(member, propertyName, seen)))];
1486
+ return intersect(ast.resolveSchemaProperties({
1487
+ node,
1488
+ propertyName
1489
+ }).map((property) => [...ast.getSchemaLiteralValues(property.schema)]));
1490
+ }
1491
+ function intersect(slices) {
1492
+ const pinned = slices.filter((values) => values.length > 0);
1493
+ return pinned.reduce((acc, values) => acc.filter((value) => values.includes(value)), pinned[0] ?? []);
1494
+ }
1495
+ function memberName(member) {
1496
+ if (member.type === "ref") return member.name ?? void 0;
1497
+ if (member.type === "intersection") return member.members?.map(memberName).find(Boolean);
1498
+ }
1431
1499
  // src/operation.ts
1432
1500
  function slugify(value) {
1433
1501
  return value.replace(/[^a-zA-Z0-9]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
@@ -1768,6 +1836,7 @@ function parseOperation({ operation, ...ctx }) {
1768
1836
  const fallback = operation.pathItem[key];
1769
1837
  return typeof fallback === "string" ? fallback : void 0;
1770
1838
  };
1839
+ const servers = operation.schema.servers ?? operation.pathItem.servers;
1771
1840
  return ast.factory.createOperation({
1772
1841
  pointer,
1773
1842
  operationId,
@@ -1781,7 +1850,8 @@ function parseOperation({ operation, ...ctx }) {
1781
1850
  parameters,
1782
1851
  requestBody,
1783
1852
  responses,
1784
- extensions: pickExtensions(operation.schema)
1853
+ extensions: pickExtensions(operation.schema),
1854
+ ...servers && { servers: toInputServers(servers) }
1785
1855
  });
1786
1856
  }
1787
1857
  // src/emit/createNode.ts
@@ -3340,25 +3410,7 @@ const adapterOas = defineExtension({
3340
3410
  document,
3341
3411
  server
3342
3412
  }),
3343
- servers: (document.servers ?? []).filter((entry) => entry.url).map(({ url, description, variables }) => {
3344
- const declared = variables && Object.keys(variables).length > 0 ? { variables } : {};
3345
- try {
3346
- return {
3347
- url: resolveServerUrl({
3348
- url,
3349
- variables
3350
- }),
3351
- description,
3352
- ...declared
3353
- };
3354
- } catch {
3355
- return {
3356
- url,
3357
- description,
3358
- ...declared
3359
- };
3360
- }
3361
- }),
3413
+ servers: toInputServers(document.servers ?? []),
3362
3414
  securitySchemes: Object.entries(document.components?.securitySchemes ?? {}).flatMap(([name, value]) => {
3363
3415
  const scheme = refs.deref(value);
3364
3416
  return scheme?.type ? [{