@apidevtools/json-schema-ref-parser 11.0.0 → 11.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # JSON Schema $Ref Parser
2
2
 
3
- _**This package needs [a new maintainer](https://github.com/APIDevTools/json-schema-ref-parser/issues/285) or at least some contributors. For more information [please read this article](https://phil.tech/2022/bundling-openapi-with-javascript/). As of v10.0.0 I am no longer spending any time working on this tool, so I can focus on scaling up my [reforestation charity](https://protect.earth/) instead of burning myself out trying to maintain a whole load of OSS projects I don't use in my vanishingly small spare time. Get in touch if you'd like to take over.** - [Phil Sturgeon](https://github.com/philsturgeon)_
4
-
5
3
  #### Parse, Resolve, and Dereference JSON Schema $ref pointers
6
4
 
7
5
  [![Build Status](https://github.com/APIDevTools/json-schema-ref-parser/workflows/CI-CD/badge.svg?branch=master)](https://github.com/APIDevTools/json-schema-ref-parser/actions)
@@ -130,7 +130,9 @@ function crawl(obj, path, pathFromRoot, parents, processedObjects, dereferencedC
130
130
  */
131
131
  function dereference$Ref($ref, path, pathFromRoot, parents, processedObjects, dereferencedCache, $refs, options) {
132
132
  // console.log('Dereferencing $ref pointer "%s" at %s', $ref.$ref, path);
133
- const $refPath = url.resolve(path, $ref.$ref);
133
+ const isExternalRef = ref_js_1.default.isExternal$Ref($ref);
134
+ const shouldResolveOnCwd = isExternalRef && options?.dereference.externalReferenceResolution === "root";
135
+ const $refPath = url.resolve(shouldResolveOnCwd ? url.cwd() : path, $ref.$ref);
134
136
  const cache = dereferencedCache.get($refPath);
135
137
  if (cache) {
136
138
  const refKeys = Object.keys($ref);
package/dist/lib/index.js CHANGED
@@ -88,6 +88,13 @@ class $RefParser {
88
88
  args.path = url.fromFileSystemPath(args.path);
89
89
  pathType = "file";
90
90
  }
91
+ else if (!args.path && args.schema && args.schema.$id) {
92
+ // when schema id has defined an URL should use that hostname to request the references,
93
+ // instead of using the current page URL
94
+ const params = url.parse(args.schema.$id);
95
+ const port = params.protocol === "https:" ? 443 : 80;
96
+ args.path = `${params.protocol}//${params.hostname}:${port}`;
97
+ }
91
98
  // Resolve the absolute path of the schema
92
99
  args.path = url.resolve(url.cwd(), args.path);
93
100
  if (args.schema && typeof args.schema === "object") {
@@ -69,6 +69,12 @@ interface $RefParserOptions {
69
69
  * @argument {JSONSchemaObject} object The JSON-Schema that the `$ref` resolved to.
70
70
  */
71
71
  onDereference?(path: string, value: JSONSchemaObject): void;
72
+ /**
73
+ * Whether a reference should resolve relative to its directory/path, or from the cwd
74
+ *
75
+ * Default: `relative`
76
+ */
77
+ externalReferenceResolution?: "relative" | "root";
72
78
  };
73
79
  }
74
80
  export declare const getNewOptions: (options: DeepPartial<$RefParserOptions>) => $RefParserOptions;
@@ -69,6 +69,7 @@ const getDefaults = () => {
69
69
  * @type {function}
70
70
  */
71
71
  excludedPathMatcher: () => false,
72
+ referenceResolution: "relative",
72
73
  },
73
74
  };
74
75
  return (0, lodash_clonedeep_1.default)(defaults);
@@ -68,11 +68,11 @@ class Pointer {
68
68
  // Crawl the object, one token at a time
69
69
  this.value = unwrapOrThrow(obj);
70
70
  for (let i = 0; i < tokens.length; i++) {
71
- if (resolveIf$Ref(this, options)) {
71
+ if (resolveIf$Ref(this, options, pathFromRoot)) {
72
72
  // The $ref path has changed, so append the remaining tokens to the path
73
73
  this.path = Pointer.join(this.path, tokens.slice(i));
74
74
  }
75
- if (typeof this.value === "object" && this.value !== null && "$ref" in this.value) {
75
+ if (typeof this.value === "object" && this.value !== null && !isRootPath(pathFromRoot) && "$ref" in this.value) {
76
76
  return this;
77
77
  }
78
78
  const token = tokens[i];
@@ -86,7 +86,7 @@ class Pointer {
86
86
  }
87
87
  // Resolve the final value
88
88
  if (!this.value || (this.value.$ref && url.resolve(this.path, this.value.$ref) !== pathFromRoot)) {
89
- resolveIf$Ref(this, options);
89
+ resolveIf$Ref(this, options, pathFromRoot);
90
90
  }
91
91
  return this;
92
92
  }
@@ -190,13 +190,14 @@ class Pointer {
190
190
  *
191
191
  * @param pointer
192
192
  * @param options
193
+ * @param [pathFromRoot] - the path of place that initiated resolving
193
194
  * @returns - Returns `true` if the resolution path changed
194
195
  */
195
- function resolveIf$Ref(pointer, options) {
196
+ function resolveIf$Ref(pointer, options, pathFromRoot) {
196
197
  // Is the value a JSON reference? (and allowed?)
197
198
  if (ref_js_1.default.isAllowed$Ref(pointer.value, options)) {
198
199
  const $refPath = url.resolve(pointer.path, pointer.value.$ref);
199
- if ($refPath === pointer.path) {
200
+ if ($refPath === pointer.path && !isRootPath(pathFromRoot)) {
200
201
  // The value is a reference to itself, so there's nothing to do.
201
202
  pointer.circular = true;
202
203
  }
@@ -254,3 +255,6 @@ function unwrapOrThrow(value) {
254
255
  }
255
256
  return value;
256
257
  }
258
+ function isRootPath(pathFromRoot) {
259
+ return typeof pathFromRoot == "string" && Pointer.parse(pathFromRoot).length == 0;
260
+ }
@@ -102,8 +102,8 @@ function crawl(obj, path, $refs, options, seen, external) {
102
102
  * including nested references that are contained in externally-referenced files.
103
103
  */
104
104
  async function resolve$Ref($ref, path, $refs, options) {
105
- // console.log('Resolving $ref pointer "%s" at %s', $ref.$ref, path);
106
- const resolvedPath = url.resolve(path, $ref.$ref);
105
+ const shouldResolveOnCwd = options.dereference.externalReferenceResolution === "root";
106
+ const resolvedPath = url.resolve(shouldResolveOnCwd ? url.cwd() : path, $ref.$ref);
107
107
  const withoutHash = url.stripHash(resolvedPath);
108
108
  // $ref.$ref = url.relative($refs._root$Ref.path, resolvedPath);
109
109
  // Do we already have this $ref?
@@ -97,7 +97,7 @@ async function download(u, httpOptions, _redirects) {
97
97
  throw (0, ono_1.ono)({ status: res.status }, `HTTP ${res.status} redirect with no location header`);
98
98
  }
99
99
  else {
100
- const redirectTo = url.resolve(u, res.headers.location);
100
+ const redirectTo = url.resolve(u.href, res.headers.location);
101
101
  return download(redirectTo, httpOptions, redirects);
102
102
  }
103
103
  }
@@ -9,6 +9,6 @@ function convertPathToPosix(filePath) {
9
9
  if (isExtendedLengthPath) {
10
10
  return filePath;
11
11
  }
12
- return filePath.split(path_1.default.win32.sep).join(path_1.default.posix.sep);
12
+ return filePath.split(path_1.default?.win32?.sep).join(path_1.default.posix.sep);
13
13
  }
14
14
  exports.default = convertPathToPosix;
@@ -1,10 +1,10 @@
1
- export declare const parse: (u: any) => URL;
1
+ export declare const parse: (u: string | URL) => URL;
2
2
  /**
3
3
  * Returns resolved target URL relative to a base URL in a manner similar to that of a Web browser resolving an anchor tag HREF.
4
4
  *
5
5
  * @returns
6
6
  */
7
- export declare function resolve(from: any, to: any): string;
7
+ export declare function resolve(from: string, to: string): string;
8
8
  /**
9
9
  * Returns the current working directory (in Node) or the current page URL (in browsers).
10
10
  *
@@ -91,4 +91,4 @@ export declare function toFileSystemPath(path: string | undefined, keepFileProto
91
91
  * @returns
92
92
  */
93
93
  export declare function safePointerToPath(pointer: any): any;
94
- export declare function relative(from: string | undefined, to: string | undefined): string;
94
+ export declare function relative(from: string, to: string): string;
@@ -48,7 +48,8 @@ exports.parse = parse;
48
48
  * @returns
49
49
  */
50
50
  function resolve(from, to) {
51
- const resolvedUrl = new URL(to, new URL(from, "resolve://"));
51
+ const fromUrl = new URL((0, convert_path_to_posix_1.default)(from), "resolve://");
52
+ const resolvedUrl = new URL((0, convert_path_to_posix_1.default)(to), fromUrl);
52
53
  if (resolvedUrl.protocol === "resolve:") {
53
54
  // `from` is a relative URL.
54
55
  const { pathname, search, hash } = resolvedUrl;
@@ -169,7 +169,9 @@ function dereference$Ref(
169
169
  ) {
170
170
  // console.log('Dereferencing $ref pointer "%s" at %s', $ref.$ref, path);
171
171
 
172
- const $refPath = url.resolve(path, $ref.$ref);
172
+ const isExternalRef = $Ref.isExternal$Ref($ref);
173
+ const shouldResolveOnCwd = isExternalRef && options?.dereference.externalReferenceResolution === "root";
174
+ const $refPath = url.resolve(shouldResolveOnCwd ? url.cwd() : path, $ref.$ref);
173
175
 
174
176
  const cache = dereferencedCache.get($refPath);
175
177
  if (cache) {
package/lib/index.ts CHANGED
@@ -100,6 +100,13 @@ export class $RefParser {
100
100
  if (url.isFileSystemPath(args.path)) {
101
101
  args.path = url.fromFileSystemPath(args.path);
102
102
  pathType = "file";
103
+ } else if (!args.path && args.schema && args.schema.$id) {
104
+ // when schema id has defined an URL should use that hostname to request the references,
105
+ // instead of using the current page URL
106
+ const params = url.parse(args.schema.$id);
107
+ const port = params.protocol === "https:" ? 443 : 80;
108
+
109
+ args.path = `${params.protocol}//${params.hostname}:${port}`;
103
110
  }
104
111
 
105
112
  // Resolve the absolute path of the schema
package/lib/options.ts CHANGED
@@ -83,6 +83,13 @@ interface $RefParserOptions {
83
83
  * @argument {JSONSchemaObject} object The JSON-Schema that the `$ref` resolved to.
84
84
  */
85
85
  onDereference?(path: string, value: JSONSchemaObject): void;
86
+
87
+ /**
88
+ * Whether a reference should resolve relative to its directory/path, or from the cwd
89
+ *
90
+ * Default: `relative`
91
+ */
92
+ externalReferenceResolution?: "relative" | "root";
86
93
  };
87
94
  }
88
95
 
@@ -149,8 +156,9 @@ const getDefaults = () => {
149
156
  * @type {function}
150
157
  */
151
158
  excludedPathMatcher: () => false,
159
+ referenceResolution: "relative",
152
160
  },
153
- };
161
+ } as $RefParserOptions;
154
162
  return cloneDeep(defaults);
155
163
  };
156
164
 
package/lib/pointer.ts CHANGED
@@ -83,12 +83,12 @@ class Pointer {
83
83
  this.value = unwrapOrThrow(obj);
84
84
 
85
85
  for (let i = 0; i < tokens.length; i++) {
86
- if (resolveIf$Ref(this, options)) {
86
+ if (resolveIf$Ref(this, options, pathFromRoot)) {
87
87
  // The $ref path has changed, so append the remaining tokens to the path
88
88
  this.path = Pointer.join(this.path, tokens.slice(i));
89
89
  }
90
90
 
91
- if (typeof this.value === "object" && this.value !== null && "$ref" in this.value) {
91
+ if (typeof this.value === "object" && this.value !== null && !isRootPath(pathFromRoot) && "$ref" in this.value) {
92
92
  return this;
93
93
  }
94
94
 
@@ -103,7 +103,7 @@ class Pointer {
103
103
 
104
104
  // Resolve the final value
105
105
  if (!this.value || (this.value.$ref && url.resolve(this.path, this.value.$ref) !== pathFromRoot)) {
106
- resolveIf$Ref(this, options);
106
+ resolveIf$Ref(this, options, pathFromRoot);
107
107
  }
108
108
 
109
109
  return this;
@@ -224,15 +224,16 @@ class Pointer {
224
224
  *
225
225
  * @param pointer
226
226
  * @param options
227
+ * @param [pathFromRoot] - the path of place that initiated resolving
227
228
  * @returns - Returns `true` if the resolution path changed
228
229
  */
229
- function resolveIf$Ref(pointer: any, options: any) {
230
+ function resolveIf$Ref(pointer: any, options: any, pathFromRoot?: any) {
230
231
  // Is the value a JSON reference? (and allowed?)
231
232
 
232
233
  if ($Ref.isAllowed$Ref(pointer.value, options)) {
233
234
  const $refPath = url.resolve(pointer.path, pointer.value.$ref);
234
235
 
235
- if ($refPath === pointer.path) {
236
+ if ($refPath === pointer.path && !isRootPath(pathFromRoot)) {
236
237
  // The value is a reference to itself, so there's nothing to do.
237
238
  pointer.circular = true;
238
239
  } else {
@@ -294,3 +295,7 @@ function unwrapOrThrow(value: any) {
294
295
 
295
296
  return value;
296
297
  }
298
+
299
+ function isRootPath(pathFromRoot: any): boolean {
300
+ return typeof pathFromRoot == "string" && Pointer.parse(pathFromRoot).length == 0;
301
+ }
package/lib/refs.ts CHANGED
@@ -95,7 +95,7 @@ export default class $Refs {
95
95
  * @param value The value to assign. Can be anything (object, string, number, etc.)
96
96
  */
97
97
  set(path: any, value: JSONSchema4Type | JSONSchema6Type | JSONSchema7Type) {
98
- const absPath = url.resolve(this._root$Ref.path, path);
98
+ const absPath = url.resolve(this._root$Ref.path!, path);
99
99
  const withoutHash = url.stripHash(absPath);
100
100
  const $ref = this._$refs[withoutHash];
101
101
 
@@ -113,7 +113,7 @@ export default class $Refs {
113
113
  * @protected
114
114
  */
115
115
  _get$Ref(path: any) {
116
- path = url.resolve(this._root$Ref.path, path);
116
+ path = url.resolve(this._root$Ref.path!, path);
117
117
  const withoutHash = url.stripHash(path);
118
118
  return this._$refs[withoutHash];
119
119
  }
@@ -145,7 +145,7 @@ export default class $Refs {
145
145
  * @protected
146
146
  */
147
147
  _resolve(path: string, pathFromRoot: string, options?: any) {
148
- const absPath = url.resolve(this._root$Ref.path, path);
148
+ const absPath = url.resolve(this._root$Ref.path!, path);
149
149
  const withoutHash = url.stripHash(absPath);
150
150
  const $ref = this._$refs[withoutHash];
151
151
 
@@ -92,9 +92,8 @@ function crawl(
92
92
  * including nested references that are contained in externally-referenced files.
93
93
  */
94
94
  async function resolve$Ref($ref: JSONSchema, path: string, $refs: $Refs, options: Options) {
95
- // console.log('Resolving $ref pointer "%s" at %s', $ref.$ref, path);
96
-
97
- const resolvedPath = url.resolve(path, $ref.$ref);
95
+ const shouldResolveOnCwd = options.dereference.externalReferenceResolution === "root";
96
+ const resolvedPath = url.resolve(shouldResolveOnCwd ? url.cwd() : path, $ref.$ref!);
98
97
  const withoutHash = url.stripHash(resolvedPath);
99
98
 
100
99
  // $ref.$ref = url.relative($refs._root$Ref.path, resolvedPath);
@@ -86,7 +86,7 @@ async function download(u: URL | string, httpOptions: HTTPResolverOptions, _redi
86
86
  } else if (!("location" in res.headers) || !res.headers.location) {
87
87
  throw ono({ status: res.status }, `HTTP ${res.status} redirect with no location header`);
88
88
  } else {
89
- const redirectTo = url.resolve(u, res.headers.location);
89
+ const redirectTo = url.resolve(u.href, res.headers.location as string);
90
90
  return download(redirectTo, httpOptions, redirects);
91
91
  }
92
92
  } else {
@@ -7,5 +7,5 @@ export default function convertPathToPosix(filePath: string) {
7
7
  return filePath;
8
8
  }
9
9
 
10
- return filePath.split(path.win32.sep).join(path.posix.sep);
10
+ return filePath.split(path?.win32?.sep).join(path.posix.sep);
11
11
  }
package/lib/util/url.ts CHANGED
@@ -16,15 +16,16 @@ const urlEncodePatterns = [/\?/g, "%3F", /#/g, "%23"];
16
16
  // RegExp patterns to URL-decode special characters for local filesystem paths
17
17
  const urlDecodePatterns = [/%23/g, "#", /%24/g, "$", /%26/g, "&", /%2C/g, ",", /%40/g, "@"];
18
18
 
19
- export const parse = (u: any) => new URL(u);
19
+ export const parse = (u: string | URL) => new URL(u);
20
20
 
21
21
  /**
22
22
  * Returns resolved target URL relative to a base URL in a manner similar to that of a Web browser resolving an anchor tag HREF.
23
23
  *
24
24
  * @returns
25
25
  */
26
- export function resolve(from: any, to: any) {
27
- const resolvedUrl = new URL(to, new URL(from, "resolve://"));
26
+ export function resolve(from: string, to: string) {
27
+ const fromUrl = new URL(convertPathToPosix(from), "resolve://");
28
+ const resolvedUrl = new URL(convertPathToPosix(to), fromUrl);
28
29
  if (resolvedUrl.protocol === "resolve:") {
29
30
  // `from` is a relative URL.
30
31
  const { pathname, search, hash } = resolvedUrl;
@@ -279,7 +280,7 @@ export function safePointerToPath(pointer: any) {
279
280
  });
280
281
  }
281
282
 
282
- export function relative(from: string | undefined, to: string | undefined) {
283
+ export function relative(from: string, to: string) {
283
284
  if (!isFileSystemPath(from) || !isFileSystemPath(to)) {
284
285
  return resolve(from, to);
285
286
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apidevtools/json-schema-ref-parser",
3
- "version": "11.0.0",
3
+ "version": "11.1.1",
4
4
  "description": "Parse, Resolve, and Dereference JSON Schema $ref pointers",
5
5
  "keywords": [
6
6
  "json",