@apidevtools/json-schema-ref-parser 11.7.3 → 11.8.2

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.
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Returns the given plugins as an array, rather than an object map.
3
+ * All other methods in this module expect an array of plugins rather than an object map.
4
+ *
5
+ * @param {object} plugins - A map of plugin objects
6
+ * @return {object[]}
7
+ */ "use strict";
8
+ Object.defineProperty(exports, "__esModule", {
9
+ value: true
10
+ });
11
+ function _export(target, all) {
12
+ for(var name in all)Object.defineProperty(target, name, {
13
+ enumerable: true,
14
+ get: all[name]
15
+ });
16
+ }
17
+ _export(exports, {
18
+ all: function() {
19
+ return all;
20
+ },
21
+ filter: function() {
22
+ return filter;
23
+ },
24
+ sort: function() {
25
+ return sort;
26
+ },
27
+ run: function() {
28
+ return run;
29
+ }
30
+ });
31
+ function _instanceof(left, right) {
32
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
33
+ return !!right[Symbol.hasInstance](left);
34
+ } else {
35
+ return left instanceof right;
36
+ }
37
+ }
38
+ function all(plugins) {
39
+ return Object.keys(plugins).filter(function(key) {
40
+ return typeof plugins[key] === "object";
41
+ }).map(function(key) {
42
+ plugins[key].name = key;
43
+ return plugins[key];
44
+ });
45
+ }
46
+ function filter(plugins, method, file) {
47
+ return plugins.filter(function(plugin) {
48
+ return !!getResult(plugin, method, file);
49
+ });
50
+ }
51
+ function sort(plugins) {
52
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
53
+ try {
54
+ for(var _iterator = plugins[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
55
+ var plugin = _step.value;
56
+ plugin.order = plugin.order || Number.MAX_SAFE_INTEGER;
57
+ }
58
+ } catch (err) {
59
+ _didIteratorError = true;
60
+ _iteratorError = err;
61
+ } finally{
62
+ try {
63
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
64
+ _iterator.return();
65
+ }
66
+ } finally{
67
+ if (_didIteratorError) {
68
+ throw _iteratorError;
69
+ }
70
+ }
71
+ }
72
+ return plugins.sort(function(a, b) {
73
+ return a.order - b.order;
74
+ });
75
+ }
76
+ function run(plugins, method, file, $refs) {
77
+ var plugin, lastError, index = 0;
78
+ return new Promise(function(resolve, reject) {
79
+ var runNextPlugin = function runNextPlugin() {
80
+ plugin = plugins[index++];
81
+ if (!plugin) {
82
+ // There are no more functions, so re-throw the last error
83
+ return reject(lastError);
84
+ }
85
+ try {
86
+ // console.log(' %s', plugin.name);
87
+ var result = getResult(plugin, method, file, callback, $refs);
88
+ if (result && typeof result.then === "function") {
89
+ // A promise was returned
90
+ result.then(onSuccess, onError);
91
+ } else if (result !== undefined) {
92
+ // A synchronous result was returned
93
+ onSuccess(result);
94
+ } else if (index === plugins.length) {
95
+ throw new Error("No promise has been returned or callback has been called.");
96
+ }
97
+ } catch (e) {
98
+ onError(e);
99
+ }
100
+ };
101
+ var callback = function callback(err, result) {
102
+ if (err) {
103
+ onError(err);
104
+ } else {
105
+ onSuccess(result);
106
+ }
107
+ };
108
+ var onSuccess = function onSuccess(result) {
109
+ // console.log(' success');
110
+ resolve({
111
+ plugin: plugin,
112
+ result: result
113
+ });
114
+ };
115
+ var onError = function onError(error) {
116
+ // console.log(' %s', err.message || err);
117
+ lastError = {
118
+ plugin: plugin,
119
+ error: error
120
+ };
121
+ runNextPlugin();
122
+ };
123
+ runNextPlugin();
124
+ });
125
+ }
126
+ /**
127
+ * Returns the value of the given property.
128
+ * If the property is a function, then the result of the function is returned.
129
+ * If the value is a RegExp, then it will be tested against the file URL.
130
+ * If the value is an aray, then it will be compared against the file extension.
131
+ *
132
+ * @param {object} obj - The object whose property/method is called
133
+ * @param {string} prop - The name of the property/method to invoke
134
+ * @param {object} file - A file info object, which will be passed to the method
135
+ * @param {function} [callback] - A callback function, which will be passed to the method
136
+ * @returns {*}
137
+ */ function getResult(obj, prop, file, callback, $refs) {
138
+ var value = obj[prop];
139
+ if (typeof value === "function") {
140
+ return value.apply(obj, [
141
+ file,
142
+ callback,
143
+ $refs
144
+ ]);
145
+ }
146
+ if (!callback) {
147
+ // The synchronous plugin functions (canParse and canRead)
148
+ // allow a "shorthand" syntax, where the user can match
149
+ // files by RegExp or by file extension.
150
+ if (_instanceof(value, RegExp)) {
151
+ return value.test(file.url);
152
+ } else if (typeof value === "string") {
153
+ return value === file.extension;
154
+ } else if (Array.isArray(value)) {
155
+ return value.indexOf(file.extension) !== -1;
156
+ }
157
+ }
158
+ return value;
159
+ }
@@ -0,0 +1,6 @@
1
+ const nodePath = require("path");
2
+
3
+ // Webpack 4 (used by browser tests) can't transpile import.meta.url
4
+ // So export the project directory using __dirname from a .cjs module
5
+ const projectDir = nodePath.resolve(__dirname, "..", "..");
6
+ module.exports = projectDir
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ parse: function() {
13
+ return parse;
14
+ },
15
+ resolve: function() {
16
+ return resolve;
17
+ },
18
+ cwd: function() {
19
+ return cwd;
20
+ },
21
+ getProtocol: function() {
22
+ return getProtocol;
23
+ },
24
+ getExtension: function() {
25
+ return getExtension;
26
+ },
27
+ stripQuery: function() {
28
+ return stripQuery;
29
+ },
30
+ getHash: function() {
31
+ return getHash;
32
+ },
33
+ stripHash: function() {
34
+ return stripHash;
35
+ },
36
+ isHttp: function() {
37
+ return isHttp;
38
+ },
39
+ isFileSystemPath: function() {
40
+ return isFileSystemPath;
41
+ },
42
+ fromFileSystemPath: function() {
43
+ return fromFileSystemPath;
44
+ },
45
+ toFileSystemPath: function() {
46
+ return toFileSystemPath;
47
+ },
48
+ safePointerToPath: function() {
49
+ return safePointerToPath;
50
+ }
51
+ });
52
+ var _projectDirCjs = /*#__PURE__*/ _interopRequireDefault(require("./projectDir.cjs"));
53
+ function _interopRequireDefault(obj) {
54
+ return obj && obj.__esModule ? obj : {
55
+ default: obj
56
+ };
57
+ }
58
+ var isWindows = /^win/.test(globalThis.process ? globalThis.process.platform : undefined), forwardSlashPattern = /\//g, protocolPattern = /^(\w{2,}):\/\//i, jsonPointerSlash = /~1/g, jsonPointerTilde = /~0/g;
59
+ // RegExp patterns to URL-encode special characters in local filesystem paths
60
+ var urlEncodePatterns = [
61
+ /\?/g,
62
+ "%3F",
63
+ /\#/g,
64
+ "%23"
65
+ ];
66
+ // RegExp patterns to URL-decode special characters for local filesystem paths
67
+ var urlDecodePatterns = [
68
+ /\%23/g,
69
+ "#",
70
+ /\%24/g,
71
+ "$",
72
+ /\%26/g,
73
+ "&",
74
+ /\%2C/g,
75
+ ",",
76
+ /\%40/g,
77
+ "@"
78
+ ];
79
+ var parse = function(u) {
80
+ return new URL(u);
81
+ };
82
+ function resolve(from, to) {
83
+ var resolvedUrl = new URL(to, new URL(from, "resolve://"));
84
+ if (resolvedUrl.protocol === "resolve:") {
85
+ // `from` is a relative URL.
86
+ var pathname = resolvedUrl.pathname, search = resolvedUrl.search, hash = resolvedUrl.hash;
87
+ return pathname + search + hash;
88
+ }
89
+ return resolvedUrl.toString();
90
+ }
91
+ function cwd() {
92
+ if (typeof window !== "undefined") {
93
+ return location.href;
94
+ }
95
+ var path = process.cwd();
96
+ var lastChar = path.slice(-1);
97
+ if (lastChar === "/" || lastChar === "\\") {
98
+ return path;
99
+ } else {
100
+ return path + "/";
101
+ }
102
+ }
103
+ function getProtocol(path) {
104
+ var match = protocolPattern.exec(path);
105
+ if (match) {
106
+ return match[1].toLowerCase();
107
+ }
108
+ }
109
+ function getExtension(path) {
110
+ var lastDot = path.lastIndexOf(".");
111
+ if (lastDot >= 0) {
112
+ return stripQuery(path.substr(lastDot).toLowerCase());
113
+ }
114
+ return "";
115
+ }
116
+ function stripQuery(path) {
117
+ var queryIndex = path.indexOf("?");
118
+ if (queryIndex >= 0) {
119
+ path = path.substr(0, queryIndex);
120
+ }
121
+ return path;
122
+ }
123
+ function getHash(path) {
124
+ var hashIndex = path.indexOf("#");
125
+ if (hashIndex >= 0) {
126
+ return path.substr(hashIndex);
127
+ }
128
+ return "#";
129
+ }
130
+ function stripHash(path) {
131
+ var hashIndex = path.indexOf("#");
132
+ if (hashIndex >= 0) {
133
+ path = path.substr(0, hashIndex);
134
+ }
135
+ return path;
136
+ }
137
+ function isHttp(path) {
138
+ var protocol = getProtocol(path);
139
+ if (protocol === "http" || protocol === "https") {
140
+ return true;
141
+ } else if (protocol === undefined) {
142
+ // There is no protocol. If we're running in a browser, then assume it's HTTP.
143
+ return typeof window !== "undefined";
144
+ } else {
145
+ // It's some other protocol, such as "ftp://", "mongodb://", etc.
146
+ return false;
147
+ }
148
+ }
149
+ function isFileSystemPath(path) {
150
+ if (process.browser) {
151
+ // We're running in a browser, so assume that all paths are URLs.
152
+ // This way, even relative paths will be treated as URLs rather than as filesystem paths
153
+ return false;
154
+ }
155
+ var protocol = getProtocol(path);
156
+ return protocol === undefined || protocol === "file";
157
+ }
158
+ function fromFileSystemPath(path) {
159
+ // Step 1: On Windows, replace backslashes with forward slashes,
160
+ // rather than encoding them as "%5C"
161
+ if (isWindows) {
162
+ var hasProjectDir = path.toUpperCase().includes(_projectDirCjs.default.replace(/\\/g, "\\").toUpperCase());
163
+ var hasProjectUri = path.toUpperCase().includes(_projectDirCjs.default.replace(/\\/g, "/").toUpperCase());
164
+ if (hasProjectDir || hasProjectUri) {
165
+ path = path.replace(/\\/g, "/");
166
+ } else {
167
+ path = "".concat(_projectDirCjs.default, "/").concat(path).replace(/\\/g, "/");
168
+ }
169
+ }
170
+ // Step 2: `encodeURI` will take care of MOST characters
171
+ path = encodeURI(path);
172
+ // Step 3: Manually encode characters that are not encoded by `encodeURI`.
173
+ // This includes characters such as "#" and "?", which have special meaning in URLs,
174
+ // but are just normal characters in a filesystem path.
175
+ for(var i = 0; i < urlEncodePatterns.length; i += 2){
176
+ path = path.replace(urlEncodePatterns[i], urlEncodePatterns[i + 1]);
177
+ }
178
+ return path;
179
+ }
180
+ function toFileSystemPath(path, keepFileProtocol) {
181
+ // Step 1: `decodeURI` will decode characters such as Cyrillic characters, spaces, etc.
182
+ path = decodeURI(path);
183
+ // Step 2: Manually decode characters that are not decoded by `decodeURI`.
184
+ // This includes characters such as "#" and "?", which have special meaning in URLs,
185
+ // but are just normal characters in a filesystem path.
186
+ for(var i = 0; i < urlDecodePatterns.length; i += 2){
187
+ path = path.replace(urlDecodePatterns[i], urlDecodePatterns[i + 1]);
188
+ }
189
+ // Step 3: If it's a "file://" URL, then format it consistently
190
+ // or convert it to a local filesystem path
191
+ var isFileUrl = path.substr(0, 7).toLowerCase() === "file://";
192
+ if (isFileUrl) {
193
+ // Strip-off the protocol, and the initial "/", if there is one
194
+ path = path[7] === "/" ? path.substr(8) : path.substr(7);
195
+ // insert a colon (":") after the drive letter on Windows
196
+ if (isWindows && path[1] === "/") {
197
+ path = path[0] + ":" + path.substr(1);
198
+ }
199
+ if (keepFileProtocol) {
200
+ // Return the consistently-formatted "file://" URL
201
+ path = "file:///" + path;
202
+ } else {
203
+ // Convert the "file://" URL to a local filesystem path.
204
+ // On Windows, it will start with something like "C:/".
205
+ // On Posix, it will start with "/"
206
+ isFileUrl = false;
207
+ path = isWindows ? path : "/" + path;
208
+ }
209
+ }
210
+ // Step 4: Normalize Windows paths (unless it's a "file://" URL)
211
+ if (isWindows && !isFileUrl) {
212
+ // Replace forward slashes with backslashes
213
+ path = path.replace(forwardSlashPattern, "\\");
214
+ // Capitalize the drive letter
215
+ if (path.substr(1, 2) === ":\\") {
216
+ path = path[0].toUpperCase() + path.substr(1);
217
+ }
218
+ }
219
+ return path;
220
+ }
221
+ function safePointerToPath(pointer) {
222
+ if (pointer.length <= 1 || pointer[0] !== "#" || pointer[1] !== "/") {
223
+ return [];
224
+ }
225
+ return pointer.slice(2).split("/").map(function(value) {
226
+ return decodeURIComponent(value).replace(jsonPointerSlash, "/").replace(jsonPointerTilde, "~");
227
+ });
228
+ }
@@ -210,7 +210,8 @@ function dereference$Ref($ref, path, pathFromRoot, parents, processedObjects, de
210
210
  }
