@kubb/adapter-oas 5.0.0-beta.63 → 5.0.0-beta.65

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.cjs CHANGED
@@ -26,6 +26,7 @@ let node_path = require("node:path");
26
26
  node_path = __toESM(node_path, 1);
27
27
  let node_fs_promises = require("node:fs/promises");
28
28
  let _readme_openapi_parser = require("@readme/openapi-parser");
29
+ let _scalar_openapi_upgrader = require("@scalar/openapi-upgrader");
29
30
  let yaml = require("yaml");
30
31
  let api_ref_bundler = require("api-ref-bundler");
31
32
  let _kubb_ast_macros = require("@kubb/ast/macros");
@@ -75,18 +76,6 @@ const SUPPORTED_METHODS = new Set([
75
76
  "trace"
76
77
  ]);
77
78
  /**
78
- * OpenAPI version string written into the stub document created during multi-spec merges.
79
- */
80
- const MERGE_OPENAPI_VERSION = "3.0.0";
81
- /**
82
- * Fallback `info.title` placed in the stub document when merging multiple API files.
83
- */
84
- const MERGE_DEFAULT_TITLE = "Merged API";
85
- /**
86
- * Fallback `info.version` placed in the stub document when merging multiple API files.
87
- */
88
- const MERGE_DEFAULT_VERSION = "1.0.0";
89
- /**
90
79
  * Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
91
80
  *
92
81
  * A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
@@ -166,15 +155,6 @@ const formatMap = {
166
155
  * ```
167
156
  */
168
157
  const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
169
- /**
170
- * Maps the `unknownType`/`emptySchemaType` option string to its `ScalarSchemaType` constant.
171
- * A `Map` (over a plain object) lets callers test key membership with `.has()`.
172
- */
173
- const typeOptionMap = new Map([
174
- ["any", _kubb_core.ast.schemaTypes.any],
175
- ["unknown", _kubb_core.ast.schemaTypes.unknown],
176
- ["void", _kubb_core.ast.schemaTypes.void]
177
- ]);
178
158
  //#endregion
179
159
  //#region ../../internals/utils/src/casing.ts
180
160
  /**
@@ -315,40 +295,6 @@ async function read(path) {
315
295
  return (0, node_fs_promises.readFile)(path, { encoding: "utf8" });
316
296
  }
317
297
  //#endregion
318
- //#region ../../internals/utils/src/object.ts
319
- /**
320
- * Returns `true` when `value` is a plain (non-null, non-array) object.
321
- *
322
- * @example
323
- * ```ts
324
- * isPlainObject({}) // true
325
- * isPlainObject([]) // false
326
- * isPlainObject(null) // false
327
- * ```
328
- */
329
- function isPlainObject(value) {
330
- return typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
331
- }
332
- /**
333
- * Recursively merges `source` into `target`, combining nested plain objects.
334
- * Arrays and non-object values from `source` override the corresponding values in `target`.
335
- *
336
- * @example
337
- * ```ts
338
- * mergeDeep({ a: { x: 1 } }, { a: { y: 2 } })
339
- * // { a: { x: 1, y: 2 } }
340
- * ```
341
- */
342
- function mergeDeep(target, source) {
343
- const result = { ...target };
344
- for (const key of Object.keys(source)) {
345
- const sv = source[key];
346
- const tv = result[key];
347
- result[key] = sv !== null && typeof sv === "object" && !Array.isArray(sv) && tv !== null && typeof tv === "object" && !Array.isArray(tv) ? mergeDeep(tv, sv) : sv;
348
- }
349
- return result;
350
- }
351
- //#endregion
352
298
  //#region ../../internals/utils/src/reserved.ts
