@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.
Files changed (51) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +30 -11
  3. package/dist/apidom-reference.browser.js +1121 -514
  4. package/dist/apidom-reference.browser.min.js +1 -1
  5. package/package.json +28 -28
  6. package/src/bundle/strategies/arazzo-1/visitor.cjs +105 -22
  7. package/src/bundle/strategies/arazzo-1/visitor.mjs +107 -24
  8. package/src/bundle/strategies/openapi-3-1/visitor.cjs +197 -47
  9. package/src/bundle/strategies/openapi-3-1/visitor.mjs +200 -50
  10. package/src/bundle/util.cjs +57 -2
  11. package/src/bundle/util.mjs +52 -0
  12. package/src/dereference/strategies/arazzo-1/index.cjs +9 -7
  13. package/src/dereference/strategies/arazzo-1/index.mjs +3 -1
  14. package/src/dereference/strategies/arazzo-1/selectors/$anchor.cjs +6 -0
  15. package/src/dereference/strategies/arazzo-1/selectors/$anchor.mjs +1 -1
  16. package/src/dereference/strategies/arazzo-1/selectors/uri.cjs +6 -0
  17. package/src/dereference/strategies/arazzo-1/selectors/uri.mjs +1 -1
  18. package/src/dereference/strategies/arazzo-1/util.cjs +6 -0
  19. package/src/dereference/strategies/arazzo-1/util.mjs +1 -1
  20. package/src/dereference/strategies/arazzo-1/visitor.cjs +67 -9
  21. package/src/dereference/strategies/arazzo-1/visitor.mjs +71 -13
  22. package/src/dereference/strategies/openapi-3-1/index.cjs +2 -1
  23. package/src/dereference/strategies/openapi-3-1/index.mjs +3 -1
  24. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.cjs +16 -4
  25. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.mjs +14 -3
  26. package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.cjs +29 -0
  27. package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.mjs +22 -0
  28. package/src/dereference/strategies/openapi-3-1/selectors/uri.cjs +54 -27
  29. package/src/dereference/strategies/openapi-3-1/selectors/uri.mjs +51 -27
  30. package/src/dereference/strategies/openapi-3-1/util.cjs +75 -14
  31. package/src/dereference/strategies/openapi-3-1/util.mjs +68 -14
  32. package/src/dereference/strategies/openapi-3-1/visitor.cjs +85 -9
  33. package/src/dereference/strategies/openapi-3-1/visitor.mjs +89 -13
  34. package/src/util/url.cjs +35 -2
  35. package/src/util/url.mjs +32 -0
  36. package/types/apidom-reference.d.ts +12 -0
  37. package/types/bundle/strategies/arazzo-1/index.d.ts +1 -1
  38. package/types/bundle/strategies/arazzo-1/visitor.d.ts +51 -6
  39. package/types/bundle/strategies/openapi-3-1/index.d.ts +1 -1
  40. package/types/bundle/strategies/openapi-3-1/visitor.d.ts +59 -3
  41. package/types/bundle/util.d.ts +39 -0
  42. package/types/dereference/strategies/arazzo-1/selectors/$anchor.d.ts +1 -1
  43. package/types/dereference/strategies/arazzo-1/selectors/uri.d.ts +1 -1
  44. package/types/dereference/strategies/arazzo-1/util.d.ts +1 -1
  45. package/types/dereference/strategies/arazzo-1/visitor.d.ts +17 -1
  46. package/types/dereference/strategies/openapi-3-1/selectors/$anchor.d.ts +8 -0
  47. package/types/dereference/strategies/openapi-3-1/selectors/json-pointer.d.ts +9 -0
  48. package/types/dereference/strategies/openapi-3-1/selectors/uri.d.ts +37 -4
  49. package/types/dereference/strategies/openapi-3-1/util.d.ts +53 -2
  50. package/types/dereference/strategies/openapi-3-1/visitor.d.ts +17 -1
  51. 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 { isJSONSchemaElement } from '@speclynx/apidom-ns-json-schema-2020-12';
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 { isAnchor, uriToAnchor, evaluate as $anchorEvaluate } from "./$anchor.mjs";
8
- import { resolveSchema$idField } from "../util.mjs";
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
- * Evaluates JSON Schema $ref containing unknown URI against ApiDOM fragment.
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
- export const evaluate = (uri, element) => {
14
- const {
15
- cache
16
- } = evaluate;
17
- const uriStrippedHash = url.stripHash(uri);
18
- const isJSONSchemaElementWith$id = path => isJSONSchemaElement(path.node) && typeof path.node.$id !== 'undefined';
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
- // warm the cache
21
- if (!cache.has(element)) {
22
- const schemaObjectPaths = filter(element, isJSONSchemaElementWith$id);
23
- cache.set(element, schemaObjectPaths.map(path => path.node));
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 matching schema
27
- const result = cache.get(element).find(e => {
28
- const $idBaseURI = resolveSchema$idField(uriStrippedHash, e);
29
- return $idBaseURI === uriStrippedHash;
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
- if (isAnchor(uriToAnchor(uri))) {
35
- // we're dealing with JSON Schema $anchor here
36
- return $anchorEvaluate(uriToAnchor(uri), result);
37
- }
38
- return jsonPointerEvaluate(result, URIFragmentIdentifier.fromURIReference(uri));
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
- evaluate.cache = new WeakMap();
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
- const resolveSchema$refField = (retrievalURI, schemaElement) => {
17
- if (typeof schemaElement.$ref === 'undefined') {
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 hash = url.getHash((0, _apidomCore.toValue)(schemaElement.$ref));
21
- const ancestorsSchemaIdentifiers = schemaElement.meta.get('ancestorsSchemaIdentifiers');
22
- const $refBaseURI = (0, _ramda.reduce)((acc, uri) => {
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 = (retrievalURI, schemaElement) => {
33
- if (typeof schemaElement.$id === 'undefined') {
96
+ const resolveSchema$idField = (baseURI, schemaElement) => {
97
+ if (!(0, _apidomDatamodel.isStringElement)(schemaElement.$id)) {
34
98
  return undefined;
35
99
  }
36
- const ancestorsSchemaIdentifiers = schemaElement.meta.get('ancestorsSchemaIdentifiers');
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 { isPrimitiveElement } from '@speclynx/apidom-datamodel';
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
- export const resolveSchema$refField = (retrievalURI, schemaElement) => {
10
- if (typeof schemaElement.$ref === 'undefined') {
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 hash = url.getHash(toValue(schemaElement.$ref));
14
- const ancestorsSchemaIdentifiers = schemaElement.meta.get('ancestorsSchemaIdentifiers');
15
- const $refBaseURI = reduce((acc, uri) => {
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 = (retrievalURI, schemaElement) => {
25
- if (typeof schemaElement.$id === 'undefined') {
81
+ export const resolveSchema$idField = (baseURI, schemaElement) => {
82
+ if (!isStringElement(schemaElement.$id)) {
26
83
  return undefined;
27
84
  }
28
- const ancestorsSchemaIdentifiers = schemaElement.meta.get('ancestorsSchemaIdentifiers');
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
- const $refBaseURI = (0, _util.resolveSchema$refField)(retrievalURI, referencingElement);
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
- const referenceAsSchema = (0, _util.maybeRefractToSchemaElement)(reference.value.result);
694
- referencedElement = (0, _uri.evaluate)(selector, referenceAsSchema);
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
- referencedElement = (0, _apidomJsonPointer.evaluate)(referenceAsSchema, selector);
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
- referencedElement = (0, _$anchor.evaluate)(selector, referenceAsSchema);
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
- referencedElement = (0, _apidomJsonPointer.evaluate)(referenceAsSchema, selector);
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
- if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isSchemaElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
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,