@speclynx/apidom-reference 1.12.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.
Files changed (263) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +202 -0
  3. package/LICENSES/AFL-3.0.txt +182 -0
  4. package/LICENSES/Apache-2.0.txt +202 -0
  5. package/LICENSES/BSD-3-Clause.txt +26 -0
  6. package/LICENSES/MIT.txt +9 -0
  7. package/NOTICE +65 -0
  8. package/README.md +2107 -0
  9. package/dist/167.apidom-reference.browser.js +10 -0
  10. package/dist/167.apidom-reference.browser.min.js +1 -0
  11. package/dist/451.apidom-reference.browser.js +10 -0
  12. package/dist/451.apidom-reference.browser.min.js +1 -0
  13. package/dist/9786785aaddf11f37840fad896531940.wasm +0 -0
  14. package/dist/apidom-reference.browser.js +85376 -0
  15. package/dist/apidom-reference.browser.min.js +1 -0
  16. package/package.json +304 -0
  17. package/src/File.cjs +50 -0
  18. package/src/File.mjs +44 -0
  19. package/src/Reference.cjs +31 -0
  20. package/src/Reference.mjs +27 -0
  21. package/src/ReferenceSet.cjs +60 -0
  22. package/src/ReferenceSet.mjs +57 -0
  23. package/src/bundle/index.cjs +61 -0
  24. package/src/bundle/index.mjs +55 -0
  25. package/src/bundle/strategies/BundleStrategy.cjs +20 -0
  26. package/src/bundle/strategies/BundleStrategy.mjs +16 -0
  27. package/src/bundle/strategies/openapi-3-1/index.cjs +35 -0
  28. package/src/bundle/strategies/openapi-3-1/index.mjs +29 -0
  29. package/src/configuration/empty.cjs +9 -0
  30. package/src/configuration/empty.mjs +1 -0
  31. package/src/configuration/saturated.cjs +95 -0
  32. package/src/configuration/saturated.mjs +87 -0
  33. package/src/dereference/index.cjs +86 -0
  34. package/src/dereference/index.mjs +79 -0
  35. package/src/dereference/strategies/DereferenceStrategy.cjs +20 -0
  36. package/src/dereference/strategies/DereferenceStrategy.mjs +16 -0
  37. package/src/dereference/strategies/apidom/index.cjs +89 -0
  38. package/src/dereference/strategies/apidom/index.mjs +84 -0
  39. package/src/dereference/strategies/apidom/selectors/element-id.cjs +36 -0
  40. package/src/dereference/strategies/apidom/selectors/element-id.mjs +30 -0
  41. package/src/dereference/strategies/apidom/visitor.cjs +165 -0
  42. package/src/dereference/strategies/apidom/visitor.mjs +159 -0
  43. package/src/dereference/strategies/asyncapi-2/index.cjs +100 -0
  44. package/src/dereference/strategies/asyncapi-2/index.mjs +94 -0
  45. package/src/dereference/strategies/asyncapi-2/visitor.cjs +412 -0
  46. package/src/dereference/strategies/asyncapi-2/visitor.mjs +406 -0
  47. package/src/dereference/strategies/openapi-2/index.cjs +102 -0
  48. package/src/dereference/strategies/openapi-2/index.mjs +96 -0
  49. package/src/dereference/strategies/openapi-2/visitor.cjs +530 -0
  50. package/src/dereference/strategies/openapi-2/visitor.mjs +524 -0
  51. package/src/dereference/strategies/openapi-3-0/index.cjs +102 -0
  52. package/src/dereference/strategies/openapi-3-0/index.mjs +96 -0
  53. package/src/dereference/strategies/openapi-3-0/visitor.cjs +519 -0
  54. package/src/dereference/strategies/openapi-3-0/visitor.mjs +513 -0
  55. package/src/dereference/strategies/openapi-3-1/index.cjs +105 -0
  56. package/src/dereference/strategies/openapi-3-1/index.mjs +96 -0
  57. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.cjs +66 -0
  58. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.mjs +55 -0
  59. package/src/dereference/strategies/openapi-3-1/selectors/uri.cjs +50 -0
  60. package/src/dereference/strategies/openapi-3-1/selectors/uri.mjs +42 -0
  61. package/src/dereference/strategies/openapi-3-1/util.cjs +67 -0
  62. package/src/dereference/strategies/openapi-3-1/util.mjs +58 -0
  63. package/src/dereference/strategies/openapi-3-1/visitor.cjs +776 -0
  64. package/src/dereference/strategies/openapi-3-1/visitor.mjs +770 -0
  65. package/src/dereference/util.cjs +31 -0
  66. package/src/dereference/util.mjs +27 -0
  67. package/src/errors/BundleError.cjs +10 -0
  68. package/src/errors/BundleError.mjs +7 -0
  69. package/src/errors/DereferenceError.cjs +10 -0
  70. package/src/errors/DereferenceError.mjs +7 -0
  71. package/src/errors/EvaluationElementIdError.cjs +10 -0
  72. package/src/errors/EvaluationElementIdError.mjs +7 -0
  73. package/src/errors/EvaluationJsonSchema$anchorError.cjs +11 -0
  74. package/src/errors/EvaluationJsonSchema$anchorError.mjs +6 -0
  75. package/src/errors/EvaluationJsonSchemaUriError.cjs +11 -0
  76. package/src/errors/EvaluationJsonSchemaUriError.mjs +6 -0
  77. package/src/errors/InvalidJsonSchema$anchorError.cjs +15 -0
  78. package/src/errors/InvalidJsonSchema$anchorError.mjs +10 -0
  79. package/src/errors/JsonSchema$anchorError.cjs +10 -0
  80. package/src/errors/JsonSchema$anchorError.mjs +7 -0
  81. package/src/errors/JsonSchemaUriError.cjs +10 -0
  82. package/src/errors/JsonSchemaUriError.mjs +7 -0
  83. package/src/errors/MaximumBundleDepthError.cjs +11 -0
  84. package/src/errors/MaximumBundleDepthError.mjs +6 -0
  85. package/src/errors/MaximumDereferenceDepthError.cjs +11 -0
  86. package/src/errors/MaximumDereferenceDepthError.mjs +6 -0
  87. package/src/errors/MaximumResolveDepthError.cjs +11 -0
  88. package/src/errors/MaximumResolveDepthError.mjs +6 -0
  89. package/src/errors/ParseError.cjs +10 -0
  90. package/src/errors/ParseError.mjs +7 -0
  91. package/src/errors/ParserError.cjs +11 -0
  92. package/src/errors/ParserError.mjs +6 -0
  93. package/src/errors/PluginError.cjs +18 -0
  94. package/src/errors/PluginError.mjs +15 -0
  95. package/src/errors/ResolveError.cjs +10 -0
  96. package/src/errors/ResolveError.mjs +7 -0
  97. package/src/errors/ResolverError.cjs +11 -0
  98. package/src/errors/ResolverError.mjs +6 -0
  99. package/src/errors/UnmatchedBundleStrategyError.cjs +11 -0
  100. package/src/errors/UnmatchedBundleStrategyError.mjs +6 -0
  101. package/src/errors/UnmatchedDereferenceStrategyError.cjs +11 -0
  102. package/src/errors/UnmatchedDereferenceStrategyError.mjs +6 -0
  103. package/src/errors/UnmatchedResolveStrategyError.cjs +11 -0
  104. package/src/errors/UnmatchedResolveStrategyError.mjs +6 -0
  105. package/src/errors/UnmatchedResolverError.cjs +11 -0
  106. package/src/errors/UnmatchedResolverError.mjs +6 -0
  107. package/src/index.cjs +142 -0
  108. package/src/index.mjs +101 -0
  109. package/src/options/index.cjs +185 -0
  110. package/src/options/index.mjs +182 -0
  111. package/src/options/util.cjs +24 -0
  112. package/src/options/util.mjs +19 -0
  113. package/src/parse/index.cjs +69 -0
  114. package/src/parse/index.mjs +63 -0
  115. package/src/parse/parsers/Parser.cjs +48 -0
  116. package/src/parse/parsers/Parser.mjs +44 -0
  117. package/src/parse/parsers/api-design-systems-json/index.cjs +55 -0
  118. package/src/parse/parsers/api-design-systems-json/index.mjs +49 -0
  119. package/src/parse/parsers/api-design-systems-yaml/index.cjs +54 -0
  120. package/src/parse/parsers/api-design-systems-yaml/index.mjs +48 -0
  121. package/src/parse/parsers/apidom-json/index.cjs +70 -0
  122. package/src/parse/parsers/apidom-json/index.mjs +64 -0
  123. package/src/parse/parsers/arazzo-json-1/index.cjs +55 -0
  124. package/src/parse/parsers/arazzo-json-1/index.mjs +49 -0
  125. package/src/parse/parsers/arazzo-yaml-1/index.cjs +54 -0
  126. package/src/parse/parsers/arazzo-yaml-1/index.mjs +48 -0
  127. package/src/parse/parsers/asyncapi-json-2/index.cjs +55 -0
  128. package/src/parse/parsers/asyncapi-json-2/index.mjs +49 -0
  129. package/src/parse/parsers/asyncapi-yaml-2/index.cjs +54 -0
  130. package/src/parse/parsers/asyncapi-yaml-2/index.mjs +48 -0
  131. package/src/parse/parsers/binary/index-browser.cjs +56 -0
  132. package/src/parse/parsers/binary/index-browser.mjs +50 -0
  133. package/src/parse/parsers/binary/index-node.cjs +51 -0
  134. package/src/parse/parsers/binary/index-node.mjs +45 -0
  135. package/src/parse/parsers/json/index.cjs +54 -0
  136. package/src/parse/parsers/json/index.mjs +48 -0
  137. package/src/parse/parsers/openapi-json-2/index.cjs +55 -0
  138. package/src/parse/parsers/openapi-json-2/index.mjs +49 -0
  139. package/src/parse/parsers/openapi-json-3-0/index.cjs +55 -0
  140. package/src/parse/parsers/openapi-json-3-0/index.mjs +49 -0
  141. package/src/parse/parsers/openapi-json-3-1/index.cjs +55 -0
  142. package/src/parse/parsers/openapi-json-3-1/index.mjs +49 -0
  143. package/src/parse/parsers/openapi-yaml-2/index.cjs +54 -0
  144. package/src/parse/parsers/openapi-yaml-2/index.mjs +48 -0
  145. package/src/parse/parsers/openapi-yaml-3-0/index.cjs +54 -0
  146. package/src/parse/parsers/openapi-yaml-3-0/index.mjs +48 -0
  147. package/src/parse/parsers/openapi-yaml-3-1/index.cjs +54 -0
  148. package/src/parse/parsers/openapi-yaml-3-1/index.mjs +48 -0
  149. package/src/parse/parsers/yaml-1-2/index.cjs +54 -0
  150. package/src/parse/parsers/yaml-1-2/index.mjs +48 -0
  151. package/src/resolve/index.cjs +67 -0
  152. package/src/resolve/index.mjs +60 -0
  153. package/src/resolve/resolvers/HTTPResolver.cjs +38 -0
  154. package/src/resolve/resolvers/HTTPResolver.mjs +31 -0
  155. package/src/resolve/resolvers/Resolver.cjs +20 -0
  156. package/src/resolve/resolvers/Resolver.mjs +16 -0
  157. package/src/resolve/resolvers/file/index-browser.cjs +24 -0
  158. package/src/resolve/resolvers/file/index-browser.mjs +19 -0
  159. package/src/resolve/resolvers/file/index-node.cjs +49 -0
  160. package/src/resolve/resolvers/file/index-node.mjs +42 -0
  161. package/src/resolve/resolvers/http-axios/index.cjs +80 -0
  162. package/src/resolve/resolvers/http-axios/index.mjs +73 -0
  163. package/src/resolve/strategies/ResolveStrategy.cjs +20 -0
  164. package/src/resolve/strategies/ResolveStrategy.mjs +16 -0
  165. package/src/resolve/strategies/apidom/index.cjs +49 -0
  166. package/src/resolve/strategies/apidom/index.mjs +43 -0
  167. package/src/resolve/strategies/asyncapi-2/index.cjs +49 -0
  168. package/src/resolve/strategies/asyncapi-2/index.mjs +43 -0
  169. package/src/resolve/strategies/openapi-2/index.cjs +49 -0
  170. package/src/resolve/strategies/openapi-2/index.mjs +43 -0
  171. package/src/resolve/strategies/openapi-3-0/index.cjs +49 -0
  172. package/src/resolve/strategies/openapi-3-0/index.mjs +43 -0
  173. package/src/resolve/strategies/openapi-3-1/index.cjs +49 -0
  174. package/src/resolve/strategies/openapi-3-1/index.mjs +43 -0
  175. package/src/resolve/util.cjs +37 -0
  176. package/src/resolve/util.mjs +30 -0
  177. package/src/util/plugins.cjs +44 -0
  178. package/src/util/plugins.mjs +37 -0
  179. package/src/util/url.cjs +288 -0
  180. package/src/util/url.mjs +274 -0
  181. package/types/File.d.ts +24 -0
  182. package/types/Reference.d.ts +23 -0
  183. package/types/ReferenceSet.d.ts +25 -0
  184. package/types/apidom-reference.d.ts +584 -0
  185. package/types/bundle/index.d.ts +7 -0
  186. package/types/bundle/strategies/BundleStrategy.d.ts +19 -0
  187. package/types/bundle/strategies/openapi-3-1/index.d.ts +26 -0
  188. package/types/configuration/empty.d.ts +1 -0
  189. package/types/configuration/saturated.d.ts +1 -0
  190. package/types/dereference/index.d.ts +11 -0
  191. package/types/dereference/strategies/DereferenceStrategy.d.ts +19 -0
  192. package/types/dereference/strategies/apidom/index.d.ts +30 -0
  193. package/types/dereference/strategies/apidom/selectors/element-id.d.ts +11 -0
  194. package/types/dereference/strategies/apidom/visitor.d.ts +32 -0
  195. package/types/dereference/strategies/asyncapi-2/index.d.ts +31 -0
  196. package/types/dereference/strategies/asyncapi-2/visitor.d.ts +43 -0
  197. package/types/dereference/strategies/openapi-2/index.d.ts +32 -0
  198. package/types/dereference/strategies/openapi-2/visitor.d.ts +47 -0
  199. package/types/dereference/strategies/openapi-3-0/index.d.ts +31 -0
  200. package/types/dereference/strategies/openapi-3-0/visitor.d.ts +49 -0
  201. package/types/dereference/strategies/openapi-3-1/index.d.ts +32 -0
  202. package/types/dereference/strategies/openapi-3-1/selectors/$anchor.d.ts +22 -0
  203. package/types/dereference/strategies/openapi-3-1/selectors/uri.d.ts +12 -0
  204. package/types/dereference/strategies/openapi-3-1/util.d.ts +21 -0
  205. package/types/dereference/strategies/openapi-3-1/visitor.d.ts +52 -0
  206. package/types/dereference/util.d.ts +9 -0
  207. package/types/errors/BundleError.d.ts +7 -0
  208. package/types/errors/DereferenceError.d.ts +7 -0
  209. package/types/errors/EvaluationElementIdError.d.ts +7 -0
  210. package/types/errors/EvaluationJsonSchema$anchorError.d.ts +7 -0
  211. package/types/errors/EvaluationJsonSchemaUriError.d.ts +7 -0
  212. package/types/errors/InvalidJsonSchema$anchorError.d.ts +8 -0
  213. package/types/errors/JsonSchema$anchorError.d.ts +7 -0
  214. package/types/errors/JsonSchemaUriError.d.ts +7 -0
  215. package/types/errors/MaximumBundleDepthError.d.ts +7 -0
  216. package/types/errors/MaximumDereferenceDepthError.d.ts +7 -0
  217. package/types/errors/MaximumResolveDepthError.d.ts +7 -0
  218. package/types/errors/ParseError.d.ts +7 -0
  219. package/types/errors/ParserError.d.ts +7 -0
  220. package/types/errors/PluginError.d.ts +12 -0
  221. package/types/errors/ResolveError.d.ts +7 -0
  222. package/types/errors/ResolverError.d.ts +7 -0
  223. package/types/errors/UnmatchedBundleStrategyError.d.ts +7 -0
  224. package/types/errors/UnmatchedDereferenceStrategyError.d.ts +7 -0
  225. package/types/errors/UnmatchedResolveStrategyError.d.ts +7 -0
  226. package/types/errors/UnmatchedResolverError.d.ts +7 -0
  227. package/types/index.d.ts +75 -0
  228. package/types/options/index.d.ts +62 -0
  229. package/types/options/util.d.ts +5 -0
  230. package/types/parse/index.d.ts +7 -0
  231. package/types/parse/parsers/Parser.d.ts +38 -0
  232. package/types/parse/parsers/api-design-systems-json/index.d.ts +21 -0
  233. package/types/parse/parsers/api-design-systems-yaml/index.d.ts +20 -0
  234. package/types/parse/parsers/apidom-json/index.d.ts +24 -0
  235. package/types/parse/parsers/arazzo-json-1/index.d.ts +21 -0
  236. package/types/parse/parsers/arazzo-yaml-1/index.d.ts +20 -0
  237. package/types/parse/parsers/asyncapi-json-2/index.d.ts +21 -0
  238. package/types/parse/parsers/asyncapi-yaml-2/index.d.ts +20 -0
  239. package/types/parse/parsers/binary/index-browser.d.ts +21 -0
  240. package/types/parse/parsers/binary/index-node.d.ts +21 -0
  241. package/types/parse/parsers/json/index.d.ts +20 -0
  242. package/types/parse/parsers/openapi-json-2/index.d.ts +21 -0
  243. package/types/parse/parsers/openapi-json-3-0/index.d.ts +21 -0
  244. package/types/parse/parsers/openapi-json-3-1/index.d.ts +21 -0
  245. package/types/parse/parsers/openapi-yaml-2/index.d.ts +20 -0
  246. package/types/parse/parsers/openapi-yaml-3-0/index.d.ts +20 -0
  247. package/types/parse/parsers/openapi-yaml-3-1/index.d.ts +20 -0
  248. package/types/parse/parsers/yaml-1-2/index.d.ts +20 -0
  249. package/types/resolve/index.d.ts +12 -0
  250. package/types/resolve/resolvers/HTTPResolver.d.ts +22 -0
  251. package/types/resolve/resolvers/Resolver.d.ts +17 -0
  252. package/types/resolve/resolvers/file/index-browser.d.ts +12 -0
  253. package/types/resolve/resolvers/file/index-node.d.ts +20 -0
  254. package/types/resolve/resolvers/http-axios/index.d.ts +30 -0
  255. package/types/resolve/strategies/ResolveStrategy.d.ts +19 -0
  256. package/types/resolve/strategies/apidom/index.d.ts +27 -0
  257. package/types/resolve/strategies/asyncapi-2/index.d.ts +27 -0
  258. package/types/resolve/strategies/openapi-2/index.d.ts +27 -0
  259. package/types/resolve/strategies/openapi-3-0/index.d.ts +27 -0
  260. package/types/resolve/strategies/openapi-3-1/index.d.ts +27 -0
  261. package/types/resolve/util.d.ts +6 -0
  262. package/types/util/plugins.d.ts +14 -0
  263. package/types/util/url.d.ts +106 -0
