Skip to content

Commit a69951c

Browse files
committed
Merge branch 'main' of https://github.com/APIDevTools/json-schema-ref-parser into bug/APIDevTools#226/external-from-external
2 parents c19202f + f6886ab commit a69951c

19 files changed

+21365
-866
lines changed

Diff for: .github/workflows/CI-CD.yaml

+4-9
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@ name: CI-CD
88

99
on:
1010
push:
11-
branches:
12-
- "*"
13-
tags-ignore:
14-
- "*"
15-
11+
pull_request:
1612
schedule:
1713
- cron: "0 0 1 * *"
1814

@@ -63,7 +59,7 @@ jobs:
6359
browser_tests:
6460
name: Browser Tests
6561
runs-on: ${{ matrix.os }}
66-
timeout-minutes: 10
62+
timeout-minutes: 15
6763
strategy:
6864
fail-fast: true
6965
matrix:
@@ -104,7 +100,7 @@ jobs:
104100
coverage:
105101
name: Code Coverage
106102
runs-on: ubuntu-latest
107-
timeout-minutes: 10
103+
timeout-minutes: 5
108104
needs:
109105
- node_tests
110106
- browser_tests
@@ -117,8 +113,7 @@ jobs:
117113

118114
release:
119115
name: Release
120-
# removed to test beta branch.
121-
# if: github.ref == 'refs/heads/master'
116+
if: github.ref == 'refs/heads/main'
122117
runs-on: ubuntu-latest
123118
timeout-minutes: 10
124119
needs:

Diff for: CHANGELOG.md

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
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+
==========
63

4+
See [GitHub Releases](https://github.com/APIDevTools/json-schema-ref-parser/releases) for more information on newer releases.
75

86
[v9.0.0](https://github.com/APIDevTools/json-schema-ref-parser/tree/v9.0.0) (2020-04-21)
97
----------------------------------------------------------------------------------------------------

Diff for: defs.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"definitions": {
3+
"astring": {
4+
"description": "astring",
5+
"$ref": "defs2.json#/definitions/bstring"
6+
}
7+
}
8+
}

Diff for: defs2.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"definitions": {
3+
"bstring": {
4+
"oneOf": [
5+
{
6+
"$ref": "#/definitions/cstring"
7+
},
8+
{
9+
"$ref": "#/definitions/dstring"
10+
}
11+
]
12+
},
13+
"cstring": {
14+
"type": "string"
15+
},
16+
"dstring": {
17+
"type": "number"
18+
}
19+
}
20+
}

Diff for: lib/index.d.ts

+25-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { JSONSchema4, JSONSchema4Type, JSONSchema6, JSONSchema6Type } from "json-schema";
1+
import { JSONSchema4, JSONSchema4Type, JSONSchema6, JSONSchema6Type, JSONSchema7, JSONSchema7Type } from "json-schema";
22

33
export = $RefParser;
44

