@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 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. [File 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).
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
- [![Travis CI](https://jstools.dev/img/badges/travis-ci.svg)](https://travis-ci.com)
160
+ [![Stoplight](https://svgshare.com/i/TK5.svg)](https://stoplight.io/?utm_source=github&utm_medium=readme&utm_campaign=json_schema_ref_parser)
161
161
  [![SauceLabs](https://jstools.dev/img/badges/sauce-labs.svg)](https://saucelabs.com)
162
162
  [![Coveralls](https://jstools.dev/img/badges/coveralls.svg)](https://coveralls.io)
@@ -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, "#", [], parser.$refs, options);
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[]} parents - An array of the parent objects that have already been dereferenced
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 (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
43
- parents.push(obj);
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
- if ($Ref.isAllowed$Ref(obj, options)) {
46
- dereferenced = dereference$Ref(obj, path, pathFromRoot, parents, $refs, options);
47
- result.circular = dereferenced.circular;
48
- result.value = dereferenced.value;
49
- }
50
- else {
51
- for (let key of Object.keys(obj)) {
52
- let keyPath = Pointer.join(path, key);
53
- let keyPathFromRoot = Pointer.join(pathFromRoot, key);
54
- let value = obj[key];
55
- let circular = false;
56
-
57
- if ($Ref.isAllowed$Ref(value, options)) {
58
- dereferenced = dereference$Ref(value, keyPath, keyPathFromRoot, parents, $refs, options);
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
- obj[key] = dereferenced.value;
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
- circular = foundCircularReference(keyPath, $refs, options);
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
- // Set the "isCircular" flag if this or any other property is circular
74
- result.circular = result.circular || circular;
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
- parents.pop();
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[]} parents - An array of the parent objects that have already been dereferenced
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.indexOf(pointer.value) !== -1;
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
- return {
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
  }
@@ -45,7 +45,7 @@ module.exports = {
45
45
 
46
46
  if (typeof data === "string") {
47
47
  try {
48
- return yaml.safeLoad(data);
48
+ return yaml.load(data);
49
49
  }
50
50
  catch (e) {
51
51
  throw new ParserError(e.message, file.url);
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
- resolveIf$Ref(this, options);
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, url.getHash(pointer.path), options);
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
- * @constructor
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>} error - The error to be pushed
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, let's inject path if necessary
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.map(normalizeError));
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)) {
@@ -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
  }
@@ -15,6 +15,10 @@ const JSONParserError = exports.JSONParserError = class JSONParserError extends
15
15
 
16
16
  Ono.extend(this);
17
17
  }
18
+
19
+ get footprint () {
20
+ return `${this.path}+${this.source}+${this.code}+${this.message}`;
21
+ }
18
22
  };
19
23
 
20
24
  setErrorName(JSONParserError);
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.5",
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
- "@babel/polyfill": "^7.10.4",
55
- "@jsdevtools/eslint-config": "^1.0.5",
56
- "@jsdevtools/host-environment": "^2.0.4",
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
- "@jsdevtools/version-bump-prompt": "^6.0.6",
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
- "eslint": "^7.5.0",
64
- "karma": "^5.0.2",
64
+ "chai": "^4.2.0",
65
+ "eslint": "^7.18.0",
65
66
  "karma-cli": "^2.0.0",
66
- "mocha": "^8.0.1",
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": "^3.9.7"
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": "^3.13.1"
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
  }