@kubb/adapter-oas 5.14.4 → 5.14.5

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
@@ -1,5 +1,6 @@
1
1
  import { defineExtension } from "@kubb/core";
2
2
  import { Diagnostics, ast, childName, enumPrimitive, enumPropName, extractRefName, macroDiscriminatorEnum, macroEnumName, macroSimplifyUnion, mergeAdjacentObjectsLazy } from "@kubb/kit";
3
+ import { join } from "node:path";
3
4
  import { access, readFile } from "node:fs/promises";
4
5
  import { upgrade } from "@scalar/openapi-upgrader";
5
6
  import { bundle } from "api-ref-bundler";
@@ -20,7 +21,7 @@ var compatibility = {
20
21
  };
21
22
  //#endregion
22
23
  //#region package.json
23
- var version = "5.14.4";
24
+ var version = "5.14.5";
24
25
  //#endregion
25
26
  //#region src/constants.ts
26
27
  /**
@@ -711,33 +712,55 @@ function inlinePathRefs(node, resolve, visiting = []) {
711
712
  * all-internal document it is a no-op that still walks the whole tree to confirm that, which
712
713
  * costs real time on a large spec.
713
714
  *
715
+ * `document` stands in for the file at `pathOrUrl`, such as inline input. Every other file or URL
716
+ * read, including one that fails, lands in `sources`. A `$ref` that cannot be read reports
717
+ * `KUBB_REF_NOT_FOUND` at the key that holds it.
718
+ *
714
719
  * @example Local file
715
720
  * `const document = await bundleDocument('./openapi.yaml')`
716
721
  *
717
722
  * @example Remote URL
718
723
  * `const document = await bundleDocument('https://example.com/openapi.yaml')`
719
724
  */
