@speclynx/apidom-reference 5.2.4 → 5.2.5
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 +12 -0
- package/README.md +30 -11
- package/dist/apidom-reference.browser.js +1121 -514
- package/dist/apidom-reference.browser.min.js +1 -1
- package/package.json +28 -28
- package/src/bundle/strategies/arazzo-1/visitor.cjs +105 -22
- package/src/bundle/strategies/arazzo-1/visitor.mjs +107 -24
- package/src/bundle/strategies/openapi-3-1/visitor.cjs +197 -47
- package/src/bundle/strategies/openapi-3-1/visitor.mjs +200 -50
- package/src/bundle/util.cjs +57 -2
- package/src/bundle/util.mjs +52 -0
- package/src/dereference/strategies/arazzo-1/index.cjs +9 -7
- package/src/dereference/strategies/arazzo-1/index.mjs +3 -1
- package/src/dereference/strategies/arazzo-1/selectors/$anchor.cjs +6 -0
- package/src/dereference/strategies/arazzo-1/selectors/$anchor.mjs +1 -1
- package/src/dereference/strategies/arazzo-1/selectors/uri.cjs +6 -0
- package/src/dereference/strategies/arazzo-1/selectors/uri.mjs +1 -1
- package/src/dereference/strategies/arazzo-1/util.cjs +6 -0
- package/src/dereference/strategies/arazzo-1/util.mjs +1 -1
- package/src/dereference/strategies/arazzo-1/visitor.cjs +67 -9
- package/src/dereference/strategies/arazzo-1/visitor.mjs +71 -13
- package/src/dereference/strategies/openapi-3-1/index.cjs +2 -1
- package/src/dereference/strategies/openapi-3-1/index.mjs +3 -1
- package/src/dereference/strategies/openapi-3-1/selectors/$anchor.cjs +16 -4
- package/src/dereference/strategies/openapi-3-1/selectors/$anchor.mjs +14 -3
- package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.cjs +29 -0
- package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.mjs +22 -0
- package/src/dereference/strategies/openapi-3-1/selectors/uri.cjs +54 -27
- package/src/dereference/strategies/openapi-3-1/selectors/uri.mjs +51 -27
- package/src/dereference/strategies/openapi-3-1/util.cjs +75 -14
- package/src/dereference/strategies/openapi-3-1/util.mjs +68 -14
- package/src/dereference/strategies/openapi-3-1/visitor.cjs +85 -9
- package/src/dereference/strategies/openapi-3-1/visitor.mjs +89 -13
- package/src/util/url.cjs +35 -2
- package/src/util/url.mjs +32 -0
- package/types/apidom-reference.d.ts +12 -0
- package/types/bundle/strategies/arazzo-1/index.d.ts +1 -1
- package/types/bundle/strategies/arazzo-1/visitor.d.ts +51 -6
- package/types/bundle/strategies/openapi-3-1/index.d.ts +1 -1
- package/types/bundle/strategies/openapi-3-1/visitor.d.ts +59 -3
- package/types/bundle/util.d.ts +39 -0
- package/types/dereference/strategies/arazzo-1/selectors/$anchor.d.ts +1 -1
- package/types/dereference/strategies/arazzo-1/selectors/uri.d.ts +1 -1
- package/types/dereference/strategies/arazzo-1/util.d.ts +1 -1
- package/types/dereference/strategies/arazzo-1/visitor.d.ts +17 -1
- package/types/dereference/strategies/openapi-3-1/selectors/$anchor.d.ts +8 -0
- package/types/dereference/strategies/openapi-3-1/selectors/json-pointer.d.ts +9 -0
- package/types/dereference/strategies/openapi-3-1/selectors/uri.d.ts +37 -4
- package/types/dereference/strategies/openapi-3-1/util.d.ts +53 -2
- package/types/dereference/strategies/openapi-3-1/visitor.d.ts +17 -1
- package/types/util/url.d.ts +10 -0
|
@@ -1,42 +1,66 @@
|
|
|
1
1
|
import { isUndefined } from 'ramda-adjunct';
|
|
2
2
|
import { filter } from '@speclynx/apidom-traverse';
|
|
3
|
-
import {
|
|
4
|
-
import { URIFragmentIdentifier, evaluate as jsonPointerEvaluate } from '@speclynx/apidom-json-pointer';
|
|
3
|
+
import { URIFragmentIdentifier } from '@speclynx/apidom-json-pointer';
|
|
5
4
|
import * as url from "../../../../util/url.mjs";
|
|
6
5
|
import EvaluationJsonSchemaUriError from "../../../../errors/EvaluationJsonSchemaUriError.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { isJSONSchemaElementWith$id, collectSchema$ids, resolveSchema$ids } from "../util.mjs";
|
|
7
|
+
import { isAnchor, uriToAnchor, locate as $anchorLocate } from "./$anchor.mjs";
|
|
8
|
+
import { locate as jsonPointerLocate } from "./json-pointer.mjs";
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Index of `$id`-bearing schemas per indexed element (a document root, or the
|
|
11
|
+
* fragment a visitor traverses). Each entry pairs a schema with the `$id`s of its
|
|
12
|
+
* enclosing schema resources (outermost first, including its own). The index is
|
|
13
|
+
* populated once per element and never refreshed, so callers scope it
|
|
14
|
+
* to a single dereference/bundle run instead of sharing it globally. Within a run the
|
|
15
|
+
* `$id` graph of an indexed document is stable: the bundle strategies mutate deep
|
|
16
|
+
* clones and place components only after the entry traversal ends, and the
|
|
17
|
+
* dereference strategies only replace `$ref`-bearing schemas.
|
|
11
18
|
* @public
|
|
12
19
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
const indexSchema$ids = element => filter(element, path => isJSONSchemaElementWith$id(path.node)).map(path => ({
|
|
24
|
+
schema: path.node,
|
|
25
|
+
$ids: collectSchema$ids(path)
|
|
26
|
+
}));
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Locates the schema addressed by JSON Schema $ref containing unknown URI within
|
|
30
|
+
* ApiDOM fragment, along with the `$id`s of the schemas enclosing it.
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
export const locate = (uri, element, options = {}) => {
|
|
34
|
+
const uriStrippedHash = url.stripHash(uri);
|
|
35
|
+
const {
|
|
36
|
+
baseURI = uriStrippedHash,
|
|
37
|
+
index = new WeakMap()
|
|
38
|
+
} = options;
|
|
39
|
+
if (!index.has(element)) {
|
|
40
|
+
index.set(element, indexSchema$ids(element));
|
|
24
41
|
}
|
|
25
42
|
|
|
26
|
-
// search for the
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (isUndefined(result)) {
|
|
43
|
+
// search for the schema whose canonical URI matches
|
|
44
|
+
const entry = index.get(element).find(({
|
|
45
|
+
$ids
|
|
46
|
+
}) => resolveSchema$ids(baseURI, $ids) === uriStrippedHash);
|
|
47
|
+
if (isUndefined(entry)) {
|
|
32
48
|
throw new EvaluationJsonSchemaUriError(`Evaluation failed on URI: "${uri}"`);
|
|
33
49
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return
|
|
50
|
+
|
|
51
|
+
// the fragment is resolved within the matched schema, whose own $id is the
|
|
52
|
+
// last of its chain; the fragment walk contributes it again when descending
|
|
53
|
+
const fragmentLocation = isAnchor(uriToAnchor(uri)) ? $anchorLocate(uriToAnchor(uri), entry.schema) : jsonPointerLocate(entry.schema, URIFragmentIdentifier.fromURIReference(uri));
|
|
54
|
+
return {
|
|
55
|
+
element: fragmentLocation.element,
|
|
56
|
+
ancestorSchema$ids: [...entry.$ids.slice(0, -1), ...fragmentLocation.ancestorSchema$ids]
|
|
57
|
+
};
|
|
39
58
|
};
|
|
40
|
-
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Evaluates JSON Schema $ref containing unknown URI against ApiDOM fragment.
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
64
|
+
export const evaluate = (uri, element, options = {}) => locate(uri, element, options).element;
|
|
41
65
|
export { EvaluationJsonSchemaUriError };
|
|
42
66
|
export { default as JsonSchemaUriError } from "../../../../errors/JsonSchemaUriError.mjs";
|
|
@@ -4,39 +4,100 @@ var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopReq
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.resolveSchema$refField = exports.resolveSchema$idField = exports.refractToSchemaElement = exports.maybeRefractToSchemaElement = void 0;
|
|
7
|
+
exports.schema$idsOf = exports.resolveSchemaBaseURI = exports.resolveSchema$refField = exports.resolveSchema$ids = exports.resolveSchema$idField = exports.refractToSchemaElement = exports.maybeRefractToSchemaElement = exports.isJSONSchemaElementWith$id = exports.detachedRootSchema$ids = exports.collectSchema$ids = void 0;
|
|
8
8
|
var _ramda = require("ramda");
|
|
9
|
+
var _ramdaAdjunct = require("ramda-adjunct");
|
|
9
10
|
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
10
11
|
var _apidomCore = require("@speclynx/apidom-core");
|
|
12
|
+
var _apidomNsJsonSchema = require("@speclynx/apidom-ns-json-schema-2020-12");
|
|
11
13
|
var _apidomNsOpenapi = require("@speclynx/apidom-ns-openapi-3-1");
|
|
12
14
|
var url = _interopRequireWildcard(require("../../../util/url.cjs"));
|
|
13
15
|
/**
|
|
16
|
+
* A schema located within a document, together with the `$id`s of the JSON Schema
|
|
17
|
+
* elements enclosing it (outermost first; the located schema's own `$id` excluded).
|
|
14
18
|
* @public
|
|
15
19
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether the element is a JSON Schema element declaring a `$id`.
|
|
23
|
+
*/
|
|
24
|
+
const isJSONSchemaElementWith$id = element => (0, _apidomNsJsonSchema.isJSONSchemaElement)(element) && (0, _apidomDatamodel.isStringElement)(element.$id);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* `$id`s declared by the JSON Schema elements among `nodes`, in order.
|
|
28
|
+
*/
|
|
29
|
+
exports.isJSONSchemaElementWith$id = isJSONSchemaElementWith$id;
|
|
30
|
+
const schema$idsOf = nodes => nodes.filter(isJSONSchemaElementWith$id).map(node => (0, _apidomCore.toValue)(node.$id));
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `$id`s of the JSON Schema elements on `path` (outermost first, the node itself
|
|
34
|
+
* included). The `$id`s are read from the elements themselves rather than from
|
|
35
|
+
* refraction-time metadata, so a `$id` assigned after refraction (e.g. by the
|
|
36
|
+
* bundler) is honored.
|
|
37
|
+
*/
|
|
38
|
+
exports.schema$idsOf = schema$idsOf;
|
|
39
|
+
const collectSchema$ids = path => schema$idsOf([...path.getAncestorNodes().reverse(), path.node]);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* `$id`s of the schemas enclosing a traversal root that is a fragment detached
|
|
43
|
+
* from its document, read from the `ancestorsSchemaIdentifiers` meta the JSON
|
|
44
|
+
* Schema refractors record. The meta also carries the root's own `$id`, which
|
|
45
|
+
* the ancestor walk contributes itself, so it is dropped. A detached fragment
|
|
46
|
+
* has no live ancestors, so the meta is the only carrier of its lexical
|
|
47
|
+
* context; this is the one reader of it the refractors must keep serving.
|
|
48
|
+
*/
|
|
49
|
+
exports.collectSchema$ids = collectSchema$ids;
|
|
50
|
+
const detachedRootSchema$ids = root => {
|
|
51
|
+
const meta = root?.meta.get('ancestorsSchemaIdentifiers');
|
|
52
|
+
const $ids = Array.isArray(meta) ? meta.filter(_ramdaAdjunct.isString) : [];
|
|
53
|
+
const own$id = isJSONSchemaElementWith$id(root) ? (0, _apidomCore.toValue)(root.$id) : undefined;
|
|
54
|
+
return $ids.length > 0 && $ids.at(-1) === own$id ? $ids.slice(0, -1) : $ids;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resolves a chain of `$id`s (outermost first) against `baseURI`, each `$id`
|
|
59
|
+
* refining the base the next one resolves against.
|
|
60
|
+
*/
|
|
61
|
+
exports.detachedRootSchema$ids = detachedRootSchema$ids;
|
|
62
|
+
const resolveSchema$ids = (baseURI, $ids) => (0, _ramda.reduce)((acc, $id) => url.resolve(acc, url.sanitize(url.stripHash($id))), baseURI, $ids);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the base URI of the schema at `path`: `baseURI` (the base URI in
|
|
66
|
+
* effect at the traversal root) refined by each `$id` on the schema's ancestor
|
|
67
|
+
* chain in turn, the schema's own `$id` included.
|
|
68
|
+
*/
|
|
69
|
+
exports.resolveSchema$ids = resolveSchema$ids;
|
|
70
|
+
const resolveSchemaBaseURI = (baseURI, path) => resolveSchema$ids(baseURI, collectSchema$ids(path));
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Resolves the `$ref` of the schema against its base URI (see
|
|
74
|
+
* `resolveSchemaBaseURI`), or returns `undefined` when the schema has no string `$ref`.
|
|
75
|
+
*
|
|
76
|
+
* @public
|
|
77
|
+
*/
|
|
78
|
+
exports.resolveSchemaBaseURI = resolveSchemaBaseURI;
|
|
79
|
+
const resolveSchema$refField = (schemaBaseURI, schemaElement) => {
|
|
80
|
+
if (!(0, _apidomDatamodel.isStringElement)(schemaElement.$ref)) {
|
|
18
81
|
return undefined;
|
|
19
82
|
}
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const $refBaseURI = (
|
|
23
|
-
return url.resolve(acc, url.sanitize(url.stripHash(uri)));
|
|
24
|
-
}, retrievalURI, [...ancestorsSchemaIdentifiers, (0, _apidomCore.toValue)(schemaElement.$ref)]);
|
|
83
|
+
const $ref = (0, _apidomCore.toValue)(schemaElement.$ref);
|
|
84
|
+
const hash = url.getHash($ref);
|
|
85
|
+
const $refBaseURI = resolveSchema$ids(schemaBaseURI, [$ref]);
|
|
25
86
|
return `${$refBaseURI}${hash === '#' ? '' : hash}`;
|
|
26
87
|
};
|
|
27
88
|
|
|
28
89
|
/**
|
|
90
|
+
* Resolves the `$id` of the schema against `baseURI`, the base URI in effect
|
|
91
|
+
* where the schema sits, or returns `undefined` when the schema declares no string `$id`.
|
|
92
|
+
*
|
|
29
93
|
* @public
|
|
30
94
|
*/
|
|
31
95
|
exports.resolveSchema$refField = resolveSchema$refField;
|
|
32
|
-
const resolveSchema$idField = (
|
|
33
|
-
if (
|
|
96
|
+
const resolveSchema$idField = (baseURI, schemaElement) => {
|
|
97
|
+
if (!(0, _apidomDatamodel.isStringElement)(schemaElement.$id)) {
|
|
34
98
|
return undefined;
|
|
35
99
|
}
|
|
36
|
-
|
|
37
|
-
return (0, _ramda.reduce)((acc, $id) => {
|
|
38
|
-
return url.resolve(acc, url.sanitize(url.stripHash($id)));
|
|
39
|
-
}, retrievalURI, ancestorsSchemaIdentifiers);
|
|
100
|
+
return resolveSchema$ids(baseURI, [(0, _apidomCore.toValue)(schemaElement.$id)]);
|
|
40
101
|
};
|
|
41
102
|
|
|
42
103
|
/**
|
|
@@ -1,34 +1,88 @@
|
|
|
1
1
|
import { reduce } from 'ramda';
|
|
2
|
-
import {
|
|
2
|
+
import { isString } from 'ramda-adjunct';
|
|
3
|
+
import { isPrimitiveElement, isStringElement } from '@speclynx/apidom-datamodel';
|
|
3
4
|
import { toValue } from '@speclynx/apidom-core';
|
|
5
|
+
import { isJSONSchemaElement } from '@speclynx/apidom-ns-json-schema-2020-12';
|
|
4
6
|
import { refractSchema } from '@speclynx/apidom-ns-openapi-3-1';
|
|
5
7
|
import * as url from "../../../util/url.mjs";
|
|
6
8
|
/**
|
|
9
|
+
* A schema located within a document, together with the `$id`s of the JSON Schema
|
|
10
|
+
* elements enclosing it (outermost first; the located schema's own `$id` excluded).
|
|
7
11
|
* @public
|
|
8
12
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Whether the element is a JSON Schema element declaring a `$id`.
|
|
15
|
+
*/
|
|
16
|
+
export const isJSONSchemaElementWith$id = element => isJSONSchemaElement(element) && isStringElement(element.$id);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `$id`s declared by the JSON Schema elements among `nodes`, in order.
|
|
20
|
+
*/
|
|
21
|
+
export const schema$idsOf = nodes => nodes.filter(isJSONSchemaElementWith$id).map(node => toValue(node.$id));
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* `$id`s of the JSON Schema elements on `path` (outermost first, the node itself
|
|
25
|
+
* included). The `$id`s are read from the elements themselves rather than from
|
|
26
|
+
* refraction-time metadata, so a `$id` assigned after refraction (e.g. by the
|
|
27
|
+
* bundler) is honored.
|
|
28
|
+
*/
|
|
29
|
+
export const collectSchema$ids = path => schema$idsOf([...path.getAncestorNodes().reverse(), path.node]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `$id`s of the schemas enclosing a traversal root that is a fragment detached
|
|
33
|
+
* from its document, read from the `ancestorsSchemaIdentifiers` meta the JSON
|
|
34
|
+
* Schema refractors record. The meta also carries the root's own `$id`, which
|
|
35
|
+
* the ancestor walk contributes itself, so it is dropped. A detached fragment
|
|
36
|
+
* has no live ancestors, so the meta is the only carrier of its lexical
|
|
37
|
+
* context; this is the one reader of it the refractors must keep serving.
|
|
38
|
+
*/
|
|
39
|
+
export const detachedRootSchema$ids = root => {
|
|
40
|
+
const meta = root?.meta.get('ancestorsSchemaIdentifiers');
|
|
41
|
+
const $ids = Array.isArray(meta) ? meta.filter(isString) : [];
|
|
42
|
+
const own$id = isJSONSchemaElementWith$id(root) ? toValue(root.$id) : undefined;
|
|
43
|
+
return $ids.length > 0 && $ids.at(-1) === own$id ? $ids.slice(0, -1) : $ids;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Resolves a chain of `$id`s (outermost first) against `baseURI`, each `$id`
|
|
48
|
+
* refining the base the next one resolves against.
|
|
49
|
+
*/
|
|
50
|
+
export const resolveSchema$ids = (baseURI, $ids) => reduce((acc, $id) => url.resolve(acc, url.sanitize(url.stripHash($id))), baseURI, $ids);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Resolves the base URI of the schema at `path`: `baseURI` (the base URI in
|
|
54
|
+
* effect at the traversal root) refined by each `$id` on the schema's ancestor
|
|
55
|
+
* chain in turn, the schema's own `$id` included.
|
|
56
|
+
*/
|
|
57
|
+
export const resolveSchemaBaseURI = (baseURI, path) => resolveSchema$ids(baseURI, collectSchema$ids(path));
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolves the `$ref` of the schema against its base URI (see
|
|
61
|
+
* `resolveSchemaBaseURI`), or returns `undefined` when the schema has no string `$ref`.
|
|
62
|
+
*
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
export const resolveSchema$refField = (schemaBaseURI, schemaElement) => {
|
|
66
|
+
if (!isStringElement(schemaElement.$ref)) {
|
|
11
67
|
return undefined;
|
|
12
68
|
}
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const $refBaseURI =
|
|
16
|
-
return url.resolve(acc, url.sanitize(url.stripHash(uri)));
|
|
17
|
-
}, retrievalURI, [...ancestorsSchemaIdentifiers, toValue(schemaElement.$ref)]);
|
|
69
|
+
const $ref = toValue(schemaElement.$ref);
|
|
70
|
+
const hash = url.getHash($ref);
|
|
71
|
+
const $refBaseURI = resolveSchema$ids(schemaBaseURI, [$ref]);
|
|
18
72
|
return `${$refBaseURI}${hash === '#' ? '' : hash}`;
|
|
19
73
|
};
|
|
20
74
|
|
|
21
75
|
/**
|
|
76
|
+
* Resolves the `$id` of the schema against `baseURI`, the base URI in effect
|
|
77
|
+
* where the schema sits, or returns `undefined` when the schema declares no string `$id`.
|
|
78
|
+
*
|
|
22
79
|
* @public
|
|
23
80
|
*/
|
|
24
|
-
export const resolveSchema$idField = (
|
|
25
|
-
if (
|
|
81
|
+
export const resolveSchema$idField = (baseURI, schemaElement) => {
|
|
82
|
+
if (!isStringElement(schemaElement.$id)) {
|
|
26
83
|
return undefined;
|
|
27
84
|
}
|
|
28
|
-
|
|
29
|
-
return reduce((acc, $id) => {
|
|
30
|
-
return url.resolve(acc, url.sanitize(url.stripHash($id)));
|
|
31
|
-
}, retrievalURI, ancestorsSchemaIdentifiers);
|
|
85
|
+
return resolveSchema$ids(baseURI, [toValue(schemaElement.$id)]);
|
|
32
86
|
};
|
|
33
87
|
|
|
34
88
|
/**
|
|
@@ -16,6 +16,7 @@ var _apidomJsonPointer = require("@speclynx/apidom-json-pointer");
|
|
|
16
16
|
var _apidomNsOpenapi = require("@speclynx/apidom-ns-openapi-3-1");
|
|
17
17
|
var _$anchor = require("./selectors/$anchor.cjs");
|
|
18
18
|
var _uri = require("./selectors/uri.cjs");
|
|
19
|
+
var _jsonPointer = require("./selectors/json-pointer.cjs");
|
|
19
20
|
var _MaximumDereferenceDepthError = _interopRequireDefault(require("../../../errors/MaximumDereferenceDepthError.cjs"));
|
|
20
21
|
var _MaximumResolveDepthError = _interopRequireDefault(require("../../../errors/MaximumResolveDepthError.cjs"));
|
|
21
22
|
var _UnresolvableReferenceError = _interopRequireDefault(require("../../../errors/UnresolvableReferenceError.cjs"));
|
|
@@ -38,8 +39,23 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
38
39
|
reference;
|
|
39
40
|
options;
|
|
40
41
|
refractCache;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Index of `$id`-bearing schemas per document, shared across nested visitors
|
|
45
|
+
* of a single dereference run so it never outlives mutations of the documents.
|
|
46
|
+
*/
|
|
47
|
+
schema$idIndex;
|
|
41
48
|
visited;
|
|
42
49
|
|
|
50
|
+
/**
|
|
51
|
+
* `$id`s of the JSON Schema elements enclosing the traversal root within its
|
|
52
|
+
* document (outermost first). Empty when the root is the document itself; a
|
|
53
|
+
* nested visitor traversing a fragment is seeded with the `$id`s of the
|
|
54
|
+
* schemas the fragment sits under, so the `$ref`s and `$id`s within resolve
|
|
55
|
+
* against the same base as they would from the document root.
|
|
56
|
+
*/
|
|
57
|
+
ancestorSchema$ids;
|
|
58
|
+
|
|
43
59
|
/**
|
|
44
60
|
* Tracks element ancestors across dive-deep traversal boundaries.
|
|
45
61
|
* Used for cycle detection: if a referenced element is found in
|
|
@@ -51,15 +67,19 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
51
67
|
options,
|
|
52
68
|
indirections = [],
|
|
53
69
|
refractCache = new WeakMap(),
|
|
70
|
+
schema$idIndex = new WeakMap(),
|
|
54
71
|
ancestors = new _util2.AncestorLineage(),
|
|
55
|
-
visited = new WeakSet()
|
|
72
|
+
visited = new WeakSet(),
|
|
73
|
+
ancestorSchema$ids = []
|
|
56
74
|
}) {
|
|
57
75
|
this.indirections = indirections;
|
|
58
76
|
this.reference = reference;
|
|
59
77
|
this.options = options;
|
|
60
78
|
this.refractCache = refractCache;
|
|
79
|
+
this.schema$idIndex = schema$idIndex;
|
|
61
80
|
this.ancestors = new _util2.AncestorLineage(...ancestors);
|
|
62
81
|
this.visited = visited;
|
|
82
|
+
this.ancestorSchema$ids = ancestorSchema$ids;
|
|
63
83
|
}
|
|
64
84
|
toAncestorLineage(path) {
|
|
65
85
|
const ancestorNodes = path.getAncestorNodes();
|
|
@@ -318,6 +338,7 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
318
338
|
indirections: [...this.indirections],
|
|
319
339
|
options: this.options,
|
|
320
340
|
refractCache: this.refractCache,
|
|
341
|
+
schema$idIndex: this.schema$idIndex,
|
|
321
342
|
visited: this.visited,
|
|
322
343
|
ancestors: ancestorsLineage
|
|
323
344
|
});
|
|
@@ -476,6 +497,7 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
476
497
|
indirections: [...this.indirections],
|
|
477
498
|
options: this.options,
|
|
478
499
|
refractCache: this.refractCache,
|
|
500
|
+
schema$idIndex: this.schema$idIndex,
|
|
479
501
|
visited: this.visited,
|
|
480
502
|
ancestors: ancestorsLineage
|
|
481
503
|
});
|
|
@@ -673,7 +695,11 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
673
695
|
let {
|
|
674
696
|
uri: retrievalURI
|
|
675
697
|
} = reference;
|
|
676
|
-
|
|
698
|
+
// base URI in effect at the traversal root: the document base refined by
|
|
699
|
+
// the $ids of the schemas enclosing the root when the root is a fragment
|
|
700
|
+
const rootBaseURI = (0, _util.resolveSchema$ids)(retrievalURI, this.ancestorSchema$ids);
|
|
701
|
+
const schemaBaseURI = (0, _util.resolveSchemaBaseURI)(rootBaseURI, path);
|
|
702
|
+
const $refBaseURI = (0, _util.resolveSchema$refField)(schemaBaseURI, referencingElement);
|
|
677
703
|
const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
|
|
678
704
|
const file = new _File.default({
|
|
679
705
|
uri: $refBaseURIStrippedHash
|
|
@@ -685,13 +711,48 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
685
711
|
|
|
686
712
|
// determining reference, proper evaluation and selection mechanism
|
|
687
713
|
let referencedElement;
|
|
714
|
+
// $ids of the schemas enclosing the referenced element within its document
|
|
715
|
+
let ancestorSchema$ids;
|
|
688
716
|
try {
|
|
689
717
|
if (isUnknownURI || isURL) {
|
|
690
718
|
// we're dealing with canonical URI or URL with possible fragment
|
|
691
719
|
retrievalURI = this.toBaseURI($refBaseURI);
|
|
692
720
|
const selector = $refBaseURI;
|
|
693
|
-
|
|
694
|
-
|
|
721
|
+
// the fragment being traversed is searched before the whole document: a
|
|
722
|
+
// fragment referenced from another document (a Response, Parameter, Path
|
|
723
|
+
// Item, ...) is refracted with its own semantics, so the Schema Objects it
|
|
724
|
+
// contains are indexed, whereas the referenced document as a whole is
|
|
725
|
+
// parsed generically and refracted as a bare JSON Schema, which leaves
|
|
726
|
+
// those Schema Objects (under `content`, `schema`, ...) unrecognized and
|
|
727
|
+
// their `$id`s out of the index
|
|
728
|
+
const documentElement = reference.value.result;
|
|
729
|
+
const rootNode = path.getAncestorNodes().at(-1) ?? path.node;
|
|
730
|
+
const fragmentElement = (0, _apidomDatamodel.isParseResultElement)(rootNode) ? rootNode.result : rootNode;
|
|
731
|
+
try {
|
|
732
|
+
// the fragment's $ids resolve against the base its enclosing chain
|
|
733
|
+
// leaves in effect, which then also prefixes the located schema's chain
|
|
734
|
+
({
|
|
735
|
+
element: referencedElement,
|
|
736
|
+
ancestorSchema$ids
|
|
737
|
+
} = (0, _uri.locate)(selector, (0, _util.maybeRefractToSchemaElement)(fragmentElement), {
|
|
738
|
+
baseURI: rootBaseURI,
|
|
739
|
+
index: this.schema$idIndex
|
|
740
|
+
}));
|
|
741
|
+
ancestorSchema$ids = [...this.ancestorSchema$ids, ...ancestorSchema$ids];
|
|
742
|
+
} catch (error) {
|
|
743
|
+
if (!(error instanceof _EvaluationJsonSchemaUriError.default) || fragmentElement === documentElement) {
|
|
744
|
+
throw error;
|
|
745
|
+
}
|
|
746
|
+
// the document index is rooted at the document, so the located chain
|
|
747
|
+
// is already document-relative and takes no prefix
|
|
748
|
+
({
|
|
749
|
+
element: referencedElement,
|
|
750
|
+
ancestorSchema$ids
|
|
751
|
+
} = (0, _uri.locate)(selector, (0, _util.maybeRefractToSchemaElement)(documentElement), {
|
|
752
|
+
baseURI: reference.uri,
|
|
753
|
+
index: this.schema$idIndex
|
|
754
|
+
}));
|
|
755
|
+
}
|
|
695
756
|
referencedElement = (0, _util.maybeRefractToSchemaElement)(referencedElement);
|
|
696
757
|
|
|
697
758
|
// ignore resolving internal Schema Objects
|
|
@@ -723,7 +784,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
723
784
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
724
785
|
const selector = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
725
786
|
const referenceAsSchema = (0, _util.maybeRefractToSchemaElement)(reference.value.result);
|
|
726
|
-
|
|
787
|
+
({
|
|
788
|
+
element: referencedElement,
|
|
789
|
+
ancestorSchema$ids
|
|
790
|
+
} = (0, _jsonPointer.locate)(referenceAsSchema, selector));
|
|
727
791
|
referencedElement = (0, _util.maybeRefractToSchemaElement)(referencedElement);
|
|
728
792
|
}
|
|
729
793
|
} catch (error) {
|
|
@@ -750,7 +814,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
750
814
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
751
815
|
const selector = (0, _$anchor.uriToAnchor)($refBaseURI);
|
|
752
816
|
const referenceAsSchema = (0, _util.maybeRefractToSchemaElement)(reference.value.result);
|
|
753
|
-
|
|
817
|
+
({
|
|
818
|
+
element: referencedElement,
|
|
819
|
+
ancestorSchema$ids
|
|
820
|
+
} = (0, _$anchor.locate)(selector, referenceAsSchema));
|
|
754
821
|
referencedElement = (0, _util.maybeRefractToSchemaElement)(referencedElement);
|
|
755
822
|
} else {
|
|
756
823
|
// we're assuming here that we're dealing with JSON Pointer here
|
|
@@ -771,7 +838,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
771
838
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
772
839
|
const selector = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
773
840
|
const referenceAsSchema = (0, _util.maybeRefractToSchemaElement)(reference.value.result);
|
|
774
|
-
|
|
841
|
+
({
|
|
842
|
+
element: referencedElement,
|
|
843
|
+
ancestorSchema$ids
|
|
844
|
+
} = (0, _jsonPointer.locate)(referenceAsSchema, selector));
|
|
775
845
|
referencedElement = (0, _util.maybeRefractToSchemaElement)(referencedElement);
|
|
776
846
|
}
|
|
777
847
|
} else {
|
|
@@ -828,7 +898,11 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
828
898
|
*/
|
|
829
899
|
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
830
900
|
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
831
|
-
|
|
901
|
+
// content transplanted into the referencing site is traversed under that
|
|
902
|
+
// site's $id chain; a fragment enclosed by a different chain has its own
|
|
903
|
+
// $refs resolved in place first, by a nested visitor seeded with that chain
|
|
904
|
+
const hasDifferentEnclosingBase = (0, _util.resolveSchema$ids)(reference.uri, ancestorSchema$ids) !== (0, _util.resolveSchema$ids)(rootBaseURI, (0, _util.schema$idsOf)(path.getAncestorNodes().reverse()));
|
|
905
|
+
if ((isExternalReference || isNonEntryDocument || hasDifferentEnclosingBase || (0, _apidomNsOpenapi.isSchemaElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
832
906
|
if (shouldDetectCircular) {
|
|
833
907
|
this.visited.add(referencedElement);
|
|
834
908
|
}
|
|
@@ -838,8 +912,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
838
912
|
indirections: [...this.indirections],
|
|
839
913
|
options: this.options,
|
|
840
914
|
refractCache: this.refractCache,
|
|
915
|
+
schema$idIndex: this.schema$idIndex,
|
|
841
916
|
visited: this.visited,
|
|
842
|
-
ancestors: ancestorsLineage
|
|
917
|
+
ancestors: ancestorsLineage,
|
|
918
|
+
ancestorSchema$ids
|
|
843
919
|
});
|
|
844
920
|
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
845
921
|
mutable: true,
|