@scalar/json-magic 0.9.6 → 0.11.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/bundle/bundle.d.ts +0 -77
  3. package/dist/bundle/bundle.d.ts.map +1 -1
  4. package/dist/bundle/bundle.js +25 -67
  5. package/dist/bundle/bundle.js.map +3 -3
  6. package/dist/bundle/plugins/fetch-urls/index.js +2 -2
  7. package/dist/bundle/plugins/fetch-urls/index.js.map +2 -2
  8. package/dist/bundle/plugins/read-files/index.js +1 -1
  9. package/dist/bundle/plugins/read-files/index.js.map +1 -1
  10. package/dist/dereference/dereference.d.ts +8 -1
  11. package/dist/dereference/dereference.d.ts.map +1 -1
  12. package/dist/dereference/dereference.js +2 -1
  13. package/dist/dereference/dereference.js.map +2 -2
  14. package/dist/helpers/is-file-path.d.ts +16 -0
  15. package/dist/helpers/is-file-path.d.ts.map +1 -0
  16. package/dist/helpers/is-file-path.js +10 -0
  17. package/dist/helpers/is-file-path.js.map +7 -0
  18. package/dist/helpers/is-http-url.d.ts +14 -0
  19. package/dist/helpers/is-http-url.d.ts.map +1 -0
  20. package/dist/helpers/is-http-url.js +12 -0
  21. package/dist/helpers/is-http-url.js.map +7 -0
  22. package/dist/helpers/resolve-reference-path.d.ts +18 -0
  23. package/dist/helpers/resolve-reference-path.d.ts.map +1 -0
  24. package/dist/helpers/resolve-reference-path.js +17 -0
  25. package/dist/helpers/resolve-reference-path.js.map +7 -0
  26. package/dist/helpers/set-value-at-path.d.ts +50 -0
  27. package/dist/helpers/set-value-at-path.d.ts.map +1 -0
  28. package/dist/helpers/set-value-at-path.js +28 -0
  29. package/dist/helpers/set-value-at-path.js.map +7 -0
  30. package/dist/helpers/to-relative-path.d.ts +10 -0
  31. package/dist/helpers/to-relative-path.d.ts.map +1 -0
  32. package/dist/helpers/to-relative-path.js +31 -0
  33. package/dist/helpers/to-relative-path.js.map +7 -0
  34. package/package.json +12 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @scalar/json-magic
2
2
 