720
- async function bundleDocument(pathOrUrl) {
725
+ async function bundleDocument(pathOrUrl, { document, sources = [] } = {}) {
721
726
  const cache = /* @__PURE__ */ new Map();
727
+ const toKey = (sourcePath) => urlRegExp.test(sourcePath) ? new URL(sourcePath).href : sourcePath;
722
728
  const resolver = (sourcePath) => {
723
- const key = urlRegExp.test(sourcePath) ? new URL(sourcePath).href : sourcePath;
729
+ if (document && sourcePath === pathOrUrl) return Promise.resolve(document);
730
+ const key = toKey(sourcePath);
724
731
  const cached = cache.get(key);
725
732
  if (cached) return cached;
733
+ sources.push(key);
726
734
  const result = resolveSource(sourcePath);
727
735
  cache.set(key, result);
728
736
  return result;
729
737
  };
730
738
  const root = await resolver(pathOrUrl);
731
739
  if (typeof root === "object" && root !== null && !hasExternalRef(root)) return root;
732
- return await bundle(pathOrUrl, resolver);
740
+ return await bundle(pathOrUrl, resolver, { hooks: { onError: (message, { path, state }) => {
741
+ const pointer = toKey(state.baseFile) === toKey(pathOrUrl) ? `#/${path.map((token) => escapePointerToken(String(token))).join("/")}` : void 0;
742
+ const diagnostic = {
743
+ code: Diagnostics.code.refNotFound,
744
+ severity: "error",
745
+ message: `Could not bundle an external \`$ref\`. ${message}.`,
746
+ help: "Check that the file or URL the `$ref` points at exists and is readable, or fix the `$ref`.",
747
+ ...pointer && { location: {
748
+ kind: "schema",
749
+ pointer
750
+ } }
751
+ };
752
+ if (!Diagnostics.report(diagnostic)) throw new Diagnostics.Error(diagnostic);
753
+ } } });
733
754
  }
755
+ const inlineInputName = "<inline input>";
734
756
  /**
735
757
  * Creates a `Document` from an `AdapterSource`.
736
758
  *
737
759
  * - `{ type: 'path' }` bundles an absolute file path (core resolves `input`) or remote URL via
738
760
  * `api-ref-bundler`, hoisting external file schemas into named `components.schemas` entries so
739
761
  * generators can emit named types and imports.
740
- * - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
762
+ * - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object, then bundles its
763
+ * external `$ref`s from `root` when it has any. Each file or URL read lands in `sources`.
741
764
  *
742
765
  * Swagger 2.0 and OpenAPI 3.0 documents are up-converted to OpenAPI 3.1 via `@scalar/openapi-upgrader`.
743
766
  *
@@ -747,14 +770,20 @@ async function bundleDocument(pathOrUrl) {
747
770
  * const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
748
771
  * ```
749
772
  */
750
- async function parseFromConfig(source) {
773
+ async function parseFromConfig(source, sources = []) {
751
774
  const raw = await (async () => {
752
- if (source.type === "data") return typeof source.data === "string" ? parseYaml(source.data, {
753
- origin: "the inline `input` string",
754
- help: "Fix the syntax at the reported line. Kubb reads `input` as an inline spec, not a file path, because it starts with `{`, `[`, `openapi:`, or `swagger:`, or spans several lines."
755
- }) : structuredClone(source.data);
775
+ if (source.type === "data") {
776
+ const data = typeof source.data === "string" ? parseYaml(source.data, {
777
+ origin: "the inline `input` string",
778
+ help: "Fix the syntax at the reported line. Kubb reads `input` as an inline spec, not a file path, because it starts with `{`, `[`, `openapi:`, or `swagger:`, or spans several lines."
779
+ }) : structuredClone(source.data);
780
+ return source.root && hasExternalRef(data) ? bundleDocument(join(source.root, inlineInputName), {
781
+ document: data,
782
+ sources
783
+ }) : data;
784
+ }
756
785
  await assertInputExists(source.path);
757
- return bundleDocument(source.path);
786
+ return bundleDocument(source.path, { sources });
758
787
  })();
759
788
  if (raw && typeof raw === "object") {
760
789
  const document = raw;
@@ -2890,6 +2919,7 @@ const adapterOas = defineExtension({
2890
2919
  }),
2891
2920
  build({ validate, contentType, server, discriminator, enums, ...parserOptions }) {
2892
2921
  let parsedDocument = null;
2922
+ let sources = [];
2893
2923
  const pendingValidations = [];
2894
2924
  const inputCache = /* @__PURE__ */ new WeakMap();
2895
2925
  /**
@@ -3023,6 +3053,9 @@ const adapterOas = defineExtension({
3023
3053
  get document() {
3024
3054
  return parsedDocument;
3025
3055
  },
3056
+ get sources() {
3057
+ return sources;
3058
+ },
3026
3059
  locate,
3027
3060
  async validate(input, options) {
3028
3061
  const document = await parseFromConfig({
@@ -3038,9 +3071,14 @@ const adapterOas = defineExtension({
3038
3071
  async parse(source, { signal } = {}) {
3039
3072
  signal?.throwIfAborted();
3040
3073
  const cached = inputCache.get(source);
3041
- if (cached) return cached;
3074
+ if (cached) {
3075
+ sources = cached.sources;
3076
+ return cached.promise;
3077
+ }
3078
+ const read = [];
3079
+ sources = read;
3042
3080
  const promise = (async () => {
3043
- const document = await parseFromConfig(source);
3081
+ const document = await parseFromConfig(source, read);
3044
3082
  signal?.throwIfAborted();
3045
3083
  assertDocument(document);
3046
3084
  if (validate) pendingValidations.push(validateDocumentInWorker(document));
@@ -3060,7 +3098,10 @@ const adapterOas = defineExtension({
3060
3098
  })
3061
3099
  });
3062
3100
  })();
3063
- inputCache.set(source, promise);
3101
+ inputCache.set(source, {
3102
+ promise,
3103
+ sources: read
3104
+ });
3064
3105
  return promise.catch((error) => {
3065
3106
  if (signal?.aborted) inputCache.delete(source);
3066
3107
  throw error;