@apidevtools/json-schema-ref-parser 9.0.5 → 9.0.9
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/CHANGELOG.md +2 -4
- package/README.md +3 -3
- package/lib/dereference.js +79 -37
- package/lib/index.d.ts +25 -11
- package/lib/parsers/yaml.js +1 -1
- package/lib/pointer.js +15 -3
- package/lib/ref.js +18 -9
- package/lib/resolve-external.js +6 -3
- package/lib/util/errors.js +4 -0
- package/lib/util/url.js +15 -1
- package/package.json +52 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
Change Log
|
|
2
|
-
|
|
3
|
-
All notable changes will be documented in this file.
|
|
4
|
-
JSON Schema $Ref Parser adheres to [Semantic Versioning](http://semver.org/).
|
|
5
|
-
|
|
2
|
+
==========
|
|
6
3
|
|
|
4
|
+
See [GitHub Releases](https://github.com/APIDevTools/json-schema-ref-parser/releases) for more information on newer releases.
|
|
7
5
|
|
|
8
6
|
[v9.0.0](https://github.com/APIDevTools/json-schema-ref-parser/tree/v9.0.0) (2020-04-21)
|
|
9
7
|
----------------------------------------------------------------------------------------------------
|
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ JSON Schema $Ref Parser is a full [JSON Reference](https://tools.ietf.org/html/d
|
|
|
52
52
|
- Can [dereference](https://apitools.dev/json-schema-ref-parser/docs/ref-parser.html#dereferencepath-options-callback) your schema, producing a plain-old JavaScript object that's easy to work with
|
|
53
53
|
- Supports [circular references](https://apitools.dev/json-schema-ref-parser/docs/#circular-refs), nested references, back-references, and cross-references between files
|
|
54
54
|
- Maintains object reference equality — `$ref` pointers to the same value always resolve to the same object instance
|
|
55
|
-
- Tested in Node and all major web browsers on Windows, Mac, and Linux
|
|
55
|
+
- Tested in Node v10, v12, & v14, and all major web browsers on Windows, Mac, and Linux
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
Example
|
|
@@ -129,7 +129,7 @@ Full API documentation is available [right here](https://apitools.dev/json-schem
|
|
|
129
129
|
|
|
130
130
|
Contributing
|
|
131
131
|
--------------------------
|
|
132
|
-
I welcome any contributions, enhancements, and bug-fixes. [
|
|
132
|
+
I welcome any contributions, enhancements, and bug-fixes. [Open an issue](https://github.com/APIDevTools/json-schema-ref-parser/issues) on GitHub and [submit a pull request](https://github.com/APIDevTools/json-schema-ref-parser/pulls).
|
|
133
133
|
|
|
134
134
|
#### Building/Testing
|
|
135
135
|
To build/test the project locally on your computer:
|
|
@@ -157,6 +157,6 @@ Big Thanks To
|
|
|
157
157
|
--------------------------
|
|
158
158
|
Thanks to these awesome companies for their support of Open Source developers ❤
|
|
159
159
|
|
|
160
|
-
[](https://stoplight.io/?utm_source=github&utm_medium=readme&utm_campaign=json_schema_ref_parser)
|
|
161
161
|
[](https://saucelabs.com)
|
|
162
162
|
[](https://coveralls.io)
|
package/lib/dereference.js
CHANGED
|
@@ -16,7 +16,7 @@ module.exports = dereference;
|
|
|
16
16
|
*/
|
|
17
17
|
function dereference (parser, options) {
|
|
18
18
|
// console.log('Dereferencing $ref pointers in %s', parser.$refs._root$Ref.path);
|
|
19
|
-
let dereferenced = crawl(parser.schema, parser.$refs._root$Ref.path, "#",
|
|
19
|
+
let dereferenced = crawl(parser.schema, parser.$refs._root$Ref.path, "#", new Set(), new Set(), new Map(), parser.$refs, options);
|
|
20
20
|
parser.$refs.circular = dereferenced.circular;
|
|
21
21
|
parser.schema = dereferenced.value;
|
|
22
22
|
}
|
|
@@ -27,55 +27,66 @@ function dereference (parser, options) {
|
|
|
27
27
|
* @param {*} obj - The value to crawl. If it's not an object or array, it will be ignored.
|
|
28
28
|
* @param {string} path - The full path of `obj`, possibly with a JSON Pointer in the hash
|
|
29
29
|
* @param {string} pathFromRoot - The path of `obj` from the schema root
|
|
30
|
-
* @param {object
|
|
30
|
+
* @param {Set<object>} parents - An array of the parent objects that have already been dereferenced
|
|
31
|
+
* @param {Set<object>} processedObjects - An array of all the objects that have already been processed
|
|
32
|
+
* @param {Map<string,object>} dereferencedCache - An map of all the dereferenced objects
|
|
31
33
|
* @param {$Refs} $refs
|
|
32
34
|
* @param {$RefParserOptions} options
|
|
33
35
|
* @returns {{value: object, circular: boolean}}
|
|
34
36
|
*/
|
|
35
|
-
function crawl (obj, path, pathFromRoot, parents, $refs, options) {
|
|
37
|
+
function crawl (obj, path, pathFromRoot, parents, processedObjects, dereferencedCache, $refs, options) {
|
|
36
38
|
let dereferenced;
|
|
37
39
|
let result = {
|
|
38
40
|
value: obj,
|
|
39
41
|
circular: false
|
|
40
42
|
};
|
|
41
43
|
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
+
if (options.dereference.circular === "ignore" || !processedObjects.has(obj)) {
|
|
45
|
+
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
|
|
46
|
+
parents.add(obj);
|
|
47
|
+
processedObjects.add(obj);
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
circular = dereferenced.circular;
|
|
60
|
-
obj[key] = dereferenced.value;
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
if (parents.indexOf(value) === -1) {
|
|
64
|
-
dereferenced = crawl(value, keyPath, keyPathFromRoot, parents, $refs, options);
|
|
49
|
+
if ($Ref.isAllowed$Ref(obj, options)) {
|
|
50
|
+
dereferenced = dereference$Ref(obj, path, pathFromRoot, parents, processedObjects, dereferencedCache, $refs, options);
|
|
51
|
+
result.circular = dereferenced.circular;
|
|
52
|
+
result.value = dereferenced.value;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
for (const key of Object.keys(obj)) {
|
|
56
|
+
let keyPath = Pointer.join(path, key);
|
|
57
|
+
let keyPathFromRoot = Pointer.join(pathFromRoot, key);
|
|
58
|
+
let value = obj[key];
|
|
59
|
+
let circular = false;
|
|
60
|
+
|
|
61
|
+
if ($Ref.isAllowed$Ref(value, options)) {
|
|
62
|
+
dereferenced = dereference$Ref(value, keyPath, keyPathFromRoot, parents, processedObjects, dereferencedCache, $refs, options);
|
|
65
63
|
circular = dereferenced.circular;
|
|
66
|
-
|
|
64
|
+
// Avoid pointless mutations; breaks frozen objects to no profit
|
|
65
|
+
if (obj[key] !== dereferenced.value) {
|
|
66
|
+
obj[key] = dereferenced.value;
|
|
67
|
+
}
|
|
67
68
|
}
|
|
68
69
|
else {
|
|
69
|
-
|
|
70
|
+
if (!parents.has(value)) {
|
|
71
|
+
dereferenced = crawl(value, keyPath, keyPathFromRoot, parents, processedObjects, dereferencedCache, $refs, options);
|
|
72
|
+
circular = dereferenced.circular;
|
|
73
|
+
// Avoid pointless mutations; breaks frozen objects to no profit
|
|
74
|
+
if (obj[key] !== dereferenced.value) {
|
|
75
|
+
obj[key] = dereferenced.value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
circular = foundCircularReference(keyPath, $refs, options);
|
|
80
|
+
}
|
|
70
81
|
}
|
|
71
|
-
}
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
// Set the "isCircular" flag if this or any other property is circular
|
|
84
|
+
result.circular = result.circular || circular;
|
|
85
|
+
}
|
|
75
86
|
}
|
|
76
|
-
}
|
|
77
87
|
|
|
78
|
-
|
|
88
|
+
parents.delete(obj);
|
|
89
|
+
}
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
return result;
|
|
@@ -87,15 +98,38 @@ function crawl (obj, path, pathFromRoot, parents, $refs, options) {
|
|
|
87
98
|
* @param {{$ref: string}} $ref - The JSON Reference to resolve
|
|
88
99
|
* @param {string} path - The full path of `$ref`, possibly with a JSON Pointer in the hash
|
|
89
100
|
* @param {string} pathFromRoot - The path of `$ref` from the schema root
|
|
90
|
-
* @param {object
|
|
101
|
+
* @param {Set<object>} parents - An array of the parent objects that have already been dereferenced
|
|
102
|
+
* @param {Set<object>} processedObjects - An array of all the objects that have already been dereferenced
|
|
103
|
+
* @param {Map<string,object>} dereferencedCache - An map of all the dereferenced objects
|
|
91
104
|
* @param {$Refs} $refs
|
|
92
105
|
* @param {$RefParserOptions} options
|
|
93
106
|
* @returns {{value: object, circular: boolean}}
|
|
94
107
|
*/
|
|
95
|
-
function dereference$Ref ($ref, path, pathFromRoot, parents, $refs, options) {
|
|
108
|
+
function dereference$Ref ($ref, path, pathFromRoot, parents, processedObjects, dereferencedCache, $refs, options) {
|
|
96
109
|
// console.log('Dereferencing $ref pointer "%s" at %s', $ref.$ref, path);
|
|
97
110
|
|
|
98
111
|
let $refPath = url.resolve(path, $ref.$ref);
|
|
112
|
+
|
|
113
|
+
const cache = dereferencedCache.get($refPath);
|
|
114
|
+
if (cache) {
|
|
115
|
+
const refKeys = Object.keys($ref);
|
|
116
|
+
if (refKeys.length > 1) {
|
|
117
|
+
const extraKeys = {};
|
|
118
|
+
for (let key of refKeys) {
|
|
119
|
+
if (key !== "$ref" && !(key in cache.value)) {
|
|
120
|
+
extraKeys[key] = $ref[key];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
circular: cache.circular,
|
|
125
|
+
value: Object.assign({}, cache.value, extraKeys),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return cache;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
99
133
|
let pointer = $refs._resolve($refPath, path, options);
|
|
100
134
|
|
|
101
135
|
if (pointer === null) {
|
|
@@ -107,7 +141,7 @@ function dereference$Ref ($ref, path, pathFromRoot, parents, $refs, options) {
|
|
|
107
141
|
|
|
108
142
|
// Check for circular references
|
|
109
143
|
let directCircular = pointer.circular;
|
|
110
|
-
let circular = directCircular || parents.
|
|
144
|
+
let circular = directCircular || parents.has(pointer.value);
|
|
111
145
|
circular && foundCircularReference(path, $refs, options);
|
|
112
146
|
|
|
113
147
|
// Dereference the JSON reference
|
|
@@ -116,7 +150,7 @@ function dereference$Ref ($ref, path, pathFromRoot, parents, $refs, options) {
|
|
|
116
150
|
// Crawl the dereferenced value (unless it's circular)
|
|
117
151
|
if (!circular) {
|
|
118
152
|
// Determine if the dereferenced value is circular
|
|
119
|
-
let dereferenced = crawl(dereferencedValue, pointer.path, pathFromRoot, parents, $refs, options);
|
|
153
|
+
let dereferenced = crawl(dereferencedValue, pointer.path, pathFromRoot, parents, processedObjects, dereferencedCache, $refs, options);
|
|
120
154
|
circular = dereferenced.circular;
|
|
121
155
|
dereferencedValue = dereferenced.value;
|
|
122
156
|
}
|
|
@@ -132,10 +166,18 @@ function dereference$Ref ($ref, path, pathFromRoot, parents, $refs, options) {
|
|
|
132
166
|
dereferencedValue.$ref = pathFromRoot;
|
|
133
167
|
}
|
|
134
168
|
|
|
135
|
-
|
|
169
|
+
|
|
170
|
+
const dereferencedObject = {
|
|
136
171
|
circular,
|
|
137
172
|
value: dereferencedValue
|
|
138
173
|
};
|
|
174
|
+
|
|
175
|
+
// only cache if no extra properties than $ref
|
|
176
|
+
if (Object.keys($ref).length === 1) {
|
|
177
|
+
dereferencedCache.set($refPath, dereferencedObject);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return dereferencedObject;
|
|
139
181
|
}
|
|
140
182
|
|
|
141
183
|
/**
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JSONSchema4, JSONSchema4Type, JSONSchema6, JSONSchema6Type } from "json-schema";
|
|
1
|
+
import { JSONSchema4, JSONSchema4Type, JSONSchema6, JSONSchema6Type, JSONSchema7, JSONSchema7Type } from "json-schema";
|
|
2
2
|
|
|
3
3
|
export = $RefParser;
|
|
4
4
|
|
|
@@ -173,7 +173,7 @@ declare class $RefParser {
|
|
|
173
173
|
// eslint-disable-next-line no-redeclare
|
|
174
174
|
declare namespace $RefParser {
|
|
175
175
|
|
|
176
|
-
export type JSONSchema = JSONSchema4 | JSONSchema6;
|
|
176
|
+
export type JSONSchema = JSONSchema4 | JSONSchema6 | JSONSchema7;
|
|
177
177
|
export type SchemaCallback = (err: Error | null, schema?: JSONSchema) => any;
|
|
178
178
|
export type $RefsCallback = (err: Error | null, $refs?: $Refs) => any;
|
|
179
179
|
|
|
@@ -208,7 +208,7 @@ declare namespace $RefParser {
|
|
|
208
208
|
file?: Partial<ResolverOptions> | boolean;
|
|
209
209
|
http?: HTTPResolverOptions | boolean;
|
|
210
210
|
} & {
|
|
211
|
-
[key: string]: Partial<ResolverOptions
|
|
211
|
+
[key: string]: Partial<ResolverOptions> | HTTPResolverOptions | boolean | undefined;
|
|
212
212
|
};
|
|
213
213
|
|
|
214
214
|
/**
|
|
@@ -284,7 +284,7 @@ declare namespace $RefParser {
|
|
|
284
284
|
read(
|
|
285
285
|
file: FileInfo,
|
|
286
286
|
callback?: (error: Error | null, data: string | null) => any
|
|
287
|
-
): string | Buffer | Promise<string | Buffer>;
|
|
287
|
+
): string | Buffer | JSONSchema | Promise<string | Buffer | JSONSchema>;
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
export interface ParserOptions {
|
|
@@ -395,7 +395,7 @@ declare namespace $RefParser {
|
|
|
395
395
|
*
|
|
396
396
|
* @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash
|
|
397
397
|
*/
|
|
398
|
-
public get($ref: string): JSONSchema4Type | JSONSchema6Type
|
|
398
|
+
public get($ref: string): JSONSchema4Type | JSONSchema6Type | JSONSchema7Type
|
|
399
399
|
|
|
400
400
|
/**
|
|
401
401
|
* Sets the value at the given path in the schema. If the property, or any of its parents, don't exist, they will be created.
|
|
@@ -403,12 +403,14 @@ declare namespace $RefParser {
|
|
|
403
403
|
* @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash
|
|
404
404
|
* @param value The value to assign. Can be anything (object, string, number, etc.)
|
|
405
405
|
*/
|
|
406
|
-
public set($ref: string, value: JSONSchema4Type | JSONSchema6Type): void
|
|
406
|
+
public set($ref: string, value: JSONSchema4Type | JSONSchema6Type | JSONSchema7Type): void
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
export type JSONParserErrorType = "EUNKNOWN" | "EPARSER" | "EUNMATCHEDPARSER" | "ERESOLVER" | "EUNMATCHEDRESOLVER" | "EMISSINGPOINTER" | "EINVALIDPOINTER";
|
|
410
410
|
|
|
411
411
|
export class JSONParserError extends Error {
|
|
412
|
+
public constructor(message: string, source: string);
|
|
413
|
+
|
|
412
414
|
public readonly name: string;
|
|
413
415
|
public readonly message: string;
|
|
414
416
|
public readonly source: string;
|
|
@@ -439,28 +441,40 @@ declare namespace $RefParser {
|
|
|
439
441
|
}
|
|
440
442
|
|
|
441
443
|
export class ParserError extends JSONParserError {
|
|
444
|
+
public constructor(message: string, source: string);
|
|
445
|
+
|
|
442
446
|
public readonly name = "ParserError";
|
|
443
447
|
public readonly code = "EPARSER";
|
|
444
448
|
}
|
|
445
449
|
export class UnmatchedParserError extends JSONParserError {
|
|
450
|
+
public constructor(source: string);
|
|
451
|
+
|
|
446
452
|
public readonly name = "UnmatchedParserError";
|
|
447
|
-
public readonly code ="EUNMATCHEDPARSER";
|
|
453
|
+
public readonly code = "EUNMATCHEDPARSER";
|
|
448
454
|
}
|
|
449
455
|
export class ResolverError extends JSONParserError {
|
|
456
|
+
public constructor(ex: Error | NodeJS.ErrnoException, source: string);
|
|
457
|
+
|
|
450
458
|
public readonly name = "ResolverError";
|
|
451
|
-
public readonly code ="ERESOLVER";
|
|
459
|
+
public readonly code = "ERESOLVER";
|
|
452
460
|
public readonly ioErrorCode?: string;
|
|
453
461
|
}
|
|
454
462
|
export class UnmatchedResolverError extends JSONParserError {
|
|
463
|
+
public constructor(source: string);
|
|
464
|
+
|
|
455
465
|
public readonly name = "UnmatchedResolverError";
|
|
456
|
-
public readonly code ="EUNMATCHEDRESOLVER";
|
|
466
|
+
public readonly code = "EUNMATCHEDRESOLVER";
|
|
457
467
|
}
|
|
458
468
|
export class MissingPointerError extends JSONParserError {
|
|
469
|
+
public constructor(token: string | number, source: string);
|
|
470
|
+
|
|
459
471
|
public readonly name = "MissingPointerError";
|
|
460
|
-
public readonly code ="EMISSINGPOINTER";
|
|
472
|
+
public readonly code = "EMISSINGPOINTER";
|
|
461
473
|
}
|
|
462
474
|
export class InvalidPointerError extends JSONParserError {
|
|
475
|
+
public constructor(pointer: string, source: string);
|
|
476
|
+
|
|
463
477
|
public readonly name = "InvalidPointerError";
|
|
464
|
-
public readonly code ="EINVALIDPOINTER";
|
|
478
|
+
public readonly code = "EINVALIDPOINTER";
|
|
465
479
|
}
|
|
466
480
|
}
|
package/lib/parsers/yaml.js
CHANGED
package/lib/pointer.js
CHANGED
|
@@ -64,6 +64,7 @@ function Pointer ($ref, path, friendlyPath) {
|
|
|
64
64
|
*
|
|
65
65
|
* @param {*} obj - The object that will be crawled
|
|
66
66
|
* @param {$RefParserOptions} options
|
|
67
|
+
* @param {string} pathFromRoot - the path of place that initiated resolving
|
|
67
68
|
*
|
|
68
69
|
* @returns {Pointer}
|
|
69
70
|
* Returns a JSON pointer whose {@link Pointer#value} is the resolved value.
|
|
@@ -71,7 +72,7 @@ function Pointer ($ref, path, friendlyPath) {
|
|
|
71
72
|
* the {@link Pointer#$ref} and {@link Pointer#path} will reflect the resolution path
|
|
72
73
|
* of the resolved value.
|
|
73
74
|
*/
|
|
74
|
-
Pointer.prototype.resolve = function (obj, options) {
|
|
75
|
+
Pointer.prototype.resolve = function (obj, options, pathFromRoot) {
|
|
75
76
|
let tokens = Pointer.parse(this.path, this.originalPath);
|
|
76
77
|
|
|
77
78
|
// Crawl the object, one token at a time
|
|
@@ -83,6 +84,10 @@ Pointer.prototype.resolve = function (obj, options) {
|
|
|
83
84
|
this.path = Pointer.join(this.path, tokens.slice(i));
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
if (typeof this.value === "object" && this.value !== null && "$ref" in this.value) {
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
86
91
|
let token = tokens[i];
|
|
87
92
|
if (this.value[token] === undefined || this.value[token] === null) {
|
|
88
93
|
this.value = null;
|
|
@@ -94,7 +99,10 @@ Pointer.prototype.resolve = function (obj, options) {
|
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
// Resolve the final value
|
|
97
|
-
|
|
102
|
+
if (!this.value || this.value.$ref && url.resolve(this.path, this.value.$ref) !== pathFromRoot) {
|
|
103
|
+
resolveIf$Ref(this, options);
|
|
104
|
+
}
|
|
105
|
+
|
|
98
106
|
return this;
|
|
99
107
|
};
|
|
100
108
|
|
|
@@ -226,7 +234,11 @@ function resolveIf$Ref (pointer, options) {
|
|
|
226
234
|
pointer.circular = true;
|
|
227
235
|
}
|
|
228
236
|
else {
|
|
229
|
-
let resolved = pointer.$ref.$refs._resolve($refPath,
|
|
237
|
+
let resolved = pointer.$ref.$refs._resolve($refPath, pointer.path, options);
|
|
238
|
+
if (resolved === null) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
|
|
230
242
|
pointer.indirections += resolved.indirections + 1;
|
|
231
243
|
|
|
232
244
|
if ($Ref.isExtended$Ref(pointer.value)) {
|
package/lib/ref.js
CHANGED
|
@@ -9,7 +9,7 @@ const { safePointerToPath, stripHash, getHash } = require("./util/url");
|
|
|
9
9
|
/**
|
|
10
10
|
* This class represents a single JSON reference and its resolved value.
|
|
11
11
|
*
|
|
12
|
-
* @
|
|
12
|
+
* @class
|
|
13
13
|
*/
|
|
14
14
|
function $Ref () {
|
|
15
15
|
/**
|
|
@@ -27,24 +27,28 @@ function $Ref () {
|
|
|
27
27
|
/**
|
|
28
28
|
* The resolved value of the JSON reference.
|
|
29
29
|
* Can be any JSON type, not just objects. Unknown file types are represented as Buffers (byte arrays).
|
|
30
|
+
*
|
|
30
31
|
* @type {?*}
|
|
31
32
|
*/
|
|
32
33
|
this.value = undefined;
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* The {@link $Refs} object that contains this {@link $Ref} object.
|
|
37
|
+
*
|
|
36
38
|
* @type {$Refs}
|
|
37
39
|
*/
|
|
38
40
|
this.$refs = undefined;
|
|
39
41
|
|
|
40
42
|
/**
|
|
41
43
|
* Indicates the type of {@link $Ref#path} (e.g. "file", "http", etc.)
|
|
44
|
+
*
|
|
42
45
|
* @type {?string}
|
|
43
46
|
*/
|
|
44
47
|
this.pathType = undefined;
|
|
45
48
|
|
|
46
49
|
/**
|
|
47
50
|
* List of all errors. Undefined if no errors.
|
|
51
|
+
*
|
|
48
52
|
* @type {Array<JSONParserError | ResolverError | ParserError | MissingPointerError>}
|
|
49
53
|
*/
|
|
50
54
|
this.errors = undefined;
|
|
@@ -53,7 +57,7 @@ function $Ref () {
|
|
|
53
57
|
/**
|
|
54
58
|
* Pushes an error to errors array.
|
|
55
59
|
*
|
|
56
|
-
* @param {Array<JSONParserError | JSONParserErrorGroup>}
|
|
60
|
+
* @param {Array<JSONParserError | JSONParserErrorGroup>} err - The error to be pushed
|
|
57
61
|
* @returns {void}
|
|
58
62
|
*/
|
|
59
63
|
$Ref.prototype.addError = function (err) {
|
|
@@ -61,17 +65,22 @@ $Ref.prototype.addError = function (err) {
|
|
|
61
65
|
this.errors = [];
|
|
62
66
|
}
|
|
63
67
|
|
|
68
|
+
const existingErrors = this.errors.map(({ footprint }) => footprint);
|
|
69
|
+
|
|
64
70
|
// the path has been almost certainly set at this point,
|
|
65
|
-
// but just in case something went wrong,
|
|
71
|
+
// but just in case something went wrong, normalizeError injects path if necessary
|
|
72
|
+
// moreover, certain errors might point at the same spot, so filter them out to reduce noise
|
|
66
73
|
if (Array.isArray(err.errors)) {
|
|
67
|
-
this.errors.push(...err.errors
|
|
74
|
+
this.errors.push(...err.errors
|
|
75
|
+
.map(normalizeError)
|
|
76
|
+
.filter(({ footprint }) => !existingErrors.includes(footprint)),
|
|
77
|
+
);
|
|
68
78
|
}
|
|
69
|
-
else {
|
|
79
|
+
else if (!existingErrors.includes(err.footprint)) {
|
|
70
80
|
this.errors.push(normalizeError(err));
|
|
71
81
|
}
|
|
72
82
|
};
|
|
73
83
|
|
|
74
|
-
|
|
75
84
|
/**
|
|
76
85
|
* Determines whether the given JSON reference exists within this {@link $Ref#value}.
|
|
77
86
|
*
|
|
@@ -106,13 +115,13 @@ $Ref.prototype.get = function (path, options) {
|
|
|
106
115
|
* @param {string} path - The full path being resolved, optionally with a JSON pointer in the hash
|
|
107
116
|
* @param {$RefParserOptions} options
|
|
108
117
|
* @param {string} friendlyPath - The original user-specified path (used for error messages)
|
|
109
|
-
* @param {string} pathFromRoot - The path of `obj` from the schema root
|
|
110
|
-
* @returns {Pointer}
|
|
118
|
+
* @param {string} pathFromRoot - The path of `obj` from the schema root
|
|
119
|
+
* @returns {Pointer | null}
|
|
111
120
|
*/
|
|
112
121
|
$Ref.prototype.resolve = function (path, options, friendlyPath, pathFromRoot) {
|
|
113
122
|
let pointer = new Pointer(this, path, friendlyPath);
|
|
114
123
|
try {
|
|
115
|
-
return pointer.resolve(this.value, options);
|
|
124
|
+
return pointer.resolve(this.value, options, pathFromRoot);
|
|
116
125
|
}
|
|
117
126
|
catch (err) {
|
|
118
127
|
if (!options || !options.continueOnError || !isHandledError(err)) {
|
package/lib/resolve-external.js
CHANGED
|
@@ -44,6 +44,7 @@ function resolveExternal (parser, options) {
|
|
|
44
44
|
* @param {string} path - The full path of `obj`, possibly with a JSON Pointer in the hash
|
|
45
45
|
* @param {$Refs} $refs
|
|
46
46
|
* @param {$RefParserOptions} options
|
|
47
|
+
* @param {Set} seen - Internal.
|
|
47
48
|
*
|
|
48
49
|
* @returns {Promise[]}
|
|
49
50
|
* Returns an array of promises. There will be one promise for each JSON reference in `obj`.
|
|
@@ -51,10 +52,12 @@ function resolveExternal (parser, options) {
|
|
|
51
52
|
* If any of the JSON references point to files that contain additional JSON references,
|
|
52
53
|
* then the corresponding promise will internally reference an array of promises.
|
|
53
54
|
*/
|
|
54
|
-
function crawl (obj, path, $refs, options) {
|
|
55
|
+
function crawl (obj, path, $refs, options, seen) {
|
|
56
|
+
seen = seen || new Set();
|
|
55
57
|
let promises = [];
|
|
56
58
|
|
|
57
|
-
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
|
|
59
|
+
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj) && !seen.has(obj)) {
|
|
60
|
+
seen.add(obj); // Track previously seen objects to avoid infinite recursion
|
|
58
61
|
if ($Ref.isExternal$Ref(obj)) {
|
|
59
62
|
promises.push(resolve$Ref(obj, path, $refs, options));
|
|
60
63
|
}
|
|
@@ -67,7 +70,7 @@ function crawl (obj, path, $refs, options) {
|
|
|
67
70
|
promises.push(resolve$Ref(value, keyPath, $refs, options));
|
|
68
71
|
}
|
|
69
72
|
else {
|
|
70
|
-
promises = promises.concat(crawl(value, keyPath, $refs, options));
|
|
73
|
+
promises = promises.concat(crawl(value, keyPath, $refs, options, seen));
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
}
|
package/lib/util/errors.js
CHANGED
package/lib/util/url.js
CHANGED
|
@@ -69,11 +69,25 @@ exports.getProtocol = function getProtocol (path) {
|
|
|
69
69
|
exports.getExtension = function getExtension (path) {
|
|
70
70
|
let lastDot = path.lastIndexOf(".");
|
|
71
71
|
if (lastDot >= 0) {
|
|
72
|
-
return path.substr(lastDot).toLowerCase();
|
|
72
|
+
return url.stripQuery(path.substr(lastDot).toLowerCase());
|
|
73
73
|
}
|
|
74
74
|
return "";
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Removes the query, if any, from the given path.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} path
|
|
81
|
+
* @returns {string}
|
|
82
|
+
*/
|
|
83
|
+
exports.stripQuery = function stripQuery (path) {
|
|
84
|
+
let queryIndex = path.indexOf("?");
|
|
85
|
+
if (queryIndex >= 0) {
|
|
86
|
+
path = path.substr(0, queryIndex);
|
|
87
|
+
}
|
|
88
|
+
return path;
|
|
89
|
+
};
|
|
90
|
+
|
|
77
91
|
/**
|
|
78
92
|
* Returns the hash (URL fragment), of the given path.
|
|
79
93
|
* If there is no hash, then the root hash ("#") is returned.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apidevtools/json-schema-ref-parser",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.9",
|
|
4
4
|
"description": "Parse, Resolve, and Dereference JSON Schema $ref pointers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"json",
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
{
|
|
21
21
|
"name": "Boris Cherny",
|
|
22
22
|
"email": "boris@performancejs.com"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "Jakub Rożek",
|
|
26
|
+
"email": "jakub@stoplight.io"
|
|
23
27
|
}
|
|
24
28
|
],
|
|
25
29
|
"homepage": "https://apitools.dev/json-schema-ref-parser/",
|
|
@@ -37,6 +41,7 @@
|
|
|
37
41
|
"lib"
|
|
38
42
|
],
|
|
39
43
|
"scripts": {
|
|
44
|
+
"build": "cp LICENSE *.md dist",
|
|
40
45
|
"clean": "shx rm -rf .nyc_output coverage",
|
|
41
46
|
"lint": "eslint lib test/fixtures test/specs",
|
|
42
47
|
"test": "npm run test:node && npm run test:typescript && npm run test:browser && npm run lint",
|
|
@@ -46,32 +51,63 @@
|
|
|
46
51
|
"coverage": "npm run coverage:node && npm run coverage:browser",
|
|
47
52
|
"coverage:node": "nyc node_modules/mocha/bin/mocha",
|
|
48
53
|
"coverage:browser": "npm run test:browser -- --coverage",
|
|
49
|
-
"upgrade": "npm-check -u && npm audit fix"
|
|
50
|
-
"bump": "bump --tag --push --all",
|
|
51
|
-
"release": "npm run upgrade && npm run clean && npm test && npm run bump"
|
|
54
|
+
"upgrade": "npm-check -u && npm audit fix"
|
|
52
55
|
},
|
|
53
56
|
"devDependencies": {
|
|
54
|
-
"@
|
|
55
|
-
"@
|
|
56
|
-
"@jsdevtools/
|
|
57
|
+
"@amanda-mitchell/semantic-release-npm-multiple": "^2.5.0",
|
|
58
|
+
"@babel/polyfill": "^7.12.1",
|
|
59
|
+
"@jsdevtools/eslint-config": "^1.0.7",
|
|
60
|
+
"@jsdevtools/host-environment": "^2.1.2",
|
|
57
61
|
"@jsdevtools/karma-config": "^3.1.7",
|
|
58
|
-
"@
|
|
59
|
-
"@types/json-schema": "^7.0.4",
|
|
60
|
-
"@types/node": "^14.0.23",
|
|
61
|
-
"chai": "^4.2.0",
|
|
62
|
+
"@types/node": "^14.14.21",
|
|
62
63
|
"chai-subset": "^1.6.0",
|
|
63
|
-
"
|
|
64
|
-
"
|
|
64
|
+
"chai": "^4.2.0",
|
|
65
|
+
"eslint": "^7.18.0",
|
|
65
66
|
"karma-cli": "^2.0.0",
|
|
66
|
-
"
|
|
67
|
+
"karma": "^5.0.2",
|
|
68
|
+
"mocha": "^8.2.1",
|
|
67
69
|
"npm-check": "^5.9.0",
|
|
68
70
|
"nyc": "^15.0.1",
|
|
71
|
+
"semantic-release-plugin-update-version-in-files": "^1.1.0",
|
|
69
72
|
"shx": "^0.3.2",
|
|
70
|
-
"typescript": "^
|
|
73
|
+
"typescript": "^4.0.5"
|
|
71
74
|
},
|
|
72
75
|
"dependencies": {
|
|
73
76
|
"@jsdevtools/ono": "^7.1.3",
|
|
77
|
+
"@types/json-schema": "^7.0.6",
|
|
74
78
|
"call-me-maybe": "^1.0.1",
|
|
75
|
-
"js-yaml": "^
|
|
79
|
+
"js-yaml": "^4.1.0"
|
|
80
|
+
},
|
|
81
|
+
"release": {
|
|
82
|
+
"branches": [
|
|
83
|
+
"main"
|
|
84
|
+
],
|
|
85
|
+
"plugins": [
|
|
86
|
+
"@semantic-release/commit-analyzer",
|
|
87
|
+
"@semantic-release/release-notes-generator",
|
|
88
|
+
[
|
|
89
|
+
"semantic-release-plugin-update-version-in-files",
|
|
90
|
+
{
|
|
91
|
+
"files": [
|
|
92
|
+
"dist/package.json"
|
|
93
|
+
],
|
|
94
|
+
"placeholder": "X.X.X"
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
"@amanda-mitchell/semantic-release-npm-multiple",
|
|
99
|
+
{
|
|
100
|
+
"registries": {
|
|
101
|
+
"scoped": {
|
|
102
|
+
"pkgRoot": "."
|
|
103
|
+
},
|
|
104
|
+
"unscoped": {
|
|
105
|
+
"pkgRoot": "dist"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"@semantic-release/github"
|
|
111
|
+
]
|
|
76
112
|
}
|
|
77
113
|
}
|