353
299
  /**
354
300
  * JavaScript and Java reserved words.
@@ -596,128 +542,29 @@ async function bundleDocument(pathOrUrl) {
596
542
  return await (0, api_ref_bundler.bundle)(pathOrUrl, resolver);
597
543
  }
598
544
  //#endregion
599
- //#region src/guards.ts
600
- /**
601
- * Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
602
- *
603
- * @example
604
- * ```ts
605
- * if (isOpenApiV2Document(doc)) {
606
- * // doc is OpenAPIV2.Document
607
- * }
608
- * ```
609
- */
610
- function isOpenApiV2Document(doc) {
611
- return !!doc && isPlainObject(doc) && !("openapi" in doc);
612
- }
613
- /**
614
- * Returns `true` when a schema should be treated as nullable.
615
- *
616
- * Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
617
- * `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
618
- *
619
- * @example
620
- * ```ts
621
- * isNullable({ type: 'string', nullable: true }) // true
622
- * isNullable({ type: ['string', 'null'] }) // true
623
- * isNullable({ type: 'string' }) // false
624
- * ```
625
- */
626
- function isNullable(schema) {
627
- if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
628
- const schemaType = schema?.type;
629
- if (schemaType === "null") return true;
630
- if (Array.isArray(schemaType)) return schemaType.includes("null");
631
- return false;
632
- }
633
- /**
634
- * Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
635
- *
636
- * @example
637
- * ```ts
638
- * isReference({ $ref: '#/components/schemas/Pet' }) // true
639
- * isReference({ type: 'string' }) // false
640
- * ```
641
- */
642
- function isReference(obj) {
643
- return !!obj && typeof obj === "object" && "$ref" in obj;
644
- }
645
- /**
646
- * Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object.
647
- *
648
- * @example
649
- * ```ts
650
- * isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
651
- * isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
652
- * ```
653
- */
654
- function isDiscriminator(obj) {
655
- const record = obj;
656
- return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
657
- }
658
- //#endregion
659
545
  //#region src/factory.ts
660
546
  /**
661
547
  * Loads and bundles an OpenAPI document, returning the raw `Document`.
662
548
  *
663
- * Accepts a file path string or an already-parsed document object. File paths and URLs are
664
- * bundled via `api-ref-bundler`, hoisting external file schemas into named `components.schemas`
665
- * entries so generators can emit named types and imports. Swagger 2.0 documents are
666
- * automatically up-converted to OpenAPI 3.0 via `swagger2openapi`.
549
+ * A string is a file path or URL: it is bundled via `api-ref-bundler`, hoisting external file
550
+ * schemas into named `components.schemas` entries so generators can emit named types and imports.
551
+ * An object is treated as an already-parsed document. Swagger 2.0 and OpenAPI 3.0 documents are
552
+ * up-converted to OpenAPI 3.1 via `@scalar/openapi-upgrader`.
667
553
  *
668
554
  * @example
669
555
  * ```ts
670
556
  * const document = await parseDocument('./openapi.yaml')
671
- * const document = await parse(rawDocumentObject, { canBundle: false })
672
- * ```
673
- */
674
- async function parseDocument(pathOrApi, { canBundle = true } = {}) {
675
- if (typeof pathOrApi === "string" && canBundle) return parseDocument(await bundleDocument(pathOrApi), { canBundle: false });
676
- const document = typeof pathOrApi === "string" ? (0, yaml.parse)(pathOrApi) : pathOrApi;
677
- if (isOpenApiV2Document(document)) {
678
- const { default: swagger2openapi } = await import("swagger2openapi");
679
- const { openapi } = await swagger2openapi.convertObj(document, { anchors: true });
680
- return openapi;
681
- }
682
- return document;
683
- }
684
- /**
685
- * Deep-merges multiple OpenAPI documents into a single `Document`.
686
- *
687
- * Each document is parsed independently, then deep-merged into one in array order.
688
- * Throws when the input array is empty.
689
- *
690
- * @example
691
- * ```ts
692
- * const document = await mergeDocuments(['./pets.yaml', './orders.yaml'])
557
+ * const document = await parseDocument(rawDocumentObject)
693
558
  * ```
694
559
  */
