@hey-api/json-schema-ref-parser 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -84
- package/dist/index.d.mts +629 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1887 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +42 -78
- package/src/__tests__/bundle.test.ts +59 -0
- package/src/__tests__/index.test.ts +43 -0
- package/src/__tests__/pointer.test.ts +34 -0
- package/src/__tests__/utils.ts +3 -0
- package/{lib → src}/bundle.ts +143 -229
- package/{lib → src}/dereference.ts +20 -43
- package/{lib → src}/index.ts +103 -125
- package/{lib → src}/options.ts +13 -9
- package/{lib → src}/parse.ts +19 -15
- package/src/parsers/binary.ts +13 -0
- package/{lib → src}/parsers/json.ts +5 -6
- package/src/parsers/text.ts +21 -0
- package/{lib → src}/parsers/yaml.ts +9 -9
- package/{lib → src}/pointer.ts +42 -23
- package/{lib → src}/ref.ts +25 -21
- package/{lib → src}/refs.ts +23 -26
- package/{lib → src}/resolve-external.ts +91 -60
- package/{lib → src}/resolvers/file.ts +7 -10
- package/{lib → src}/resolvers/url.ts +12 -8
- package/{lib → src}/types/index.ts +9 -2
- package/src/util/convert-path-to-posix.ts +8 -0
- package/{lib → src}/util/errors.ts +38 -36
- package/{lib → src}/util/is-windows.ts +1 -1
- package/{lib → src}/util/plugins.ts +7 -8
- package/{lib → src}/util/url.ts +41 -42
- package/dist/lib/__tests__/bundle.test.d.ts +0 -1
- package/dist/lib/__tests__/bundle.test.js +0 -50
- package/dist/lib/__tests__/index.test.d.ts +0 -1
- package/dist/lib/__tests__/index.test.js +0 -43
- package/dist/lib/__tests__/pointer.test.d.ts +0 -1
- package/dist/lib/__tests__/pointer.test.js +0 -27
- package/dist/lib/bundle.d.ts +0 -26
- package/dist/lib/bundle.js +0 -600
- package/dist/lib/dereference.d.ts +0 -11
- package/dist/lib/dereference.js +0 -226
- package/dist/lib/index.d.ts +0 -92
- package/dist/lib/index.js +0 -525
- package/dist/lib/options.d.ts +0 -61
- package/dist/lib/options.js +0 -45
- package/dist/lib/parse.d.ts +0 -13
- package/dist/lib/parse.js +0 -87
- package/dist/lib/parsers/binary.d.ts +0 -2
- package/dist/lib/parsers/binary.js +0 -12
- package/dist/lib/parsers/json.d.ts +0 -2
- package/dist/lib/parsers/json.js +0 -38
- package/dist/lib/parsers/text.d.ts +0 -2
- package/dist/lib/parsers/text.js +0 -18
- package/dist/lib/parsers/yaml.d.ts +0 -2
- package/dist/lib/parsers/yaml.js +0 -28
- package/dist/lib/pointer.d.ts +0 -88
- package/dist/lib/pointer.js +0 -297
- package/dist/lib/ref.d.ts +0 -180
- package/dist/lib/ref.js +0 -226
- package/dist/lib/refs.d.ts +0 -127
- package/dist/lib/refs.js +0 -232
- package/dist/lib/resolve-external.d.ts +0 -13
- package/dist/lib/resolve-external.js +0 -151
- package/dist/lib/resolvers/file.d.ts +0 -6
- package/dist/lib/resolvers/file.js +0 -61
- package/dist/lib/resolvers/url.d.ts +0 -17
- package/dist/lib/resolvers/url.js +0 -62
- package/dist/lib/types/index.d.ts +0 -43
- package/dist/lib/types/index.js +0 -2
- package/dist/lib/util/convert-path-to-posix.d.ts +0 -1
- package/dist/lib/util/convert-path-to-posix.js +0 -14
- package/dist/lib/util/errors.d.ts +0 -56
- package/dist/lib/util/errors.js +0 -112
- package/dist/lib/util/is-windows.d.ts +0 -1
- package/dist/lib/util/is-windows.js +0 -6
- package/dist/lib/util/plugins.d.ts +0 -16
- package/dist/lib/util/plugins.js +0 -45
- package/dist/lib/util/url.d.ts +0 -79
- package/dist/lib/util/url.js +0 -285
- package/dist/vite.config.d.ts +0 -2
- package/dist/vite.config.js +0 -19
- package/lib/__tests__/bundle.test.ts +0 -52
- package/lib/__tests__/index.test.ts +0 -45
- package/lib/__tests__/pointer.test.ts +0 -26
- package/lib/__tests__/spec/circular-ref-with-description.json +0 -11
- package/lib/__tests__/spec/multiple-refs.json +0 -34
- package/lib/__tests__/spec/openapi-paths-ref.json +0 -46
- package/lib/__tests__/spec/path-parameter.json +0 -16
- package/lib/parsers/binary.ts +0 -13
- package/lib/parsers/text.ts +0 -21
- package/lib/util/convert-path-to-posix.ts +0 -11
- /package/{LICENSE → LICENSE.md} +0 -0
|
@@ -1,40 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import cloneDeep from "lodash/cloneDeep";
|
|
3
|
-
import Pointer from "./pointer.js";
|
|
4
|
-
import { ono } from "@jsdevtools/ono";
|
|
5
|
-
import * as url from "./util/url.js";
|
|
6
|
-
import type $Refs from "./refs.js";
|
|
7
|
-
import type { DereferenceOptions, ParserOptions } from "./options.js";
|
|
8
|
-
import type { JSONSchema } from "./types";
|
|
9
|
-
import type { $RefParser } from "./index";
|
|
10
|
-
import { TimeoutError } from "./util/errors";
|
|
1
|
+
import { ono } from '@jsdevtools/ono';
|
|
11
2
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
* @param options
|
|
20
|
-
*/
|
|
21
|
-
function dereference(parser: $RefParser, options: ParserOptions) {
|
|
22
|
-
const start = Date.now();
|
|
23
|
-
// console.log('Dereferencing $ref pointers in %s', parser.$refs._root$Ref.path);
|
|
24
|
-
const dereferenced = crawl<JSONSchema>(
|
|
25
|
-
parser.schema,
|
|
26
|
-
parser.$refs._root$Ref.path!,
|
|
27
|
-
"#",
|
|
28
|
-
new Set(),
|
|
29
|
-
new Set(),
|
|
30
|
-
new Map(),
|
|
31
|
-
parser.$refs,
|
|
32
|
-
options,
|
|
33
|
-
start,
|
|
34
|
-
);
|
|
35
|
-
parser.$refs.circular = dereferenced.circular;
|
|
36
|
-
parser.schema = dereferenced.value;
|
|
37
|
-
}
|
|
3
|
+
import type { DereferenceOptions, ParserOptions } from './options';
|
|
4
|
+
import Pointer from './pointer';
|
|
5
|
+
import $Ref from './ref';
|
|
6
|
+
import type $Refs from './refs';
|
|
7
|
+
import type { JSONSchema } from './types';
|
|
8
|
+
import { TimeoutError } from './util/errors';
|
|
9
|
+
import * as url from './util/url';
|
|
38
10
|
|
|
39
11
|
/**
|
|
40
12
|
* Recursively crawls the given value, and dereferences any JSON references.
|
|
@@ -63,8 +35,8 @@ function crawl<S extends object = JSONSchema>(
|
|
|
63
35
|
) {
|
|
64
36
|
let dereferenced;
|
|
65
37
|
const result = {
|
|
66
|
-
value: obj,
|
|
67
38
|
circular: false,
|
|
39
|
+
value: obj,
|
|
68
40
|
};
|
|
69
41
|
|
|
70
42
|
if (options && options.timeoutMs) {
|
|
@@ -75,8 +47,13 @@ function crawl<S extends object = JSONSchema>(
|
|
|
75
47
|
const derefOptions = (options.dereference || {}) as DereferenceOptions;
|
|
76
48
|
const isExcludedPath = derefOptions.excludedPathMatcher || (() => false);
|
|
77
49
|
|
|
78
|
-
if (derefOptions?.circular ===
|
|
79
|
-
if (
|
|
50
|
+
if (derefOptions?.circular === 'ignore' || !processedObjects.has(obj)) {
|
|
51
|
+
if (
|
|
52
|
+
obj &&
|
|
53
|
+
typeof obj === 'object' &&
|
|
54
|
+
!ArrayBuffer.isView(obj) &&
|
|
55
|
+
!isExcludedPath(pathFromRoot)
|
|
56
|
+
) {
|
|
80
57
|
parents.add(obj);
|
|
81
58
|
processedObjects.add(obj);
|
|
82
59
|
|
|
@@ -191,19 +168,19 @@ function dereference$Ref<S extends object = JSONSchema>(
|
|
|
191
168
|
if (refKeys.length > 1) {
|
|
192
169
|
const extraKeys = {};
|
|
193
170
|
for (const key of refKeys) {
|
|
194
|
-
if (key !==
|
|
171
|
+
if (key !== '$ref' && !(key in cache.value)) {
|
|
195
172
|
// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
|
196
173
|
extraKeys[key] = $ref[key];
|
|
197
174
|
}
|
|
198
175
|
}
|
|
199
176
|
return {
|
|
200
177
|
circular: cache.circular,
|
|
201
|
-
value: Object.assign({},
|
|
178
|
+
value: Object.assign({}, structuredClone(cache.value), extraKeys),
|
|
202
179
|
};
|
|
203
180
|
}
|
|
204
181
|
|
|
205
182
|
// Return a deep-cloned value so each occurrence is an independent copy
|
|
206
|
-
return { circular: cache.circular, value:
|
|
183
|
+
return { circular: cache.circular, value: structuredClone(cache.value) };
|
|
207
184
|
}
|
|
208
185
|
|
|
209
186
|
const pointer = $refs._resolve($refPath, path, options);
|
|
@@ -243,7 +220,7 @@ function dereference$Ref<S extends object = JSONSchema>(
|
|
|
243
220
|
dereferencedValue = dereferenced.value;
|
|
244
221
|
}
|
|
245
222
|
|
|
246
|
-
if (circular && !directCircular && options.dereference?.circular ===
|
|
223
|
+
if (circular && !directCircular && options.dereference?.circular === 'ignore') {
|
|
247
224
|
// The user has chosen to "ignore" circular references, so don't change the value
|
|
248
225
|
dereferencedValue = $ref;
|
|
249
226
|
}
|
package/{lib → src}/index.ts
RENAMED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import type { JSONSchema } from
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
1
|
+
import { ono } from '@jsdevtools/ono';
|
|
2
|
+
|
|
3
|
+
import { bundle as _bundle } from './bundle';
|
|
4
|
+
import { getJsonSchemaRefParserDefaultOptions } from './options';
|
|
5
|
+
import { newFile, parseFile } from './parse';
|
|
6
|
+
import $Refs from './refs';
|
|
7
|
+
import { resolveExternal } from './resolve-external';
|
|
8
|
+
import { fileResolver } from './resolvers/file';
|
|
9
|
+
import { urlResolver } from './resolvers/url';
|
|
10
|
+
import type { JSONSchema } from './types';
|
|
11
|
+
import { isHandledError, JSONParserErrorGroup } from './util/errors';
|
|
12
|
+
import * as url from './util/url';
|
|
13
13
|
|
|
14
14
|
interface ResolvedInput {
|
|
15
15
|
path: string;
|
|
16
16
|
schema: string | JSONSchema | Buffer | Awaited<JSONSchema> | undefined;
|
|
17
|
-
type:
|
|
17
|
+
type: 'file' | 'json' | 'url';
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export
|
|
20
|
+
export function getResolvedInput({
|
|
21
21
|
pathOrUrlOrSchema,
|
|
22
22
|
}: {
|
|
23
23
|
pathOrUrlOrSchema: JSONSchema | string | unknown;
|
|
24
|
-
}): ResolvedInput
|
|
24
|
+
}): ResolvedInput {
|
|
25
25
|
if (!pathOrUrlOrSchema) {
|
|
26
26
|
throw ono(`Expected a file path, URL, or object. Got ${pathOrUrlOrSchema}`);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const resolvedInput: ResolvedInput = {
|
|
30
|
-
path: typeof pathOrUrlOrSchema ===
|
|
30
|
+
path: typeof pathOrUrlOrSchema === 'string' ? pathOrUrlOrSchema : '',
|
|
31
31
|
schema: undefined,
|
|
32
|
-
type:
|
|
32
|
+
type: 'url',
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
// If the path is a filesystem path, then convert it to a URL.
|
|
@@ -40,34 +40,27 @@ export const getResolvedInput = ({
|
|
|
40
40
|
// If it doesn't work for your use-case, then use a URL instead.
|
|
41
41
|
if (resolvedInput.path && url.isFileSystemPath(resolvedInput.path)) {
|
|
42
42
|
resolvedInput.path = url.fromFileSystemPath(resolvedInput.path);
|
|
43
|
-
resolvedInput.type =
|
|
44
|
-
} else if (!resolvedInput.path && pathOrUrlOrSchema && typeof pathOrUrlOrSchema ===
|
|
45
|
-
if (
|
|
43
|
+
resolvedInput.type = 'file';
|
|
44
|
+
} else if (!resolvedInput.path && pathOrUrlOrSchema && typeof pathOrUrlOrSchema === 'object') {
|
|
45
|
+
if ('$id' in pathOrUrlOrSchema && pathOrUrlOrSchema.$id) {
|
|
46
46
|
// when schema id has defined an URL should use that hostname to request the references,
|
|
47
47
|
// instead of using the current page URL
|
|
48
48
|
const { hostname, protocol } = new URL(pathOrUrlOrSchema.$id as string);
|
|
49
|
-
resolvedInput.path = `${protocol}//${hostname}:${protocol ===
|
|
50
|
-
resolvedInput.type =
|
|
49
|
+
resolvedInput.path = `${protocol}//${hostname}:${protocol === 'https:' ? 443 : 80}`;
|
|
50
|
+
resolvedInput.type = 'url';
|
|
51
51
|
} else {
|
|
52
52
|
resolvedInput.schema = pathOrUrlOrSchema;
|
|
53
|
-
resolvedInput.type =
|
|
53
|
+
resolvedInput.type = 'json';
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
if (resolvedInput.type !==
|
|
57
|
+
if (resolvedInput.type !== 'json') {
|
|
58
58
|
// resolve the absolute path of the schema
|
|
59
59
|
resolvedInput.path = url.resolve(url.cwd(), resolvedInput.path);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
return resolvedInput;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const _ensureResolvedInputPath = (input: ResolvedInput, fallbackPath: string): ResolvedInput => {
|
|
66
|
-
if (input.type === "json" && (!input.path || input.path.length === 0)) {
|
|
67
|
-
return { ...input, path: fallbackPath };
|
|
68
|
-
}
|
|
69
|
-
return input;
|
|
70
|
-
};
|
|
63
|
+
}
|
|
71
64
|
|
|
72
65
|
// NOTE: previously used helper removed as unused
|
|
73
66
|
|
|
@@ -168,39 +161,6 @@ export class $RefParser {
|
|
|
168
161
|
return this.schema!;
|
|
169
162
|
}
|
|
170
163
|
|
|
171
|
-
/**
|
|
172
|
-
* Dereferences all `$ref` pointers in the JSON Schema, replacing each reference with its resolved value. This results in a schema object that does not contain any `$ref` pointers. Instead, it's a normal JavaScript object tree that can easily be crawled and used just like any other JavaScript object. This is great for programmatic usage, especially when using tools that don't understand JSON references.
|
|
173
|
-
*
|
|
174
|
-
* The dereference method maintains object reference equality, meaning that all `$ref` pointers that point to the same object will be replaced with references to the same object. Again, this is great for programmatic usage, but it does introduce the risk of circular references, so be careful if you intend to serialize the schema using `JSON.stringify()`. Consider using the bundle method instead, which does not create circular references.
|
|
175
|
-
*
|
|
176
|
-
* See https://apitools.dev/json-schema-ref-parser/docs/ref-parser.html#dereferenceschema-options-callback
|
|
177
|
-
*
|
|
178
|
-
* @param pathOrUrlOrSchema A JSON Schema object, or the file path or URL of a JSON Schema file.
|
|
179
|
-
*/
|
|
180
|
-
public async dereference({
|
|
181
|
-
fetch,
|
|
182
|
-
pathOrUrlOrSchema,
|
|
183
|
-
}: {
|
|
184
|
-
fetch?: RequestInit;
|
|
185
|
-
pathOrUrlOrSchema: JSONSchema | string | unknown;
|
|
186
|
-
}): Promise<JSONSchema> {
|
|
187
|
-
await this.parse({
|
|
188
|
-
fetch,
|
|
189
|
-
pathOrUrlOrSchema,
|
|
190
|
-
});
|
|
191
|
-
await resolveExternal(this, this.options);
|
|
192
|
-
const errors = JSONParserErrorGroup.getParserErrors(this);
|
|
193
|
-
if (errors.length > 0) {
|
|
194
|
-
throw new JSONParserErrorGroup(this);
|
|
195
|
-
}
|
|
196
|
-
_dereference(this, this.options);
|
|
197
|
-
const errors2 = JSONParserErrorGroup.getParserErrors(this);
|
|
198
|
-
if (errors2.length > 0) {
|
|
199
|
-
throw new JSONParserErrorGroup(this);
|
|
200
|
-
}
|
|
201
|
-
return this.schema!;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
164
|
/**
|
|
205
165
|
* Parses the given JSON schema.
|
|
206
166
|
* This method does not resolve any JSON references.
|
|
@@ -231,9 +191,9 @@ export class $RefParser {
|
|
|
231
191
|
if (schema) {
|
|
232
192
|
// immediately add a new $Ref with the schema object as value
|
|
233
193
|
const $ref = this.$refs._add(path);
|
|
234
|
-
$ref.pathType = url.isFileSystemPath(path) ?
|
|
194
|
+
$ref.pathType = url.isFileSystemPath(path) ? 'file' : 'http';
|
|
235
195
|
$ref.value = schema;
|
|
236
|
-
} else if (type !==
|
|
196
|
+
} else if (type !== 'json') {
|
|
237
197
|
const file = newFile(path);
|
|
238
198
|
|
|
239
199
|
// Add a new $Ref for this file, even though we don't have the value yet.
|
|
@@ -241,25 +201,24 @@ export class $RefParser {
|
|
|
241
201
|
const $refAdded = this.$refs._add(file.url);
|
|
242
202
|
$refAdded.pathType = type;
|
|
243
203
|
try {
|
|
244
|
-
const resolver = type ===
|
|
204
|
+
const resolver = type === 'file' ? fileResolver : urlResolver;
|
|
245
205
|
await resolver.handler({
|
|
246
206
|
arrayBuffer,
|
|
247
207
|
fetch,
|
|
248
208
|
file,
|
|
249
209
|
});
|
|
250
|
-
const parseResult = await parseFile(file, this.options);
|
|
210
|
+
const parseResult = await parseFile(file, this.options.parse);
|
|
251
211
|
$refAdded.value = parseResult.result;
|
|
252
212
|
schema = parseResult.result;
|
|
253
|
-
} catch (
|
|
254
|
-
if (isHandledError(
|
|
255
|
-
$refAdded.value =
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (isHandledError(error)) {
|
|
215
|
+
$refAdded.value = error;
|
|
256
216
|
}
|
|
257
|
-
|
|
258
|
-
throw err;
|
|
217
|
+
throw error;
|
|
259
218
|
}
|
|
260
219
|
}
|
|
261
220
|
|
|
262
|
-
if (schema === null || typeof schema !==
|
|
221
|
+
if (schema === null || typeof schema !== 'object' || Buffer.isBuffer(schema)) {
|
|
263
222
|
throw ono.syntax(`"${this.$refs._root$Ref.path || schema}" is not a valid JSON Schema`);
|
|
264
223
|
}
|
|
265
224
|
|
|
@@ -282,20 +241,23 @@ export class $RefParser {
|
|
|
282
241
|
resolvedInputs?: ResolvedInput[];
|
|
283
242
|
}): Promise<{ schemaMany: JSONSchema[] }> {
|
|
284
243
|
const resolvedInputs = [...(_resolvedInputs || [])];
|
|
285
|
-
resolvedInputs.push(
|
|
244
|
+
resolvedInputs.push(
|
|
245
|
+
...(pathOrUrlOrSchemas.map((schema) => getResolvedInput({ pathOrUrlOrSchema: schema })) ||
|
|
246
|
+
[]),
|
|
247
|
+
);
|
|
286
248
|
|
|
287
249
|
this.schemaMany = [];
|
|
288
250
|
this.schemaManySources = [];
|
|
289
251
|
this.sourcePathToPrefix = new Map();
|
|
290
252
|
|
|
291
253
|
for (let i = 0; i < resolvedInputs.length; i++) {
|
|
292
|
-
const resolvedInput = resolvedInputs[i]
|
|
254
|
+
const resolvedInput = resolvedInputs[i]!;
|
|
293
255
|
const { path, type } = resolvedInput;
|
|
294
256
|
let { schema } = resolvedInput;
|
|
295
257
|
|
|
296
258
|
if (schema) {
|
|
297
259
|
// keep schema as-is
|
|
298
|
-
} else if (type !==
|
|
260
|
+
} else if (type !== 'json') {
|
|
299
261
|
const file = newFile(path);
|
|
300
262
|
|
|
301
263
|
// Add a new $Ref for this file, even though we don't have the value yet.
|
|
@@ -303,25 +265,24 @@ export class $RefParser {
|
|
|
303
265
|
const $refAdded = this.$refs._add(file.url);
|
|
304
266
|
$refAdded.pathType = type;
|
|
305
267
|
try {
|
|
306
|
-
const resolver = type ===
|
|
268
|
+
const resolver = type === 'file' ? fileResolver : urlResolver;
|
|
307
269
|
await resolver.handler({
|
|
308
270
|
arrayBuffer: arrayBuffer?.[i],
|
|
309
271
|
fetch,
|
|
310
272
|
file,
|
|
311
273
|
});
|
|
312
|
-
const parseResult = await parseFile(file, this.options);
|
|
274
|
+
const parseResult = await parseFile(file, this.options.parse);
|
|
313
275
|
$refAdded.value = parseResult.result;
|
|
314
276
|
schema = parseResult.result;
|
|
315
|
-
} catch (
|
|
316
|
-
if (isHandledError(
|
|
317
|
-
$refAdded.value =
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (isHandledError(error)) {
|
|
279
|
+
$refAdded.value = error;
|
|
318
280
|
}
|
|
319
|
-
|
|
320
|
-
throw err;
|
|
281
|
+
throw error;
|
|
321
282
|
}
|
|
322
283
|
}
|
|
323
284
|
|
|
324
|
-
if (schema === null || typeof schema !==
|
|
285
|
+
if (schema === null || typeof schema !== 'object' || Buffer.isBuffer(schema)) {
|
|
325
286
|
throw ono.syntax(`"${this.$refs._root$Ref.path || schema}" is not a valid JSON Schema`);
|
|
326
287
|
}
|
|
327
288
|
|
|
@@ -337,7 +298,7 @@ export class $RefParser {
|
|
|
337
298
|
public mergeMany(): JSONSchema {
|
|
338
299
|
const schemas = this.schemaMany || [];
|
|
339
300
|
if (schemas.length === 0) {
|
|
340
|
-
throw ono(
|
|
301
|
+
throw ono('mergeMany called with no schemas. Did you run parseMany?');
|
|
341
302
|
}
|
|
342
303
|
|
|
343
304
|
const merged: any = {};
|
|
@@ -346,19 +307,19 @@ export class $RefParser {
|
|
|
346
307
|
let chosenOpenapi: string | undefined;
|
|
347
308
|
let chosenSwagger: string | undefined;
|
|
348
309
|
for (const s of schemas) {
|
|
349
|
-
if (!chosenOpenapi && s && typeof (s as any).openapi ===
|
|
310
|
+
if (!chosenOpenapi && s && typeof (s as any).openapi === 'string') {
|
|
350
311
|
chosenOpenapi = (s as any).openapi;
|
|
351
312
|
}
|
|
352
|
-
if (!chosenSwagger && s && typeof (s as any).swagger ===
|
|
313
|
+
if (!chosenSwagger && s && typeof (s as any).swagger === 'string') {
|
|
353
314
|
chosenSwagger = (s as any).swagger;
|
|
354
315
|
}
|
|
355
316
|
if (chosenOpenapi && chosenSwagger) {
|
|
356
317
|
break;
|
|
357
318
|
}
|
|
358
319
|
}
|
|
359
|
-
if (typeof chosenOpenapi ===
|
|
320
|
+
if (typeof chosenOpenapi === 'string') {
|
|
360
321
|
merged.openapi = chosenOpenapi;
|
|
361
|
-
} else if (typeof chosenSwagger ===
|
|
322
|
+
} else if (typeof chosenSwagger === 'string') {
|
|
362
323
|
merged.swagger = chosenSwagger;
|
|
363
324
|
}
|
|
364
325
|
|
|
@@ -366,7 +327,7 @@ export class $RefParser {
|
|
|
366
327
|
const infoAccumulator: any = {};
|
|
367
328
|
for (const s of schemas) {
|
|
368
329
|
const info = (s as any)?.info;
|
|
369
|
-
if (info && typeof info ===
|
|
330
|
+
if (info && typeof info === 'object') {
|
|
370
331
|
for (const [k, v] of Object.entries(info)) {
|
|
371
332
|
if (infoAccumulator[k] === undefined && v !== undefined) {
|
|
372
333
|
infoAccumulator[k] = JSON.parse(JSON.stringify(v));
|
|
@@ -385,8 +346,8 @@ export class $RefParser {
|
|
|
385
346
|
const arr = (s as any)?.servers;
|
|
386
347
|
if (Array.isArray(arr)) {
|
|
387
348
|
for (const srv of arr) {
|
|
388
|
-
if (srv && typeof srv ===
|
|
389
|
-
const key = `${srv.url ||
|
|
349
|
+
if (srv && typeof srv === 'object') {
|
|
350
|
+
const key = `${srv.url || ''}|${srv.description || ''}`;
|
|
390
351
|
if (!seenServers.has(key)) {
|
|
391
352
|
seenServers.add(key);
|
|
392
353
|
servers.push(JSON.parse(JSON.stringify(srv)));
|
|
@@ -403,15 +364,15 @@ export class $RefParser {
|
|
|
403
364
|
merged.components = {};
|
|
404
365
|
|
|
405
366
|
const componentSections = [
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
367
|
+
'schemas',
|
|
368
|
+
'parameters',
|
|
369
|
+
'requestBodies',
|
|
370
|
+
'responses',
|
|
371
|
+
'headers',
|
|
372
|
+
'securitySchemes',
|
|
373
|
+
'examples',
|
|
374
|
+
'links',
|
|
375
|
+
'callbacks',
|
|
415
376
|
];
|
|
416
377
|
for (const sec of componentSections) {
|
|
417
378
|
merged.components[sec] = {};
|
|
@@ -423,14 +384,14 @@ export class $RefParser {
|
|
|
423
384
|
|
|
424
385
|
const baseName = (p: string) => {
|
|
425
386
|
try {
|
|
426
|
-
const withoutHash = p.split(
|
|
427
|
-
const parts = withoutHash.split(
|
|
428
|
-
const filename = parts[parts.length - 1] ||
|
|
429
|
-
const dot = filename.lastIndexOf(
|
|
387
|
+
const withoutHash = p.split('#')[0]!;
|
|
388
|
+
const parts = withoutHash.split('/');
|
|
389
|
+
const filename = parts[parts.length - 1] || 'schema';
|
|
390
|
+
const dot = filename.lastIndexOf('.');
|
|
430
391
|
const raw = dot > 0 ? filename.substring(0, dot) : filename;
|
|
431
|
-
return raw.replace(/[^A-Za-z0-9_-]/g,
|
|
392
|
+
return raw.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
432
393
|
} catch {
|
|
433
|
-
return
|
|
394
|
+
return 'schema';
|
|
434
395
|
}
|
|
435
396
|
};
|
|
436
397
|
const unique = (set: Set<string>, proposed: string) => {
|
|
@@ -450,7 +411,7 @@ export class $RefParser {
|
|
|
450
411
|
const base = `#/components/${m[1]}/${m[2]}`;
|
|
451
412
|
const mapped = refMap.get(base);
|
|
452
413
|
if (mapped) {
|
|
453
|
-
return mapped + (m[3] ||
|
|
414
|
+
return mapped + (m[3] || '');
|
|
454
415
|
}
|
|
455
416
|
}
|
|
456
417
|
// OAS2: #/definitions/{name}...
|
|
@@ -460,7 +421,7 @@ export class $RefParser {
|
|
|
460
421
|
const mapped = refMap.get(base);
|
|
461
422
|
if (mapped) {
|
|
462
423
|
// map definitions -> components/schemas
|
|
463
|
-
return mapped + (m[2] ||
|
|
424
|
+
return mapped + (m[2] || '');
|
|
464
425
|
}
|
|
465
426
|
}
|
|
466
427
|
return ref;
|
|
@@ -479,28 +440,28 @@ export class $RefParser {
|
|
|
479
440
|
if (Array.isArray(obj)) {
|
|
480
441
|
return obj.map((v) => cloneAndRewrite(v, refMap, tagMap, opIdPrefix, basePath));
|
|
481
442
|
}
|
|
482
|
-
if (typeof obj !==
|
|
443
|
+
if (typeof obj !== 'object') {
|
|
483
444
|
return obj;
|
|
484
445
|
}
|
|
485
446
|
|
|
486
447
|
const out: any = {};
|
|
487
448
|
for (const [k, v] of Object.entries(obj)) {
|
|
488
|
-
if (k ===
|
|
449
|
+
if (k === '$ref' && typeof v === 'string') {
|
|
489
450
|
const s = v as string;
|
|
490
|
-
if (s.startsWith(
|
|
451
|
+
if (s.startsWith('#')) {
|
|
491
452
|
out[k] = rewriteRef(s, refMap);
|
|
492
453
|
} else {
|
|
493
454
|
const proto = url.getProtocol(s);
|
|
494
455
|
if (proto === undefined) {
|
|
495
456
|
// relative external ref -> absolutize against source base path
|
|
496
|
-
out[k] = url.resolve(basePath +
|
|
457
|
+
out[k] = url.resolve(basePath + '#', s);
|
|
497
458
|
} else {
|
|
498
459
|
out[k] = s;
|
|
499
460
|
}
|
|
500
461
|
}
|
|
501
|
-
} else if (k ===
|
|
462
|
+
} else if (k === 'tags' && Array.isArray(v) && v.every((x) => typeof x === 'string')) {
|
|
502
463
|
out[k] = v.map((t) => tagMap.get(t) || t);
|
|
503
|
-
} else if (k ===
|
|
464
|
+
} else if (k === 'operationId' && typeof v === 'string') {
|
|
504
465
|
out[k] = unique(usedOpIds, `${opIdPrefix}_${v}`);
|
|
505
466
|
} else {
|
|
506
467
|
out[k] = cloneAndRewrite(v as any, refMap, tagMap, opIdPrefix, basePath);
|
|
@@ -517,7 +478,12 @@ export class $RefParser {
|
|
|
517
478
|
// Track prefix for this source path (strip hash). Only map real file/http paths
|
|
518
479
|
const withoutHash = url.stripHash(sourcePath);
|
|
519
480
|
const protocol = url.getProtocol(withoutHash);
|
|
520
|
-
if (
|
|
481
|
+
if (
|
|
482
|
+
protocol === undefined ||
|
|
483
|
+
protocol === 'file' ||
|
|
484
|
+
protocol === 'http' ||
|
|
485
|
+
protocol === 'https'
|
|
486
|
+
) {
|
|
521
487
|
this.sourcePathToPrefix.set(withoutHash, prefix);
|
|
522
488
|
}
|
|
523
489
|
|
|
@@ -535,7 +501,7 @@ export class $RefParser {
|
|
|
535
501
|
|
|
536
502
|
const srcTags: any[] = Array.isArray(schema.tags) ? schema.tags : [];
|
|
537
503
|
for (const t of srcTags) {
|
|
538
|
-
if (!t || typeof t !==
|
|
504
|
+
if (!t || typeof t !== 'object' || typeof t.name !== 'string') {
|
|
539
505
|
continue;
|
|
540
506
|
}
|
|
541
507
|
const desired = t.name;
|
|
@@ -551,7 +517,13 @@ export class $RefParser {
|
|
|
551
517
|
const group = (schema.components && schema.components[sec]) || {};
|
|
552
518
|
for (const [name, val] of Object.entries(group)) {
|
|
553
519
|
const newName = `${prefix}_${name}`;
|
|
554
|
-
merged.components[sec][newName] = cloneAndRewrite(
|
|
520
|
+
merged.components[sec][newName] = cloneAndRewrite(
|
|
521
|
+
val,
|
|
522
|
+
refMap,
|
|
523
|
+
tagMap,
|
|
524
|
+
prefix,
|
|
525
|
+
url.stripHash(sourcePath),
|
|
526
|
+
);
|
|
555
527
|
}
|
|
556
528
|
}
|
|
557
529
|
|
|
@@ -559,10 +531,16 @@ export class $RefParser {
|
|
|
559
531
|
for (const [p, item] of Object.entries(srcPaths)) {
|
|
560
532
|
let targetPath = p;
|
|
561
533
|
if (merged.paths[p]) {
|
|
562
|
-
const trimmed = p.startsWith(
|
|
534
|
+
const trimmed = p.startsWith('/') ? p.substring(1) : p;
|
|
563
535
|
targetPath = `/${prefix}/${trimmed}`;
|
|
564
536
|
}
|
|
565
|
-
merged.paths[targetPath] = cloneAndRewrite(
|
|
537
|
+
merged.paths[targetPath] = cloneAndRewrite(
|
|
538
|
+
item,
|
|
539
|
+
refMap,
|
|
540
|
+
tagMap,
|
|
541
|
+
prefix,
|
|
542
|
+
url.stripHash(sourcePath),
|
|
543
|
+
);
|
|
566
544
|
}
|
|
567
545
|
}
|
|
568
546
|
|
|
@@ -574,12 +552,12 @@ export class $RefParser {
|
|
|
574
552
|
const rootPath = this.schemaManySources[0] || url.cwd();
|
|
575
553
|
this.$refs = new $Refs();
|
|
576
554
|
const rootRef = this.$refs._add(rootPath);
|
|
577
|
-
rootRef.pathType = url.isFileSystemPath(rootPath) ?
|
|
555
|
+
rootRef.pathType = url.isFileSystemPath(rootPath) ? 'file' : 'http';
|
|
578
556
|
rootRef.value = merged;
|
|
579
557
|
this.schema = merged;
|
|
580
558
|
return merged as JSONSchema;
|
|
581
559
|
}
|
|
582
560
|
}
|
|
583
561
|
|
|
584
|
-
export { sendRequest } from
|
|
585
|
-
export type { JSONSchema } from
|
|
562
|
+
export { sendRequest } from './resolvers/url';
|
|
563
|
+
export type { JSONSchema } from './types';
|
package/{lib → src}/options.ts
RENAMED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { textParser } from
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import type { JSONSchemaObject, Plugin } from "./types/index.js";
|
|
1
|
+
import { binaryParser } from './parsers/binary';
|
|
2
|
+
import { jsonParser } from './parsers/json';
|
|
3
|
+
import { textParser } from './parsers/text';
|
|
4
|
+
import { yamlParser } from './parsers/yaml';
|
|
5
|
+
import type { JSONSchemaObject, Plugin } from './types';
|
|
7
6
|
|
|
8
7
|
export interface DereferenceOptions {
|
|
9
8
|
/**
|
|
@@ -13,7 +12,7 @@ export interface DereferenceOptions {
|
|
|
13
12
|
*
|
|
14
13
|
* If set to `"ignore"`, then circular references will simply be ignored. No error will be thrown, but the `$Refs.circular` property will still be set to `true`.
|
|
15
14
|
*/
|
|
16
|
-
circular?: boolean |
|
|
15
|
+
circular?: boolean | 'ignore';
|
|
17
16
|
/**
|
|
18
17
|
* A function, called for each path, which can return true to stop this path and all
|
|
19
18
|
* subpaths from being dereferenced further. This is useful in schemas where some
|
|
@@ -28,7 +27,12 @@ export interface DereferenceOptions {
|
|
|
28
27
|
* @argument {JSONSchemaObject} parent - The parent of the dereferenced object
|
|
29
28
|
* @argument {string} parentPropName - The prop name of the parent object whose value was dereferenced
|
|
30
29
|
*/
|
|
31
|
-
onDereference?(
|
|
30
|
+
onDereference?(
|
|
31
|
+
path: string,
|
|
32
|
+
value: JSONSchemaObject,
|
|
33
|
+
parent?: JSONSchemaObject,
|
|
34
|
+
parentPropName?: string,
|
|
35
|
+
): void;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
/**
|
|
@@ -82,7 +86,7 @@ export const getJsonSchemaRefParserDefaultOptions = (): $RefParserOptions => ({
|
|
|
82
86
|
*/
|
|
83
87
|
excludedPathMatcher: () => false,
|
|
84
88
|
// @ts-expect-error
|
|
85
|
-
referenceResolution:
|
|
89
|
+
referenceResolution: 'relative',
|
|
86
90
|
},
|
|
87
91
|
/**
|
|
88
92
|
* Determines how different types of files will be parsed.
|