3
+ ## 0.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#8095](https://github.com/scalar/scalar/pull/8095): feat: support loading files into the store
8
+ - [#8096](https://github.com/scalar/scalar/pull/8096): feat: store relative urls/paths against base under the url mappings
9
+
10
+ ## 0.10.0
11
+
12
+ ### Minor Changes
13
+
14
+ - [#8052](https://github.com/scalar/scalar/pull/8052): feat: allow custom LoaderPlugin plugins in dereference
15
+
3
16
  ## 0.9.6
4
17
 
5
18
  ### Patch Changes
@@ -1,32 +1,4 @@
1
1
  import type { UnknownObject } from '../types.js';
2
- /**
3
- * Checks if a string is a remote URL (starts with http:// or https://)
4
- * @param value - The URL string to check
5
- * @returns true if the string is a remote URL, false otherwise
6
- * @example
7
- * ```ts
8
- * isRemoteUrl('https://example.com/schema.json') // true
9
- * isRemoteUrl('http://api.example.com/schemas/user.json') // true
10
- * isRemoteUrl('#/components/schemas/User') // false
11
- * isRemoteUrl('./local-schema.json') // false
12
- * ```
13
- */
14
- export declare function isRemoteUrl(value: string): boolean;
15
- /**
16
- * Checks if a string represents a file path by ensuring it's not a remote URL,
17
- * YAML content, or JSON content.
18
- *
19
- * @param value - The string to check
20
- * @returns true if the string appears to be a file path, false otherwise
21
- * @example
22
- * ```ts
23
- * isFilePath('./schemas/user.json') // true
24
- * isFilePath('https://example.com/schema.json') // false
25
- * isFilePath('{"type": "object"}') // false
26
- * isFilePath('type: object') // false
27
- * ```
28
- */
29
- export declare function isFilePath(value: string): boolean;
30
2
  /**
31
3
  * Checks if a string is a local reference (starts with #)
32
4
  * @param value - The reference string to check
@@ -46,55 +18,6 @@ export type ResolveResult = {
46
18
  } | {
47
19
  ok: false;
48
20
  };
49
- /**
50
- * Sets a value at a specified path in an object, creating intermediate objects/arrays as needed.
51
- * This function traverses the object structure and creates any missing intermediate objects
52
- * or arrays based on the path segments. If the next segment is a numeric string, it creates
53
- * an array instead of an object.
54
- *
55
- * ⚠️ Warning: Be careful with object keys that look like numbers (e.g. "123") as this function
56
- * will interpret them as array indices and create arrays instead of objects. If you need to
57
- * use numeric-looking keys, consider prefixing them with a non-numeric character.
58
- *
59
- * @param obj - The target object to set the value in
60
- * @param path - The JSON pointer path where the value should be set
61
- * @param value - The value to set at the specified path
62
- * @throws {Error} If attempting to set a value at the root path ('')
63
- *
64
- * @example
65
- * const obj = {}
66
- * setValueAtPath(obj, '/foo/bar/0', 'value')
67
- * // Result:
68
- * // {
69
- * // foo: {
70
- * // bar: ['value']
71
- * // }
72
- * // }
73
- *
74
- * @example
75
- * const obj = { existing: { path: 'old' } }
76
- * setValueAtPath(obj, '/existing/path', 'new')
77
- * // Result:
78
- * // {
79
- * // existing: {
80
- * // path: 'new'
81
- * // }
82
- * // }
83
- *
84
- * @example
85
- * // ⚠️ Warning: This will create an array instead of an object with key "123"
86
- * setValueAtPath(obj, '/foo/123/bar', 'value')
87
- * // Result:
88
- * // {
89
- * // foo: [
90
- * // undefined,
91
- * // undefined,
92
- * // undefined,
93
- * // { bar: 'value' }
94
- * // ]
95
- * // }
96
- */
97
- export declare function setValueAtPath(obj: any, path: string, value: any): void;
98
21
  /**
99
22
  * Prefixes an internal JSON reference with a given path prefix.
100
23
  * Takes a local reference (starting with #) and prepends the provided prefix segments.
@@ -1 +1 @@
1
- {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle.ts"],"names":[],"mappings":"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"}
@@ -1,25 +1,16 @@
1
- import { path } from "@scalar/helpers/node/path";
2
1
  import { isObject } from "@scalar/helpers/object/is-object";
3
2
  import { convertToLocalRef } from "../helpers/convert-to-local-ref.js";
4
3
  import { getId, getSchemas } from "../helpers/get-schemas.js";
5
4
  import { getValueByPath } from "../helpers/get-value-by-path.js";
5
+ import { isFilePath } from "../helpers/is-file-path.js";
6
+ import { isHttpUrl } from "../helpers/is-http-url.js";
7
+ import { resolveReferencePath } from "../helpers/resolve-reference-path.js";
8
+ import { setValueAtPath } from "../helpers/set-value-at-path.js";
9
+ import { toRelativePath } from "../helpers/to-relative-path.js";
6
10
  import { escapeJsonPointer } from "../helpers/escape-json-pointer.js";
7
11
  import { getSegmentsFromPath } from "../helpers/get-segments-from-path.js";
8
- import { isJsonObject } from "../helpers/is-json-object.js";
9
- import { isYaml } from "../helpers/is-yaml.js";
10
12
  import { getHash, uniqueValueGeneratorFactory } from "./value-generator.js";
11
13
  const hasRef = (value) => isObject(value) && "$ref" in value && typeof value["$ref"] === "string";
12
- function isRemoteUrl(value) {
13
- try {
14
- const url = new URL(value);
15
- return url.protocol === "http:" || url.protocol === "https:";
16
- } catch {
17
- return false;
18
- }
19
- }
20
- function isFilePath(value) {
21
- return !isRemoteUrl(value) && !isYaml(value) && !isJsonObject(value);
22
- }
23
14
  function isLocalRef(value) {
24
15
  return value.startsWith("#");
25
16
  }
@@ -32,42 +23,6 @@ function resolveContents(value, plugins) {
32
23
  ok: false
33
24
  });
34
25
  }
35
- function setValueAtPath(obj, path2, value) {
36
- if (path2 === "") {
37
- throw new Error("Cannot set value at root ('') pointer");
38
- }
39
- const parts = getSegmentsFromPath(path2);
40
- let current = obj;
41
- for (let i = 0; i < parts.length; i++) {
42
- const key = parts[i];
43
- const isLast = i === parts.length - 1;
44
- const nextKey = parts[i + 1];
45
- const shouldBeArray = /^\d+$/.test(nextKey ?? "");
46
- if (isLast) {
47
- current[key] = value;
48
- } else {
49
- if (!(key in current) || typeof current[key] !== "object") {
50
- current[key] = shouldBeArray ? [] : {};
51
- }
52
- current = current[key];
53
- }
54
- }
55
- }
56
- function resolveReferencePath(base, relativePath) {
57
- if (isRemoteUrl(relativePath)) {
58
- return relativePath;
59
- }
60
- if (isRemoteUrl(base)) {
61
- const url = new URL(base);
62
- if (relativePath.startsWith("/")) {
63
- url.pathname = relativePath;
64
- return url.toString();
65
- }
66
- const mergedPath = path.join(path.dirname(url.pathname), relativePath);
67
- return new URL(mergedPath, base).toString();
68
- }
69
- return path.join(path.dirname(base), relativePath);
70
- }
71
26
  function prefixInternalRef(input, prefix) {
72
27
  if (!isLocalRef(input)) {
73
28
  throw "Please provide an internal ref";
@@ -169,18 +124,23 @@ async function bundle(input, config) {
169
124
  const schemas = getSchemas(documentRoot);
170
125
  const isPartialBundling = config.root !== void 0 && config.root !== rawSpecification || config.depth !== void 0;
171
126
  const processedNodes = config.visitedNodes ?? /* @__PURE__ */ new Set();
172
- const defaultOrigin = () => {
127
+ const getDefaultOrigin = () => {
128
+ const id = getId(documentRoot);
129
+ if (id) {
130
+ return id;
131
+ }
173
132
  if (config.origin) {
174
133
  return config.origin;
175
134
  }
176
135
  if (typeof input !== "string") {
177
- return "";
136
+ return "/";
178
137
  }
179
- if (isRemoteUrl(input) || isFilePath(input)) {
138
+ if (isHttpUrl(input) || isFilePath(input)) {
180
139
  return input;
181
140
  }
182
- return "";
141
+ return "/";
183
142
  };
143
+ const defaultOrigin = getDefaultOrigin();
184
144
  if (documentRoot[config.externalDocumentsMappingsKey] === void 0) {
185
145
  documentRoot[config.externalDocumentsMappingsKey] = {};
186
146
  }
@@ -200,7 +160,7 @@ async function bundle(input, config) {
200
160
  }
201
161
  }
202
162
  };
203
- const bundler = async (root, origin = defaultOrigin(), isChunkParent = false, depth = 0, currentPath = [], parent = null) => {
163
+ const bundler = async (root, origin = defaultOrigin, isChunkParent = false, depth = 0, currentPath = [], parent = null) => {
204
164
  if (config.depth !== void 0 && depth > config.depth) {
205
165
  return;
206
166
  }
@@ -234,15 +194,16 @@ async function bundle(input, config) {
234
194
  await executeHooks("onAfterNodeProcess", root, context);
235
195
  return;
236
196
  }
237
- const [prefix, path2 = ""] = ref.split("#", 2);
197
+ const [prefix, path = ""] = ref.split("#", 2);
238
198
  const resolvedPath = resolveReferencePath(id ?? origin, prefix);
239
- const compressedPath = await generate(resolvedPath);
240
- const seen = cache.has(resolvedPath);
199
+ const relativePath = toRelativePath(resolvedPath, defaultOrigin);
200
+ const compressedPath = await generate(relativePath);
201
+ const seen = cache.has(relativePath);
241
202
  if (!seen) {
242
- cache.set(resolvedPath, resolveContents(resolvedPath, loaderPlugins));
203
+ cache.set(relativePath, resolveContents(resolvedPath, loaderPlugins));
243
204
  }
244
205
  await executeHooks("onResolveStart", root);
245
- const result = await cache.get(resolvedPath);
206
+ const result = await cache.get(relativePath);
246
207
  if (result.ok) {
247
208
  if (!seen) {
248
209
  if (!isChunk) {
@@ -256,21 +217,21 @@ async function bundle(input, config) {
256
217
  setValueAtPath(
257
218
  documentRoot,
258
219
  `/${config.externalDocumentsMappingsKey}/${escapeJsonPointer(compressedPath)}`,
259
- resolvedPath
220
+ relativePath
260
221
  );
261
222
  }
262
223
  if (config.treeShake === true) {
263
224
  resolveAndCopyReferences(
264
225
  documentRoot,
265
226
  { [config.externalDocumentsKey]: { [compressedPath]: result.data } },
266
- prefixInternalRef(`#${path2}`, [config.externalDocumentsKey, compressedPath]).substring(1),
227
+ prefixInternalRef(`#${path}`, [config.externalDocumentsKey, compressedPath]).substring(1),
267
228
  config.externalDocumentsKey,
268
229
  compressedPath
269
230
  );
270
231
  } else if (!seen) {
271
232
  setValueAtPath(documentRoot, `/${config.externalDocumentsKey}/${compressedPath}`, result.data);
272
233
  }
273
- root.$ref = prefixInternalRef(`#${path2}`, [config.externalDocumentsKey, compressedPath]);
234
+ root.$ref = prefixInternalRef(`#${path}`, [config.externalDocumentsKey, compressedPath]);
274
235
  await executeHooks("onResolveSuccess", root);
275
236
  await executeHooks("onAfterNodeProcess", root, context);
276
237
  return;
@@ -300,12 +261,9 @@ async function bundle(input, config) {
300
261
  export {
301
262
  bundle,
302
263
  extensions,
303
- isFilePath,
304
264
  isLocalRef,
305
- isRemoteUrl,
306
265
  prefixInternalRef,
307
266
  prefixInternalRefRecursive,
308
- resolveAndCopyReferences,
309
- setValueAtPath
267
+ resolveAndCopyReferences
310
268
  };
311
269
  //# sourceMappingURL=bundle.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/bundle/bundle.ts"],
4
- "sourcesContent": ["import { path } from '@scalar/helpers/node/path'\nimport { isObject } from '@scalar/helpers/object/is-object'\n\nimport { convertToLocalRef } from '@/helpers/convert-to-local-ref'\nimport { getId, getSchemas } from '@/helpers/get-schemas'\nimport { getValueByPath } from '@/helpers/get-value-by-path'\nimport type { UnknownObject } from '@/types'\n\nimport { escapeJsonPointer } from '../helpers/escape-json-pointer'\nimport { getSegmentsFromPath } from '../helpers/get-segments-from-path'\nimport { isJsonObject } from '../helpers/is-json-object'\nimport { isYaml } from '../helpers/is-yaml'\nimport { getHash, uniqueValueGeneratorFactory } from './value-generator'\n\n/** Type guard to check if a value is an object with a $ref property */\nconst hasRef = (value: unknown): value is UnknownObject & Record<'$ref', string> =>\n isObject(value) && '$ref' in value && typeof value['$ref'] === 'string'\n\n/**\n * Checks if a string is a remote URL (starts with http:// or https://)\n * @param value - The URL string to check\n * @returns true if the string is a remote URL, false otherwise\n * @example\n * ```ts\n * isRemoteUrl('https://example.com/schema.json') // true\n * isRemoteUrl('http://api.example.com/schemas/user.json') // true\n * isRemoteUrl('#/components/schemas/User') // false\n * isRemoteUrl('./local-schema.json') // false\n * ```\n */\nexport function isRemoteUrl(value: string) {\n try {\n const url = new URL(value)\n return url.protocol === 'http:' || url.protocol === 'https:'\n } catch {\n return false\n }\n}\n\n/**\n * Checks if a string represents a file path by ensuring it's not a remote URL,\n * YAML content, or JSON content.\n *\n * @param value - The string to check\n * @returns true if the string appears to be a file path, false otherwise\n * @example\n * ```ts\n * isFilePath('./schemas/user.json') // true\n * isFilePath('https://example.com/schema.json') // false\n * isFilePath('{\"type\": \"object\"}') // false\n * isFilePath('type: object') // false\n * ```\n */\nexport function isFilePath(value: string) {\n return !isRemoteUrl(value) && !isYaml(value) && !isJsonObject(value)\n}\n\n/**\n * Checks if a string is a local reference (starts with #)\n * @param value - The reference string to check\n * @returns true if the string is a local reference, false otherwise\n * @example\n * ```ts\n * isLocalRef('#/components/schemas/User') // true\n * isLocalRef('https://example.com/schema.json') // false\n * isLocalRef('./local-schema.json') // false\n * ```\n */\nexport function isLocalRef(value: string): boolean {\n return value.startsWith('#')\n}\n\nexport type ResolveResult = { ok: true; data: unknown; raw: string } | { ok: false }\n\n/**\n * Resolves a string by finding and executing the appropriate plugin.\n * @param value - The string to resolve (URL, file path, etc)\n * @param plugins - Array of plugins that can handle different types of strings\n * @returns A promise that resolves to either the content or an error result\n * @example\n * // Using a URL plugin\n * await resolveContents('https://example.com/schema.json', [urlPlugin])\n * // Using a file plugin\n * await resolveContents('./schemas/user.json', [filePlugin])\n * // No matching plugin returns { ok: false }\n * await resolveContents('#/components/schemas/User', [urlPlugin, filePlugin])\n */\nfunction resolveContents(value: string, plugins: LoaderPlugin[]): Promise<ResolveResult> {\n const plugin = plugins.find((p) => p.validate(value))\n\n if (plugin) {\n return plugin.exec(value)\n }\n\n return Promise.resolve({\n ok: false,\n })\n}\n\n/**\n * Sets a value at a specified path in an object, creating intermediate objects/arrays as needed.\n * This function traverses the object structure and creates any missing intermediate objects\n * or arrays based on the path segments. If the next segment is a numeric string, it creates\n * an array instead of an object.\n *\n * \u26A0\uFE0F Warning: Be careful with object keys that look like numbers (e.g. \"123\") as this function\n * will interpret them as array indices and create arrays instead of objects. If you need to\n * use numeric-looking keys, consider prefixing them with a non-numeric character.\n *\n * @param obj - The target object to set the value in\n * @param path - The JSON pointer path where the value should be set\n * @param value - The value to set at the specified path\n * @throws {Error} If attempting to set a value at the root path ('')\n *\n * @example\n * const obj = {}\n * setValueAtPath(obj, '/foo/bar/0', 'value')\n * // Result:\n * // {\n * // foo: {\n * // bar: ['value']\n * // }\n * // }\n *\n * @example\n * const obj = { existing: { path: 'old' } }\n * setValueAtPath(obj, '/existing/path', 'new')\n * // Result:\n * // {\n * // existing: {\n * // path: 'new'\n * // }\n * // }\n *\n * @example\n * // \u26A0\uFE0F Warning: This will create an array instead of an object with key \"123\"\n * setValueAtPath(obj, '/foo/123/bar', 'value')\n * // Result:\n * // {\n * // foo: [\n * // undefined,\n * // undefined,\n * // undefined,\n * // { bar: 'value' }\n * // ]\n * // }\n */\nexport function setValueAtPath(obj: any, path: string, value: any): void {\n if (path === '') {\n throw new Error(\"Cannot set value at root ('') pointer\")\n }\n\n const parts = getSegmentsFromPath(path)\n\n let current = obj\n\n for (let i = 0; i < parts.length; i++) {\n const key = parts[i]\n const isLast = i === parts.length - 1\n\n const nextKey = parts[i + 1]\n const shouldBeArray = /^\\d+$/.test(nextKey ?? '')\n\n if (isLast) {\n current[key] = value\n } else {\n if (!(key in current) || typeof current[key] !== 'object') {\n current[key] = shouldBeArray ? [] : {}\n }\n current = current[key]\n }\n }\n}\n\n/**\n * Resolves a reference path by combining a base path with a relative path.\n * Handles both remote URLs and local file paths.\n *\n * @param base - The base path (can be a URL or local file path)\n * @param relativePath - The relative path to resolve against the base\n * @returns The resolved absolute path\n * @example\n * // Resolve remote URL\n * resolveReferencePath('https://example.com/api/schema.json', 'user.json')\n * // Returns: 'https://example.com/api/user.json'\n *\n * // Resolve local path\n * resolveReferencePath('/path/to/schema.json', 'user.json')\n * // Returns: '/path/to/user.json'\n */\nfunction resolveReferencePath(base: string, relativePath: string) {\n if (isRemoteUrl(relativePath)) {\n return relativePath\n }\n\n if (isRemoteUrl(base)) {\n const url = new URL(base)\n\n // If the url stars with a / we want it to resolve from the origin so we replace the pathname\n if (relativePath.startsWith('/')) {\n url.pathname = relativePath\n return url.toString()\n }\n\n const mergedPath = path.join(path.dirname(url.pathname), relativePath)\n return new URL(mergedPath, base).toString()\n }\n\n return path.join(path.dirname(base), relativePath)\n}\n\n/**\n * Prefixes an internal JSON reference with a given path prefix.\n * Takes a local reference (starting with #) and prepends the provided prefix segments.\n *\n * @param input - The internal reference string to prefix (must start with #)\n * @param prefix - Array of path segments to prepend to the reference\n * @returns The prefixed reference string\n * @throws Error if input is not a local reference\n * @example\n * prefixInternalRef('#/components/schemas/User', ['definitions'])\n * // Returns: '#/definitions/components/schemas/User'\n */\nexport function prefixInternalRef(input: string, prefix: string[]) {\n if (!isLocalRef(input)) {\n throw 'Please provide an internal ref'\n }\n\n return `#/${prefix.map(escapeJsonPointer).join('/')}${input.substring(1)}`\n}\n\n/**\n * Updates internal references in an object by adding a prefix to their paths.\n * Recursively traverses the input object and modifies any local $ref references\n * by prepending the given prefix to their paths. This is used when embedding external\n * documents to maintain correct reference paths relative to the main document.\n *\n * @param input - The object to update references in\n * @param prefix - Array of path segments to prepend to internal reference paths\n * @returns void\n * @example\n * ```ts\n * const input = {\n * foo: {\n * $ref: '#/components/schemas/User'\n * }\n * }\n * prefixInternalRefRecursive(input, ['definitions'])\n * // Result:\n * // {\n * // foo: {\n * // $ref: '#/definitions/components/schemas/User'\n * // }\n * // }\n * ```\n */\nexport function prefixInternalRefRecursive(input: unknown, prefix: string[]) {\n if (Array.isArray(input)) {\n input.forEach((el) => prefixInternalRefRecursive(el, prefix))\n return\n }\n\n if (!isObject(input)) {\n return\n }\n\n Object.values(input).forEach((el) => prefixInternalRefRecursive(el, prefix))\n\n if (typeof input === 'object' && '$ref' in input && typeof input['$ref'] === 'string') {\n const ref = input['$ref']\n\n if (!isLocalRef(ref)) {\n return\n }\n\n input['$ref'] = prefixInternalRef(ref, prefix)\n }\n}\n\n/**\n * Resolves and copies referenced values from a source document to a target document.\n * This function traverses the document and copies referenced values to the target document,\n * while tracking processed references to avoid duplicates. It only processes references\n * that belong to the same external document.\n *\n * @param targetDocument - The document to copy referenced values to\n * @param sourceDocument - The source document containing the references\n * @param referencePath - The JSON pointer path to the reference\n * @param externalRefsKey - The key used for external references (e.g. 'x-ext')\n * @param documentKey - The key identifying the external document\n * @param bundleLocalRefs - Also bundles the local refs\n * @param processedNodes - Set of already processed nodes to prevent duplicates\n * @example\n * ```ts\n * const source = {\n * components: {\n * schemas: {\n * User: {\n * $ref: '#/x-ext/users~1schema/definitions/Person'\n * }\n * }\n * }\n * }\n *\n * const target = {}\n * resolveAndCopyReferences(\n * target,\n * source,\n * '/components/schemas/User',\n * 'x-ext',\n * 'users/schema'\n * )\n * // Result: target will contain the User schema with resolved references\n * ```\n */\nexport const resolveAndCopyReferences = (\n targetDocument: unknown,\n sourceDocument: unknown,\n referencePath: string,\n externalRefsKey: string,\n documentKey: string,\n bundleLocalRefs = false,\n processedNodes = new Set(),\n) => {\n const referencedValue = getValueByPath(sourceDocument, getSegmentsFromPath(referencePath)).value\n\n if (processedNodes.has(referencedValue)) {\n return\n }\n processedNodes.add(referencedValue)\n\n setValueAtPath(targetDocument, referencePath, referencedValue)\n\n // Do the same for each local ref\n const traverse = (node: unknown) => {\n if (!node || typeof node !== 'object') {\n return\n }\n\n if ('$ref' in node && typeof node['$ref'] === 'string') {\n // We only process references from the same external document because:\n // 1. Other documents will be handled in separate recursive branches\n // 2. The source document only contains the current document's content\n // This prevents undefined behavior and maintains proper document boundaries\n if (node['$ref'].startsWith(`#/${externalRefsKey}/${escapeJsonPointer(documentKey)}`)) {\n resolveAndCopyReferences(\n targetDocument,\n sourceDocument,\n node['$ref'].substring(1),\n externalRefsKey,\n documentKey,\n bundleLocalRefs,\n processedNodes,\n )\n }\n // Bundle the local refs as well\n else if (bundleLocalRefs) {\n resolveAndCopyReferences(\n targetDocument,\n sourceDocument,\n node['$ref'].substring(1),\n externalRefsKey,\n documentKey,\n bundleLocalRefs,\n processedNodes,\n )\n }\n }\n\n for (const value of Object.values(node)) {\n traverse(value)\n }\n }\n\n traverse(referencedValue)\n}\n\n/**\n * A loader plugin for resolving external references during bundling.\n * Loader plugins are responsible for handling specific types of external references,\n * such as files, URLs, or custom protocols. Each loader plugin must provide:\n *\n * - A `validate` function to determine if the plugin can handle a given reference string.\n * - An `exec` function to asynchronously fetch and resolve the referenced data,\n * returning a Promise that resolves to a `ResolveResult`.\n *\n * Loader plugins enable extensible support for different reference sources in the bundler.\n *\n * @property type - The plugin type, always 'loader' for loader plugins.\n * @property validate - Function to check if the plugin can handle a given reference value.\n * @property exec - Function to fetch and resolve the reference, returning the resolved data.\n */\nexport type LoaderPlugin = {\n type: 'loader'\n // Returns true if this plugin can handle the given reference value\n validate: (value: string) => boolean\n // Asynchronously fetches and resolves the reference, returning the resolved data\n exec: (value: string) => Promise<ResolveResult>\n}\n\n/**\n * Context information for a node during traversal or processing.\n *\n * Note: The `path` parameter represents the path to the current node being processed.\n * If you are performing a partial bundle (i.e., providing a custom root), this path will be relative\n * to the root you provide, not the absolute root of the original document. You may need to prefix\n * it with your own base path if you want to construct a full path from the absolute document root.\n *\n * - `path`: The JSON pointer path (as an array of strings) from the document root to the current node.\n * - `resolutionCache`: A cache for storing promises of resolved references.\n */\ntype NodeProcessContext = {\n path: readonly string[]\n resolutionCache: Map<string, Promise<Readonly<ResolveResult>>>\n parentNode: UnknownObject | null\n rootNode: UnknownObject\n loaders: LoaderPlugin[]\n}\n\n/**\n * A plugin type for lifecycle hooks, allowing custom logic to be injected into the bundler's process.\n * This type extends the Config['hooks'] interface and is identified by type: 'lifecycle'.\n */\nexport type LifecyclePlugin = { type: 'lifecycle' } & Config['hooks']\n\n/**\n * Represents a plugin used by the bundler for extensibility.\n *\n * Plugins can be either:\n * - Loader plugins: Responsible for resolving and loading external references (e.g., from files, URLs, or custom sources).\n * - Lifecycle plugins: Provide lifecycle hooks to customize or extend the bundling process.\n *\n * Loader plugins must implement:\n * - `validate`: Checks if the plugin can handle a given reference value.\n * - `exec`: Asynchronously resolves and returns the referenced data.\n *\n * Lifecycle plugins extend the bundler's lifecycle hooks for custom logic.\n */\nexport type Plugin = LoaderPlugin | LifecyclePlugin\n\n/**\n * Configuration options for the bundler.\n * Controls how external references are resolved and processed during bundling.\n */\ntype Config = {\n /**\n * Array of plugins that handle resolving references from different sources.\n * Each plugin is responsible for fetching and processing data from specific sources\n * like URLs or the filesystem.\n */\n plugins: Plugin[]\n\n /**\n * Optional root object that serves as the base document when bundling a subpart.\n * This allows resolving references relative to the root document's location,\n * ensuring proper path resolution for nested references.\n */\n root?: UnknownObject\n\n /**\n * Optional maximum depth for reference resolution.\n * Limits how deeply the bundler will follow and resolve nested $ref pointers.\n * Useful for preventing infinite recursion or excessive resource usage.\n */\n depth?: number\n\n /**\n * Optional origin path for the bundler.\n * Used to resolve relative paths in references, especially when the input is a string URL or file path.\n * If not provided, the bundler will use the input value as the origin.\n */\n origin?: string\n\n /**\n * Optional cache to store promises of resolved references.\n * Helps avoid duplicate fetches/reads of the same resource by storing\n * the resolution promises for reuse.\n */\n cache?: Map<string, Promise<ResolveResult>>\n\n /**\n * Cache of visited nodes during partial bundling.\n * Used to prevent re-bundling the same tree multiple times when doing partial bundling,\n * improving performance by avoiding redundant processing of already bundled sections.\n */\n visitedNodes?: Set<unknown>\n\n /**\n * Enable tree shaking to optimize the bundle size.\n * When enabled, only the parts of external documents that are actually referenced\n * will be included in the final bundle.\n */\n treeShake: boolean\n\n /**\n * Optional flag to generate a URL map.\n * When enabled, tracks the original source URLs of bundled references\n * in an section for reference mapping defined by externalDocumentsMappingsKey.\n */\n urlMap?: boolean\n\n /**\n * Custom OpenAPI extension key used to store external references.\n * This key will contain all bundled external documents.\n * The key is used to maintain a clean separation between the main\n * OpenAPI document and its bundled external references.\n * @default 'x-ext'\n */\n externalDocumentsKey?: string\n\n /**\n * Custom OpenAPI extension key used to maintain a mapping between\n * hashed keys and their original URLs.\n * This mapping is essential for tracking the source of bundled references\n * @default 'x-ext-urls'\n */\n externalDocumentsMappingsKey?: string\n\n /**\n * Optional function to compress input URLs or file paths before bundling.\n * Returns either a Promise resolving to the compressed string or the compressed string directly.\n */\n compress?: (value: string) => Promise<string> | string\n\n /**\n * Optional hooks to monitor the bundler's lifecycle.\n * Allows tracking the progress and status of reference resolution.\n */\n hooks?: Partial<{\n /**\n * Optional hook called when the bundler starts resolving a $ref.\n * Useful for tracking or logging the beginning of a reference resolution.\n */\n onResolveStart: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook called when the bundler fails to resolve a $ref.\n * Can be used for error handling, logging, or custom error reporting.\n */\n onResolveError: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook called when the bundler successfully resolves a $ref.\n * Useful for tracking successful resolutions or custom post-processing.\n */\n onResolveSuccess: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook invoked before processing a node.\n * Can be used for preprocessing, mutation, or custom logic before the node is handled by the bundler.\n */\n onBeforeNodeProcess: (node: UnknownObject, context: NodeProcessContext) => void | Promise<void>\n /**\n * Optional hook invoked after processing a node.\n * Useful for postprocessing, cleanup, or custom logic after the node has been handled by the bundler.\n */\n onAfterNodeProcess: (node: UnknownObject, context: NodeProcessContext) => void | Promise<void>\n }>\n}\n\n/**\n * Extension keys used for bundling external references in OpenAPI documents.\n * These custom extensions help maintain the structure and traceability of bundled documents.\n */\nexport const extensions = {\n /**\n * Custom OpenAPI extension key used to store external references.\n * This key will contain all bundled external documents.\n * The x-ext key is used to maintain a clean separation between the main\n * OpenAPI document and its bundled external references.\n */\n externalDocuments: 'x-ext',\n\n /**\n * Custom OpenAPI extension key used to maintain a mapping between\n * hashed keys and their original URLs in x-ext.\n * This mapping is essential for tracking the source of bundled references\n */\n externalDocumentsMappings: 'x-ext-urls',\n} as const\n\ntype HookFn<T extends keyof Config['hooks']> = NonNullable<Config['hooks'][T]> extends (...args: infer A) => any\n ? (...args: A) => any\n : never\n\n/**\n * Bundles an OpenAPI specification by resolving all external references.\n * This function traverses the input object recursively and embeds external $ref\n * references into an x-ext section. External references can be URLs or local files.\n * The original $refs are updated to point to their embedded content in the x-ext section.\n * If the input is an object, it will be modified in place by adding an x-ext\n * property to store resolved external references.\n *\n * @param input - The OpenAPI specification to bundle. Can be either an object or string.\n * If a string is provided, it will be resolved using the provided plugins.\n * If no plugin can process the input, the onReferenceError hook will be invoked\n * and an error will be emitted to the console.\n * @param config - Configuration object containing plugins and options for bundling OpenAPI specifications\n * @returns A promise that resolves to the bundled specification with all references embedded\n * @example\n * // Example with object input\n * const spec = {\n * paths: {\n * '/users': {\n * $ref: 'https://example.com/schemas/users.yaml'\n * }\n * }\n * }\n *\n * const bundled = await bundle(spec, {\n * plugins: [fetchUrls()],\n * treeShake: true,\n * urlMap: true,\n * hooks: {\n * onResolveStart: (ref) => console.log('Resolving:', ref.$ref),\n * onResolveSuccess: (ref) => console.log('Resolved:', ref.$ref),\n * onResolveError: (ref) => console.log('Failed to resolve:', ref.$ref)\n * }\n * })\n * // Result:\n * // {\n * // paths: {\n * // '/users': {\n * // $ref: '#/x-ext/abc123'\n * // }\n * // },\n * // 'x-ext': {\n * // 'abc123': {\n * // // Resolved content from users.yaml\n * // }\n * // },\n * // 'x-ext-urls': {\n * // 'https://example.com/schemas/users.yaml': 'abc123'\n * // }\n * // }\n *\n * // Example with URL input\n * const bundledFromUrl = await bundle('https://example.com/openapi.yaml', {\n * plugins: [fetchUrls()],\n * treeShake: true,\n * urlMap: true,\n * hooks: {\n * onResolveStart: (ref) => console.log('Resolving:', ref.$ref),\n * onResolveSuccess: (ref) => console.log('Resolved:', ref.$ref),\n * onResolveError: (ref) => console.log('Failed to resolve:', ref.$ref)\n * }\n * })\n * // The function will first fetch the OpenAPI spec from the URL,\n * // then bundle all its external references into the x-ext section\n */\nexport async function bundle(input: UnknownObject | string, config: Config) {\n // Set the default external documents key and mappings key if not provided in the config\n config.externalDocumentsKey = config.externalDocumentsKey ?? extensions.externalDocuments\n config.externalDocumentsMappingsKey = config.externalDocumentsMappingsKey ?? extensions.externalDocumentsMappings\n\n // Cache for storing promises of resolved external references (URLs and local files)\n // to avoid duplicate fetches/reads of the same resource\n const cache = config.cache ?? new Map<string, Promise<ResolveResult>>()\n\n const loaderPlugins = config.plugins.filter((it) => it.type === 'loader')\n const lifecyclePlugin = config.plugins.filter((it) => it.type === 'lifecycle')\n\n /**\n * Resolves the input value by either returning it directly if it's not a string,\n * or attempting to resolve it using the provided plugins if it is a string.\n * @returns The resolved input data or throws an error if resolution fails\n */\n const resolveInput = async () => {\n if (typeof input !== 'string') {\n return input\n }\n const result = await resolveContents(input, loaderPlugins)\n\n if (result.ok && typeof result.data === 'object') {\n return result.data\n }\n\n throw new Error(\n 'Failed to resolve input: Please provide a valid string value or pass a loader to process the input',\n )\n }\n\n // Resolve the input specification, which could be either a direct object or a string URL/path\n const rawSpecification = await resolveInput()\n\n // Document root used to write all external documents\n // We need this when we want to do a partial bundle of a document\n const documentRoot = config.root ?? rawSpecification\n\n // Extract all $id and $anchor values from the document to identify local schemas\n const schemas = getSchemas(documentRoot)\n\n // Determines if the bundling operation is partial.\n // Partial bundling occurs when:\n // - A root document is provided that is different from the raw specification being bundled, or\n // - A maximum depth is specified in the config.\n // In these cases, only a subset of the document may be bundled.\n const isPartialBundling =\n (config.root !== undefined && config.root !== rawSpecification) || config.depth !== undefined\n\n // Set of nodes that have already been processed during bundling to prevent duplicate processing\n const processedNodes = config.visitedNodes ?? new Set()\n\n // Determines the initial origin path for the bundler based on the input type.\n // For string inputs that are URLs or file paths, uses the input as the origin.\n // For non-string inputs or other string types, returns an empty string.\n const defaultOrigin = () => {\n if (config.origin) {\n return config.origin\n }\n\n if (typeof input !== 'string') {\n return ''\n }\n\n if (isRemoteUrl(input) || isFilePath(input)) {\n return input\n }\n\n return ''\n }\n\n // Create the cache to store the compressed values to their map values\n if (documentRoot[config.externalDocumentsMappingsKey] === undefined) {\n documentRoot[config.externalDocumentsMappingsKey] = {}\n }\n const { generate } = uniqueValueGeneratorFactory(\n config.compress ?? getHash,\n documentRoot[config.externalDocumentsMappingsKey],\n )\n\n /**\n * Executes lifecycle hooks defined both in the bundler configuration and any extended lifecycle plugins.\n * This utility function ensures that all relevant hooks for a given event type are called in order:\n * - First, the hook directly provided via the config (if present)\n * - Then, all matching hooks from registered lifecycle plugins (if present)\n *\n * Hooks are awaited in sequence for the given event type and argument list.\n *\n * @param type The hook event type, corresponding to a key of Config['hooks'].\n * @param args Arguments to pass to the hook function, matching HookFn<T>.\n */\n const executeHooks = async <T extends keyof Config['hooks']>(\n type: T,\n ...args: Parameters<HookFn<T>>\n ): Promise<void> => {\n // Run hook defined directly in config, if present\n const hook = config.hooks?.[type] as HookFn<T> | undefined\n if (hook) {\n await hook(...args)\n }\n // Additionally run the hook for every lifecycle plugin, if present\n for (const plugin of lifecyclePlugin) {\n const pluginHook = plugin[type] as HookFn<T> | undefined\n if (pluginHook) {\n await pluginHook(...args)\n }\n }\n }\n\n const bundler = async (\n root: unknown,\n origin: string = defaultOrigin(),\n isChunkParent = false,\n depth = 0,\n currentPath: readonly string[] = [],\n parent: UnknownObject = null,\n ) => {\n // If a maximum depth is set in the config, stop bundling when the current depth reaches or exceeds it\n if (config.depth !== undefined && depth > config.depth) {\n return\n }\n\n if (!isObject(root) && !Array.isArray(root)) {\n return\n }\n\n // Skip if this node has already been processed to prevent infinite recursion\n // and duplicate processing of the same node\n if (processedNodes.has(root)) {\n return\n }\n // Mark this node as processed before continuing\n processedNodes.add(root)\n\n const context = {\n path: currentPath,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n }\n\n await executeHooks('onBeforeNodeProcess', root as UnknownObject, context)\n\n const id = getId(root)\n\n if (hasRef(root)) {\n const ref = root['$ref']\n const isChunk = '$global' in root && typeof root['$global'] === 'boolean' && root['$global']\n\n // Try to convert the reference to a local reference if possible\n // This handles cases where the reference points to a local schema using $id or $anchor\n // If it can be converted to a local reference, we do not need to bundle it\n // and can skip further processing for this reference\n // In case of partial bundling, we still need to ensure that all dependencies\n // of the local reference are bundled to create a complete and self-contained partial bundle\n // This is important to maintain the integrity of the partial bundle\n const localRef = convertToLocalRef(ref, id ?? origin, schemas)\n\n if (localRef !== undefined) {\n if (isPartialBundling) {\n const segments = getSegmentsFromPath(`/${localRef}`)\n const parent = segments.length > 0 ? getValueByPath(documentRoot, segments.slice(0, -1)).value : undefined\n\n const targetValue = getValueByPath(documentRoot, segments)\n\n // When doing partial bundling, we need to recursively bundle all dependencies\n // referenced by this local reference to ensure the partial bundle is complete.\n // This includes not just the direct reference but also all its dependencies,\n // creating a complete and self-contained partial bundle.\n await bundler(targetValue.value, targetValue.context, isChunkParent, depth + 1, segments, parent)\n }\n await executeHooks('onAfterNodeProcess', root as UnknownObject, context)\n return\n }\n\n const [prefix, path = ''] = ref.split('#', 2)\n\n // Combine the current origin with the new path to resolve relative references\n // correctly within the context of the external file being processed\n const resolvedPath = resolveReferencePath(id ?? origin, prefix)\n\n // Generate a unique compressed path for the external document\n // This is used as a key to store and reference the bundled external document\n // The compression helps reduce the overall file size of the bundled document\n const compressedPath = await generate(resolvedPath)\n\n const seen = cache.has(resolvedPath)\n\n if (!seen) {\n cache.set(resolvedPath, resolveContents(resolvedPath, loaderPlugins))\n }\n\n await executeHooks('onResolveStart', root)\n\n // Resolve the remote document\n const result = await cache.get(resolvedPath)\n\n if (result.ok) {\n // Process the result only once to avoid duplicate processing and prevent multiple prefixing\n // of internal references, which would corrupt the reference paths\n if (!seen) {\n // Skip prefixing for chunks since they are meant to be self-contained and their\n // internal references should remain relative to their original location. Chunks\n // are typically used for modular components that need to maintain their own\n // reference context without being affected by the main document's structure.\n if (!isChunk) {\n // Update internal references in the resolved document to use the correct base path.\n // When we embed external documents, their internal references need to be updated to\n // maintain the correct path context relative to the main document. This is crucial\n // because internal references in the external document are relative to its original\n // location, but when embedded, they need to be relative to their new location in\n // the main document's x-ext section. Without this update, internal references\n // would point to incorrect locations and break the document structure.\n prefixInternalRefRecursive(result.data, [extensions.externalDocuments, compressedPath])\n }\n\n // Recursively process the resolved content\n // to handle any nested references it may contain. We pass the resolvedPath as the new origin\n // to ensure any relative references within this content are resolved correctly relative to\n // their new location in the bundled document.\n await bundler(result.data, isChunk ? origin : resolvedPath, isChunk, depth + 1, [\n config.externalDocumentsKey,\n compressedPath,\n documentRoot[config.externalDocumentsMappingsKey],\n ])\n\n // Store the mapping between hashed keys and original URLs in x-ext-urls\n // This allows tracking which external URLs were bundled and their corresponding locations\n setValueAtPath(\n documentRoot,\n `/${config.externalDocumentsMappingsKey}/${escapeJsonPointer(compressedPath)}`,\n resolvedPath,\n )\n }\n\n if (config.treeShake === true) {\n // Store only the subtree that is actually used\n // This optimizes the bundle size by only including the parts of the external document\n // that are referenced, rather than the entire document\n resolveAndCopyReferences(\n documentRoot,\n { [config.externalDocumentsKey]: { [compressedPath]: result.data } },\n prefixInternalRef(`#${path}`, [config.externalDocumentsKey, compressedPath]).substring(1),\n config.externalDocumentsKey,\n compressedPath,\n )\n } else if (!seen) {\n // Store the external document in the main document's x-ext key\n // When tree shaking is disabled, we include the entire external document\n // This preserves all content and is faster since we don't need to analyze and copy\n // specific parts. This approach is ideal when storing the result in memory\n // as it avoids the overhead of tree shaking operations\n setValueAtPath(documentRoot, `/${config.externalDocumentsKey}/${compressedPath}`, result.data)\n }\n\n // Update the $ref to point to the embedded document in x-ext\n // This is necessary because we need to maintain the correct path context\n // for the embedded document while preserving its internal structure\n root.$ref = prefixInternalRef(`#${path}`, [config.externalDocumentsKey, compressedPath])\n\n await executeHooks('onResolveSuccess', root)\n\n await executeHooks('onAfterNodeProcess', root as UnknownObject, context)\n return\n }\n\n await executeHooks('onResolveError', root)\n\n await executeHooks('onAfterNodeProcess', root as UnknownObject, context)\n return console.warn(\n `Failed to resolve external reference \"${resolvedPath}\". The reference may be invalid, inaccessible, or missing a loader for this type of reference.`,\n )\n }\n\n // Recursively traverse all child properties of the current object to resolve nested $ref references.\n // This step ensures that any $refs located deeper within the object hierarchy are discovered and processed.\n // We explicitly skip the extension keys (x-ext and x-ext-urls) to avoid reprocessing already bundled or mapped content.\n await Promise.all(\n Object.entries(root).map(async ([key, value]) => {\n if (key === config.externalDocumentsKey || key === config.externalDocumentsMappingsKey) {\n return\n }\n\n await bundler(value, id ?? origin, isChunkParent, depth + 1, [...currentPath, key], root as UnknownObject)\n }),\n )\n\n await executeHooks('onAfterNodeProcess', root as UnknownObject, context)\n }\n\n await bundler(rawSpecification)\n\n // Keep urlMappings when doing partial bundling to track hash values and handle collisions\n // For full bundling without urlMap config, remove the mappings to clean up the output\n if (!config.urlMap && !isPartialBundling) {\n // Remove the external document mappings from the output when doing a full bundle without urlMap config\n delete documentRoot[config.externalDocumentsMappingsKey]\n }\n\n return rawSpecification\n}\n"],
5
- "mappings": "AAAA,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAEzB,SAAS,yBAAyB;AAClC,SAAS,OAAO,kBAAkB;AAClC,SAAS,sBAAsB;AAG/B,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AACvB,SAAS,SAAS,mCAAmC;AAGrD,MAAM,SAAS,CAAC,UACd,SAAS,KAAK,KAAK,UAAU,SAAS,OAAO,MAAM,MAAM,MAAM;AAc1D,SAAS,YAAY,OAAe;AACzC,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,KAAK;AACzB,WAAO,IAAI,aAAa,WAAW,IAAI,aAAa;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAgBO,SAAS,WAAW,OAAe;AACxC,SAAO,CAAC,YAAY,KAAK,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,aAAa,KAAK;AACrE;AAaO,SAAS,WAAW,OAAwB;AACjD,SAAO,MAAM,WAAW,GAAG;AAC7B;AAiBA,SAAS,gBAAgB,OAAe,SAAiD;AACvF,QAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC;AAEpD,MAAI,QAAQ;AACV,WAAO,OAAO,KAAK,KAAK;AAAA,EAC1B;AAEA,SAAO,QAAQ,QAAQ;AAAA,IACrB,IAAI;AAAA,EACN,CAAC;AACH;AAkDO,SAAS,eAAe,KAAUA,OAAc,OAAkB;AACvE,MAAIA,UAAS,IAAI;AACf,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AAEA,QAAM,QAAQ,oBAAoBA,KAAI;AAEtC,MAAI,UAAU;AAEd,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,MAAM,MAAM,CAAC;AACnB,UAAM,SAAS,MAAM,MAAM,SAAS;AAEpC,UAAM,UAAU,MAAM,IAAI,CAAC;AAC3B,UAAM,gBAAgB,QAAQ,KAAK,WAAW,EAAE;AAEhD,QAAI,QAAQ;AACV,cAAQ,GAAG,IAAI;AAAA,IACjB,OAAO;AACL,UAAI,EAAE,OAAO,YAAY,OAAO,QAAQ,GAAG,MAAM,UAAU;AACzD,gBAAQ,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC;AAAA,MACvC;AACA,gBAAU,QAAQ,GAAG;AAAA,IACvB;AAAA,EACF;AACF;AAkBA,SAAS,qBAAqB,MAAc,cAAsB;AAChE,MAAI,YAAY,YAAY,GAAG;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI,YAAY,IAAI,GAAG;AACrB,UAAM,MAAM,IAAI,IAAI,IAAI;AAGxB,QAAI,aAAa,WAAW,GAAG,GAAG;AAChC,UAAI,WAAW;AACf,aAAO,IAAI,SAAS;AAAA,IACtB;AAEA,UAAM,aAAa,KAAK,KAAK,KAAK,QAAQ,IAAI,QAAQ,GAAG,YAAY;AACrE,WAAO,IAAI,IAAI,YAAY,IAAI,EAAE,SAAS;AAAA,EAC5C;AAEA,SAAO,KAAK,KAAK,KAAK,QAAQ,IAAI,GAAG,YAAY;AACnD;AAcO,SAAS,kBAAkB,OAAe,QAAkB;AACjE,MAAI,CAAC,WAAW,KAAK,GAAG;AACtB,UAAM;AAAA,EACR;AAEA,SAAO,KAAK,OAAO,IAAI,iBAAiB,EAAE,KAAK,GAAG,CAAC,GAAG,MAAM,UAAU,CAAC,CAAC;AAC1E;AA2BO,SAAS,2BAA2B,OAAgB,QAAkB;AAC3E,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,QAAQ,CAAC,OAAO,2BAA2B,IAAI,MAAM,CAAC;AAC5D;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB;AAAA,EACF;AAEA,SAAO,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,2BAA2B,IAAI,MAAM,CAAC;AAE3E,MAAI,OAAO,UAAU,YAAY,UAAU,SAAS,OAAO,MAAM,MAAM,MAAM,UAAU;AACrF,UAAM,MAAM,MAAM,MAAM;AAExB,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB;AAAA,IACF;AAEA,UAAM,MAAM,IAAI,kBAAkB,KAAK,MAAM;AAAA,EAC/C;AACF;AAsCO,MAAM,2BAA2B,CACtC,gBACA,gBACA,eACA,iBACA,aACA,kBAAkB,OAClB,iBAAiB,oBAAI,IAAI,MACtB;AACH,QAAM,kBAAkB,eAAe,gBAAgB,oBAAoB,aAAa,CAAC,EAAE;AAE3F,MAAI,eAAe,IAAI,eAAe,GAAG;AACvC;AAAA,EACF;AACA,iBAAe,IAAI,eAAe;AAElC,iBAAe,gBAAgB,eAAe,eAAe;AAG7D,QAAM,WAAW,CAAC,SAAkB;AAClC,QAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC;AAAA,IACF;AAEA,QAAI,UAAU,QAAQ,OAAO,KAAK,MAAM,MAAM,UAAU;AAKtD,UAAI,KAAK,MAAM,EAAE,WAAW,KAAK,eAAe,IAAI,kBAAkB,WAAW,CAAC,EAAE,GAAG;AACrF;AAAA,UACE;AAAA,UACA;AAAA,UACA,KAAK,MAAM,EAAE,UAAU,CAAC;AAAA,UACxB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF,WAES,iBAAiB;AACxB;AAAA,UACE;AAAA,UACA;AAAA,UACA,KAAK,MAAM,EAAE,UAAU,CAAC;AAAA,UACxB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,eAAW,SAAS,OAAO,OAAO,IAAI,GAAG;AACvC,eAAS,KAAK;AAAA,IAChB;AAAA,EACF;AAEA,WAAS,eAAe;AAC1B;AA0LO,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOxB,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOnB,2BAA2B;AAC7B;AAuEA,eAAsB,OAAO,OAA+B,QAAgB;AAE1E,SAAO,uBAAuB,OAAO,wBAAwB,WAAW;AACxE,SAAO,+BAA+B,OAAO,gCAAgC,WAAW;AAIxF,QAAM,QAAQ,OAAO,SAAS,oBAAI,IAAoC;AAEtE,QAAM,gBAAgB,OAAO,QAAQ,OAAO,CAAC,OAAO,GAAG,SAAS,QAAQ;AACxE,QAAM,kBAAkB,OAAO,QAAQ,OAAO,CAAC,OAAO,GAAG,SAAS,WAAW;AAO7E,QAAM,eAAe,YAAY;AAC/B,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AACA,UAAM,SAAS,MAAM,gBAAgB,OAAO,aAAa;AAEzD,QAAI,OAAO,MAAM,OAAO,OAAO,SAAS,UAAU;AAChD,aAAO,OAAO;AAAA,IAChB;AAEA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAGA,QAAM,mBAAmB,MAAM,aAAa;AAI5C,QAAM,eAAe,OAAO,QAAQ;AAGpC,QAAM,UAAU,WAAW,YAAY;AAOvC,QAAM,oBACH,OAAO,SAAS,UAAa,OAAO,SAAS,oBAAqB,OAAO,UAAU;AAGtF,QAAM,iBAAiB,OAAO,gBAAgB,oBAAI,IAAI;AAKtD,QAAM,gBAAgB,MAAM;AAC1B,QAAI,OAAO,QAAQ;AACjB,aAAO,OAAO;AAAA,IAChB;AAEA,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AAEA,QAAI,YAAY,KAAK,KAAK,WAAW,KAAK,GAAG;AAC3C,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAGA,MAAI,aAAa,OAAO,4BAA4B,MAAM,QAAW;AACnE,iBAAa,OAAO,4BAA4B,IAAI,CAAC;AAAA,EACvD;AACA,QAAM,EAAE,SAAS,IAAI;AAAA,IACnB,OAAO,YAAY;AAAA,IACnB,aAAa,OAAO,4BAA4B;AAAA,EAClD;AAaA,QAAM,eAAe,OACnB,SACG,SACe;AAElB,UAAM,OAAO,OAAO,QAAQ,IAAI;AAChC,QAAI,MAAM;AACR,YAAM,KAAK,GAAG,IAAI;AAAA,IACpB;AAEA,eAAW,UAAU,iBAAiB;AACpC,YAAM,aAAa,OAAO,IAAI;AAC9B,UAAI,YAAY;AACd,cAAM,WAAW,GAAG,IAAI;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,OACd,MACA,SAAiB,cAAc,GAC/B,gBAAgB,OAChB,QAAQ,GACR,cAAiC,CAAC,GAClC,SAAwB,SACrB;AAEH,QAAI,OAAO,UAAU,UAAa,QAAQ,OAAO,OAAO;AACtD;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,IAAI,KAAK,CAAC,MAAM,QAAQ,IAAI,GAAG;AAC3C;AAAA,IACF;AAIA,QAAI,eAAe,IAAI,IAAI,GAAG;AAC5B;AAAA,IACF;AAEA,mBAAe,IAAI,IAAI;AAEvB,UAAM,UAAU;AAAA,MACd,MAAM;AAAA,MACN,iBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAEA,UAAM,aAAa,uBAAuB,MAAuB,OAAO;AAExE,UAAM,KAAK,MAAM,IAAI;AAErB,QAAI,OAAO,IAAI,GAAG;AAChB,YAAM,MAAM,KAAK,MAAM;AACvB,YAAM,UAAU,aAAa,QAAQ,OAAO,KAAK,SAAS,MAAM,aAAa,KAAK,SAAS;AAS3F,YAAM,WAAW,kBAAkB,KAAK,MAAM,QAAQ,OAAO;AAE7D,UAAI,aAAa,QAAW;AAC1B,YAAI,mBAAmB;AACrB,gBAAM,WAAW,oBAAoB,IAAI,QAAQ,EAAE;AACnD,gBAAMC,UAAS,SAAS,SAAS,IAAI,eAAe,cAAc,SAAS,MAAM,GAAG,EAAE,CAAC,EAAE,QAAQ;AAEjG,gBAAM,cAAc,eAAe,cAAc,QAAQ;AAMzD,gBAAM,QAAQ,YAAY,OAAO,YAAY,SAAS,eAAe,QAAQ,GAAG,UAAUA,OAAM;AAAA,QAClG;AACA,cAAM,aAAa,sBAAsB,MAAuB,OAAO;AACvE;AAAA,MACF;AAEA,YAAM,CAAC,QAAQD,QAAO,EAAE,IAAI,IAAI,MAAM,KAAK,CAAC;AAI5C,YAAM,eAAe,qBAAqB,MAAM,QAAQ,MAAM;AAK9D,YAAM,iBAAiB,MAAM,SAAS,YAAY;AAElD,YAAM,OAAO,MAAM,IAAI,YAAY;AAEnC,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,cAAc,gBAAgB,cAAc,aAAa,CAAC;AAAA,MACtE;AAEA,YAAM,aAAa,kBAAkB,IAAI;AAGzC,YAAM,SAAS,MAAM,MAAM,IAAI,YAAY;AAE3C,UAAI,OAAO,IAAI;AAGb,YAAI,CAAC,MAAM;AAKT,cAAI,CAAC,SAAS;AAQZ,uCAA2B,OAAO,MAAM,CAAC,WAAW,mBAAmB,cAAc,CAAC;AAAA,UACxF;AAMA,gBAAM,QAAQ,OAAO,MAAM,UAAU,SAAS,cAAc,SAAS,QAAQ,GAAG;AAAA,YAC9E,OAAO;AAAA,YACP;AAAA,YACA,aAAa,OAAO,4BAA4B;AAAA,UAClD,CAAC;AAID;AAAA,YACE;AAAA,YACA,IAAI,OAAO,4BAA4B,IAAI,kBAAkB,cAAc,CAAC;AAAA,YAC5E;AAAA,UACF;AAAA,QACF;AAEA,YAAI,OAAO,cAAc,MAAM;AAI7B;AAAA,YACE;AAAA,YACA,EAAE,CAAC,OAAO,oBAAoB,GAAG,EAAE,CAAC,cAAc,GAAG,OAAO,KAAK,EAAE;AAAA,YACnE,kBAAkB,IAAIA,KAAI,IAAI,CAAC,OAAO,sBAAsB,cAAc,CAAC,EAAE,UAAU,CAAC;AAAA,YACxF,OAAO;AAAA,YACP;AAAA,UACF;AAAA,QACF,WAAW,CAAC,MAAM;AAMhB,yBAAe,cAAc,IAAI,OAAO,oBAAoB,IAAI,cAAc,IAAI,OAAO,IAAI;AAAA,QAC/F;AAKA,aAAK,OAAO,kBAAkB,IAAIA,KAAI,IAAI,CAAC,OAAO,sBAAsB,cAAc,CAAC;AAEvF,cAAM,aAAa,oBAAoB,IAAI;AAE3C,cAAM,aAAa,sBAAsB,MAAuB,OAAO;AACvE;AAAA,MACF;AAEA,YAAM,aAAa,kBAAkB,IAAI;AAEzC,YAAM,aAAa,sBAAsB,MAAuB,OAAO;AACvE,aAAO,QAAQ;AAAA,QACb,yCAAyC,YAAY;AAAA,MACvD;AAAA,IACF;AAKA,UAAM,QAAQ;AAAA,MACZ,OAAO,QAAQ,IAAI,EAAE,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM;AAC/C,YAAI,QAAQ,OAAO,wBAAwB,QAAQ,OAAO,8BAA8B;AACtF;AAAA,QACF;AAEA,cAAM,QAAQ,OAAO,MAAM,QAAQ,eAAe,QAAQ,GAAG,CAAC,GAAG,aAAa,GAAG,GAAG,IAAqB;AAAA,MAC3G,CAAC;AAAA,IACH;AAEA,UAAM,aAAa,sBAAsB,MAAuB,OAAO;AAAA,EACzE;AAEA,QAAM,QAAQ,gBAAgB;AAI9B,MAAI,CAAC,OAAO,UAAU,CAAC,mBAAmB;AAExC,WAAO,aAAa,OAAO,4BAA4B;AAAA,EACzD;AAEA,SAAO;AACT;",
6
- "names": ["path", "parent"]
4
+ "sourcesContent": ["import { isObject } from '@scalar/helpers/object/is-object'\n\nimport { convertToLocalRef } from '@/helpers/convert-to-local-ref'\nimport { getId, getSchemas } from '@/helpers/get-schemas'\nimport { getValueByPath } from '@/helpers/get-value-by-path'\nimport { isFilePath } from '@/helpers/is-file-path'\nimport { isHttpUrl } from '@/helpers/is-http-url'\nimport { resolveReferencePath } from '@/helpers/resolve-reference-path'\nimport { setValueAtPath } from '@/helpers/set-value-at-path'\nimport { toRelativePath } from '@/helpers/to-relative-path'\nimport type { UnknownObject } from '@/types'\n\nimport { escapeJsonPointer } from '../helpers/escape-json-pointer'\nimport { getSegmentsFromPath } from '../helpers/get-segments-from-path'\nimport { getHash, uniqueValueGeneratorFactory } from './value-generator'\n\n/** Type guard to check if a value is an object with a $ref property */\nconst hasRef = (value: unknown): value is UnknownObject & Record<'$ref', string> =>\n isObject(value) && '$ref' in value && typeof value['$ref'] === 'string'\n\n/**\n * Checks if a string is a local reference (starts with #)\n * @param value - The reference string to check\n * @returns true if the string is a local reference, false otherwise\n * @example\n * ```ts\n * isLocalRef('#/components/schemas/User') // true\n * isLocalRef('https://example.com/schema.json') // false\n * isLocalRef('./local-schema.json') // false\n * ```\n */\nexport function isLocalRef(value: string): boolean {\n return value.startsWith('#')\n}\n\nexport type ResolveResult = { ok: true; data: unknown; raw: string } | { ok: false }\n\n/**\n * Resolves a string by finding and executing the appropriate plugin.\n * @param value - The string to resolve (URL, file path, etc)\n * @param plugins - Array of plugins that can handle different types of strings\n * @returns A promise that resolves to either the content or an error result\n * @example\n * // Using a URL plugin\n * await resolveContents('https://example.com/schema.json', [urlPlugin])\n * // Using a file plugin\n * await resolveContents('./schemas/user.json', [filePlugin])\n * // No matching plugin returns { ok: false }\n * await resolveContents('#/components/schemas/User', [urlPlugin, filePlugin])\n */\nfunction resolveContents(value: string, plugins: LoaderPlugin[]): Promise<ResolveResult> {\n const plugin = plugins.find((p) => p.validate(value))\n\n if (plugin) {\n return plugin.exec(value)\n }\n\n return Promise.resolve({\n ok: false,\n })\n}\n\n/**\n * Prefixes an internal JSON reference with a given path prefix.\n * Takes a local reference (starting with #) and prepends the provided prefix segments.\n *\n * @param input - The internal reference string to prefix (must start with #)\n * @param prefix - Array of path segments to prepend to the reference\n * @returns The prefixed reference string\n * @throws Error if input is not a local reference\n * @example\n * prefixInternalRef('#/components/schemas/User', ['definitions'])\n * // Returns: '#/definitions/components/schemas/User'\n */\nexport function prefixInternalRef(input: string, prefix: string[]) {\n if (!isLocalRef(input)) {\n throw 'Please provide an internal ref'\n }\n\n return `#/${prefix.map(escapeJsonPointer).join('/')}${input.substring(1)}`\n}\n\n/**\n * Updates internal references in an object by adding a prefix to their paths.\n * Recursively traverses the input object and modifies any local $ref references\n * by prepending the given prefix to their paths. This is used when embedding external\n * documents to maintain correct reference paths relative to the main document.\n *\n * @param input - The object to update references in\n * @param prefix - Array of path segments to prepend to internal reference paths\n * @returns void\n * @example\n * ```ts\n * const input = {\n * foo: {\n * $ref: '#/components/schemas/User'\n * }\n * }\n * prefixInternalRefRecursive(input, ['definitions'])\n * // Result:\n * // {\n * // foo: {\n * // $ref: '#/definitions/components/schemas/User'\n * // }\n * // }\n * ```\n */\nexport function prefixInternalRefRecursive(input: unknown, prefix: string[]) {\n if (Array.isArray(input)) {\n input.forEach((el) => prefixInternalRefRecursive(el, prefix))\n return\n }\n\n if (!isObject(input)) {\n return\n }\n\n Object.values(input).forEach((el) => prefixInternalRefRecursive(el, prefix))\n\n if (typeof input === 'object' && '$ref' in input && typeof input['$ref'] === 'string') {\n const ref = input['$ref']\n\n if (!isLocalRef(ref)) {\n return\n }\n\n input['$ref'] = prefixInternalRef(ref, prefix)\n }\n}\n\n/**\n * Resolves and copies referenced values from a source document to a target document.\n * This function traverses the document and copies referenced values to the target document,\n * while tracking processed references to avoid duplicates. It only processes references\n * that belong to the same external document.\n *\n * @param targetDocument - The document to copy referenced values to\n * @param sourceDocument - The source document containing the references\n * @param referencePath - The JSON pointer path to the reference\n * @param externalRefsKey - The key used for external references (e.g. 'x-ext')\n * @param documentKey - The key identifying the external document\n * @param bundleLocalRefs - Also bundles the local refs\n * @param processedNodes - Set of already processed nodes to prevent duplicates\n * @example\n * ```ts\n * const source = {\n * components: {\n * schemas: {\n * User: {\n * $ref: '#/x-ext/users~1schema/definitions/Person'\n * }\n * }\n * }\n * }\n *\n * const target = {}\n * resolveAndCopyReferences(\n * target,\n * source,\n * '/components/schemas/User',\n * 'x-ext',\n * 'users/schema'\n * )\n * // Result: target will contain the User schema with resolved references\n * ```\n */\nexport const resolveAndCopyReferences = (\n targetDocument: unknown,\n sourceDocument: unknown,\n referencePath: string,\n externalRefsKey: string,\n documentKey: string,\n bundleLocalRefs = false,\n processedNodes = new Set(),\n) => {\n const referencedValue = getValueByPath(sourceDocument, getSegmentsFromPath(referencePath)).value\n\n if (processedNodes.has(referencedValue)) {\n return\n }\n processedNodes.add(referencedValue)\n\n setValueAtPath(targetDocument, referencePath, referencedValue)\n\n // Do the same for each local ref\n const traverse = (node: unknown) => {\n if (!node || typeof node !== 'object') {\n return\n }\n\n if ('$ref' in node && typeof node['$ref'] === 'string') {\n // We only process references from the same external document because:\n // 1. Other documents will be handled in separate recursive branches\n // 2. The source document only contains the current document's content\n // This prevents undefined behavior and maintains proper document boundaries\n if (node['$ref'].startsWith(`#/${externalRefsKey}/${escapeJsonPointer(documentKey)}`)) {\n resolveAndCopyReferences(\n targetDocument,\n sourceDocument,\n node['$ref'].substring(1),\n externalRefsKey,\n documentKey,\n bundleLocalRefs,\n processedNodes,\n )\n }\n // Bundle the local refs as well\n else if (bundleLocalRefs) {\n resolveAndCopyReferences(\n targetDocument,\n sourceDocument,\n node['$ref'].substring(1),\n externalRefsKey,\n documentKey,\n bundleLocalRefs,\n processedNodes,\n )\n }\n }\n\n for (const value of Object.values(node)) {\n traverse(value)\n }\n }\n\n traverse(referencedValue)\n}\n\n/**\n * A loader plugin for resolving external references during bundling.\n * Loader plugins are responsible for handling specific types of external references,\n * such as files, URLs, or custom protocols. Each loader plugin must provide:\n *\n * - A `validate` function to determine if the plugin can handle a given reference string.\n * - An `exec` function to asynchronously fetch and resolve the referenced data,\n * returning a Promise that resolves to a `ResolveResult`.\n *\n * Loader plugins enable extensible support for different reference sources in the bundler.\n *\n * @property type - The plugin type, always 'loader' for loader plugins.\n * @property validate - Function to check if the plugin can handle a given reference value.\n * @property exec - Function to fetch and resolve the reference, returning the resolved data.\n */\nexport type LoaderPlugin = {\n type: 'loader'\n // Returns true if this plugin can handle the given reference value\n validate: (value: string) => boolean\n // Asynchronously fetches and resolves the reference, returning the resolved data\n exec: (value: string) => Promise<ResolveResult>\n}\n\n/**\n * Context information for a node during traversal or processing.\n *\n * Note: The `path` parameter represents the path to the current node being processed.\n * If you are performing a partial bundle (i.e., providing a custom root), this path will be relative\n * to the root you provide, not the absolute root of the original document. You may need to prefix\n * it with your own base path if you want to construct a full path from the absolute document root.\n *\n * - `path`: The JSON pointer path (as an array of strings) from the document root to the current node.\n * - `resolutionCache`: A cache for storing promises of resolved references.\n */\ntype NodeProcessContext = {\n path: readonly string[]\n resolutionCache: Map<string, Promise<Readonly<ResolveResult>>>\n parentNode: UnknownObject | null\n rootNode: UnknownObject\n loaders: LoaderPlugin[]\n}\n\n/**\n * A plugin type for lifecycle hooks, allowing custom logic to be injected into the bundler's process.\n * This type extends the Config['hooks'] interface and is identified by type: 'lifecycle'.\n */\nexport type LifecyclePlugin = { type: 'lifecycle' } & Config['hooks']\n\n/**\n * Represents a plugin used by the bundler for extensibility.\n *\n * Plugins can be either:\n * - Loader plugins: Responsible for resolving and loading external references (e.g., from files, URLs, or custom sources).\n * - Lifecycle plugins: Provide lifecycle hooks to customize or extend the bundling process.\n *\n * Loader plugins must implement:\n * - `validate`: Checks if the plugin can handle a given reference value.\n * - `exec`: Asynchronously resolves and returns the referenced data.\n *\n * Lifecycle plugins extend the bundler's lifecycle hooks for custom logic.\n */\nexport type Plugin = LoaderPlugin | LifecyclePlugin\n\n/**\n * Configuration options for the bundler.\n * Controls how external references are resolved and processed during bundling.\n */\ntype Config = {\n /**\n * Array of plugins that handle resolving references from different sources.\n * Each plugin is responsible for fetching and processing data from specific sources\n * like URLs or the filesystem.\n */\n plugins: Plugin[]\n\n /**\n * Optional root object that serves as the base document when bundling a subpart.\n * This allows resolving references relative to the root document's location,\n * ensuring proper path resolution for nested references.\n */\n root?: UnknownObject\n\n /**\n * Optional maximum depth for reference resolution.\n * Limits how deeply the bundler will follow and resolve nested $ref pointers.\n * Useful for preventing infinite recursion or excessive resource usage.\n */\n depth?: number\n\n /**\n * Optional origin path for the bundler.\n * Used to resolve relative paths in references, especially when the input is a string URL or file path.\n * If not provided, the bundler will use the input value as the origin.\n */\n origin?: string\n\n /**\n * Optional cache to store promises of resolved references.\n * Helps avoid duplicate fetches/reads of the same resource by storing\n * the resolution promises for reuse.\n */\n cache?: Map<string, Promise<ResolveResult>>\n\n /**\n * Cache of visited nodes during partial bundling.\n * Used to prevent re-bundling the same tree multiple times when doing partial bundling,\n * improving performance by avoiding redundant processing of already bundled sections.\n */\n visitedNodes?: Set<unknown>\n\n /**\n * Enable tree shaking to optimize the bundle size.\n * When enabled, only the parts of external documents that are actually referenced\n * will be included in the final bundle.\n */\n treeShake: boolean\n\n /**\n * Optional flag to generate a URL map.\n * When enabled, tracks the original source URLs of bundled references\n * in an section for reference mapping defined by externalDocumentsMappingsKey.\n */\n urlMap?: boolean\n\n /**\n * Custom OpenAPI extension key used to store external references.\n * This key will contain all bundled external documents.\n * The key is used to maintain a clean separation between the main\n * OpenAPI document and its bundled external references.\n * @default 'x-ext'\n */\n externalDocumentsKey?: string\n\n /**\n * Custom OpenAPI extension key used to maintain a mapping between\n * hashed keys and their original URLs.\n * This mapping is essential for tracking the source of bundled references\n * @default 'x-ext-urls'\n */\n externalDocumentsMappingsKey?: string\n\n /**\n * Optional function to compress input URLs or file paths before bundling.\n * Returns either a Promise resolving to the compressed string or the compressed string directly.\n */\n compress?: (value: string) => Promise<string> | string\n\n /**\n * Optional hooks to monitor the bundler's lifecycle.\n * Allows tracking the progress and status of reference resolution.\n */\n hooks?: Partial<{\n /**\n * Optional hook called when the bundler starts resolving a $ref.\n * Useful for tracking or logging the beginning of a reference resolution.\n */\n onResolveStart: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook called when the bundler fails to resolve a $ref.\n * Can be used for error handling, logging, or custom error reporting.\n */\n onResolveError: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook called when the bundler successfully resolves a $ref.\n * Useful for tracking successful resolutions or custom post-processing.\n */\n onResolveSuccess: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook invoked before processing a node.\n * Can be used for preprocessing, mutation, or custom logic before the node is handled by the bundler.\n */\n onBeforeNodeProcess: (node: UnknownObject, context: NodeProcessContext) => void | Promise<void>\n /**\n * Optional hook invoked after processing a node.\n * Useful for postprocessing, cleanup, or custom logic after the node has been handled by the bundler.\n */\n onAfterNodeProcess: (node: UnknownObject, context: NodeProcessContext) => void | Promise<void>\n }>\n}\n\n/**\n * Extension keys used for bundling external references in OpenAPI documents.\n * These custom extensions help maintain the structure and traceability of bundled documents.\n */\nexport const extensions = {\n /**\n * Custom OpenAPI extension key used to store external references.\n * This key will contain all bundled external documents.\n * The x-ext key is used to maintain a clean separation between the main\n * OpenAPI document and its bundled external references.\n */\n externalDocuments: 'x-ext',\n\n /**\n * Custom OpenAPI extension key used to maintain a mapping between\n * hashed keys and their original URLs in x-ext.\n * This mapping is essential for tracking the source of bundled references\n */\n externalDocumentsMappings: 'x-ext-urls',\n} as const\n\ntype HookFn<T extends keyof Config['hooks']> = NonNullable<Config['hooks'][T]> extends (...args: infer A) => any\n ? (...args: A) => any\n : never\n\n/**\n * Bundles an OpenAPI specification by resolving all external references.\n * This function traverses the input object recursively and embeds external $ref\n * references into an x-ext section. External references can be URLs or local files.\n * The original $refs are updated to point to their embedded content in the x-ext section.\n * If the input is an object, it will be modified in place by adding an x-ext\n * property to store resolved external references.\n *\n * @param input - The OpenAPI specification to bundle. Can be either an object or string.\n * If a string is provided, it will be resolved using the provided plugins.\n * If no plugin can process the input, the onReferenceError hook will be invoked\n * and an error will be emitted to the console.\n * @param config - Configuration object containing plugins and options for bundling OpenAPI specifications\n * @returns A promise that resolves to the bundled specification with all references embedded\n * @example\n * // Example with object input\n * const spec = {\n * paths: {\n * '/users': {\n * $ref: 'https://example.com/schemas/users.yaml'\n * }\n * }\n * }\n *\n * const bundled = await bundle(spec, {\n * plugins: [fetchUrls()],\n * treeShake: true,\n * urlMap: true,\n * hooks: {\n * onResolveStart: (ref) => console.log('Resolving:', ref.$ref),\n * onResolveSuccess: (ref) => console.log('Resolved:', ref.$ref),\n * onResolveError: (ref) => console.log('Failed to resolve:', ref.$ref)\n * }\n * })\n * // Result:\n * // {\n * // paths: {\n * // '/users': {\n * // $ref: '#/x-ext/abc123'\n * // }\n * // },\n * // 'x-ext': {\n * // 'abc123': {\n * // // Resolved content from users.yaml\n * // }\n * // },\n * // 'x-ext-urls': {\n * // 'https://example.com/schemas/users.yaml': 'abc123'\n * // }\n * // }\n *\n * // Example with URL input\n * const bundledFromUrl = await bundle('https://example.com/openapi.yaml', {\n * plugins: [fetchUrls()],\n * treeShake: true,\n * urlMap: true,\n * hooks: {\n * onResolveStart: (ref) => console.log('Resolving:', ref.$ref),\n * onResolveSuccess: (ref) => console.log('Resolved:', ref.$ref),\n * onResolveError: (ref) => console.log('Failed to resolve:', ref.$ref)\n * }\n * })\n * // The function will first fetch the OpenAPI spec from the URL,\n * // then bundle all its external references into the x-ext section\n */\nexport async function bundle(input: UnknownObject | string, config: Config) {\n // Set the default external documents key and mappings key if not provided in the config\n config.externalDocumentsKey = config.externalDocumentsKey ?? extensions.externalDocuments\n config.externalDocumentsMappingsKey = config.externalDocumentsMappingsKey ?? extensions.externalDocumentsMappings\n\n // Cache for storing promises of resolved external references (URLs and local files)\n // to avoid duplicate fetches/reads of the same resource\n const cache = config.cache ?? new Map<string, Promise<ResolveResult>>()\n\n const loaderPlugins = config.plugins.filter((it) => it.type === 'loader')\n const lifecyclePlugin = config.plugins.filter((it) => it.type === 'lifecycle')\n\n /**\n * Resolves the input value by either returning it directly if it's not a string,\n * or attempting to resolve it using the provided plugins if it is a string.\n * @returns The resolved input data or throws an error if resolution fails\n */\n const resolveInput = async () => {\n if (typeof input !== 'string') {\n return input\n }\n const result = await resolveContents(input, loaderPlugins)\n\n if (result.ok && typeof result.data === 'object') {\n return result.data\n }\n\n throw new Error(\n 'Failed to resolve input: Please provide a valid string value or pass a loader to process the input',\n )\n }\n\n // Resolve the input specification, which could be either a direct object or a string URL/path\n const rawSpecification = await resolveInput()\n\n // Document root used to write all external documents\n // We need this when we want to do a partial bundle of a document\n const documentRoot = config.root ?? rawSpecification\n\n // Extract all $id and $anchor values from the document to identify local schemas\n const schemas = getSchemas(documentRoot)\n\n // Determines if the bundling operation is partial.\n // Partial bundling occurs when:\n // - A root document is provided that is different from the raw specification being bundled, or\n // - A maximum depth is specified in the config.\n // In these cases, only a subset of the document may be bundled.\n const isPartialBundling =\n (config.root !== undefined && config.root !== rawSpecification) || config.depth !== undefined\n\n // Set of nodes that have already been processed during bundling to prevent duplicate processing\n const processedNodes = config.visitedNodes ?? new Set()\n\n // Determines the initial origin path for the bundler based on the input type.\n // For string inputs that are URLs or file paths, uses the input as the origin.\n // For non-string inputs or other string types, returns an '/' as a root path.\n const getDefaultOrigin = () => {\n // Id field is the first priority\n const id = getId(documentRoot)\n if (id) {\n return id\n }\n\n if (config.origin) {\n return config.origin\n }\n\n if (typeof input !== 'string') {\n return '/'\n }\n\n if (isHttpUrl(input) || isFilePath(input)) {\n return input\n }\n\n return '/'\n }\n\n const defaultOrigin = getDefaultOrigin()\n\n // Create the cache to store the compressed values to their map values\n if (documentRoot[config.externalDocumentsMappingsKey] === undefined) {\n documentRoot[config.externalDocumentsMappingsKey] = {}\n }\n const { generate } = uniqueValueGeneratorFactory(\n config.compress ?? getHash,\n documentRoot[config.externalDocumentsMappingsKey],\n )\n\n /**\n * Executes lifecycle hooks defined both in the bundler configuration and any extended lifecycle plugins.\n * This utility function ensures that all relevant hooks for a given event type are called in order:\n * - First, the hook directly provided via the config (if present)\n * - Then, all matching hooks from registered lifecycle plugins (if present)\n *\n * Hooks are awaited in sequence for the given event type and argument list.\n *\n * @param type The hook event type, corresponding to a key of Config['hooks'].\n * @param args Arguments to pass to the hook function, matching HookFn<T>.\n */\n const executeHooks = async <T extends keyof Config['hooks']>(\n type: T,\n ...args: Parameters<HookFn<T>>\n ): Promise<void> => {\n // Run hook defined directly in config, if present\n const hook = config.hooks?.[type] as HookFn<T> | undefined\n if (hook) {\n await hook(...args)\n }\n // Additionally run the hook for every lifecycle plugin, if present\n for (const plugin of lifecyclePlugin) {\n const pluginHook = plugin[type] as HookFn<T> | undefined\n if (pluginHook) {\n await pluginHook(...args)\n }\n }\n }\n\n const bundler = async (\n root: unknown,\n origin: string = defaultOrigin,\n isChunkParent = false,\n depth = 0,\n currentPath: readonly string[] = [],\n parent: UnknownObject = null,\n ) => {\n // If a maximum depth is set in the config, stop bundling when the current depth reaches or exceeds it\n if (config.depth !== undefined && depth > config.depth) {\n return\n }\n\n if (!isObject(root) && !Array.isArray(root)) {\n return\n }\n\n // Skip if this node has already been processed to prevent infinite recursion\n // and duplicate processing of the same node\n if (processedNodes.has(root)) {\n return\n }\n // Mark this node as processed before continuing\n processedNodes.add(root)\n\n const context = {\n path: currentPath,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n }\n\n await executeHooks('onBeforeNodeProcess', root as UnknownObject, context)\n\n const id = getId(root)\n\n if (hasRef(root)) {\n const ref = root['$ref']\n const isChunk = '$global' in root && typeof root['$global'] === 'boolean' && root['$global']\n\n // Try to convert the reference to a local reference if possible\n // This handles cases where the reference points to a local schema using $id or $anchor\n // If it can be converted to a local reference, we do not need to bundle it\n // and can skip further processing for this reference\n // In case of partial bundling, we still need to ensure that all dependencies\n // of the local reference are bundled to create a complete and self-contained partial bundle\n // This is important to maintain the integrity of the partial bundle\n const localRef = convertToLocalRef(ref, id ?? origin, schemas)\n\n if (localRef !== undefined) {\n if (isPartialBundling) {\n const segments = getSegmentsFromPath(`/${localRef}`)\n const parent = segments.length > 0 ? getValueByPath(documentRoot, segments.slice(0, -1)).value : undefined\n\n const targetValue = getValueByPath(documentRoot, segments)\n\n // When doing partial bundling, we need to recursively bundle all dependencies\n // referenced by this local reference to ensure the partial bundle is complete.\n // This includes not just the direct reference but also all its dependencies,\n // creating a complete and self-contained partial bundle.\n await bundler(targetValue.value, targetValue.context, isChunkParent, depth + 1, segments, parent)\n }\n await executeHooks('onAfterNodeProcess', root as UnknownObject, context)\n return\n }\n\n const [prefix, path = ''] = ref.split('#', 2)\n\n // Combine the current origin with the new path to resolve relative references\n // correctly within the context of the external file being processed\n const resolvedPath = resolveReferencePath(id ?? origin, prefix)\n const relativePath = toRelativePath(resolvedPath, defaultOrigin)\n\n // Generate a unique compressed path for the external document\n // This is used as a key to store and reference the bundled external document\n // The compression helps reduce the overall file size of the bundled document\n const compressedPath = await generate(relativePath)\n\n const seen = cache.has(relativePath)\n\n if (!seen) {\n cache.set(relativePath, resolveContents(resolvedPath, loaderPlugins))\n }\n\n await executeHooks('onResolveStart', root)\n\n // Resolve the remote document\n const result = await cache.get(relativePath)\n\n if (result.ok) {\n // Process the result only once to avoid duplicate processing and prevent multiple prefixing\n // of internal references, which would corrupt the reference paths\n if (!seen) {\n // Skip prefixing for chunks since they are meant to be self-contained and their\n // internal references should remain relative to their original location. Chunks\n // are typically used for modular components that need to maintain their own\n // reference context without being affected by the main document's structure.\n if (!isChunk) {\n // Update internal references in the resolved document to use the correct base path.\n // When we embed external documents, their internal references need to be updated to\n // maintain the correct path context relative to the main document. This is crucial\n // because internal references in the external document are relative to its original\n // location, but when embedded, they need to be relative to their new location in\n // the main document's x-ext section. Without this update, internal references\n // would point to incorrect locations and break the document structure.\n prefixInternalRefRecursive(result.data, [extensions.externalDocuments, compressedPath])\n }\n\n // Recursively process the resolved content\n // to handle any nested references it may contain. We pass the resolvedPath as the new origin\n // to ensure any relative references within this content are resolved correctly relative to\n // their new location in the bundled document.\n await bundler(result.data, isChunk ? origin : resolvedPath, isChunk, depth + 1, [\n config.externalDocumentsKey,\n compressedPath,\n documentRoot[config.externalDocumentsMappingsKey],\n ])\n\n // Store the mapping between hashed keys and original URLs in x-ext-urls\n // This allows tracking which external URLs were bundled and their corresponding locations\n setValueAtPath(\n documentRoot,\n `/${config.externalDocumentsMappingsKey}/${escapeJsonPointer(compressedPath)}`,\n relativePath,\n )\n }\n\n if (config.treeShake === true) {\n // Store only the subtree that is actually used\n // This optimizes the bundle size by only including the parts of the external document\n // that are referenced, rather than the entire document\n resolveAndCopyReferences(\n documentRoot,\n { [config.externalDocumentsKey]: { [compressedPath]: result.data } },\n prefixInternalRef(`#${path}`, [config.externalDocumentsKey, compressedPath]).substring(1),\n config.externalDocumentsKey,\n compressedPath,\n )\n } else if (!seen) {\n // Store the external document in the main document's x-ext key\n // When tree shaking is disabled, we include the entire external document\n // This preserves all content and is faster since we don't need to analyze and copy\n // specific parts. This approach is ideal when storing the result in memory\n // as it avoids the overhead of tree shaking operations\n setValueAtPath(documentRoot, `/${config.externalDocumentsKey}/${compressedPath}`, result.data)\n }\n\n // Update the $ref to point to the embedded document in x-ext\n // This is necessary because we need to maintain the correct path context\n // for the embedded document while preserving its internal structure\n root.$ref = prefixInternalRef(`#${path}`, [config.externalDocumentsKey, compressedPath])\n\n await executeHooks('onResolveSuccess', root)\n\n await executeHooks('onAfterNodeProcess', root as UnknownObject, context)\n return\n }\n\n await executeHooks('onResolveError', root)\n\n await executeHooks('onAfterNodeProcess', root as UnknownObject, context)\n return console.warn(\n `Failed to resolve external reference \"${resolvedPath}\". The reference may be invalid, inaccessible, or missing a loader for this type of reference.`,\n )\n }\n\n // Recursively traverse all child properties of the current object to resolve nested $ref references.\n // This step ensures that any $refs located deeper within the object hierarchy are discovered and processed.\n // We explicitly skip the extension keys (x-ext and x-ext-urls) to avoid reprocessing already bundled or mapped content.\n await Promise.all(\n Object.entries(root).map(async ([key, value]) => {\n if (key === config.externalDocumentsKey || key === config.externalDocumentsMappingsKey) {\n return\n }\n\n await bundler(value, id ?? origin, isChunkParent, depth + 1, [...currentPath, key], root as UnknownObject)\n }),\n )\n\n await executeHooks('onAfterNodeProcess', root as UnknownObject, context)\n }\n\n await bundler(rawSpecification)\n\n // Keep urlMappings when doing partial bundling to track hash values and handle collisions\n // For full bundling without urlMap config, remove the mappings to clean up the output\n if (!config.urlMap && !isPartialBundling) {\n // Remove the external document mappings from the output when doing a full bundle without urlMap config\n delete documentRoot[config.externalDocumentsMappingsKey]\n }\n\n return rawSpecification\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["parent"]
7
7
  }
@@ -1,5 +1,5 @@
1
- import { isRemoteUrl } from "../../../bundle/bundle.js";
2
1
  import { createLimiter } from "../../../bundle/create-limiter.js";
2
+ import { isHttpUrl } from "../../../helpers/is-http-url.js";
3
3
  import { normalize } from "../../../helpers/normalize.js";
4
4
  const getHost = (url) => {
5
5
  try {
@@ -45,7 +45,7 @@ function fetchUrls(config) {
45
45
  const limiter = config?.limit ? createLimiter(config.limit) : (fn) => fn();
46
46
  return {
47
47
  type: "loader",
48
- validate: isRemoteUrl,
48
+ validate: isHttpUrl,
49
49
  exec: (value) => fetchUrl(value, limiter, config)
50
50
  };
51
51
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/bundle/plugins/fetch-urls/index.ts"],
4
- "sourcesContent": ["import type { LoaderPlugin, ResolveResult } from '@/bundle'\nimport { isRemoteUrl } from '@/bundle/bundle'\nimport { createLimiter } from '@/bundle/create-limiter'\nimport { normalize } from '@/helpers/normalize'\n\ntype FetchConfig = Partial<{\n headers: { headers: HeadersInit; domains: string[] }[]\n fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>\n}>\n\n/**\n * Safely checks for host from a URL\n * Needed because we cannot create a URL from a relative remote URL ex: examples/openapi.json\n */\nconst getHost = (url: string): string | null => {\n try {\n return new URL(url).host\n } catch {\n return null\n }\n}\n\n/**\n * Fetches and normalizes data from a remote URL\n * @param url - The URL to fetch data from\n * @returns A promise that resolves to either the normalized data or an error result\n * @example\n * ```ts\n * const result = await fetchUrl('https://api.example.com/data.json')\n * if (result.ok) {\n * console.log(result.data) // The normalized data\n * } else {\n * console.log('Failed to fetch data')\n * }\n * ```\n */\nexport async function fetchUrl(\n url: string,\n limiter: <T>(fn: () => Promise<T>) => Promise<T>,\n config?: FetchConfig,\n): Promise<ResolveResult> {\n try {\n const host = getHost(url)\n\n // Get the headers that match the domain\n const headers = config?.headers?.find((a) => a.domains.find((d) => d === host) !== undefined)?.headers\n\n const exec = config?.fetch ?? fetch\n\n const result = await limiter(() =>\n exec(url, {\n headers,\n }),\n )\n\n if (result.ok) {\n const body = await result.text()\n\n return {\n ok: true,\n data: normalize(body),\n raw: body,\n }\n }\n\n const contentType = result.headers.get('Content-Type') ?? ''\n\n // Warn if the content type is HTML or XML as we only support JSON/YAML\n if (['text/html', 'application/xml'].includes(contentType)) {\n console.warn(`[WARN] We only support JSON/YAML formats, received ${contentType}`)\n }\n\n console.warn(`[WARN] Fetch failed with status ${result.status} ${result.statusText} for URL: ${url}`)\n return {\n ok: false,\n }\n } catch {\n console.warn(`[WARN] Failed to parse JSON/YAML from URL: ${url}`)\n return {\n ok: false,\n }\n }\n}\n\n/**\n * Creates a plugin for handling remote URL references.\n * This plugin validates and fetches data from HTTP/HTTPS URLs.\n *\n * @returns A plugin object with validate and exec functions\n * @example\n * const urlPlugin = fetchUrls()\n * if (urlPlugin.validate('https://example.com/schema.json')) {\n * const result = await urlPlugin.exec('https://example.com/schema.json')\n * }\n */\nexport function fetchUrls(config?: FetchConfig & Partial<{ limit: number | null }>): LoaderPlugin {\n // If there is a limit specified we limit the number of concurrent calls\n const limiter = config?.limit ? createLimiter(config.limit) : <T>(fn: () => Promise<T>) => fn()\n\n return {\n type: 'loader',\n validate: isRemoteUrl,\n exec: (value) => fetchUrl(value, limiter, config),\n }\n}\n"],
5
- "mappings": "AACA,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAW1B,MAAM,UAAU,CAAC,QAA+B;AAC9C,MAAI;AACF,WAAO,IAAI,IAAI,GAAG,EAAE;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAgBA,eAAsB,SACpB,KACA,SACA,QACwB;AACxB,MAAI;AACF,UAAM,OAAO,QAAQ,GAAG;AAGxB,UAAM,UAAU,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,CAAC,MAAM,MAAM,IAAI,MAAM,MAAS,GAAG;AAE/F,UAAM,OAAO,QAAQ,SAAS;AAE9B,UAAM,SAAS,MAAM;AAAA,MAAQ,MAC3B,KAAK,KAAK;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,IAAI;AACb,YAAM,OAAO,MAAM,OAAO,KAAK;AAE/B,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM,UAAU,IAAI;AAAA,QACpB,KAAK;AAAA,MACP;AAAA,IACF;AAEA,UAAM,cAAc,OAAO,QAAQ,IAAI,cAAc,KAAK;AAG1D,QAAI,CAAC,aAAa,iBAAiB,EAAE,SAAS,WAAW,GAAG;AAC1D,cAAQ,KAAK,sDAAsD,WAAW,EAAE;AAAA,IAClF;AAEA,YAAQ,KAAK,mCAAmC,OAAO,MAAM,IAAI,OAAO,UAAU,aAAa,GAAG,EAAE;AACpG,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF,QAAQ;AACN,YAAQ,KAAK,8CAA8C,GAAG,EAAE;AAChE,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF;AACF;AAaO,SAAS,UAAU,QAAwE;AAEhG,QAAM,UAAU,QAAQ,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAI,OAAyB,GAAG;AAE9F,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM,CAAC,UAAU,SAAS,OAAO,SAAS,MAAM;AAAA,EAClD;AACF;",
4
+ "sourcesContent": ["import type { LoaderPlugin, ResolveResult } from '@/bundle'\nimport { createLimiter } from '@/bundle/create-limiter'\nimport { isHttpUrl } from '@/helpers/is-http-url'\nimport { normalize } from '@/helpers/normalize'\n\ntype FetchConfig = Partial<{\n headers: { headers: HeadersInit; domains: string[] }[]\n fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>\n}>\n\n/**\n * Safely checks for host from a URL\n * Needed because we cannot create a URL from a relative remote URL ex: examples/openapi.json\n */\nconst getHost = (url: string): string | null => {\n try {\n return new URL(url).host\n } catch {\n return null\n }\n}\n\n/**\n * Fetches and normalizes data from a remote URL\n * @param url - The URL to fetch data from\n * @returns A promise that resolves to either the normalized data or an error result\n * @example\n * ```ts\n * const result = await fetchUrl('https://api.example.com/data.json')\n * if (result.ok) {\n * console.log(result.data) // The normalized data\n * } else {\n * console.log('Failed to fetch data')\n * }\n * ```\n */\nexport async function fetchUrl(\n url: string,\n limiter: <T>(fn: () => Promise<T>) => Promise<T>,\n config?: FetchConfig,\n): Promise<ResolveResult> {\n try {\n const host = getHost(url)\n\n // Get the headers that match the domain\n const headers = config?.headers?.find((a) => a.domains.find((d) => d === host) !== undefined)?.headers\n\n const exec = config?.fetch ?? fetch\n\n const result = await limiter(() =>\n exec(url, {\n headers,\n }),\n )\n\n if (result.ok) {\n const body = await result.text()\n\n return {\n ok: true,\n data: normalize(body),\n raw: body,\n }\n }\n\n const contentType = result.headers.get('Content-Type') ?? ''\n\n // Warn if the content type is HTML or XML as we only support JSON/YAML\n if (['text/html', 'application/xml'].includes(contentType)) {\n console.warn(`[WARN] We only support JSON/YAML formats, received ${contentType}`)\n }\n\n console.warn(`[WARN] Fetch failed with status ${result.status} ${result.statusText} for URL: ${url}`)\n return {\n ok: false,\n }\n } catch {\n console.warn(`[WARN] Failed to parse JSON/YAML from URL: ${url}`)\n return {\n ok: false,\n }\n }\n}\n\n/**\n * Creates a plugin for handling remote URL references.\n * This plugin validates and fetches data from HTTP/HTTPS URLs.\n *\n * @returns A plugin object with validate and exec functions\n * @example\n * const urlPlugin = fetchUrls()\n * if (urlPlugin.validate('https://example.com/schema.json')) {\n * const result = await urlPlugin.exec('https://example.com/schema.json')\n * }\n */\nexport function fetchUrls(config?: FetchConfig & Partial<{ limit: number | null }>): LoaderPlugin {\n // If there is a limit specified we limit the number of concurrent calls\n const limiter = config?.limit ? createLimiter(config.limit) : <T>(fn: () => Promise<T>) => fn()\n\n return {\n type: 'loader',\n validate: isHttpUrl,\n exec: (value) => fetchUrl(value, limiter, config),\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAW1B,MAAM,UAAU,CAAC,QAA+B;AAC9C,MAAI;AACF,WAAO,IAAI,IAAI,GAAG,EAAE;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAgBA,eAAsB,SACpB,KACA,SACA,QACwB;AACxB,MAAI;AACF,UAAM,OAAO,QAAQ,GAAG;AAGxB,UAAM,UAAU,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,CAAC,MAAM,MAAM,IAAI,MAAM,MAAS,GAAG;AAE/F,UAAM,OAAO,QAAQ,SAAS;AAE9B,UAAM,SAAS,MAAM;AAAA,MAAQ,MAC3B,KAAK,KAAK;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,IAAI;AACb,YAAM,OAAO,MAAM,OAAO,KAAK;AAE/B,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM,UAAU,IAAI;AAAA,QACpB,KAAK;AAAA,MACP;AAAA,IACF;AAEA,UAAM,cAAc,OAAO,QAAQ,IAAI,cAAc,KAAK;AAG1D,QAAI,CAAC,aAAa,iBAAiB,EAAE,SAAS,WAAW,GAAG;AAC1D,cAAQ,KAAK,sDAAsD,WAAW,EAAE;AAAA,IAClF;AAEA,YAAQ,KAAK,mCAAmC,OAAO,MAAM,IAAI,OAAO,UAAU,aAAa,GAAG,EAAE;AACpG,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF,QAAQ;AACN,YAAQ,KAAK,8CAA8C,GAAG,EAAE;AAChE,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF;AACF;AAaO,SAAS,UAAU,QAAwE;AAEhG,QAAM,UAAU,QAAQ,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAI,OAAyB,GAAG;AAE9F,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM,CAAC,UAAU,SAAS,OAAO,SAAS,MAAM;AAAA,EAClD;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- import { isFilePath } from "../../../bundle/bundle.js";
1
+ import { isFilePath } from "../../../helpers/is-file-path.js";
2
2
  import { normalize } from "../../../helpers/normalize.js";
3
3
  async function readFile(path) {
4
4
  const fs = typeof window === "undefined" ? await import("node:fs/promises") : void 0;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/bundle/plugins/read-files/index.ts"],
4
- "sourcesContent": ["import type { LoaderPlugin, ResolveResult } from '@/bundle'\nimport { isFilePath } from '@/bundle/bundle'\nimport { normalize } from '@/helpers/normalize'\n\n/**\n * Reads and normalizes data from a local file\n * @param path - The file path to read from\n * @returns A promise that resolves to either the normalized data or an error result\n * @example\n * ```ts\n * const result = await readFile('./schemas/user.json')\n * if (result.ok) {\n * console.log(result.data) // The normalized data\n * } else {\n * console.log('Failed to read file')\n * }\n * ```\n */\nexport async function readFile(path: string): Promise<ResolveResult> {\n const fs = typeof window === 'undefined' ? await import('node:fs/promises') : undefined\n\n if (fs === undefined) {\n throw 'Can not use readFiles plugin outside of a node environment'\n }\n\n try {\n const fileContents = await fs.readFile(path, { encoding: 'utf-8' })\n\n return {\n ok: true,\n data: normalize(fileContents),\n raw: fileContents,\n }\n } catch {\n return {\n ok: false,\n }\n }\n}\n\n/**\n * Creates a plugin for handling local file references.\n * This plugin validates and reads data from local filesystem paths.\n *\n * @returns A plugin object with validate and exec functions\n * @example\n * const filePlugin = readFiles()\n * if (filePlugin.validate('./local-schema.json')) {\n * const result = await filePlugin.exec('./local-schema.json')\n * }\n */\nexport function readFiles(): LoaderPlugin {\n return {\n type: 'loader',\n validate: isFilePath,\n exec: readFile,\n }\n}\n"],
4
+ "sourcesContent": ["import type { LoaderPlugin, ResolveResult } from '@/bundle'\nimport { isFilePath } from '@/helpers/is-file-path'\nimport { normalize } from '@/helpers/normalize'\n\n/**\n * Reads and normalizes data from a local file\n * @param path - The file path to read from\n * @returns A promise that resolves to either the normalized data or an error result\n * @example\n * ```ts\n * const result = await readFile('./schemas/user.json')\n * if (result.ok) {\n * console.log(result.data) // The normalized data\n * } else {\n * console.log('Failed to read file')\n * }\n * ```\n */\nexport async function readFile(path: string): Promise<ResolveResult> {\n const fs = typeof window === 'undefined' ? await import('node:fs/promises') : undefined\n\n if (fs === undefined) {\n throw 'Can not use readFiles plugin outside of a node environment'\n }\n\n try {\n const fileContents = await fs.readFile(path, { encoding: 'utf-8' })\n\n return {\n ok: true,\n data: normalize(fileContents),\n raw: fileContents,\n }\n } catch {\n return {\n ok: false,\n }\n }\n}\n\n/**\n * Creates a plugin for handling local file references.\n * This plugin validates and reads data from local filesystem paths.\n *\n * @returns A plugin object with validate and exec functions\n * @example\n * const filePlugin = readFiles()\n * if (filePlugin.validate('./local-schema.json')) {\n * const result = await filePlugin.exec('./local-schema.json')\n * }\n */\nexport function readFiles(): LoaderPlugin {\n return {\n type: 'loader',\n validate: isFilePath,\n exec: readFile,\n }\n}\n"],
5
5
  "mappings": "AACA,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAgB1B,eAAsB,SAAS,MAAsC;AACnE,QAAM,KAAK,OAAO,WAAW,cAAc,MAAM,OAAO,kBAAkB,IAAI;AAE9E,MAAI,OAAO,QAAW;AACpB,UAAM;AAAA,EACR;AAEA,MAAI;AACF,UAAM,eAAe,MAAM,GAAG,SAAS,MAAM,EAAE,UAAU,QAAQ,CAAC;AAElE,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM,UAAU,YAAY;AAAA,MAC5B,KAAK;AAAA,IACP;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF;AACF;AAaO,SAAS,YAA0B;AACxC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { type Plugin } from '../bundle/index.js';
1
2
  import type { UnknownObject } from '../types.js';
2
3
  type DereferenceResult = {
3
4
  success: true;
@@ -17,7 +18,7 @@ type ReturnDereferenceResult<Opt extends {
17
18
  * Otherwise, it bundles the document, resolving all $refs (including remote ones), and returns a promise.
18
19
  *
19
20
  * @param input - JSON Schema object to dereference.
20
- * @param options - Optional settings. If `sync` is true, dereferencing is synchronous.
21
+ * @param options - Optional settings. If `sync` is true, dereferencing is synchronous. If `plugins` is provided, those plugins are used for resolution instead of the default `fetchUrls()`.
21
22
  * @returns A DereferenceResult (or Promise thereof) indicating success and the dereferenced data, or errors.
22
23
  *
23
24
  * @example
@@ -37,9 +38,15 @@ type ReturnDereferenceResult<Opt extends {
37
38
  * console.error(result.errors);
38
39
  * }
39
40
  * });
41
+ *
42
+ * @example
43
+ * // Asynchronous dereference (with custom loader plugin)
44
+ * const plugin = { type: 'loader', validate: (v) => v.startsWith('workspace:'), exec: (v) => resolve(v) };
45
+ * const result = await dereference({ $ref: 'workspace:my-schema' }, { plugins: [plugin] });
40
46
  */
41
47
  export declare const dereference: <Opts extends {
42
48
  sync?: boolean;
49
+ plugins?: Plugin[];
43
50
  }>(input: UnknownObject, options?: Opts) => ReturnDereferenceResult<Opts>;
44
51
  export {};
45
52
  //# sourceMappingURL=dereference.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dereference.d.ts","sourceRoot":"","sources":["../../src/dereference/dereference.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,KAAK,iBAAiB,GAClB;IACE,OAAO,EAAE,IAAI,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;CACpB,GACD;IACE,OAAO,EAAE,KAAK,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAEL,KAAK,uBAAuB,CAAC,GAAG,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,GACnF,iBAAiB,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EACzD,OAAO,aAAa,EACpB,UAAU,IAAI,KACb,uBAAuB,CAAC,IAAI,CAgC9B,CAAA"}
1
+ {"version":3,"file":"dereference.d.ts","sourceRoot":"","sources":["../../src/dereference/dereference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,UAAU,CAAA;AAG9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,KAAK,iBAAiB,GAClB;IACE,OAAO,EAAE,IAAI,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;CACpB,GACD;IACE,OAAO,EAAE,KAAK,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAEL,KAAK,uBAAuB,CAAC,GAAG,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,GACnF,iBAAiB,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,EAC7E,OAAO,aAAa,EACpB,UAAU,IAAI,KACb,uBAAuB,CAAC,IAAI,CAiC9B,CAAA"}
@@ -9,8 +9,9 @@ const dereference = (input, options) => {
9
9
  };
10
10
  }
11
11
  const errors = [];
12
+ const plugins = options?.plugins || [fetchUrls()];
12
13
  return bundle(input, {
13
- plugins: [fetchUrls()],
14
+ plugins,
14
15
  treeShake: false,
15
16
  urlMap: true,
16
17
  hooks: {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/dereference/dereference.ts"],
4
- "sourcesContent": ["import { bundle } from '@/bundle'\nimport { fetchUrls } from '@/bundle/plugins/fetch-urls'\nimport { createMagicProxy } from '@/magic-proxy'\nimport type { UnknownObject } from '@/types'\n\ntype DereferenceResult =\n | {\n success: true\n data: UnknownObject\n }\n | {\n success: false\n errors: string[]\n }\n\ntype ReturnDereferenceResult<Opt extends { sync?: boolean }> = Opt['sync'] extends true\n ? DereferenceResult\n : Promise<DereferenceResult>\n\n/**\n * Dereferences a JSON object, resolving all $ref pointers.\n *\n * This function can operate synchronously (no remote refs, no async plugins) or asynchronously (with remote refs).\n * If `options.sync` is true, it simply wraps the input in a magic proxy and returns it.\n * Otherwise, it bundles the document, resolving all $refs (including remote ones), and returns a promise.\n *\n * @param input - JSON Schema object to dereference.\n * @param options - Optional settings. If `sync` is true, dereferencing is synchronous.\n * @returns A DereferenceResult (or Promise thereof) indicating success and the dereferenced data, or errors.\n *\n * @example\n * // Synchronous dereference (no remote refs)\n * const result = dereference({ openapi: '3.0.0', info: { title: 'My API', version: '1.0.0' } }, { sync: true });\n * if (result.success) {\n * console.log(result.data); // Magic proxy-wrapped document\n * }\n *\n * @example\n * // Asynchronous dereference (with remote refs)\n * dereference({ $ref: 'https://example.com/api.yaml' })\n * .then(result => {\n * if (result.success) {\n * console.log(result.data); // Fully dereferenced document\n * } else {\n * console.error(result.errors);\n * }\n * });\n */\nexport const dereference = <Opts extends { sync?: boolean }>(\n input: UnknownObject,\n options?: Opts,\n): ReturnDereferenceResult<Opts> => {\n if (options?.sync) {\n return {\n success: true,\n data: createMagicProxy(input),\n } as ReturnDereferenceResult<Opts>\n }\n\n const errors: string[] = []\n\n return bundle(input, {\n plugins: [fetchUrls()],\n treeShake: false,\n urlMap: true,\n hooks: {\n onResolveError(node) {\n errors.push(`Failed to resolve ${node.$ref}`)\n },\n },\n }).then((result) => {\n if (errors.length > 0) {\n return {\n success: false,\n errors,\n }\n }\n\n return {\n success: true,\n data: createMagicProxy(result as UnknownObject),\n }\n }) as ReturnDereferenceResult<Opts>\n}\n"],
5
- "mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,wBAAwB;AA8C1B,MAAM,cAAc,CACzB,OACA,YACkC;AAClC,MAAI,SAAS,MAAM;AACjB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,iBAAiB,KAAK;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,SAAmB,CAAC;AAE1B,SAAO,OAAO,OAAO;AAAA,IACnB,SAAS,CAAC,UAAU,CAAC;AAAA,IACrB,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,OAAO;AAAA,MACL,eAAe,MAAM;AACnB,eAAO,KAAK,qBAAqB,KAAK,IAAI,EAAE;AAAA,MAC9C;AAAA,IACF;AAAA,EACF,CAAC,EAAE,KAAK,CAAC,WAAW;AAClB,QAAI,OAAO,SAAS,GAAG;AACrB,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,iBAAiB,MAAuB;AAAA,IAChD;AAAA,EACF,CAAC;AACH;",
4
+ "sourcesContent": ["import { type Plugin, bundle } from '@/bundle'\nimport { fetchUrls } from '@/bundle/plugins/fetch-urls'\nimport { createMagicProxy } from '@/magic-proxy'\nimport type { UnknownObject } from '@/types'\n\ntype DereferenceResult =\n | {\n success: true\n data: UnknownObject\n }\n | {\n success: false\n errors: string[]\n }\n\ntype ReturnDereferenceResult<Opt extends { sync?: boolean }> = Opt['sync'] extends true\n ? DereferenceResult\n : Promise<DereferenceResult>\n\n/**\n * Dereferences a JSON object, resolving all $ref pointers.\n *\n * This function can operate synchronously (no remote refs, no async plugins) or asynchronously (with remote refs).\n * If `options.sync` is true, it simply wraps the input in a magic proxy and returns it.\n * Otherwise, it bundles the document, resolving all $refs (including remote ones), and returns a promise.\n *\n * @param input - JSON Schema object to dereference.\n * @param options - Optional settings. If `sync` is true, dereferencing is synchronous. If `plugins` is provided, those plugins are used for resolution instead of the default `fetchUrls()`.\n * @returns A DereferenceResult (or Promise thereof) indicating success and the dereferenced data, or errors.\n *\n * @example\n * // Synchronous dereference (no remote refs)\n * const result = dereference({ openapi: '3.0.0', info: { title: 'My API', version: '1.0.0' } }, { sync: true });\n * if (result.success) {\n * console.log(result.data); // Magic proxy-wrapped document\n * }\n *\n * @example\n * // Asynchronous dereference (with remote refs)\n * dereference({ $ref: 'https://example.com/api.yaml' })\n * .then(result => {\n * if (result.success) {\n * console.log(result.data); // Fully dereferenced document\n * } else {\n * console.error(result.errors);\n * }\n * });\n *\n * @example\n * // Asynchronous dereference (with custom loader plugin)\n * const plugin = { type: 'loader', validate: (v) => v.startsWith('workspace:'), exec: (v) => resolve(v) };\n * const result = await dereference({ $ref: 'workspace:my-schema' }, { plugins: [plugin] });\n */\nexport const dereference = <Opts extends { sync?: boolean; plugins?: Plugin[] }>(\n input: UnknownObject,\n options?: Opts,\n): ReturnDereferenceResult<Opts> => {\n if (options?.sync) {\n return {\n success: true,\n data: createMagicProxy(input),\n } as ReturnDereferenceResult<Opts>\n }\n\n const errors: string[] = []\n const plugins = options?.plugins || [fetchUrls()]\n\n return bundle(input, {\n plugins,\n treeShake: false,\n urlMap: true,\n hooks: {\n onResolveError(node) {\n errors.push(`Failed to resolve ${node.$ref}`)\n },\n },\n }).then((result) => {\n if (errors.length > 0) {\n return {\n success: false,\n errors,\n }\n }\n\n return {\n success: true,\n data: createMagicProxy(result as UnknownObject),\n }\n }) as ReturnDereferenceResult<Opts>\n}\n"],
5
+ "mappings": "AAAA,SAAsB,cAAc;AACpC,SAAS,iBAAiB;AAC1B,SAAS,wBAAwB;AAmD1B,MAAM,cAAc,CACzB,OACA,YACkC;AAClC,MAAI,SAAS,MAAM;AACjB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,iBAAiB,KAAK;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,SAAmB,CAAC;AAC1B,QAAM,UAAU,SAAS,WAAW,CAAC,UAAU,CAAC;AAEhD,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,OAAO;AAAA,MACL,eAAe,MAAM;AACnB,eAAO,KAAK,qBAAqB,KAAK,IAAI,EAAE;AAAA,MAC9C;AAAA,IACF;AAAA,EACF,CAAC,EAAE,KAAK,CAAC,WAAW;AAClB,QAAI,OAAO,SAAS,GAAG;AACrB,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,iBAAiB,MAAuB;AAAA,IAChD;AAAA,EACF,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Checks if a string represents a file path by ensuring it's not a remote URL,
3
+ * YAML content, or JSON content.
4
+ *
5
+ * @param value - The string to check
6
+ * @returns true if the string appears to be a file path, false otherwise
7
+ * @example
8
+ * ```ts
9
+ * isFilePath('./schemas/user.json') // true
10
+ * isFilePath('https://example.com/schema.json') // false
11
+ * isFilePath('{"type": "object"}') // false
12
+ * isFilePath('type: object') // false
13
+ * ```
14
+ */
15
+ export declare function isFilePath(value: string): boolean;
16
+ //# sourceMappingURL=is-file-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-file-path.d.ts","sourceRoot":"","sources":["../../src/helpers/is-file-path.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,WAEvC"}
@@ -0,0 +1,10 @@
1
+ import { isHttpUrl } from "../helpers/is-http-url.js";
2
+ import { isJsonObject } from "../helpers/is-json-object.js";
3
+ import { isYaml } from "../helpers/is-yaml.js";
4
+ function isFilePath(value) {
5
+ return !isHttpUrl(value) && !isYaml(value) && !isJsonObject(value);
6
+ }
7
+ export {
8
+ isFilePath
9
+ };
10
+ //# sourceMappingURL=is-file-path.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/helpers/is-file-path.ts"],
4
+ "sourcesContent": ["import { isHttpUrl } from '@/helpers/is-http-url'\nimport { isJsonObject } from '@/helpers/is-json-object'\nimport { isYaml } from '@/helpers/is-yaml'\n\n/**\n * Checks if a string represents a file path by ensuring it's not a remote URL,\n * YAML content, or JSON content.\n *\n * @param value - The string to check\n * @returns true if the string appears to be a file path, false otherwise\n * @example\n * ```ts\n * isFilePath('./schemas/user.json') // true\n * isFilePath('https://example.com/schema.json') // false\n * isFilePath('{\"type\": \"object\"}') // false\n * isFilePath('type: object') // false\n * ```\n */\nexport function isFilePath(value: string) {\n return !isHttpUrl(value) && !isYaml(value) && !isJsonObject(value)\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AAgBhB,SAAS,WAAW,OAAe;AACxC,SAAO,CAAC,UAAU,KAAK,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,aAAa,KAAK;AACnE;",
6
+ "names": []
7
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Checks if a string is a remote URL (starts with http:// or https://)
3
+ * @param value - The URL string to check
4
+ * @returns true if the string is a remote URL, false otherwise
5
+ * @example
6
+ * ```ts
7
+ * isHttpUrl('https://example.com/schema.json') // true
8
+ * isHttpUrl('http://api.example.com/schemas/user.json') // true
9
+ * isHttpUrl('#/components/schemas/User') // false
10
+ * isHttpUrl('./local-schema.json') // false
11
+ * ```
12
+ */
13
+ export declare function isHttpUrl(value: string): boolean;
14
+ //# sourceMappingURL=is-http-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-http-url.d.ts","sourceRoot":"","sources":["../../src/helpers/is-http-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,WAOtC"}
@@ -0,0 +1,12 @@
1
+ function isHttpUrl(value) {
2
+ try {
3
+ const url = new URL(value);
4
+ return url.protocol === "http:" || url.protocol === "https:";
5
+ } catch {
6
+ return false;
7
+ }
8
+ }
9
+ export {
10
+ isHttpUrl
11
+ };
12
+ //# sourceMappingURL=is-http-url.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/helpers/is-http-url.ts"],
4
+ "sourcesContent": ["/**\n * Checks if a string is a remote URL (starts with http:// or https://)\n * @param value - The URL string to check\n * @returns true if the string is a remote URL, false otherwise\n * @example\n * ```ts\n * isHttpUrl('https://example.com/schema.json') // true\n * isHttpUrl('http://api.example.com/schemas/user.json') // true\n * isHttpUrl('#/components/schemas/User') // false\n * isHttpUrl('./local-schema.json') // false\n * ```\n */\nexport function isHttpUrl(value: string) {\n try {\n const url = new URL(value)\n return url.protocol === 'http:' || url.protocol === 'https:'\n } catch {\n return false\n }\n}\n"],
5
+ "mappings": "AAYO,SAAS,UAAU,OAAe;AACvC,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,KAAK;AACzB,WAAO,IAAI,aAAa,WAAW,IAAI,aAAa;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Resolves a reference path by combining a base path with a relative path.
3
+ * Handles both remote URLs and local file paths.
4
+ *
5
+ * @param base - The base path (can be a URL or local file path)
6
+ * @param relativePath - The relative path to resolve against the base
7
+ * @returns The resolved absolute path
8
+ * @example
9
+ * // Resolve remote URL
10
+ * resolveReferencePath('https://example.com/api/schema.json', 'user.json')
11
+ * // Returns: 'https://example.com/api/user.json'
12
+ *
13
+ * // Resolve local path
14
+ * resolveReferencePath('/path/to/schema.json', 'user.json')
15
+ * // Returns: '/path/to/user.json'
16
+ */
17
+ export declare const resolveReferencePath: (base: string, relativePath: string) => string;
18
+ //# sourceMappingURL=resolve-reference-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-reference-path.d.ts","sourceRoot":"","sources":["../../src/helpers/resolve-reference-path.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,cAAc,MAAM,WAYtE,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { path } from "@scalar/helpers/node/path";
2
+ import { isHttpUrl } from "../helpers/is-http-url.js";
3
+ const resolveReferencePath = (base, relativePath) => {
4
+ if (isHttpUrl(relativePath)) {
5
+ return relativePath;
6
+ }
7
+ if (isHttpUrl(base)) {
8
+ const baseUrl = new URL(base);
9
+ baseUrl.pathname = path.resolve(path.dirname(baseUrl.pathname), relativePath);
10
+ return baseUrl.toString();
11
+ }
12
+ return path.resolve(path.dirname(base), relativePath);
13
+ };
14
+ export {
15
+ resolveReferencePath
16
+ };
17
+ //# sourceMappingURL=resolve-reference-path.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/helpers/resolve-reference-path.ts"],
4
+ "sourcesContent": ["import { path } from '@scalar/helpers/node/path'\n\nimport { isHttpUrl } from '@/helpers/is-http-url'\n\n/**\n * Resolves a reference path by combining a base path with a relative path.\n * Handles both remote URLs and local file paths.\n *\n * @param base - The base path (can be a URL or local file path)\n * @param relativePath - The relative path to resolve against the base\n * @returns The resolved absolute path\n * @example\n * // Resolve remote URL\n * resolveReferencePath('https://example.com/api/schema.json', 'user.json')\n * // Returns: 'https://example.com/api/user.json'\n *\n * // Resolve local path\n * resolveReferencePath('/path/to/schema.json', 'user.json')\n * // Returns: '/path/to/user.json'\n */\nexport const resolveReferencePath = (base: string, relativePath: string) => {\n if (isHttpUrl(relativePath)) {\n return relativePath\n }\n\n if (isHttpUrl(base)) {\n const baseUrl = new URL(base)\n baseUrl.pathname = path.resolve(path.dirname(baseUrl.pathname), relativePath)\n return baseUrl.toString()\n }\n\n return path.resolve(path.dirname(base), relativePath)\n}\n"],
5
+ "mappings": "AAAA,SAAS,YAAY;AAErB,SAAS,iBAAiB;AAkBnB,MAAM,uBAAuB,CAAC,MAAc,iBAAyB;AAC1E,MAAI,UAAU,YAAY,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI,UAAU,IAAI,GAAG;AACnB,UAAM,UAAU,IAAI,IAAI,IAAI;AAC5B,YAAQ,WAAW,KAAK,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,GAAG,YAAY;AAC5E,WAAO,QAAQ,SAAS;AAAA,EAC1B;AAEA,SAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI,GAAG,YAAY;AACtD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Sets a value at a specified path in an object, creating intermediate objects/arrays as needed.
3
+ * This function traverses the object structure and creates any missing intermediate objects
4
+ * or arrays based on the path segments. If the next segment is a numeric string, it creates
5
+ * an array instead of an object.
6
+ *
7
+ * ⚠️ Warning: Be careful with object keys that look like numbers (e.g. "123") as this function
8
+ * will interpret them as array indices and create arrays instead of objects. If you need to
9
+ * use numeric-looking keys, consider prefixing them with a non-numeric character.
10
+ *
11
+ * @param obj - The target object to set the value in
12
+ * @param path - The JSON pointer path where the value should be set
13
+ * @param value - The value to set at the specified path
14
+ * @throws {Error} If attempting to set a value at the root path ('')
15
+ *
16
+ * @example
17
+ * const obj = {}
18
+ * setValueAtPath(obj, '/foo/bar/0', 'value')
19
+ * // Result:
20
+ * // {
21
+ * // foo: {
22
+ * // bar: ['value']
23
+ * // }
24
+ * // }
25
+ *
26
+ * @example
27
+ * const obj = { existing: { path: 'old' } }
28
+ * setValueAtPath(obj, '/existing/path', 'new')
29
+ * // Result:
30
+ * // {
31
+ * // existing: {
32
+ * // path: 'new'
33
+ * // }
34
+ * // }
35
+ *
36
+ * @example
37
+ * // ⚠️ Warning: This will create an array instead of an object with key "123"
38
+ * setValueAtPath(obj, '/foo/123/bar', 'value')
39
+ * // Result:
40
+ * // {
41
+ * // foo: [
42
+ * // undefined,
43
+ * // undefined,
44
+ * // undefined,
45
+ * // { bar: 'value' }
46
+ * // ]
47
+ * // }
48
+ */
49
+ export declare function setValueAtPath(obj: any, path: string, value: any): void;
50
+ //# sourceMappingURL=set-value-at-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-value-at-path.d.ts","sourceRoot":"","sources":["../../src/helpers/set-value-at-path.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,CA4BvE"}
@@ -0,0 +1,28 @@
1
+ import { preventPollution } from "@scalar/helpers/object/prevent-pollution";
2
+ import { getSegmentsFromPath } from "../helpers/get-segments-from-path.js";
3
+ function setValueAtPath(obj, path, value) {
4
+ if (path === "") {
5
+ throw new Error("Cannot set value at root ('') pointer");
6
+ }
7
+ const parts = getSegmentsFromPath(path);
8
+ parts.forEach((part) => preventPollution(part));
9
+ let current = obj;
10
+ for (let i = 0; i < parts.length; i++) {
11
+ const key = parts[i];
12
+ const isLast = i === parts.length - 1;
13
+ const nextKey = parts[i + 1];
14
+ const shouldBeArray = /^\d+$/.test(nextKey ?? "");
15
+ if (isLast) {
16
+ current[key] = value;
17
+ } else {
18
+ if (!(key in current) || typeof current[key] !== "object") {
19
+ current[key] = shouldBeArray ? [] : {};
20
+ }
21
+ current = current[key];
22
+ }
23
+ }
24
+ }
25
+ export {
26
+ setValueAtPath
27
+ };
28
+ //# sourceMappingURL=set-value-at-path.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/helpers/set-value-at-path.ts"],
4
+ "sourcesContent": ["import { preventPollution } from '@scalar/helpers/object/prevent-pollution'\n\nimport { getSegmentsFromPath } from '@/helpers/get-segments-from-path'\n\n/**\n * Sets a value at a specified path in an object, creating intermediate objects/arrays as needed.\n * This function traverses the object structure and creates any missing intermediate objects\n * or arrays based on the path segments. If the next segment is a numeric string, it creates\n * an array instead of an object.\n *\n * \u26A0\uFE0F Warning: Be careful with object keys that look like numbers (e.g. \"123\") as this function\n * will interpret them as array indices and create arrays instead of objects. If you need to\n * use numeric-looking keys, consider prefixing them with a non-numeric character.\n *\n * @param obj - The target object to set the value in\n * @param path - The JSON pointer path where the value should be set\n * @param value - The value to set at the specified path\n * @throws {Error} If attempting to set a value at the root path ('')\n *\n * @example\n * const obj = {}\n * setValueAtPath(obj, '/foo/bar/0', 'value')\n * // Result:\n * // {\n * // foo: {\n * // bar: ['value']\n * // }\n * // }\n *\n * @example\n * const obj = { existing: { path: 'old' } }\n * setValueAtPath(obj, '/existing/path', 'new')\n * // Result:\n * // {\n * // existing: {\n * // path: 'new'\n * // }\n * // }\n *\n * @example\n * // \u26A0\uFE0F Warning: This will create an array instead of an object with key \"123\"\n * setValueAtPath(obj, '/foo/123/bar', 'value')\n * // Result:\n * // {\n * // foo: [\n * // undefined,\n * // undefined,\n * // undefined,\n * // { bar: 'value' }\n * // ]\n * // }\n */\nexport function setValueAtPath(obj: any, path: string, value: any): void {\n if (path === '') {\n throw new Error(\"Cannot set value at root ('') pointer\")\n }\n\n const parts = getSegmentsFromPath(path)\n\n // Prevent prototype pollution\n parts.forEach((part) => preventPollution(part))\n\n let current = obj\n\n for (let i = 0; i < parts.length; i++) {\n const key = parts[i]\n const isLast = i === parts.length - 1\n\n const nextKey = parts[i + 1]\n const shouldBeArray = /^\\d+$/.test(nextKey ?? '')\n\n if (isLast) {\n current[key] = value\n } else {\n if (!(key in current) || typeof current[key] !== 'object') {\n current[key] = shouldBeArray ? [] : {}\n }\n current = current[key]\n }\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,wBAAwB;AAEjC,SAAS,2BAA2B;AAkD7B,SAAS,eAAe,KAAU,MAAc,OAAkB;AACvE,MAAI,SAAS,IAAI;AACf,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AAEA,QAAM,QAAQ,oBAAoB,IAAI;AAGtC,QAAM,QAAQ,CAAC,SAAS,iBAAiB,IAAI,CAAC;AAE9C,MAAI,UAAU;AAEd,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,MAAM,MAAM,CAAC;AACnB,UAAM,SAAS,MAAM,MAAM,SAAS;AAEpC,UAAM,UAAU,MAAM,IAAI,CAAC;AAC3B,UAAM,gBAAgB,QAAQ,KAAK,WAAW,EAAE;AAEhD,QAAI,QAAQ;AACV,cAAQ,GAAG,IAAI;AAAA,IACjB,OAAO;AACL,UAAI,EAAE,OAAO,YAAY,OAAO,QAAQ,GAAG,MAAM,UAAU;AACzD,gBAAQ,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC;AAAA,MACvC;AACA,gBAAU,QAAQ,GAAG;AAAA,IACvB;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Converts an input path or URL to a relative path based on the provided base.
3
+ * Handles both remote URLs and local file system paths.
4
+ * - If both input and base are remote URLs and share the same origin, computes the relative pathname.
5
+ * - If base is a remote URL but input is local, returns a remote URL with a relative pathname.
6
+ * - If input is a remote URL but base is local, returns input as is.
7
+ * - Otherwise, computes the relative path between two local paths.
8
+ */
9
+ export declare const toRelativePath: (input: string, base: string) => string;
10
+ //# sourceMappingURL=to-relative-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-relative-path.d.ts","sourceRoot":"","sources":["../../src/helpers/to-relative-path.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,EAAE,MAAM,MAAM,WAoCzD,CAAA"}
@@ -0,0 +1,31 @@
1
+ import { path } from "@scalar/helpers/node/path";
2
+ import { isHttpUrl } from "../helpers/is-http-url.js";
3
+ const toRelativePath = (input, base) => {
4
+ if (isHttpUrl(input) && isHttpUrl(base)) {
5
+ const inputUrl = new URL(input);
6
+ const baseUrl = new URL(base);
7
+ if (inputUrl.origin !== baseUrl.origin) {
8
+ return input;
9
+ }
10
+ const baseDir2 = path.dirname(path.resolve(baseUrl.pathname));
11
+ const inputPath2 = path.resolve(inputUrl.pathname);
12
+ return path.relative(baseDir2, inputPath2);
13
+ }
14
+ if (isHttpUrl(base)) {
15
+ const baseUrl = new URL(base);
16
+ const baseDir2 = path.dirname(path.resolve(baseUrl.pathname));
17
+ const inputPath2 = path.resolve(input);
18
+ baseUrl.pathname = path.relative(baseDir2, inputPath2);
19
+ return baseUrl.toString();
20
+ }
21
+ if (isHttpUrl(input)) {
22
+ return input;
23
+ }
24
+ const baseDir = path.dirname(path.resolve(base));
25
+ const inputPath = path.resolve(input);
26
+ return path.relative(baseDir, inputPath);
27
+ };
28
+ export {
29
+ toRelativePath
30
+ };
31
+ //# sourceMappingURL=to-relative-path.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/helpers/to-relative-path.ts"],
4
+ "sourcesContent": ["import { path } from '@scalar/helpers/node/path'\n\nimport { isHttpUrl } from '@/helpers/is-http-url'\n\n/**\n * Converts an input path or URL to a relative path based on the provided base.\n * Handles both remote URLs and local file system paths.\n * - If both input and base are remote URLs and share the same origin, computes the relative pathname.\n * - If base is a remote URL but input is local, returns a remote URL with a relative pathname.\n * - If input is a remote URL but base is local, returns input as is.\n * - Otherwise, computes the relative path between two local paths.\n */\nexport const toRelativePath = (input: string, base: string) => {\n // Both input and base are remote URLs\n if (isHttpUrl(input) && isHttpUrl(base)) {\n const inputUrl = new URL(input)\n const baseUrl = new URL(base)\n // If origins aren't the same, return input as is\n if (inputUrl.origin !== baseUrl.origin) {\n return input\n }\n\n // Get the directory of the base URL pathname (not the file itself)\n const baseDir = path.dirname(path.resolve(baseUrl.pathname))\n const inputPath = path.resolve(inputUrl.pathname)\n // Return the relative path from baseDir to inputPath\n return path.relative(baseDir, inputPath)\n }\n\n // Base is a remote URL, input is a local path\n if (isHttpUrl(base)) {\n const baseUrl = new URL(base)\n const baseDir = path.dirname(path.resolve(baseUrl.pathname))\n const inputPath = path.resolve(input)\n // Set the pathname of the base URL to the relative path and return the URL as a string\n baseUrl.pathname = path.relative(baseDir, inputPath)\n return baseUrl.toString()\n }\n\n // Input is a remote URL, base is a local path; just return input\n if (isHttpUrl(input)) {\n return input\n }\n\n // Both input and base are local paths; return the relative path\n const baseDir = path.dirname(path.resolve(base))\n const inputPath = path.resolve(input)\n return path.relative(baseDir, inputPath)\n}\n"],
5
+ "mappings": "AAAA,SAAS,YAAY;AAErB,SAAS,iBAAiB;AAUnB,MAAM,iBAAiB,CAAC,OAAe,SAAiB;AAE7D,MAAI,UAAU,KAAK,KAAK,UAAU,IAAI,GAAG;AACvC,UAAM,WAAW,IAAI,IAAI,KAAK;AAC9B,UAAM,UAAU,IAAI,IAAI,IAAI;AAE5B,QAAI,SAAS,WAAW,QAAQ,QAAQ;AACtC,aAAO;AAAA,IACT;AAGA,UAAMA,WAAU,KAAK,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,CAAC;AAC3D,UAAMC,aAAY,KAAK,QAAQ,SAAS,QAAQ;AAEhD,WAAO,KAAK,SAASD,UAASC,UAAS;AAAA,EACzC;AAGA,MAAI,UAAU,IAAI,GAAG;AACnB,UAAM,UAAU,IAAI,IAAI,IAAI;AAC5B,UAAMD,WAAU,KAAK,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,CAAC;AAC3D,UAAMC,aAAY,KAAK,QAAQ,KAAK;AAEpC,YAAQ,WAAW,KAAK,SAASD,UAASC,UAAS;AACnD,WAAO,QAAQ,SAAS;AAAA,EAC1B;AAGA,MAAI,UAAU,KAAK,GAAG;AACpB,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,KAAK,QAAQ,KAAK,QAAQ,IAAI,CAAC;AAC/C,QAAM,YAAY,KAAK,QAAQ,KAAK;AACpC,SAAO,KAAK,SAAS,SAAS,SAAS;AACzC;",
6
+ "names": ["baseDir", "inputPath"]
7
+ }
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "url": "git+https://github.com/scalar/scalar.git",
11
11
  "directory": "packages/json-magic"
12
12
  },
13
- "version": "0.9.6",
13
+ "version": "0.11.0",
14
14
  "engines": {
15
15
  "node": ">=20"
16
16
  },
@@ -51,6 +51,16 @@
51
51
  "types": "./dist/helpers/get-segments-from-path.d.ts",
52
52
  "default": "./dist/helpers/get-segments-from-path.js"
53
53
  },
54
+ "./helpers/is-file-path": {
55
+ "import": "./dist/helpers/is-file-path.js",
56
+ "types": "./dist/helpers/is-file-path.d.ts",
57
+ "default": "./dist/helpers/is-file-path.js"
58
+ },
59
+ "./helpers/is-http-url": {
60
+ "import": "./dist/helpers/is-http-url.js",
61
+ "types": "./dist/helpers/is-http-url.d.ts",
62
+ "default": "./dist/helpers/is-http-url.js"
63
+ },
54
64
  "./helpers/unescape-json-pointer": {
55
65
  "import": "./dist/helpers/unescape-json-pointer.js",
56
66
  "types": "./dist/helpers/unescape-json-pointer.d.ts",
@@ -77,7 +87,7 @@
77
87
  },
78
88
  "scripts": {
79
89
  "build": "scalar-build-esbuild",
80
- "test": "vitest test",
90
+ "test": "vitest",
81
91
  "types:build": "scalar-types-build",
82
92
  "types:check": "scalar-types-check"
83
93
  }