@kubb/oas 4.31.1 → 4.31.2

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
@@ -51,6 +51,8 @@ jsonpointer = __toESM(jsonpointer);
51
51
  let oas = require("oas");
52
52
  oas = __toESM(oas);
53
53
  let oas_utils = require("oas/utils");
54
+ let node_fs = require("node:fs");
55
+ node_fs = __toESM(node_fs);
54
56
  let node_path = require("node:path");
55
57
  node_path = __toESM(node_path);
56
58
  let _kubb_core_transformers = require("@kubb/core/transformers");
@@ -4443,9 +4445,90 @@ function getDefaultValue(schema) {
4443
4445
  }
4444
4446
  if (schema.type === "object" || schema.properties) return "{}";
4445
4447
  }
4448
+ /**
4449
+ * Recursively collect all external local-file $ref prefixes (e.g. "api-definitions.yml")
4450
+ * from an object tree. URL refs (http/https) are ignored.
4451
+ */
4452
+ function collectExternalFilePaths(obj, files) {
4453
+ if (!obj || typeof obj !== "object") return;
4454
+ if (Array.isArray(obj)) {
4455
+ for (const item of obj) collectExternalFilePaths(item, files);
4456
+ return;
4457
+ }
4458
+ for (const [key, value] of Object.entries(obj)) if (key === "$ref" && typeof value === "string") {
4459
+ const hashIdx = value.indexOf("#");
4460
+ const filePart = hashIdx > 0 ? value.slice(0, hashIdx) : hashIdx === -1 ? value : "";
4461
+ if (filePart && !filePart.startsWith("http://") && !filePart.startsWith("https://")) files.add(filePart);
4462
+ } else collectExternalFilePaths(value, files);
4463
+ }
4464
+ /**
4465
+ * Replace all $refs that start with `externalFile#` with the corresponding
4466
+ * internal ref (i.e. just the fragment part, `#/...`).
4467
+ */
4468
+ function replaceExternalRefsInPlace(obj, externalFile) {
4469
+ if (!obj || typeof obj !== "object") return;
4470
+ if (Array.isArray(obj)) {
4471
+ for (const item of obj) replaceExternalRefsInPlace(item, externalFile);
4472
+ return;
4473
+ }
4474
+ const record = obj;
4475
+ for (const key of Object.keys(record)) {
4476
+ const value = record[key];
4477
+ if (key === "$ref" && typeof value === "string" && value.startsWith(`${externalFile}#`)) record[key] = value.slice(externalFile.length);
4478
+ else if (value && typeof value === "object") replaceExternalRefsInPlace(value, externalFile);
4479
+ }
4480
+ }
4481
+ /**
4482
+ * Before bundling, scan the main spec file for external local-file references and merge
4483
+ * their `components` sections into the main document. This ensures that schemas defined
4484
+ * in external files (e.g. `api-definitions.yml#/components/schemas/Parcel`) end up in
4485
+ * `#/components/schemas/Parcel` of the bundled output, rather than being inlined as
4486
+ * anonymous path-based refs.
4487
+ *
4488
+ * Returns the merged document, or `null` if no external file components were found.
4489
+ */
4490
+ function mergeExternalFileComponents(mainFilePath) {
4491
+ let mainContent;
4492
+ try {
4493
+ mainContent = node_fs.default.readFileSync(mainFilePath, "utf-8");
4494
+ } catch {
4495
+ return null;
4496
+ }
4497
+ const mainDoc = import_yaml.parse(mainContent);
4498
+ if (!mainDoc || typeof mainDoc !== "object") return null;
4499
+ const mainDir = node_path.default.dirname(mainFilePath);
4500
+ const externalFiles = /* @__PURE__ */ new Set();
4501
+ collectExternalFilePaths(mainDoc, externalFiles);
4502
+ if (externalFiles.size === 0) return null;
4503
+ let hasMergedComponents = false;
4504
+ for (const externalFile of externalFiles) {
4505
+ const externalFilePath = node_path.default.resolve(mainDir, externalFile);
4506
+ let externalContent;
4507
+ try {
4508
+ externalContent = node_fs.default.readFileSync(externalFilePath, "utf-8");
4509
+ } catch {
4510
+ continue;
4511
+ }
4512
+ const externalDoc = import_yaml.parse(externalContent);
4513
+ if (!externalDoc?.components || typeof externalDoc.components !== "object") continue;
4514
+ const mainComponents = mainDoc.components ?? {};
4515
+ mainDoc.components = mainComponents;
4516
+ for (const [componentType, components] of Object.entries(externalDoc.components)) {
4517
+ if (!components || typeof components !== "object") continue;
4518
+ mainComponents[componentType] = {
4519
+ ...components,
4520
+ ...mainComponents[componentType] ?? {}
4521
+ };
4522
+ hasMergedComponents = true;
4523
+ }
4524
+ }
4525
+ if (!hasMergedComponents) return null;
4526
+ for (const externalFile of externalFiles) replaceExternalRefsInPlace(mainDoc, externalFile);
4527
+ return mainDoc;
4528
+ }
4446
4529
  async function parse(pathOrApi, { oasClass = Oas, enablePaths = true } = {}) {
4447
4530
  if (typeof pathOrApi === "string" && !pathOrApi.match(/\n/) && !pathOrApi.match(/^\s*\{/) && enablePaths) try {
4448
- return parse(await (0, _readme_openapi_parser.bundle)(pathOrApi), {
4531
+ return parse(await (0, _readme_openapi_parser.bundle)(mergeExternalFileComponents(pathOrApi) ?? pathOrApi), {
4449
4532
  oasClass,
4450
4533
  enablePaths
4451
4534
  });