211
211
  /**
212
212
  * Called when a circular reference is found.
213
- * It sets the {@link $Refs#circular} flag, and throws an error if options.dereference.circular is false.
213
+ * It sets the {@link $Refs#circular} flag, executes the options.dereference.onCircular callback,
214
+ * and throws an error if options.dereference.circular is false.
214
215
  *
215
216
  * @param keyPath - The JSON Reference path of the circular reference
216
217
  * @param $refs
@@ -219,6 +220,7 @@ function dereference$Ref($ref, path, pathFromRoot, parents, processedObjects, de
219
220
  */
220
221
  function foundCircularReference(keyPath, $refs, options) {
221
222
  $refs.circular = true;
223
+ options?.dereference?.onCircular?.(keyPath);
222
224
  if (!options.dereference.circular) {
223
225
  throw ono_1.ono.reference(`Circular $ref pointer found at ${keyPath}`);
224
226
  }
@@ -44,8 +44,8 @@ export declare class $RefParser<S extends object = JSONSchema, O extends ParserO
44
44
  parse(schema: S | string | unknown, options: O, callback: SchemaCallback<S>): Promise<void>;
45
45
  parse(baseUrl: string, schema: S | string | unknown, options: O): Promise<S>;
46
46
  parse(baseUrl: string, schema: S | string | unknown, options: O, callback: SchemaCallback<S>): Promise<void>;
47
- static parse<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown): Promise<S>;
48
- static parse<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown, callback: SchemaCallback<S>): Promise<void>;
47
+ static parse<S extends object = JSONSchema>(schema: S | string | unknown): Promise<S>;
48
+ static parse<S extends object = JSONSchema>(schema: S | string | unknown, callback: SchemaCallback<S>): Promise<void>;
49
49
  static parse<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown, options: O): Promise<S>;