695
- async function mergeDocuments(pathOrApi) {
696
- const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, { canBundle: false })));
697
- if (documents.length === 0) throw new _kubb_core.Diagnostics.Error({
698
- code: _kubb_core.Diagnostics.code.inputRequired,
699
- severity: "error",
700
- message: "No OAS documents were provided for merging.",
701
- help: "Pass at least one path or document to `input.path`.",
702
- location: { kind: "config" }
703
- });
704
- const seed = {
705
- openapi: MERGE_OPENAPI_VERSION,
706
- info: {
707
- title: MERGE_DEFAULT_TITLE,
708
- version: MERGE_DEFAULT_VERSION
709
- },
710
- paths: {},
711
- components: { schemas: {} }
712
- };
713
- return parseDocument(documents.reduce((acc, current) => mergeDeep(acc, current), seed));
560
+ async function parseDocument(pathOrApi) {
561
+ if (typeof pathOrApi === "string") return parseDocument(await bundleDocument(pathOrApi));
562
+ return (0, _scalar_openapi_upgrader.upgrade)(pathOrApi, "3.1");
714
563
  }
715
564
  /**
716
565
  * Creates a `Document` from an `AdapterSource`.
717
566
  *
718
- * Handles all three source types:
719
567
  * - `{ type: 'path' }` resolves and bundles a local file path or remote URL.
720
- * - `{ type: 'paths' }` merges multiple file paths into a single document.
721
568
  * - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
722
569
  *
723
570
  * @example
@@ -727,11 +574,7 @@ async function mergeDocuments(pathOrApi) {
727
574
  * ```
728
575
  */
729
576
  async function parseFromConfig(source) {
730
- if (source.type === "data") {
731
- if (typeof source.data === "object") return parseDocument(structuredClone(source.data));
732
- return parseDocument(source.data, { canBundle: false });
733
- }
734
- if (source.type === "paths") return mergeDocuments(source.paths);
577
+ if (source.type === "data") return parseDocument(typeof source.data === "string" ? (0, yaml.parse)(source.data) : structuredClone(source.data));
735
578
  if (Url.canParse(source.path)) return parseDocument(source.path);
736
579
  const resolved = node_path.default.resolve(node_path.default.dirname(source.path), source.path);
737
580
  await assertInputExists(resolved);
@@ -790,7 +633,7 @@ function createRefNode(node, target) {
790
633
  deprecated: node.deprecated,
791
634
  description: node.description,
792
635
  default: node.default,
793
- example: node.example
636
+ examples: node.examples
794
637
  });
795
638
  }
796
639
  /**
@@ -931,6 +774,53 @@ function plan(roots, context) {
931
774
  };
932
775
  }
933
776
  //#endregion
777
+ //#region src/guards.ts
778
+ /**
779
+ * Returns `true` when a schema should be treated as nullable.
780
+ *
781
+ * Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
782
+ * `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
783
+ *
784
+ * @example
785
+ * ```ts
786
+ * isNullable({ type: 'string', nullable: true }) // true
787
+ * isNullable({ type: ['string', 'null'] }) // true
788
+ * isNullable({ type: 'string' }) // false
789
+ * ```
790
+ */
791
+ function isNullable(schema) {
792
+ if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
793
+ const schemaType = schema?.type;
794
+ if (schemaType === "null") return true;
795
+ if (Array.isArray(schemaType)) return schemaType.includes("null");
796
+ return false;
797
+ }
798
+ /**
799
+ * Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
800
+ *
801
+ * @example
802
+ * ```ts
803
+ * isReference({ $ref: '#/components/schemas/Pet' }) // true
804
+ * isReference({ type: 'string' }) // false
805
+ * ```
806
+ */
807
+ function isReference(obj) {
808
+ return !!obj && typeof obj === "object" && "$ref" in obj;
809
+ }
810
+ /**
811
+ * Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object.
812
+ *
813
+ * @example
814
+ * ```ts
815
+ * isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
816
+ * isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
817
+ * ```
818
+ */
819
+ function isDiscriminator(obj) {
820
+ const record = obj;
821
+ return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
822
+ }
823
+ //#endregion
934
824
  //#region src/refs.ts
