@kubb/oas 4.18.5 → 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 +197 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -14
- package/dist/index.d.ts +25 -14
- package/dist/index.js +197 -23
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/Oas.ts +68 -41
- package/src/index.ts +1 -0
- package/src/utils.spec.ts +288 -1
- package/src/utils.ts +259 -11
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");
|
|
@@ -4373,9 +4374,6 @@ function isParameterObject(obj) {
|
|
|
4373
4374
|
* Determines if a schema is nullable, considering:
|
|
4374
4375
|
* - OpenAPI 3.0 `nullable` / `x-nullable`
|
|
4375
4376
|
* - OpenAPI 3.1 JSON Schema `type: ['null', ...]` or `type: 'null'`
|
|
4376
|
-
*
|
|
4377
|
-
* @param schema - The schema object to check.
|
|
4378
|
-
* @returns `true` if the schema is marked as nullable; otherwise, `false`.
|
|
4379
4377
|
*/
|
|
4380
4378
|
function isNullable(schema) {
|
|
4381
4379
|
if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
|
|
@@ -4386,16 +4384,12 @@ function isNullable(schema) {
|
|
|
4386
4384
|
}
|
|
4387
4385
|
/**
|
|
4388
4386
|
* Determines if the given object is an OpenAPI ReferenceObject.
|
|
4389
|
-
*
|
|
4390
|
-
* @returns True if {@link obj} is a ReferenceObject; otherwise, false.
|
|
4391
4387
|
*/
|
|
4392
4388
|
function isReference(obj) {
|
|
4393
4389
|
return !!obj && (0, oas_types.isRef)(obj);
|
|
4394
4390
|
}
|
|
4395
4391
|
/**
|
|
4396
4392
|
* Determines if the given object is a SchemaObject with a discriminator property of type DiscriminatorObject.
|
|
4397
|
-
*
|
|
4398
|
-
* @returns True if {@link obj} is a SchemaObject containing a non-string {@link discriminator} property.
|
|
4399
4393
|
*/
|
|
4400
4394
|
function isDiscriminator(obj) {
|
|
4401
4395
|
return !!obj && obj?.["discriminator"] && typeof obj.discriminator !== "string";
|
|
@@ -4404,9 +4398,6 @@ function isDiscriminator(obj) {
|
|
|
4404
4398
|
* Determines whether a schema is required.
|
|
4405
4399
|
*
|
|
4406
4400
|
* Returns true if the schema has a non-empty {@link SchemaObject.required} array or a truthy {@link SchemaObject.required} property.
|
|
4407
|
-
*
|
|
4408
|
-
* @param schema - The schema object to check.
|
|
4409
|
-
* @returns True if the schema is required; otherwise, false.
|
|
4410
4401
|
*/
|
|
4411
4402
|
function isRequired(schema) {
|
|
4412
4403
|
if (!schema) return false;
|
|
@@ -4502,6 +4493,149 @@ function parseFromConfig(config, oasClass = Oas) {
|
|
|
4502
4493
|
if (new _kubb_core_utils.URLPath(config.input.path).isURL) return parse(config.input.path, { oasClass });
|
|
4503
4494
|
return parse(node_path.default.resolve(config.root, config.input.path), { oasClass });
|
|
4504
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
|
+
}
|
|
4505
4639
|
|
|
4506
4640
|
//#endregion
|
|
4507
4641
|
//#region src/Oas.ts
|
|
@@ -4753,20 +4887,60 @@ var Oas = class extends oas.default {
|
|
|
4753
4887
|
});
|
|
4754
4888
|
}
|
|
4755
4889
|
async validate() {
|
|
4756
|
-
return
|
|
4757
|
-
enablePaths: true,
|
|
4758
|
-
colorizeErrors: true
|
|
4759
|
-
}).validate({ parser: { validate: { errors: { colorize: true } } } });
|
|
4890
|
+
return validate(this.api);
|
|
4760
4891
|
}
|
|
4761
4892
|
flattenSchema(schema) {
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
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
|
+
};
|
|
4770
4944
|
}
|
|
4771
4945
|
};
|
|
4772
4946
|
|
|
@@ -4810,4 +4984,5 @@ Object.defineProperty(exports, 'matchesMimeType', {
|
|
|
4810
4984
|
exports.merge = merge;
|
|
4811
4985
|
exports.parse = parse;
|
|
4812
4986
|
exports.parseFromConfig = parseFromConfig;
|
|
4987
|
+
exports.validate = validate;
|
|
4813
4988
|
//# sourceMappingURL=index.cjs.map
|