50
50
  static parse<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown, options: O, callback: SchemaCallback<S>): Promise<void>;
51
51
  static parse<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(baseUrl: string, schema: S | string | unknown, options: O): Promise<S>;
@@ -95,8 +95,8 @@ export declare class $RefParser<S extends object = JSONSchema, O extends ParserO
95
95
  * @param options (optional)
96
96
  * @param callback (optional) A callback that will receive the bundled schema object
97
97
  */
98
- static bundle<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown): Promise<S>;
99
- static bundle<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown, callback: SchemaCallback<S>): Promise<void>;
98
+ static bundle<S extends object = JSONSchema>(schema: S | string | unknown): Promise<S>;
99
+ static bundle<S extends object = JSONSchema>(schema: S | string | unknown, callback: SchemaCallback<S>): Promise<void>;
100
100
  static bundle<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown, options: O): Promise<S>;
101
101
  static bundle<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown, options: O, callback: SchemaCallback<S>): Promise<void>;
102
102
  static bundle<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(baseUrl: string, schema: S | string | unknown, options: O): Promise<S>;
@@ -129,8 +129,8 @@ export declare class $RefParser<S extends object = JSONSchema, O extends ParserO
129
129
  * @param options (optional)
130
130
  * @param callback (optional) A callback that will receive the dereferenced schema object