935
825
  const _refCache = /* @__PURE__ */ new WeakMap();
936
826
  /**
@@ -1412,14 +1302,16 @@ function getResponseBody(responseBody, contentType) {
1412
1302
  * getResponseSchema(document, operation, '4XX') // {}
1413
1303
  * ```
1414
1304
  */
1415
- function getResponseSchema(document, operation, statusCode, options = {}) {
1416
- if (operation.schema.responses) {
1417
- const responses = operation.schema.responses;
1418
- for (const key in responses) {
1419
- const schema = responses[key];
1420
- if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
1421
- }
1305
+ function resolveResponseRefs(document, operation) {
1306
+ const responses = operation.schema.responses;
1307
+ if (!responses) return;
1308
+ for (const key in responses) {
1309
+ const schema = responses[key];
1310
+ if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
1422
1311
  }
1312
+ }
1313
+ function getResponseSchema(document, operation, statusCode, options = {}) {
1314
+ resolveResponseRefs(document, operation);
1423
1315
  const responseBody = getResponseBody(getResponseByStatusCode({
1424
1316
  document,
1425
1317
  operation,
@@ -1563,9 +1455,6 @@ const semanticSuffixes = {
1563
1455
  responses: "Response",
1564
1456
  requestBodies: "Request"
1565
1457
  };
1566
- function getSemanticSuffix(source) {
1567
- return semanticSuffixes[source];
1568
- }
1569
1458
  function resolveSchemaRef(document, schema) {
1570
1459
  if (!isReference(schema)) return schema;
1571
1460
  const resolved = resolveRef(document, schema.$ref);
@@ -1620,7 +1509,7 @@ function getSchemas(document, { contentType }) {
1620
1509
  }
1621
1510
  }
1622
1511
  items.forEach((item, index) => {
1623
- const suffix = isSingle ? "" : hasMultipleSources ? getSemanticSuffix(item.source) : index === 0 ? "" : String(index + 1);
1512
+ const suffix = isSingle ? "" : hasMultipleSources ? semanticSuffixes[item.source] : index === 0 ? "" : String(index + 1);
1624
1513
  const uniqueName = item.originalName + suffix;
1625
1514
  schemas[uniqueName] = item.schema;
1626
1515
  nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
@@ -1667,6 +1556,15 @@ function getDateType(options, format) {
1667
1556
  /**
1668
1557
  * Collects the shared metadata fields passed to every `createSchema` call.
1669
1558
  */
1559
+ /**
1560
+ * Reads schema examples as an array. OAS 3.1 uses an `examples` array, but specs (including ones
1561
+ * labeled 3.1) still use the singular OAS 3.0 `example`, which the upgrader only converts on the
1562
+ * 3.0 -> 3.1 hop. Normalize both into one array so the AST node exposes only `examples`.
1563
+ */
1564
+ function extractExamples(schema) {
1565
+ if (Array.isArray(schema.examples)) return schema.examples;
1566
+ return schema.example !== void 0 ? [schema.example] : void 0;
1567
+ }
1670
1568
  function buildSchemaNode(schema, name, nullable, defaultValue) {
1671
1569
  return {
1672
1570
  name,
@@ -1677,7 +1575,7 @@ function buildSchemaNode(schema, name, nullable, defaultValue) {
1677
1575
  readOnly: schema.readOnly,
1678
1576
  writeOnly: schema.writeOnly,
1679
1577
  default: defaultValue,
1680
- example: schema.example,
1578
+ examples: extractExamples(schema),
1681
1579
  format: schema.format
1682
1580
  };
1683
1581
  }
@@ -1713,13 +1611,7 @@ function getRequestBodyContentTypes(document, operation) {
1713
1611
  * ```
1714
1612
  */
1715
1613
  function getResponseBodyContentTypes(document, operation, statusCode) {
1716
- if (operation.schema.responses) {
1717
- const responses = operation.schema.responses;
1718
- for (const key in responses) {
1719
- const schema = responses[key];
1720
- if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
1721
- }
1722
- }
1614
+ resolveResponseRefs(document, operation);
1723
1615
  const responseObj = getResponseByStatusCode({
1724
1616
  document,
1725
1617
  operation,
@@ -1863,7 +1755,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
1863
1755
  readOnly: schema.readOnly ?? memberNode.readOnly,
1864
1756
  writeOnly: schema.writeOnly ?? memberNode.writeOnly,
1865
1757
  default: mergedDefault,
1866
- example: schema.example ?? memberNode.example,
1758
+ examples: extractExamples(schema) ?? memberNode.examples,
1867
1759
  pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
1868
1760
  format: schema.format ?? memberNode.format
1869
1761
  });
@@ -2107,7 +1999,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2107
1999
  readOnly: schema.readOnly,
2108
2000
  writeOnly: schema.writeOnly,
2109
2001
  default: enumDefault,
2110
- example: schema.example,
2002
+ examples: extractExamples(schema),
2111
2003
  format: schema.format
2112
2004
  };
2113
2005
  const extensionKey = enumExtensionKeys.find((key) => key in schema);
@@ -2165,10 +2057,10 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2165
2057
  if (additionalProperties === true) return true;
2166
2058
  if (additionalProperties === false) return false;
2167
2059
  if (additionalProperties && Object.keys(additionalProperties).length > 0) return parseSchema({ schema: additionalProperties }, rawOptions);
2168
- if (additionalProperties) return _kubb_core.ast.factory.createSchema({ type: typeOptionMap.get(options.unknownType) });
2060
+ if (additionalProperties) return _kubb_core.ast.factory.createSchema({ type: options.unknownType });
2169
2061
  })();
2170
2062
  const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
2171
- const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? _kubb_core.ast.factory.createSchema({ type: typeOptionMap.get(options.unknownType) }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
2063
+ const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? _kubb_core.ast.factory.createSchema({ type: options.unknownType }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
2172
2064
  const objectNode = _kubb_core.ast.factory.createSchema({
2173
2065
  type: "object",
2174
2066
  primitive: "object",
@@ -2444,7 +2336,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2444
2336
  const node = rule.convert(schemaCtx);
2445
2337
  if (node) return node;
2446
2338
  }
2447
- const emptyType = typeOptionMap.get(options.emptySchemaType);
2339
+ const emptyType = options.emptySchemaType;
2448
2340
  return _kubb_core.ast.factory.createSchema({
2449
2341
  type: emptyType,
2450
2342
  name,
@@ -2463,7 +2355,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2463
2355
  const schema = param["schema"] ? parseSchema({
2464
2356
  schema: param["schema"],
2465
2357
  name: schemaName
2466
- }, options) : _kubb_core.ast.factory.createSchema({ type: typeOptionMap.get(options.unknownType) });
2358
+ }, options) : _kubb_core.ast.factory.createSchema({ type: options.unknownType });
2467
2359
  return _kubb_core.ast.factory.createParameter({
2468
2360
  name: paramName,
2469
2361
  in: param["in"],
@@ -2551,7 +2443,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
2551
2443
  schema: raw && Object.keys(raw).length > 0 ? parseSchema({
2552
2444
  schema: raw,
2553
2445
  name: responseName
2554
- }, options) : _kubb_core.ast.factory.createSchema({ type: typeOptionMap.get(options.emptySchemaType) }),
2446
+ }, options) : _kubb_core.ast.factory.createSchema({ type: options.emptySchemaType }),
2555
2447
  keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
2556
2448
  };
2557
2449
  };