@kubb/oas 4.18.4 → 4.19.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.cjs +213 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -53
- package/dist/index.d.ts +66 -53
- package/dist/index.js +213 -33
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/Oas.ts +77 -50
- package/src/index.ts +1 -0
- package/src/utils.spec.ts +298 -11
- package/src/utils.ts +276 -13
package/dist/index.cjs
CHANGED
|
@@ -52,6 +52,7 @@ let oas = require("oas");
|
|
|
52
52
|
oas = __toESM(oas);
|
|
53
53
|
let node_path = require("node:path");
|
|
54
54
|
node_path = __toESM(node_path);
|
|
55
|
+
let _kubb_core_transformers = require("@kubb/core/transformers");
|
|
55
56
|
let _kubb_core_utils = require("@kubb/core/utils");
|
|
56
57
|
let oas_types = require("oas/types");
|
|
57
58
|
let oas_normalize = require("oas-normalize");
|
|
@@ -4370,26 +4371,25 @@ function isParameterObject(obj) {
|
|
|
4370
4371
|
return obj && "in" in obj;
|
|
4371
4372
|
}
|
|
4372
4373
|
/**
|
|
4373
|
-
* Determines if a schema is nullable, considering
|
|
4374
|
-
*
|
|
4375
|
-
*
|
|
4376
|
-
* @returns `true` if the schema is marked as nullable; otherwise, `false`.
|
|
4374
|
+
* Determines if a schema is nullable, considering:
|
|
4375
|
+
* - OpenAPI 3.0 `nullable` / `x-nullable`
|
|
4376
|
+
* - OpenAPI 3.1 JSON Schema `type: ['null', ...]` or `type: 'null'`
|
|
4377
4377
|
*/
|
|
4378
4378
|
function isNullable(schema) {
|
|
4379
|
-
|
|
4379
|
+
if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
|
|
4380
|
+
const schemaType = schema?.type;
|
|
4381
|
+
if (schemaType === "null") return true;
|
|
4382
|
+
if (Array.isArray(schemaType)) return schemaType.includes("null");
|
|
4383
|
+
return false;
|
|
4380
4384
|
}
|
|
4381
4385
|
/**
|
|
4382
4386
|
* Determines if the given object is an OpenAPI ReferenceObject.
|
|
4383
|
-
*
|
|
4384
|
-
* @returns True if {@link obj} is a ReferenceObject; otherwise, false.
|
|
4385
4387
|
*/
|
|
4386
4388
|
function isReference(obj) {
|
|
4387
4389
|
return !!obj && (0, oas_types.isRef)(obj);
|
|
4388
4390
|
}
|
|
4389
4391
|
/**
|
|
4390
4392
|
* Determines if the given object is a SchemaObject with a discriminator property of type DiscriminatorObject.
|
|
4391
|
-
*
|
|
4392
|
-
* @returns True if {@link obj} is a SchemaObject containing a non-string {@link discriminator} property.
|
|
4393
4393
|
*/
|
|
4394
4394
|
function isDiscriminator(obj) {
|
|
4395
4395
|
return !!obj && obj?.["discriminator"] && typeof obj.discriminator !== "string";
|
|
@@ -4398,9 +4398,6 @@ function isDiscriminator(obj) {
|
|
|
4398
4398
|
* Determines whether a schema is required.
|
|
4399
4399
|
*
|
|
4400
4400
|
* Returns true if the schema has a non-empty {@link SchemaObject.required} array or a truthy {@link SchemaObject.required} property.
|
|
4401
|
-
*
|
|
4402
|
-
* @param schema - The schema object to check.
|
|
4403
|
-
* @returns True if the schema is required; otherwise, false.
|
|
4404
4401
|
*/
|
|
4405
4402
|
function isRequired(schema) {
|
|
4406
4403
|
if (!schema) return false;
|
|
@@ -4496,6 +4493,149 @@ function parseFromConfig(config, oasClass = Oas) {
|
|
|
4496
4493
|
if (new _kubb_core_utils.URLPath(config.input.path).isURL) return parse(config.input.path, { oasClass });
|
|
4497
4494
|
return parse(node_path.default.resolve(config.root, config.input.path), { oasClass });
|
|
4498
4495
|
}
|
|
4496
|
+
/**
|
|
4497
|
+
* Flatten allOf schemas by merging keyword-only fragments.
|
|
4498
|
+
* Only flattens schemas where allOf items don't contain structural keys or $refs.
|
|
4499
|
+
*/
|
|
4500
|
+
function flattenSchema(schema) {
|
|
4501
|
+
if (!schema?.allOf || schema.allOf.length === 0) return schema || null;
|
|
4502
|
+
if (schema.allOf.some((item) => (0, oas_types.isRef)(item))) return schema;
|
|
4503
|
+
const isPlainFragment = (item) => !Object.keys(item).some((key) => STRUCTURAL_KEYS.has(key));
|
|
4504
|
+
if (!schema.allOf.every((item) => isPlainFragment(item))) return schema;
|
|
4505
|
+
const merged = { ...schema };
|
|
4506
|
+
delete merged.allOf;
|
|
4507
|
+
for (const fragment of schema.allOf) for (const [key, value] of Object.entries(fragment)) if (merged[key] === void 0) merged[key] = value;
|
|
4508
|
+
return merged;
|
|
4509
|
+
}
|
|
4510
|
+
/**
|
|
4511
|
+
* Validate an OpenAPI document using oas-normalize.
|
|
4512
|
+
*/
|
|
4513
|
+
async function validate(document) {
|
|
4514
|
+
return new oas_normalize.default(document, {
|
|
4515
|
+
enablePaths: true,
|
|
4516
|
+
colorizeErrors: true
|
|
4517
|
+
}).validate({ parser: { validate: { errors: { colorize: true } } } });
|
|
4518
|
+
}
|
|
4519
|
+
/**
|
|
4520
|
+
* Collect all schema $ref dependencies recursively.
|
|
4521
|
+
*/
|
|
4522
|
+
function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
|
|
4523
|
+
if (Array.isArray(schema)) {
|
|
4524
|
+
for (const item of schema) collectRefs(item, refs);
|
|
4525
|
+
return refs;
|
|
4526
|
+
}
|
|
4527
|
+
if (schema && typeof schema === "object") for (const [key, value] of Object.entries(schema)) if (key === "$ref" && typeof value === "string") {
|
|
4528
|
+
const match = value.match(/^#\/components\/schemas\/(.+)$/);
|
|
4529
|
+
if (match) refs.add(match[1]);
|
|
4530
|
+
} else collectRefs(value, refs);
|
|
4531
|
+
return refs;
|
|
4532
|
+
}
|
|
4533
|
+
/**
|
|
4534
|
+
* Sort schemas topologically so referenced schemas appear first.
|
|
4535
|
+
*/
|
|
4536
|
+
function sortSchemas(schemas) {
|
|
4537
|
+
const deps = /* @__PURE__ */ new Map();
|
|
4538
|
+
for (const [name, schema] of Object.entries(schemas)) deps.set(name, Array.from(collectRefs(schema)));
|
|
4539
|
+
const sorted = [];
|
|
4540
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4541
|
+
function visit(name, stack = /* @__PURE__ */ new Set()) {
|
|
4542
|
+
if (visited.has(name)) return;
|
|
4543
|
+
if (stack.has(name)) return;
|
|
4544
|
+
stack.add(name);
|
|
4545
|
+
const children = deps.get(name) || [];
|
|
4546
|
+
for (const child of children) if (deps.has(child)) visit(child, stack);
|
|
4547
|
+
stack.delete(name);
|
|
4548
|
+
visited.add(name);
|
|
4549
|
+
sorted.push(name);
|
|
4550
|
+
}
|
|
4551
|
+
for (const name of Object.keys(schemas)) visit(name);
|
|
4552
|
+
const sortedSchemas = {};
|
|
4553
|
+
for (const name of sorted) sortedSchemas[name] = schemas[name];
|
|
4554
|
+
return sortedSchemas;
|
|
4555
|
+
}
|
|
4556
|
+
/**
|
|
4557
|
+
* Extract schema from content object (used by responses and requestBodies).
|
|
4558
|
+
* Returns null if the schema is just a $ref (not a unique type definition).
|
|
4559
|
+
*/
|
|
4560
|
+
function extractSchemaFromContent(content, preferredContentType) {
|
|
4561
|
+
if (!content) return null;
|
|
4562
|
+
const firstContentType = Object.keys(content)[0] || "application/json";
|
|
4563
|
+
const schema = content[preferredContentType || firstContentType]?.schema;
|
|
4564
|
+
if (schema && "$ref" in schema) return null;
|
|
4565
|
+
return schema || null;
|
|
4566
|
+
}
|
|
4567
|
+
/**
|
|
4568
|
+
* Get semantic suffix for a schema source.
|
|
4569
|
+
*/
|
|
4570
|
+
function getSemanticSuffix(source) {
|
|
4571
|
+
switch (source) {
|
|
4572
|
+
case "schemas": return "Schema";
|
|
4573
|
+
case "responses": return "Response";
|
|
4574
|
+
case "requestBodies": return "Request";
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4577
|
+
/**
|
|
4578
|
+
* Legacy resolution strategy - no collision detection, just use original names.
|
|
4579
|
+
* This preserves backward compatibility when collisionDetection is false.
|
|
4580
|
+
* @deprecated
|
|
4581
|
+
*/
|
|
4582
|
+
function legacyResolve(schemasWithMeta) {
|
|
4583
|
+
const schemas = {};
|
|
4584
|
+
const nameMapping = /* @__PURE__ */ new Map();
|
|
4585
|
+
for (const item of schemasWithMeta) {
|
|
4586
|
+
schemas[item.originalName] = item.schema;
|
|
4587
|
+
const refPath = `#/components/${item.source}/${item.originalName}`;
|
|
4588
|
+
nameMapping.set(refPath, item.originalName);
|
|
4589
|
+
}
|
|
4590
|
+
return {
|
|
4591
|
+
schemas,
|
|
4592
|
+
nameMapping
|
|
4593
|
+
};
|
|
4594
|
+
}
|
|
4595
|
+
/**
|
|
4596
|
+
* Resolve name collisions by applying suffixes based on collision type.
|
|
4597
|
+
*
|
|
4598
|
+
* Strategy:
|
|
4599
|
+
* - Same-component collisions (e.g., "Variant" + "variant" both in schemas): numeric suffixes (Variant, Variant2)
|
|
4600
|
+
* - Cross-component collisions (e.g., "Pet" in schemas + "Pet" in requestBodies): semantic suffixes (PetSchema, PetRequest)
|
|
4601
|
+
*/
|
|
4602
|
+
function resolveCollisions(schemasWithMeta) {
|
|
4603
|
+
const schemas = {};
|
|
4604
|
+
const nameMapping = /* @__PURE__ */ new Map();
|
|
4605
|
+
const normalizedNames = /* @__PURE__ */ new Map();
|
|
4606
|
+
for (const item of schemasWithMeta) {
|
|
4607
|
+
const normalized = (0, _kubb_core_transformers.pascalCase)(item.originalName);
|
|
4608
|
+
if (!normalizedNames.has(normalized)) normalizedNames.set(normalized, []);
|
|
4609
|
+
normalizedNames.get(normalized).push(item);
|
|
4610
|
+
}
|
|
4611
|
+
for (const [, items] of normalizedNames) {
|
|
4612
|
+
if (items.length === 1) {
|
|
4613
|
+
const item = items[0];
|
|
4614
|
+
schemas[item.originalName] = item.schema;
|
|
4615
|
+
const refPath = `#/components/${item.source}/${item.originalName}`;
|
|
4616
|
+
nameMapping.set(refPath, item.originalName);
|
|
4617
|
+
continue;
|
|
4618
|
+
}
|
|
4619
|
+
if (new Set(items.map((item) => item.source)).size === 1) items.forEach((item, index) => {
|
|
4620
|
+
const suffix = index === 0 ? "" : (index + 1).toString();
|
|
4621
|
+
const uniqueName = item.originalName + suffix;
|
|
4622
|
+
schemas[uniqueName] = item.schema;
|
|
4623
|
+
const refPath = `#/components/${item.source}/${item.originalName}`;
|
|
4624
|
+
nameMapping.set(refPath, uniqueName);
|
|
4625
|
+
});
|
|
4626
|
+
else items.forEach((item) => {
|
|
4627
|
+
const suffix = getSemanticSuffix(item.source);
|
|
4628
|
+
const uniqueName = item.originalName + suffix;
|
|
4629
|
+
schemas[uniqueName] = item.schema;
|
|
4630
|
+
const refPath = `#/components/${item.source}/${item.originalName}`;
|
|
4631
|
+
nameMapping.set(refPath, uniqueName);
|
|
4632
|
+
});
|
|
4633
|
+
}
|
|
4634
|
+
return {
|
|
4635
|
+
schemas,
|
|
4636
|
+
nameMapping
|
|
4637
|
+
};
|
|
4638
|
+
}
|
|
4499
4639
|
|
|
4500
4640
|
//#endregion
|
|
4501
4641
|
//#region src/Oas.ts
|
|
@@ -4664,17 +4804,17 @@ var Oas = class extends oas.default {
|
|
|
4664
4804
|
if (!(contentType in responseBody.content)) return false;
|
|
4665
4805
|
return responseBody.content[contentType];
|
|
4666
4806
|
}
|
|
4667
|
-
let
|
|
4807
|
+
let availableContentType;
|
|
4668
4808
|
const contentTypes = Object.keys(responseBody.content);
|
|
4669
4809
|
contentTypes.forEach((mt) => {
|
|
4670
|
-
if (!
|
|
4810
|
+
if (!availableContentType && oas_utils.matchesMimeType.json(mt)) availableContentType = mt;
|
|
4671
4811
|
});
|
|
4672
|
-
if (!
|
|
4673
|
-
if (!
|
|
4812
|
+
if (!availableContentType) contentTypes.forEach((mt) => {
|
|
4813
|
+
if (!availableContentType) availableContentType = mt;
|
|
4674
4814
|
});
|
|
4675
|
-
if (
|
|
4676
|
-
|
|
4677
|
-
responseBody.content[
|
|
4815
|
+
if (availableContentType) return [
|
|
4816
|
+
availableContentType,
|
|
4817
|
+
responseBody.content[availableContentType],
|
|
4678
4818
|
...responseBody.description ? [responseBody.description] : []
|
|
4679
4819
|
];
|
|
4680
4820
|
return false;
|
|
@@ -4746,21 +4886,61 @@ var Oas = class extends oas.default {
|
|
|
4746
4886
|
properties: {}
|
|
4747
4887
|
});
|
|
4748
4888
|
}
|
|
4749
|
-
async
|
|
4750
|
-
return
|
|
4751
|
-
enablePaths: true,
|
|
4752
|
-
colorizeErrors: true
|
|
4753
|
-
}).validate({ parser: { validate: { errors: { colorize: true } } } });
|
|
4889
|
+
async validate() {
|
|
4890
|
+
return validate(this.api);
|
|
4754
4891
|
}
|
|
4755
4892
|
flattenSchema(schema) {
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4893
|
+
return flattenSchema(schema);
|
|
4894
|
+
}
|
|
4895
|
+
/**
|
|
4896
|
+
* Get schemas from OpenAPI components (schemas, responses, requestBodies).
|
|
4897
|
+
* Returns schemas in dependency order along with name mapping for collision resolution.
|
|
4898
|
+
*/
|
|
4899
|
+
getSchemas(options = {}) {
|
|
4900
|
+
const contentType = options.contentType ?? this.#options.contentType;
|
|
4901
|
+
const includes = options.includes ?? [
|
|
4902
|
+
"schemas",
|
|
4903
|
+
"requestBodies",
|
|
4904
|
+
"responses"
|
|
4905
|
+
];
|
|
4906
|
+
const shouldResolveCollisions = options.collisionDetection ?? this.#options.collisionDetection ?? false;
|
|
4907
|
+
const components = this.getDefinition().components;
|
|
4908
|
+
const schemasWithMeta = [];
|
|
4909
|
+
if (includes.includes("schemas")) {
|
|
4910
|
+
const componentSchemas = components?.schemas || {};
|
|
4911
|
+
for (const [name, schema] of Object.entries(componentSchemas)) schemasWithMeta.push({
|
|
4912
|
+
schema,
|
|
4913
|
+
source: "schemas",
|
|
4914
|
+
originalName: name
|
|
4915
|
+
});
|
|
4916
|
+
}
|
|
4917
|
+
if (includes.includes("responses")) {
|
|
4918
|
+
const responses = components?.responses || {};
|
|
4919
|
+
for (const [name, response] of Object.entries(responses)) {
|
|
4920
|
+
const schema = extractSchemaFromContent(response.content, contentType);
|
|
4921
|
+
if (schema) schemasWithMeta.push({
|
|
4922
|
+
schema,
|
|
4923
|
+
source: "responses",
|
|
4924
|
+
originalName: name
|
|
4925
|
+
});
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
if (includes.includes("requestBodies")) {
|
|
4929
|
+
const requestBodies = components?.requestBodies || {};
|
|
4930
|
+
for (const [name, request] of Object.entries(requestBodies)) {
|
|
4931
|
+
const schema = extractSchemaFromContent(request.content, contentType);
|
|
4932
|
+
if (schema) schemasWithMeta.push({
|
|
4933
|
+
schema,
|
|
4934
|
+
source: "requestBodies",
|
|
4935
|
+
originalName: name
|
|
4936
|
+
});
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4939
|
+
const { schemas, nameMapping } = shouldResolveCollisions ? resolveCollisions(schemasWithMeta) : legacyResolve(schemasWithMeta);
|
|
4940
|
+
return {
|
|
4941
|
+
schemas: sortSchemas(schemas),
|
|
4942
|
+
nameMapping
|
|
4943
|
+
};
|
|
4764
4944
|
}
|
|
4765
4945
|
};
|
|
4766
4946
|
|
|
@@ -4804,4 +4984,5 @@ Object.defineProperty(exports, 'matchesMimeType', {
|
|
|
4804
4984
|
exports.merge = merge;
|
|
4805
4985
|
exports.parse = parse;
|
|
4806
4986
|
exports.parseFromConfig = parseFromConfig;
|
|
4987
|
+
exports.validate = validate;
|
|
4807
4988
|
//# sourceMappingURL=index.cjs.map
|