@@ -0,0 +1,770 @@
1
+ import { propEq, none } from 'ramda';
2
+ import { isUndefined } from 'ramda-adjunct';
3
+ import { isElement, isPrimitiveElement, isStringElement, isMemberElement, isObjectElement, IdentityManager, visit, find, cloneShallow, cloneDeep, toValue, RefElement } from '@speclynx/apidom-core';
4
+ import { ApiDOMError } from '@speclynx/apidom-error';
5
+ import { evaluate as jsonPointerEvaluate, URIFragmentIdentifier } from '@speclynx/apidom-json-pointer';
6
+ import { getNodeType, isReferenceLikeElement, keyMap, ReferenceElement, PathItemElement, OperationElement, SchemaElement, isPathItemElement, isReferenceElement, isSchemaElement, isOperationElement, isBooleanJsonSchemaElement } from '@speclynx/apidom-ns-openapi-3-1';
7
+ import { isAnchor, uriToAnchor, evaluate as $anchorEvaluate } from "./selectors/$anchor.mjs";
8
+ import { evaluate as uriEvaluate } from "./selectors/uri.mjs";
9
+ import MaximumDereferenceDepthError from "../../../errors/MaximumDereferenceDepthError.mjs";
10
+ import MaximumResolveDepthError from "../../../errors/MaximumResolveDepthError.mjs";
11
+ import * as url from "../../../util/url.mjs";
12
+ import parse from "../../../parse/index.mjs";
13
+ import Reference from "../../../Reference.mjs";
14
+ import File from "../../../File.mjs";
15
+ import { resolveSchema$refField, maybeRefractToSchemaElement } from "./util.mjs";
16
+ import { AncestorLineage } from "../../util.mjs";
17
+ import EvaluationJsonSchemaUriError from "../../../errors/EvaluationJsonSchemaUriError.mjs";
18
+ // @ts-ignore
19
+ const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')];
20
+
21
+ // initialize element identity manager
22
+ const identityManager = new IdentityManager();
23
+
24
+ /**
25
+ * Custom mutation replacer.
26
+ * @public
27
+ */
28
+ export const mutationReplacer = (newElement, oldElement, key, parent) => {
29
+ if (isMemberElement(parent)) {
30
+ parent.value = newElement;
31
+ } else if (Array.isArray(parent)) {
32
+ parent[key] = newElement;
33
+ }
34
+ };
35
+
36
+ /**
37
+ * @public
38
+ */
39
+
40
+ /**
41
+ * @public
42
+ */
43
+ class OpenAPI3_1DereferenceVisitor {
44
+ indirections;
45
+ namespace;
46
+ reference;
47
+ options;
48
+ ancestors;
49
+ refractCache;
50
+ constructor({
51
+ reference,
52
+ namespace,
53
+ options,
54
+ indirections = [],
55
+ ancestors = new AncestorLineage(),
56
+ refractCache = new Map()
57
+ }) {
58
+ this.indirections = indirections;
59
+ this.namespace = namespace;
60
+ this.reference = reference;
61
+ this.options = options;
62
+ this.ancestors = new AncestorLineage(...ancestors);
63
+ this.refractCache = refractCache;
64
+ }
65
+ toBaseURI(uri) {
66
+ return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
67
+ }
68
+ async toReference(uri) {
69
+ // detect maximum depth of resolution
70
+ if (this.reference.depth >= this.options.resolve.maxDepth) {
71
+ throw new MaximumResolveDepthError(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`);
72
+ }
73
+ const baseURI = this.toBaseURI(uri);
74
+ const {
75
+ refSet
76
+ } = this.reference;
77
+
78
+ // we've already processed this Reference in past
79
+ if (refSet.has(baseURI)) {
80
+ return refSet.find(propEq(baseURI, 'uri'));
81
+ }
82
+ const parseResult = await parse(url.unsanitize(baseURI), {
83
+ ...this.options,
84
+ parse: {
85
+ ...this.options.parse,
86
+ mediaType: 'text/plain'
87
+ }
88
+ });
89
+
90
+ // register new mutable reference with a refSet
91
+ const mutableReference = new Reference({
92
+ uri: baseURI,
93
+ value: cloneDeep(parseResult),
94
+ depth: this.reference.depth + 1
95
+ });
96
+ refSet.add(mutableReference);
97
+ if (this.options.dereference.immutable) {
98
+ // register new immutable reference with a refSet
99
+ const immutableReference = new Reference({
100
+ uri: `immutable://${baseURI}`,
101
+ value: parseResult,
102
+ depth: this.reference.depth + 1
103
+ });
104
+ refSet.add(immutableReference);
105
+ }
106
+ return mutableReference;
107
+ }
108
+ toAncestorLineage(ancestors) {
109
+ /**
110
+ * Compute full ancestors lineage.
111
+ * Ancestors are flatten to unwrap all Element instances.
112
+ */
113
+ const directAncestors = new Set(ancestors.filter(isElement));
114
+ const ancestorsLineage = new AncestorLineage(...this.ancestors, directAncestors);
115
+ return [ancestorsLineage, directAncestors];
116
+ }
117
+ async ReferenceElement(referencingElement, key, parent, path, ancestors, link) {
118
+ // skip current referencing element as it's already been access
119
+ if (this.indirections.includes(referencingElement)) {
120
+ return false;
121
+ }
122
+ const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
123
+ const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref));
124
+ const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
125
+ const isExternalReference = !isInternalReference;
126
+
127
+ // ignore resolving internal Reference Objects
128
+ if (!this.options.resolve.internal && isInternalReference) {
129
+ // skip traversing this reference element and all it's child elements
130
+ return false;
131
+ }
132
+ // ignore resolving external Reference Objects
133
+ if (!this.options.resolve.external && isExternalReference) {
134
+ // skip traversing this reference element and all it's child elements
135
+ return false;
136
+ }
137
+ const reference = await this.toReference(toValue(referencingElement.$ref));
138
+ const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
139
+ this.indirections.push(referencingElement);
140
+ const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI);
141
+
142
+ // possibly non-semantic fragment
143
+ let referencedElement = jsonPointerEvaluate(reference.value.result, jsonPointer);
144
+ referencedElement.id = identityManager.identify(referencedElement);
145
+
146
+ // applying semantics to a fragment
147
+ if (isPrimitiveElement(referencedElement)) {
148
+ const referencedElementType = toValue(referencingElement.meta.get('referenced-element'));
149
+ const cacheKey = `${referencedElementType}-${toValue(identityManager.identify(referencedElement))}`;
150
+ if (this.refractCache.has(cacheKey)) {
151
+ referencedElement = this.refractCache.get(cacheKey);
152
+ } else if (isReferenceLikeElement(referencedElement)) {
153
+ // handling indirect references
154
+ referencedElement = ReferenceElement.refract(referencedElement);
155
+ referencedElement.setMetaProperty('referenced-element', referencedElementType);
156
+ this.refractCache.set(cacheKey, referencedElement);
157
+ } else {
158
+ // handling direct references
159
+ const ElementClass = this.namespace.getElementClass(referencedElementType);
160
+ referencedElement = ElementClass.refract(referencedElement);
161
+ this.refractCache.set(cacheKey, referencedElement);
162
+ }
163
+ }
164
+
165
+ // detect direct or indirect reference
166
+ if (referencingElement === referencedElement) {
167
+ throw new ApiDOMError('Recursive Reference Object detected');
168
+ }
169
+
170
+ // detect maximum depth of dereferencing
171
+ if (this.indirections.length > this.options.dereference.maxDepth) {
172
+ throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
173
+ }
174
+
175
+ // detect second deep dive into the same fragment and avoid it
176
+ if (ancestorsLineage.includes(referencedElement)) {
177
+ reference.refSet.circular = true;
178
+ if (this.options.dereference.circular === 'error') {
179
+ throw new ApiDOMError('Circular reference detected');
180
+ } else if (this.options.dereference.circular === 'replace') {
181
+ const refElement = new RefElement(referencedElement.id, {
182
+ type: 'reference',
183
+ uri: reference.uri,
184
+ $ref: toValue(referencingElement.$ref)
185
+ });
186
+ const replacer = this.options.dereference.strategyOpts['openapi-3-1']?.circularReplacer ?? this.options.dereference.circularReplacer;
187
+ const replacement = replacer(refElement);
188
+ link.replaceWith(replacement, mutationReplacer);
189
+ return !parent ? replacement : false;
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Dive deep into the fragment.
195
+ *
196
+ * Cases to consider:
197
+ * 1. We're crossing document boundary
198
+ * 2. Fragment is from non-root document
199
+ * 3. Fragment is a Reference Object. We need to follow it to get the eventual value
200
+ * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
201
+ */
202
+ const isNonRootDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
203
+ const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
204
+ if ((isExternalReference || isNonRootDocument || isReferenceElement(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
205
+ // append referencing reference to ancestors lineage
206
+ directAncestors.add(referencingElement);
207
+ const visitor = new OpenAPI3_1DereferenceVisitor({
208
+ reference,
209
+ namespace: this.namespace,
210
+ indirections: [...this.indirections],
211
+ options: this.options,
212
+ refractCache: this.refractCache,
213
+ ancestors: ancestorsLineage
214
+ });
215
+ referencedElement = await visitAsync(referencedElement, visitor, {
216
+ keyMap,
217
+ nodeTypeGetter: getNodeType
218
+ });
219
+
220
+ // remove referencing reference from ancestors lineage
221
+ directAncestors.delete(referencingElement);
222
+ }
223
+ this.indirections.pop();
224
+
225
+ /**
226
+ * Creating a new version of referenced element to avoid modifying the original one.
227
+ */
228
+ const mergedElement = cloneShallow(referencedElement);
229
+ // assign unique id to merged element
230
+ mergedElement.setMetaProperty('id', identityManager.generateId());
231
+ // annotate fragment with info about original Reference element
232
+ mergedElement.setMetaProperty('ref-fields', {
233
+ $ref: toValue(referencingElement.$ref),
234
+ // @ts-ignore
235
+ description: toValue(referencingElement.description),
236
+ // @ts-ignore
237
+ summary: toValue(referencingElement.summary)
238
+ });
239
+ // annotate fragment with info about origin
240
+ mergedElement.setMetaProperty('ref-origin', reference.uri);
241
+ // annotate fragment with info about referencing element
242
+ mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
243
+
244
+ // override description and summary (outer has higher priority then inner)
245
+ if (isObjectElement(referencedElement) && isObjectElement(mergedElement)) {
246
+ if (referencingElement.hasKey('description') && 'description' in referencedElement) {
247
+ mergedElement.remove('description');
248
+ mergedElement.set('description', referencingElement.get('description'));
249
+ }
250
+ if (referencingElement.hasKey('summary') && 'summary' in referencedElement) {
251
+ mergedElement.remove('summary');
252
+ mergedElement.set('summary', referencingElement.get('summary'));
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Transclude referencing element with merged referenced element.
258
+ */
259
+ link.replaceWith(mergedElement, mutationReplacer);
260
+
261
+ /**
262
+ * We're at the root of the tree, so we're just replacing the entire tree.
263
+ */
264
+ return !parent ? mergedElement : false;
265
+ }
266
+ async PathItemElement(referencingElement, key, parent, path, ancestors, link) {
267
+ // ignore PathItemElement without $ref field
268
+ if (!isStringElement(referencingElement.$ref)) {
269
+ return undefined;
270
+ }
271
+
272
+ // skip current referencing element as it's already been access
273
+ if (this.indirections.includes(referencingElement)) {
274
+ return false;
275
+ }
276
+ const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
277
+ const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref));
278
+ const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
279
+ const isExternalReference = !isInternalReference;
280
+
281
+ // ignore resolving external Path Item Objects
282
+ if (!this.options.resolve.internal && isInternalReference) {
283
+ // skip traversing this Path Item element but traverse all it's child elements
284
+ return undefined;
285
+ }
286
+ // ignore resolving external Path Item Objects
287
+ if (!this.options.resolve.external && isExternalReference) {
288
+ // skip traversing this Path Item element but traverse all it's child elements
289
+ return undefined;
290
+ }
291
+ const reference = await this.toReference(toValue(referencingElement.$ref));
292
+ const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
293
+ this.indirections.push(referencingElement);
294
+ const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI);
295
+
296
+ // possibly non-semantic referenced element
297
+ let referencedElement = jsonPointerEvaluate(reference.value.result, jsonPointer);
298
+ referencedElement.id = identityManager.identify(referencedElement);
299
+
300
+ /**
301
+ * Applying semantics to a referenced element if semantics are missing.
302
+ */
303
+ if (isPrimitiveElement(referencedElement)) {
304
+ const cacheKey = `path-item-${toValue(identityManager.identify(referencedElement))}`;
305
+ if (this.refractCache.has(cacheKey)) {
306
+ referencedElement = this.refractCache.get(cacheKey);
307
+ } else {
308
+ referencedElement = PathItemElement.refract(referencedElement);
309
+ this.refractCache.set(cacheKey, referencedElement);
310
+ }
311
+ }
312
+
313
+ // detect direct or indirect reference
314
+ if (referencingElement === referencedElement) {
315
+ throw new ApiDOMError('Recursive Path Item Object reference detected');
316
+ }
317
+
318
+ // detect maximum depth of dereferencing
319
+ if (this.indirections.length > this.options.dereference.maxDepth) {
320
+ throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
321
+ }
322
+
323
+ // detect second deep dive into the same fragment and avoid it
324
+ if (ancestorsLineage.includes(referencedElement)) {
325
+ reference.refSet.circular = true;
326
+ if (this.options.dereference.circular === 'error') {
327
+ throw new ApiDOMError('Circular reference detected');
328
+ } else if (this.options.dereference.circular === 'replace') {
329
+ const refElement = new RefElement(referencedElement.id, {
330
+ type: 'path-item',
331
+ uri: reference.uri,
332
+ $ref: toValue(referencingElement.$ref)
333
+ });
334
+ const replacer = this.options.dereference.strategyOpts['openapi-3-1']?.circularReplacer ?? this.options.dereference.circularReplacer;
335
+ const replacement = replacer(refElement);
336
+ link.replaceWith(replacement, mutationReplacer);
337
+ return !parent ? replacement : false;
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Dive deep into the fragment.
343
+ *
344
+ * Cases to consider:
345
+ * 1. We're crossing document boundary
346
+ * 2. Fragment is from non-root document
347
+ * 3. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
348
+ * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
349
+ */
350
+ const isNonRootDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
351
+ const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
352
+ if ((isExternalReference || isNonRootDocument || isPathItemElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
353
+ // append referencing reference to ancestors lineage
354
+ directAncestors.add(referencingElement);
355
+ const visitor = new OpenAPI3_1DereferenceVisitor({
356
+ reference,
357
+ namespace: this.namespace,
358
+ indirections: [...this.indirections],
359
+ options: this.options,
360
+ refractCache: this.refractCache,
361
+ ancestors: ancestorsLineage
362
+ });
363
+ referencedElement = await visitAsync(referencedElement, visitor, {
364
+ keyMap,
365
+ nodeTypeGetter: getNodeType
366
+ });
367
+
368
+ // remove referencing reference from ancestors lineage
369
+ directAncestors.delete(referencingElement);
370
+ }
371
+ this.indirections.pop();
372
+
373
+ /**
374
+ * Creating a new version of Path Item by merging fields from referenced Path Item with referencing one.
375
+ */
376
+ if (isPathItemElement(referencedElement)) {
377
+ const mergedElement = new PathItemElement([...referencedElement.content], cloneDeep(referencedElement.meta), cloneDeep(referencedElement.attributes));
378
+ // assign unique id to merged element
379
+ mergedElement.setMetaProperty('id', identityManager.generateId());
380
+ // existing keywords from referencing PathItemElement overrides ones from referenced element
381
+ referencingElement.forEach((value, keyElement, item) => {
382
+ mergedElement.remove(toValue(keyElement));
383
+ mergedElement.content.push(item);
384
+ });
385
+ mergedElement.remove('$ref');
386
+
387
+ // annotate referenced element with info about original referencing element
388
+ mergedElement.setMetaProperty('ref-fields', {
389
+ $ref: toValue(referencingElement.$ref)
390
+ });
391
+ // annotate referenced element with info about origin
392
+ mergedElement.setMetaProperty('ref-origin', reference.uri);
393
+ // annotate fragment with info about referencing element
394
+ mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
395
+ referencedElement = mergedElement;
396
+ }
397
+
398
+ /**
399
+ * Transclude referencing element with merged referenced element.
400
+ */
401
+ link.replaceWith(referencedElement, mutationReplacer);
402
+
403
+ /**
404
+ * We're at the root of the tree, so we're just replacing the entire tree.
405
+ */
406
+ return !parent ? referencedElement : undefined;
407
+ }
408
+ async LinkElement(linkElement, key, parent, path, ancestors, link) {
409
+ // ignore LinkElement without operationRef or operationId field
410
+ if (!isStringElement(linkElement.operationRef) && !isStringElement(linkElement.operationId)) {
411
+ return undefined;
412
+ }
413
+
414
+ // operationRef and operationId fields are mutually exclusive
415
+ if (isStringElement(linkElement.operationRef) && isStringElement(linkElement.operationId)) {
416
+ throw new ApiDOMError('LinkElement operationRef and operationId fields are mutually exclusive.');
417
+ }
418
+ let operationElement;
419
+ if (isStringElement(linkElement.operationRef)) {
420
+ // possibly non-semantic referenced element
421
+ const jsonPointer = URIFragmentIdentifier.fromURIReference(toValue(linkElement.operationRef));
422
+ const retrievalURI = this.toBaseURI(toValue(linkElement.operationRef));
423
+ const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
424
+ const isExternalReference = !isInternalReference;
425
+
426
+ // ignore resolving internal Operation Object reference
427
+ if (!this.options.resolve.internal && isInternalReference) {
428
+ // skip traversing this Link element but traverse all it's child elements
429
+ return undefined;
430
+ }
431
+ // ignore resolving external Operation Object reference
432
+ if (!this.options.resolve.external && isExternalReference) {
433
+ // skip traversing this Link element but traverse all it's child elements
434
+ return undefined;
435
+ }
436
+ const reference = await this.toReference(toValue(linkElement.operationRef));
437
+ operationElement = jsonPointerEvaluate(reference.value.result, jsonPointer);
438
+ // applying semantics to a referenced element
439
+ if (isPrimitiveElement(operationElement)) {
440
+ const cacheKey = `operation-${toValue(identityManager.identify(operationElement))}`;
441
+ if (this.refractCache.has(cacheKey)) {
442
+ operationElement = this.refractCache.get(cacheKey);
443
+ } else {
444
+ operationElement = OperationElement.refract(operationElement);
445
+ this.refractCache.set(cacheKey, operationElement);
446
+ }
447
+ }
448
+ // create shallow clone to be able to annotate with metadata
449
+ operationElement = cloneShallow(operationElement);
450
+ // annotate operation element with info about origin
451
+ operationElement.setMetaProperty('ref-origin', reference.uri);
452
+ const linkElementCopy = cloneShallow(linkElement);
453
+ linkElementCopy.operationRef?.meta.set('operation', operationElement);
454
+
455
+ /**
456
+ * Transclude Link Object containing Operation Object in its meta.
457
+ */
458
+ link.replaceWith(linkElementCopy, mutationReplacer);
459
+
460
+ /**
461
+ * We're at the root of the tree, so we're just replacing the entire tree.
462
+ */
463
+ return !parent ? linkElementCopy : undefined;
464
+ }
465
+ if (isStringElement(linkElement.operationId)) {
466
+ const operationId = toValue(linkElement.operationId);
467
+ const reference = await this.toReference(url.unsanitize(this.reference.uri));
468
+ operationElement = find(e => isOperationElement(e) && isElement(e.operationId) && e.operationId.equals(operationId), reference.value.result);
469
+ // OperationElement not found by its operationId
470
+ if (isUndefined(operationElement)) {
471
+ throw new ApiDOMError(`OperationElement(operationId=${operationId}) not found.`);
472
+ }
473
+ const linkElementCopy = cloneShallow(linkElement);
474
+ linkElementCopy.operationId?.meta.set('operation', operationElement);
475
+
476
+ /**
477
+ * Transclude Link Object containing Operation Object in its meta.
478
+ */
479
+ link.replaceWith(linkElementCopy, mutationReplacer);
480
+
481
+ /**
482
+ * We're at the root of the tree, so we're just replacing the entire tree.
483
+ */
484
+ return !parent ? linkElementCopy : undefined;
485
+ }
486
+ return undefined;
487
+ }
488
+ async ExampleElement(exampleElement, key, parent, path, ancestors, link) {
489
+ // ignore ExampleElement without externalValue field
490
+ if (!isStringElement(exampleElement.externalValue)) {
491
+ return undefined;
492
+ }
493
+
494
+ // value and externalValue fields are mutually exclusive
495
+ if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) {
496
+ throw new ApiDOMError('ExampleElement value and externalValue fields are mutually exclusive.');
497
+ }
498
+ const retrievalURI = this.toBaseURI(toValue(exampleElement.externalValue));
499
+ const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
500
+ const isExternalReference = !isInternalReference;
501
+
502
+ // ignore resolving internal Example Objects
503
+ if (!this.options.resolve.internal && isInternalReference) {
504
+ // skip traversing this Example element but traverse all it's child elements
505
+ return undefined;
506
+ }
507
+ // ignore resolving external Example Objects
508
+ if (!this.options.resolve.external && isExternalReference) {
509
+ // skip traversing this Example element but traverse all it's child elements
510
+ return undefined;
511
+ }
512
+ const reference = await this.toReference(toValue(exampleElement.externalValue));
513
+
514
+ // shallow clone of the referenced element
515
+ const valueElement = cloneShallow(reference.value.result);
516
+ // annotate operation element with info about origin
517
+ valueElement.setMetaProperty('ref-origin', reference.uri);
518
+ const exampleElementCopy = cloneShallow(exampleElement);
519
+ exampleElementCopy.value = valueElement;
520
+
521
+ /**
522
+ * Transclude Example Object containing external value.
523
+ */
524
+ link.replaceWith(exampleElementCopy, mutationReplacer);
525
+
526
+ /**
527
+ * We're at the root of the tree, so we're just replacing the entire tree.
528
+ */
529
+ return !parent ? exampleElementCopy : undefined;
530
+ }
531
+ async SchemaElement(referencingElement, key, parent, path, ancestors, link) {
532
+ // skip current referencing schema as $ref keyword was not defined
533
+ if (!isStringElement(referencingElement.$ref)) {
534
+ return undefined;
535
+ }
536
+
537
+ // skip current referencing element as it's already been access
538
+ if (this.indirections.includes(referencingElement)) {
539
+ return false;
540
+ }
541
+ const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
542
+
543
+ // compute baseURI using rules around $id and $ref keywords
544
+ let reference = await this.toReference(url.unsanitize(this.reference.uri));
545
+ let {
546
+ uri: retrievalURI
547
+ } = reference;
548
+ const $refBaseURI = resolveSchema$refField(retrievalURI, referencingElement);
549
+ const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
550
+ const file = new File({
551
+ uri: $refBaseURIStrippedHash
552
+ });
553
+ const isUnknownURI = none(r => r.canRead(file), this.options.resolve.resolvers);
554
+ const isURL = !isUnknownURI;
555
+ let isInternalReference = url.stripHash(this.reference.uri) === $refBaseURI;
556
+ let isExternalReference = !isInternalReference;
557
+ this.indirections.push(referencingElement);
558
+
559
+ // determining reference, proper evaluation and selection mechanism
560
+ let referencedElement;
561
+ try {
562
+ if (isUnknownURI || isURL) {
563
+ // we're dealing with canonical URI or URL with possible fragment
564
+ retrievalURI = this.toBaseURI($refBaseURI);
565
+ const selector = $refBaseURI;
566
+ const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
567
+ referencedElement = uriEvaluate(selector, referenceAsSchema);
568
+ referencedElement = maybeRefractToSchemaElement(referencedElement);
569
+ referencedElement.id = identityManager.identify(referencedElement);
570
+
571
+ // ignore resolving internal Schema Objects
572
+ if (!this.options.resolve.internal && isInternalReference) {
573
+ // skip traversing this schema element but traverse all it's child elements
574
+ return undefined;
575
+ }
576
+ // ignore resolving external Schema Objects
577
+ if (!this.options.resolve.external && isExternalReference) {
578
+ // skip traversing this schema element but traverse all it's child elements
579
+ return undefined;
580
+ }
581
+ } else {
582
+ // we're assuming here that we're dealing with JSON Pointer here
583
+ retrievalURI = this.toBaseURI($refBaseURI);
584
+ isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
585
+ isExternalReference = !isInternalReference;
586
+
587
+ // ignore resolving internal Schema Objects
588
+ if (!this.options.resolve.internal && isInternalReference) {
589
+ // skip traversing this schema element but traverse all it's child elements
590
+ return undefined;
591
+ }
592
+ // ignore resolving external Schema Objects
593
+ if (!this.options.resolve.external && isExternalReference) {
594
+ // skip traversing this schema element but traverse all it's child elements
595
+ return undefined;
596
+ }
597
+ reference = await this.toReference(url.unsanitize($refBaseURI));
598
+ const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
599
+ const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
600
+ referencedElement = jsonPointerEvaluate(referenceAsSchema, selector);
601
+ referencedElement = maybeRefractToSchemaElement(referencedElement);
602
+ referencedElement.id = identityManager.identify(referencedElement);
603
+ }
604
+ } catch (error) {
605
+ /**
606
+ * No SchemaElement($id=URL) was not found, so we're going to try to resolve
607
+ * the URL and assume the returned response is a JSON Schema.
608
+ */
609
+ if (isURL && error instanceof EvaluationJsonSchemaUriError) {
610
+ if (isAnchor(uriToAnchor($refBaseURI))) {
611
+ // we're dealing with JSON Schema $anchor here
612
+ isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
613
+ isExternalReference = !isInternalReference;
614
+
615
+ // ignore resolving internal Schema Objects
616
+ if (!this.options.resolve.internal && isInternalReference) {
617
+ // skip traversing this schema element but traverse all it's child elements
618
+ return undefined;
619
+ }
620
+ // ignore resolving external Schema Objects
621
+ if (!this.options.resolve.external && isExternalReference) {
622
+ // skip traversing this schema element but traverse all it's child elements
623
+ return undefined;
624
+ }
625
+ reference = await this.toReference(url.unsanitize($refBaseURI));
626
+ const selector = uriToAnchor($refBaseURI);
627
+ const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
628
+ referencedElement = $anchorEvaluate(selector, referenceAsSchema);
629
+ referencedElement = maybeRefractToSchemaElement(referencedElement);
630
+ referencedElement.id = identityManager.identify(referencedElement);
631
+ } else {
632
+ // we're assuming here that we're dealing with JSON Pointer here
633
+ retrievalURI = this.toBaseURI($refBaseURI);
634
+ isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
635
+ isExternalReference = !isInternalReference;
636
+
637
+ // ignore resolving internal Schema Objects
638
+ if (!this.options.resolve.internal && isInternalReference) {
639
+ // skip traversing this schema element but traverse all it's child elements
640
+ return undefined;
641
+ }
642
+ // ignore resolving external Schema Objects
643
+ if (!this.options.resolve.external && isExternalReference) {
644
+ // skip traversing this schema element but traverse all it's child elements
645
+ return undefined;
646
+ }
647
+ reference = await this.toReference(url.unsanitize($refBaseURI));
648
+ const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
649
+ const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
650
+ referencedElement = jsonPointerEvaluate(referenceAsSchema, selector);
651
+ referencedElement = maybeRefractToSchemaElement(referencedElement);
652
+ referencedElement.id = identityManager.identify(referencedElement);
653
+ }
654
+ } else {
655
+ throw error;
656
+ }
657
+ }
658
+
659
+ // detect direct or indirect reference
660
+ if (referencingElement === referencedElement) {
661
+ throw new ApiDOMError('Recursive Schema Object reference detected');
662
+ }
663
+
664
+ // detect maximum depth of dereferencing
665
+ if (this.indirections.length > this.options.dereference.maxDepth) {
666
+ throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
667
+ }
668
+
669
+ // detect second deep dive into the same fragment and avoid it
670
+ if (ancestorsLineage.includes(referencedElement)) {
671
+ reference.refSet.circular = true;
672
+ if (this.options.dereference.circular === 'error') {
673
+ throw new ApiDOMError('Circular reference detected');
674
+ } else if (this.options.dereference.circular === 'replace') {
675
+ const refElement = new RefElement(referencedElement.id, {
676
+ type: 'json-schema',
677
+ uri: reference.uri,
678
+ $ref: toValue(referencingElement.$ref)
679
+ });
680
+ const replacer = this.options.dereference.strategyOpts['openapi-3-1']?.circularReplacer ?? this.options.dereference.circularReplacer;
681
+ const replacement = replacer(refElement);
682
+ link.replaceWith(replacement, mutationReplacer);
683
+ return !parent ? replacement : false;
684
+ }
685
+ }
686
+
687
+ /**
688
+ * Dive deep into the fragment.
689
+ *
690
+ * Cases to consider:
691
+ * 1. We're crossing document boundary
692
+ * 2. Fragment is from non-root document
693
+ * 3. Fragment is a Schema Object with $ref field. We need to follow it to get the eventual value
694
+ * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
695
+ */
696
+ const isNonRootDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
697
+ const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
698
+ if ((isExternalReference || isNonRootDocument || isSchemaElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
699
+ // append referencing reference to ancestors lineage
700
+ directAncestors.add(referencingElement);
701
+ const visitor = new OpenAPI3_1DereferenceVisitor({
702
+ reference,
703
+ namespace: this.namespace,
704
+ indirections: [...this.indirections],
705
+ options: this.options,
706
+ refractCache: this.refractCache,
707
+ ancestors: ancestorsLineage
708
+ });
709
+ referencedElement = await visitAsync(referencedElement, visitor, {
710
+ keyMap,
711
+ nodeTypeGetter: getNodeType
712
+ });
713
+
714
+ // remove referencing reference from ancestors lineage
715
+ directAncestors.delete(referencingElement);
716
+ }
717
+ this.indirections.pop();
718
+
719
+ // Boolean JSON Schemas
720
+ if (isBooleanJsonSchemaElement(referencedElement)) {
721
+ const booleanJsonSchemaElement = cloneDeep(referencedElement);
722
+ // assign unique id to merged element
723
+ booleanJsonSchemaElement.setMetaProperty('id', identityManager.generateId());
724
+ // annotate referenced element with info about original referencing element
725
+ booleanJsonSchemaElement.setMetaProperty('ref-fields', {
726
+ $ref: toValue(referencingElement.$ref)
727
+ });
728
+ // annotate referenced element with info about origin
729
+ booleanJsonSchemaElement.setMetaProperty('ref-origin', reference.uri);
730
+ // annotate fragment with info about referencing element
731
+ booleanJsonSchemaElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
732
+ link.replaceWith(booleanJsonSchemaElement, mutationReplacer);
733
+ return !parent ? booleanJsonSchemaElement : false;
734
+ }
735
+
736
+ /**
737
+ * Creating a new version of Schema Object by merging fields from referenced Schema Object with referencing one.
738
+ */
739
+ if (isSchemaElement(referencedElement)) {
740
+ const mergedElement = new SchemaElement([...referencedElement.content], cloneDeep(referencedElement.meta), cloneDeep(referencedElement.attributes));
741
+ // assign unique id to merged element
742
+ mergedElement.setMetaProperty('id', identityManager.generateId());
743
+ // existing keywords from referencing schema overrides ones from referenced schema
744
+ referencingElement.forEach((value, keyElement, item) => {
745
+ mergedElement.remove(toValue(keyElement));
746
+ mergedElement.content.push(item);
747
+ });
748
+ mergedElement.remove('$ref');
749
+ // annotate referenced element with info about original referencing element
750
+ mergedElement.setMetaProperty('ref-fields', {
751
+ $ref: toValue(referencingElement.$ref)
752
+ });
753
+ // annotate fragment with info about origin
754
+ mergedElement.setMetaProperty('ref-origin', reference.uri);
755
+ // annotate fragment with info about referencing element
756
+ mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
757
+ referencedElement = mergedElement;
758
+ }
759
+ /**
760
+ * Transclude referencing element with merged referenced element.
761
+ */
762
+ link.replaceWith(referencedElement, mutationReplacer);
763
+
764
+ /**
765
+ * We're at the root of the tree, so we're just replacing the entire tree.
766
+ */
767
+ return !parent ? referencedElement : undefined;
768
+ }
769
+ }
770
+ export default OpenAPI3_1DereferenceVisitor;