@@ -173,7 +173,7 @@ declare class $RefParser {
173173
// eslint-disable-next-line no-redeclare
174174
declare namespace $RefParser {
175175

176-
export type JSONSchema = JSONSchema4 | JSONSchema6;
176+
export type JSONSchema = JSONSchema4 | JSONSchema6 | JSONSchema7;
177177
export type SchemaCallback = (err: Error | null, schema?: JSONSchema) => any;
178178
export type $RefsCallback = (err: Error | null, $refs?: $Refs) => any;
179179

@@ -208,7 +208,7 @@ declare namespace $RefParser {
208208
file?: Partial<ResolverOptions> | boolean;
209209
http?: HTTPResolverOptions | boolean;
210210
} & {
211-
[key: string]: Partial<ResolverOptions> | boolean;
211+
[key: string]: Partial<ResolverOptions> | HTTPResolverOptions | boolean | undefined;
212212
};
213213

214214
/**
@@ -284,7 +284,7 @@ declare namespace $RefParser {
284284
read(
285285
file: FileInfo,
286286
callback?: (error: Error | null, data: string | null) => any
287-
): string | Buffer | Promise<string | Buffer>;
287+
): string | Buffer | JSONSchema | Promise<string | Buffer | JSONSchema>;
288288
}
289289

290290
export interface ParserOptions {
@@ -395,20 +395,22 @@ declare namespace $RefParser {
395395
*
396396
* @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash
397397
*/
398-
public get($ref: string): JSONSchema4Type | JSONSchema6Type
398+
public get($ref: string): JSONSchema4Type | JSONSchema6Type | JSONSchema7Type
399399

400400
/**
401401
* 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.
402402
*
403403
* @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash
404404
* @param value The value to assign. Can be anything (object, string, number, etc.)
405405
*/
406-
public set($ref: string, value: JSONSchema4Type | JSONSchema6Type): void
406+
public set($ref: string, value: JSONSchema4Type | JSONSchema6Type | JSONSchema7Type): void
407407
}
408408

409409
export type JSONParserErrorType = "EUNKNOWN" | "EPARSER" | "EUNMATCHEDPARSER" | "ERESOLVER" | "EUNMATCHEDRESOLVER" | "EMISSINGPOINTER" | "EINVALIDPOINTER";
410410

411411
export class JSONParserError extends Error {
412+
public constructor(message: string, source: string);
413+
412414
public readonly name: string;
413415
public readonly message: string;
414416
public readonly source: string;
@@ -439,28 +441,40 @@ declare namespace $RefParser {
439441
}
440442

441443
export class ParserError extends JSONParserError {
444+
public constructor(message: string, source: string);
445+
442446
public readonly name = "ParserError";
443447
public readonly code = "EPARSER";
444448
}
445449
export class UnmatchedParserError extends JSONParserError {
450+
public constructor(source: string);
451+
446452
public readonly name = "UnmatchedParserError";
447-
public readonly code ="EUNMATCHEDPARSER";
453+
public readonly code = "EUNMATCHEDPARSER";
448454
}
449455
export class ResolverError extends JSONParserError {
456+
public constructor(ex: Error | NodeJS.ErrnoException, source: string);
457+
450458
public readonly name = "ResolverError";
451-
public readonly code ="ERESOLVER";
459+
public readonly code = "ERESOLVER";
452460
public readonly ioErrorCode?: string;
453461
}
454462
export class UnmatchedResolverError extends JSONParserError {
463+
public constructor(source: string);
464+
455465
public readonly name = "UnmatchedResolverError";
456-
public readonly code ="EUNMATCHEDRESOLVER";
466+
public readonly code = "EUNMATCHEDRESOLVER";
457467
}
458468
export class MissingPointerError extends JSONParserError {
469+
public constructor(token: string | number, source: string);
470+
459471
public readonly name = "MissingPointerError";
460-
public readonly code ="EMISSINGPOINTER";
472+
public readonly code = "EMISSINGPOINTER";
461473
}
462474
export class InvalidPointerError extends JSONParserError {
475+
public constructor(pointer: string, source: string);
476+
463477
public readonly name = "InvalidPointerError";
464-
public readonly code ="EINVALIDPOINTER";
478+
public readonly code = "EINVALIDPOINTER";
465479
}
466480
}

Diff for: lib/parsers/yaml.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ module.exports = {
4545

4646
if (typeof data === "string") {
4747
try {
48-
return yaml.safeLoad(data);
48+
return yaml.load(data);
4949
}
5050
catch (e) {
5151
throw new ParserError(e.message, file.url);

Diff for: lib/pointer.js

+4
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ function resolveIf$Ref (pointer, options) {
235235
}
236236
else {
237237
let resolved = pointer.$ref.$refs._resolve($refPath, pointer.path, options);
238+
if (resolved === null) {
239+
return false;
240+
}
241+
238242
pointer.indirections += resolved.indirections + 1;
239243

240244
if ($Ref.isExtended$Ref(pointer.value)) {

Diff for: lib/ref.js

+17-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const { safePointerToPath, stripHash, getHash } = require("./util/url");
99
/**
1010
* This class represents a single JSON reference and its resolved value.
1111
*
12-
* @constructor
12+
* @class
1313
*/
1414
function $Ref () {
1515
/**
@@ -27,24 +27,28 @@ function $Ref () {
2727
/**
2828
* The resolved value of the JSON reference.
2929
* Can be any JSON type, not just objects. Unknown file types are represented as Buffers (byte arrays).
30+
*
3031
* @type {?*}
3132
*/
3233
this.value = undefined;
3334

3435
/**
3536
* The {@link $Refs} object that contains this {@link $Ref} object.
37+
*
3638
* @type {$Refs}
3739
*/
3840
this.$refs = undefined;
3941

4042
/**
4143
* Indicates the type of {@link $Ref#path} (e.g. "file", "http", etc.)
44+
*
4245
* @type {?string}
4346
*/
4447
this.pathType = undefined;
4548

4649
/**
4750
* List of all errors. Undefined if no errors.
51+
*
4852
* @type {Array<JSONParserError | ResolverError | ParserError | MissingPointerError>}
4953
*/
5054
this.errors = undefined;
@@ -53,25 +57,30 @@ function $Ref () {
5357
/**
5458
* Pushes an error to errors array.
5559
*
56-
* @param {Array<JSONParserError | JSONParserErrorGroup>} error - The error to be pushed
60+
* @param {Array<JSONParserError | JSONParserErrorGroup>} err - The error to be pushed
5761
* @returns {void}
5862
*/
5963
$Ref.prototype.addError = function (err) {
6064
if (this.errors === undefined) {
6165
this.errors = [];
6266
}
6367

68+
const existingErrors = this.errors.map(({ footprint }) => footprint);
69+
6470
// 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
6673
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+
);
6878
}
69-
else {
79+
else if (!existingErrors.includes(err.footprint)) {
7080
this.errors.push(normalizeError(err));
7181
}
7282
};
7383

74-
7584
/**
7685
* Determines whether the given JSON reference exists within this {@link $Ref#value}.
7786
*
@@ -106,8 +115,8 @@ $Ref.prototype.get = function (path, options) {
106115
* @param {string} path - The full path being resolved, optionally with a JSON pointer in the hash
107116
* @param {$RefParserOptions} options
108117
* @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}
111120
*/
112121
$Ref.prototype.resolve = function (path, options, friendlyPath, pathFromRoot) {
113122
let pointer = new Pointer(this, path, friendlyPath);

Diff for: lib/resolve-external.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,20 @@ function resolveExternal (parser, options) {
4444
* @param {string} path - The full path of `obj`, possibly with a JSON Pointer in the hash
4545
* @param {$Refs} $refs
4646
* @param {$RefParserOptions} options
47+
* @param {Set} seen - Internal.
4748
*
4849
* @returns {Promise[]}
4950
* Returns an array of promises. There will be one promise for each JSON reference in `obj`.
5051
* If `obj` does not contain any JSON references, then the array will be empty.
5152
* If any of the JSON references point to files that contain additional JSON references,
5253
* then the corresponding promise will internally reference an array of promises.
5354
*/
54-
function crawl (obj, path, $refs, options) {
55+
function crawl (obj, path, $refs, options, seen) {
56+
seen = seen || new Set();
5557
let promises = [];
5658

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
5861
if ($Ref.isExternal$Ref(obj)) {
5962
promises.push(resolve$Ref(obj, path, $refs, options));
6063
}
@@ -65,7 +68,7 @@ function crawl (obj, path, $refs, options) {
6568
if ($Ref.isExternal$Ref(value)) {
6669
promises.push(resolve$Ref(value, keyPath, $refs, options));
6770
}
68-
promises = promises.concat(crawl(value, keyPath, $refs, options));
71+
promises = promises.concat(crawl(value, keyPath, $refs, options, seen));
6972
}
7073
}
7174

Diff for: lib/util/errors.js

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ const JSONParserError = exports.JSONParserError = class JSONParserError extends
1515

1616
Ono.extend(this);
1717
}
18+
19+
get footprint () {
20+
return `${this.path}+${this.source}+${this.code}+${this.message}`;
21+
}
1822
};
1923

2024
setErrorName(JSONParserError);

0 commit comments

Comments
 (0)