131
131
  */
132
- static dereference<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown): Promise<S>;
133
- static dereference<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown, callback: SchemaCallback<S>): Promise<void>;
132
+ static dereference<S extends object = JSONSchema>(schema: S | string | unknown): Promise<S>;
133
+ static dereference<S extends object = JSONSchema>(schema: S | string | unknown, callback: SchemaCallback<S>): Promise<void>;
134
134
  static dereference<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown, options: O): Promise<S>;
135
135
  static dereference<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(schema: S | string | unknown, options: O, callback: SchemaCallback<S>): Promise<void>;
136
136
  static dereference<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(baseUrl: string, schema: S | string | unknown, options: O): Promise<S>;
@@ -17,6 +17,12 @@ export interface DereferenceOptions {
17
17
  * subpaths contain literal $ref keys that should not be dereferenced.
18
18
  */
19
19
  excludedPathMatcher?(path: string): boolean;
20
+ /**
21
+ * Callback invoked during circular reference detection.
22
+ *
23
+ * @argument {string} path - The path that is circular (ie. the `$ref` string)
24
+ */
25
+ onCircular?(path: string): void;
20
26
  /**
21
27
  * Callback invoked during dereferencing.
22
28
  *
@@ -376,6 +382,7 @@ export declare const getNewOptions: <S extends object = JSONSchema, O extends Pa
376
382
  dereference?: {
377
383
  circular?: boolean | "ignore" | undefined;
378
384
  excludedPathMatcher?: {} | undefined;
385
+ onCircular?: {} | undefined;
379
386
  onDereference?: {} | undefined;
380
387
  externalReferenceResolution?: "relative" | "root" | undefined;
381
388
  } | undefined;
@@ -83,6 +83,7 @@ class Pointer {
83
83
  */
84
84
  resolve(obj, options, pathFromRoot) {
85
85
  const tokens = Pointer.parse(this.path, this.originalPath);
86
+ const found = [];
86
87
  // Crawl the object, one token at a time
87
88
  this.value = unwrapOrThrow(obj);
88
89
  for (let i = 0; i < tokens.length; i++) {
@@ -110,11 +111,16 @@ class Pointer {
110
111
  continue;
111
112
  }
112
113
  this.value = null;
113
- throw new errors_js_1.MissingPointerError(token, decodeURI(this.originalPath));
114
+ const path = this.$ref.path || "";
115
+ const targetRef = this.path.replace(path, "");
116
+ const targetFound = Pointer.join("", found);
117
+ const parentPath = pathFromRoot?.replace(path, "");
118
+ throw new errors_js_1.MissingPointerError(token, decodeURI(this.originalPath), targetRef, targetFound, parentPath);
114
119
  }
115
120
  else {
116
121
  this.value = this.value[token];
117
122
  }
123
+ found.push(token);
118
124
  }
119
125
  // Resolve the final value
120
126
  if (!this.value || (this.value.$ref && url.resolve(this.path, this.value.$ref) !== pathFromRoot)) {
@@ -41,7 +41,11 @@ export declare class UnmatchedResolverError extends JSONParserError {
41
41
  export declare class MissingPointerError extends JSONParserError {
42
42
  code: JSONParserErrorType;
43
43
  name: string;
44
- constructor(token: string, path: string);
44
+ targetToken: any;
45
+ targetRef: string;
46
+ targetFound: string;
47
+ parentPath: string;
48
+ constructor(token: any, path: any, targetRef: any, targetFound: any, parentPath: any);
45
49
  }
46
50
  export declare class TimeoutError extends JSONParserError {
47
51
  code: JSONParserErrorType;
@@ -78,10 +78,14 @@ class UnmatchedResolverError extends JSONParserError {
78
78
  }
79
79
  exports.UnmatchedResolverError = UnmatchedResolverError;
80
80
  class MissingPointerError extends JSONParserError {
81
- constructor(token, path) {
81
+ constructor(token, path, targetRef, targetFound, parentPath) {
82
82
  super(`Missing $ref pointer "${(0, url_js_1.getHash)(path)}". Token "${token}" does not exist.`, (0, url_js_1.stripHash)(path));
83
83
  this.code = "EMISSINGPOINTER";
84
84
  this.name = "MissingPointerError";
85
+ this.targetToken = token;
86
+ this.targetRef = targetRef;
87
+ this.targetFound = targetFound;
88
+ this.parentPath = parentPath;
85
89
  }
86
90
  }
87
91
  exports.MissingPointerError = MissingPointerError;
@@ -18,7 +18,7 @@ export declare function filter(plugins: Plugin[], method: any, file: any): Plugi
18
18
  * Sorts the given plugins, in place, by their `order` property.
19
19
  */
20
20
  export declare function sort(plugins: Plugin[]): Plugin[];
21
- export interface PluginResult<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>> {
21
+ export interface PluginResult<S extends object = JSONSchema> {
22
22
  plugin: Plugin;
23
23
  result?: string | Buffer | S;
24
24
  error?: any;
@@ -31,4 +31,4 @@ export interface PluginResult<S extends object = JSONSchema, O extends ParserOpt
31
31
  * If the promise rejects, or the callback is called with an error, then the next plugin is called.
32
32
  * If ALL plugins fail, then the last error is thrown.
33
33
  */
34
- export declare function run<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(plugins: Plugin[], method: keyof Plugin | keyof ResolverOptions<S>, file: FileInfo, $refs: $Refs<S, O>): Promise<PluginResult<S, O>>;
34
+ export declare function run<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(plugins: Plugin[], method: keyof Plugin | keyof ResolverOptions<S>, file: FileInfo, $refs: $Refs<S, O>): Promise<PluginResult<S>>;
@@ -73,10 +73,11 @@ exports.parse = parse;
73
73
  * @returns
74
74
  */
75
75
  function resolve(from, to) {
76
- const fromUrl = new URL((0, convert_path_to_posix_1.default)(from), "resolve://");
76
+ // we use a non-existent URL to check if its a relative URL
77
+ const fromUrl = new URL((0, convert_path_to_posix_1.default)(from), "https://aaa.nonexistanturl.com");
77
78
  const resolvedUrl = new URL((0, convert_path_to_posix_1.default)(to), fromUrl);
78
79
  const endSpaces = to.match(/(\s*)$/)?.[1] || "";
79
- if (resolvedUrl.protocol === "resolve:") {
80
+ if (resolvedUrl.hostname === "aaa.nonexistanturl.com") {
80
81
  // `from` is a relative URL.
81
82
  const { pathname, search, hash } = resolvedUrl;
82
83
  return pathname + search + hash + endSpaces;
@@ -272,7 +272,8 @@ function dereference$Ref<S extends object = JSONSchema, O extends ParserOptions<
272
272
 
273
273
  /**
274
274
  * Called when a circular reference is found.
275
- * It sets the {@link $Refs#circular} flag, and throws an error if options.dereference.circular is false.
275
+ * It sets the {@link $Refs#circular} flag, executes the options.dereference.onCircular callback,
276
+ * and throws an error if options.dereference.circular is false.
276
277
  *
277
278
  * @param keyPath - The JSON Reference path of the circular reference
278
279
  * @param $refs
@@ -281,6 +282,8 @@ function dereference$Ref<S extends object = JSONSchema, O extends ParserOptions<
281
282
  */
282
283
  function foundCircularReference(keyPath: any, $refs: any, options: any) {
283
284
  $refs.circular = true;
285
+ options?.dereference?.onCircular?.(keyPath);
286
+
284
287
  if (!options.dereference.circular) {
285
288
  throw ono.reference(`Circular $ref pointer found at ${keyPath}`);
286
289
  }
package/lib/index.ts CHANGED
@@ -144,10 +144,8 @@ export class $RefParser<S extends object = JSONSchema, O extends ParserOptions<S
144
144
  }
145
145
  }
146
146
 
147
- public static parse<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(
148
- schema: S | string | unknown,
149
- ): Promise<S>;
150
- public static parse<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(
147
+ public static parse<S extends object = JSONSchema>(schema: S | string | unknown): Promise<S>;
148
+ public static parse<S extends object = JSONSchema>(
151
149
  schema: S | string | unknown,
152
150
  callback: SchemaCallback<S>,
153
151
  ): Promise<void>;
@@ -269,10 +267,8 @@ export class $RefParser<S extends object = JSONSchema, O extends ParserOptions<S
269
267
  * @param options (optional)
270
268
  * @param callback (optional) A callback that will receive the bundled schema object
271
269
  */
272
- public static bundle<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(
273
- schema: S | string | unknown,
274
- ): Promise<S>;
275
- public static bundle<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(
270
+ public static bundle<S extends object = JSONSchema>(schema: S | string | unknown): Promise<S>;
271
+ public static bundle<S extends object = JSONSchema>(
276
272
  schema: S | string | unknown,
277
273
  callback: SchemaCallback<S>,
278
274
  ): Promise<void>;
@@ -343,10 +339,8 @@ export class $RefParser<S extends object = JSONSchema, O extends ParserOptions<S
343
339
  * @param options (optional)
344
340
  * @param callback (optional) A callback that will receive the dereferenced schema object
345
341
  */
346
- public static dereference<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(
347
- schema: S | string | unknown,
348
- ): Promise<S>;
349
- public static dereference<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(
342
+ public static dereference<S extends object = JSONSchema>(schema: S | string | unknown): Promise<S>;
343
+ public static dereference<S extends object = JSONSchema>(
350
344
  schema: S | string | unknown,
351
345
  callback: SchemaCallback<S>,
352
346
  ): Promise<void>;
package/lib/options.ts CHANGED
@@ -29,6 +29,13 @@ export interface DereferenceOptions {
29
29
  */
30
30
  excludedPathMatcher?(path: string): boolean;
31
31
 
32
+ /**
33
+ * Callback invoked during circular reference detection.
34
+ *
35
+ * @argument {string} path - The path that is circular (ie. the `$ref` string)
36
+ */
37
+ onCircular?(path: string): void;
38
+
32
39
  /**
33
40
  * Callback invoked during dereferencing.
34
41
  *
package/lib/pointer.ts CHANGED
@@ -88,6 +88,7 @@ class Pointer<S extends object = JSONSchema, O extends ParserOptions<S> = Parser
88
88
  */
89
89
  resolve(obj: S, options?: O, pathFromRoot?: string) {
90
90
  const tokens = Pointer.parse(this.path, this.originalPath);
91
+ const found: any = [];
91
92
 
92
93
  // Crawl the object, one token at a time
93
94
  this.value = unwrapOrThrow(obj);
@@ -103,6 +104,7 @@ class Pointer<S extends object = JSONSchema, O extends ParserOptions<S> = Parser
103
104
  }
104
105
 
105
106
  const token = tokens[i];
107
+
106
108
  if (this.value[token] === undefined || (this.value[token] === null && i === tokens.length - 1)) {
107
109
  // one final case is if the entry itself includes slashes, and was parsed out as a token - we can join the remaining tokens and try again
108
110
  let didFindSubstringSlashMatch = false;
@@ -120,10 +122,19 @@ class Pointer<S extends object = JSONSchema, O extends ParserOptions<S> = Parser
120
122
  }
121
123
 
122
124
  this.value = null;
123
- throw new MissingPointerError(token, decodeURI(this.originalPath));
125
+
126
+ const path = this.$ref.path || "";
127
+
128
+ const targetRef = this.path.replace(path, "");
129
+ const targetFound = Pointer.join("", found);
130
+ const parentPath = pathFromRoot?.replace(path, "");
131
+
132
+ throw new MissingPointerError(token, decodeURI(this.originalPath), targetRef, targetFound, parentPath);
124
133
  } else {
125
134
  this.value = this.value[token];
126
135
  }
136
+
137
+ found.push(token);
127
138
  }
128
139
 
129
140
  // Resolve the final value