@speclynx/apidom-reference 3.2.0 → 4.0.0

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 (192) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/apidom-reference.browser.js +162 -127
  3. package/dist/apidom-reference.browser.min.js +1 -1
  4. package/package.json +25 -25
  5. package/types/dereference/strategies/apidom/visitor.d.ts +1 -1
  6. package/types/dereference/strategies/arazzo-1/visitor.d.ts +1 -1
  7. package/types/dereference/strategies/asyncapi-2/visitor.d.ts +1 -1
  8. package/types/dereference/strategies/openapi-2/visitor.d.ts +1 -1
  9. package/types/dereference/strategies/openapi-3-0/visitor.d.ts +1 -1
  10. package/types/dereference/strategies/openapi-3-1/visitor.d.ts +1 -1
  11. package/src/File.cjs +0 -50
  12. package/src/File.mjs +0 -44
  13. package/src/Reference.cjs +0 -31
  14. package/src/Reference.mjs +0 -27
  15. package/src/ReferenceSet.cjs +0 -60
  16. package/src/ReferenceSet.mjs +0 -57
  17. package/src/bundle/index.cjs +0 -61
  18. package/src/bundle/index.mjs +0 -55
  19. package/src/bundle/strategies/BundleStrategy.cjs +0 -20
  20. package/src/bundle/strategies/BundleStrategy.mjs +0 -16
  21. package/src/bundle/strategies/openapi-3-1/index.cjs +0 -35
  22. package/src/bundle/strategies/openapi-3-1/index.mjs +0 -29
  23. package/src/configuration/empty.cjs +0 -9
  24. package/src/configuration/empty.mjs +0 -1
  25. package/src/configuration/saturated.cjs +0 -88
  26. package/src/configuration/saturated.mjs +0 -80
  27. package/src/dereference/index.cjs +0 -90
  28. package/src/dereference/index.mjs +0 -83
  29. package/src/dereference/strategies/DereferenceStrategy.cjs +0 -20
  30. package/src/dereference/strategies/DereferenceStrategy.mjs +0 -16
  31. package/src/dereference/strategies/apidom/index.cjs +0 -89
  32. package/src/dereference/strategies/apidom/index.mjs +0 -83
  33. package/src/dereference/strategies/apidom/selectors/element-id.cjs +0 -47
  34. package/src/dereference/strategies/apidom/selectors/element-id.mjs +0 -41
  35. package/src/dereference/strategies/apidom/visitor.cjs +0 -259
  36. package/src/dereference/strategies/apidom/visitor.mjs +0 -252
  37. package/src/dereference/strategies/arazzo-1/index.cjs +0 -109
  38. package/src/dereference/strategies/arazzo-1/index.mjs +0 -100
  39. package/src/dereference/strategies/arazzo-1/selectors/$anchor.cjs +0 -12
  40. package/src/dereference/strategies/arazzo-1/selectors/$anchor.mjs +0 -1
  41. package/src/dereference/strategies/arazzo-1/selectors/uri.cjs +0 -8
  42. package/src/dereference/strategies/arazzo-1/selectors/uri.mjs +0 -1
  43. package/src/dereference/strategies/arazzo-1/source-descriptions.cjs +0 -248
  44. package/src/dereference/strategies/arazzo-1/source-descriptions.mjs +0 -243
  45. package/src/dereference/strategies/arazzo-1/util.cjs +0 -37
  46. package/src/dereference/strategies/arazzo-1/util.mjs +0 -29
  47. package/src/dereference/strategies/arazzo-1/visitor.cjs +0 -501
  48. package/src/dereference/strategies/arazzo-1/visitor.mjs +0 -494
  49. package/src/dereference/strategies/asyncapi-2/index.cjs +0 -94
  50. package/src/dereference/strategies/asyncapi-2/index.mjs +0 -88
  51. package/src/dereference/strategies/asyncapi-2/visitor.cjs +0 -495
  52. package/src/dereference/strategies/asyncapi-2/visitor.mjs +0 -488
  53. package/src/dereference/strategies/openapi-2/index.cjs +0 -96
  54. package/src/dereference/strategies/openapi-2/index.mjs +0 -90
  55. package/src/dereference/strategies/openapi-2/visitor.cjs +0 -623
  56. package/src/dereference/strategies/openapi-2/visitor.mjs +0 -616
  57. package/src/dereference/strategies/openapi-3-0/index.cjs +0 -96
  58. package/src/dereference/strategies/openapi-3-0/index.mjs +0 -90
  59. package/src/dereference/strategies/openapi-3-0/visitor.cjs +0 -615
  60. package/src/dereference/strategies/openapi-3-0/visitor.mjs +0 -608
  61. package/src/dereference/strategies/openapi-3-1/index.cjs +0 -99
  62. package/src/dereference/strategies/openapi-3-1/index.mjs +0 -90
  63. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.cjs +0 -67
  64. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.mjs +0 -56
  65. package/src/dereference/strategies/openapi-3-1/selectors/uri.cjs +0 -50
  66. package/src/dereference/strategies/openapi-3-1/selectors/uri.mjs +0 -42
  67. package/src/dereference/strategies/openapi-3-1/util.cjs +0 -68
  68. package/src/dereference/strategies/openapi-3-1/util.mjs +0 -59
  69. package/src/dereference/strategies/openapi-3-1/visitor.cjs +0 -867
  70. package/src/dereference/strategies/openapi-3-1/visitor.mjs +0 -860
  71. package/src/dereference/util.cjs +0 -31
  72. package/src/dereference/util.mjs +0 -27
  73. package/src/errors/BundleError.cjs +0 -10
  74. package/src/errors/BundleError.mjs +0 -7
  75. package/src/errors/DereferenceError.cjs +0 -10
  76. package/src/errors/DereferenceError.mjs +0 -7
  77. package/src/errors/EvaluationElementIdError.cjs +0 -10
  78. package/src/errors/EvaluationElementIdError.mjs +0 -7
  79. package/src/errors/EvaluationJsonSchema$anchorError.cjs +0 -11
  80. package/src/errors/EvaluationJsonSchema$anchorError.mjs +0 -6
  81. package/src/errors/EvaluationJsonSchemaUriError.cjs +0 -11
  82. package/src/errors/EvaluationJsonSchemaUriError.mjs +0 -6
  83. package/src/errors/InvalidJsonSchema$anchorError.cjs +0 -15
  84. package/src/errors/InvalidJsonSchema$anchorError.mjs +0 -10
  85. package/src/errors/JsonSchema$anchorError.cjs +0 -10
  86. package/src/errors/JsonSchema$anchorError.mjs +0 -7
  87. package/src/errors/JsonSchemaUriError.cjs +0 -10
  88. package/src/errors/JsonSchemaUriError.mjs +0 -7
  89. package/src/errors/MaximumBundleDepthError.cjs +0 -11
  90. package/src/errors/MaximumBundleDepthError.mjs +0 -6
  91. package/src/errors/MaximumDereferenceDepthError.cjs +0 -11
  92. package/src/errors/MaximumDereferenceDepthError.mjs +0 -6
  93. package/src/errors/MaximumResolveDepthError.cjs +0 -11
  94. package/src/errors/MaximumResolveDepthError.mjs +0 -6
  95. package/src/errors/ParseError.cjs +0 -10
  96. package/src/errors/ParseError.mjs +0 -7
  97. package/src/errors/ParserError.cjs +0 -11
  98. package/src/errors/ParserError.mjs +0 -6
  99. package/src/errors/PluginError.cjs +0 -18
  100. package/src/errors/PluginError.mjs +0 -15
  101. package/src/errors/ResolveError.cjs +0 -10
  102. package/src/errors/ResolveError.mjs +0 -7
  103. package/src/errors/ResolverError.cjs +0 -11
  104. package/src/errors/ResolverError.mjs +0 -6
  105. package/src/errors/UnmatchedBundleStrategyError.cjs +0 -11
  106. package/src/errors/UnmatchedBundleStrategyError.mjs +0 -6
  107. package/src/errors/UnmatchedDereferenceStrategyError.cjs +0 -11
  108. package/src/errors/UnmatchedDereferenceStrategyError.mjs +0 -6
  109. package/src/errors/UnmatchedParserError.cjs +0 -11
  110. package/src/errors/UnmatchedParserError.mjs +0 -6
  111. package/src/errors/UnmatchedResolveStrategyError.cjs +0 -11
  112. package/src/errors/UnmatchedResolveStrategyError.mjs +0 -6
  113. package/src/errors/UnmatchedResolverError.cjs +0 -11
  114. package/src/errors/UnmatchedResolverError.mjs +0 -6
  115. package/src/errors/UnresolvableReferenceError.cjs +0 -11
  116. package/src/errors/UnresolvableReferenceError.mjs +0 -6
  117. package/src/index.cjs +0 -146
  118. package/src/index.mjs +0 -103
  119. package/src/options/index.cjs +0 -194
  120. package/src/options/index.mjs +0 -191
  121. package/src/options/util.cjs +0 -24
  122. package/src/options/util.mjs +0 -19
  123. package/src/parse/index.cjs +0 -69
  124. package/src/parse/index.mjs +0 -63
  125. package/src/parse/parsers/Parser.cjs +0 -62
  126. package/src/parse/parsers/Parser.mjs +0 -58
  127. package/src/parse/parsers/apidom-json/index.cjs +0 -70
  128. package/src/parse/parsers/apidom-json/index.mjs +0 -64
  129. package/src/parse/parsers/arazzo-json-1/index.cjs +0 -62
  130. package/src/parse/parsers/arazzo-json-1/index.mjs +0 -56
  131. package/src/parse/parsers/arazzo-json-1/source-descriptions.cjs +0 -221
  132. package/src/parse/parsers/arazzo-json-1/source-descriptions.mjs +0 -214
  133. package/src/parse/parsers/arazzo-yaml-1/index.cjs +0 -62
  134. package/src/parse/parsers/arazzo-yaml-1/index.mjs +0 -56
  135. package/src/parse/parsers/arazzo-yaml-1/source-descriptions.cjs +0 -12
  136. package/src/parse/parsers/arazzo-yaml-1/source-descriptions.mjs +0 -7
  137. package/src/parse/parsers/asyncapi-json-2/index.cjs +0 -54
  138. package/src/parse/parsers/asyncapi-json-2/index.mjs +0 -48
  139. package/src/parse/parsers/asyncapi-yaml-2/index.cjs +0 -54
  140. package/src/parse/parsers/asyncapi-yaml-2/index.mjs +0 -48
  141. package/src/parse/parsers/binary/index-browser.cjs +0 -56
  142. package/src/parse/parsers/binary/index-browser.mjs +0 -50
  143. package/src/parse/parsers/binary/index-node.cjs +0 -51
  144. package/src/parse/parsers/binary/index-node.mjs +0 -45
  145. package/src/parse/parsers/json/index.cjs +0 -53
  146. package/src/parse/parsers/json/index.mjs +0 -47
  147. package/src/parse/parsers/openapi-json-2/index.cjs +0 -54
  148. package/src/parse/parsers/openapi-json-2/index.mjs +0 -48
  149. package/src/parse/parsers/openapi-json-3-0/index.cjs +0 -54
  150. package/src/parse/parsers/openapi-json-3-0/index.mjs +0 -48
  151. package/src/parse/parsers/openapi-json-3-1/index.cjs +0 -54
  152. package/src/parse/parsers/openapi-json-3-1/index.mjs +0 -48
  153. package/src/parse/parsers/openapi-yaml-2/index.cjs +0 -54
  154. package/src/parse/parsers/openapi-yaml-2/index.mjs +0 -48
  155. package/src/parse/parsers/openapi-yaml-3-0/index.cjs +0 -54
  156. package/src/parse/parsers/openapi-yaml-3-0/index.mjs +0 -48
  157. package/src/parse/parsers/openapi-yaml-3-1/index.cjs +0 -54
  158. package/src/parse/parsers/openapi-yaml-3-1/index.mjs +0 -48
  159. package/src/parse/parsers/yaml-1-2/index.cjs +0 -56
  160. package/src/parse/parsers/yaml-1-2/index.mjs +0 -50
  161. package/src/resolve/index.cjs +0 -67
  162. package/src/resolve/index.mjs +0 -60
  163. package/src/resolve/resolvers/HTTPResolver.cjs +0 -45
  164. package/src/resolve/resolvers/HTTPResolver.mjs +0 -37
  165. package/src/resolve/resolvers/Resolver.cjs +0 -20
  166. package/src/resolve/resolvers/Resolver.mjs +0 -16
  167. package/src/resolve/resolvers/file/index-browser.cjs +0 -24
  168. package/src/resolve/resolvers/file/index-browser.mjs +0 -19
  169. package/src/resolve/resolvers/file/index-node.cjs +0 -49
  170. package/src/resolve/resolvers/file/index-node.mjs +0 -42
  171. package/src/resolve/resolvers/http-axios/cache/MemoryCache.cjs +0 -41
  172. package/src/resolve/resolvers/http-axios/cache/MemoryCache.mjs +0 -37
  173. package/src/resolve/resolvers/http-axios/index.cjs +0 -113
  174. package/src/resolve/resolvers/http-axios/index.mjs +0 -105
  175. package/src/resolve/strategies/ResolveStrategy.cjs +0 -20
  176. package/src/resolve/strategies/ResolveStrategy.mjs +0 -16
  177. package/src/resolve/strategies/apidom/index.cjs +0 -49
  178. package/src/resolve/strategies/apidom/index.mjs +0 -43
  179. package/src/resolve/strategies/asyncapi-2/index.cjs +0 -49
  180. package/src/resolve/strategies/asyncapi-2/index.mjs +0 -43
  181. package/src/resolve/strategies/openapi-2/index.cjs +0 -49
  182. package/src/resolve/strategies/openapi-2/index.mjs +0 -43
  183. package/src/resolve/strategies/openapi-3-0/index.cjs +0 -49
  184. package/src/resolve/strategies/openapi-3-0/index.mjs +0 -43
  185. package/src/resolve/strategies/openapi-3-1/index.cjs +0 -49
  186. package/src/resolve/strategies/openapi-3-1/index.mjs +0 -43
  187. package/src/resolve/util.cjs +0 -37
  188. package/src/resolve/util.mjs +0 -30
  189. package/src/util/plugins.cjs +0 -39
  190. package/src/util/plugins.mjs +0 -34
  191. package/src/util/url.cjs +0 -288
  192. package/src/util/url.mjs +0 -274
@@ -1,860 +0,0 @@
1
- import { propEq, none } from 'ramda';
2
- import { isUndefined } from 'ramda-adjunct';
3
- import { isElement, isStringElement, isObjectElement, RefElement, cloneShallow, cloneDeep } from '@speclynx/apidom-datamodel';
4
- import { toValue, fixedFields, toYAML } from '@speclynx/apidom-core';
5
- import { ApiDOMStructuredError } from '@speclynx/apidom-error';
6
- import { traverse, traverseAsync, find } from '@speclynx/apidom-traverse';
7
- import { evaluate as jsonPointerEvaluate, URIFragmentIdentifier } from '@speclynx/apidom-json-pointer';
8
- import { isReferenceLikeElement, isPathItemElement, isReferenceElement, isSchemaElement, isOperationElement, isBooleanJSONSchemaElement, refract, refractReference, refractPathItem, refractOperation } from '@speclynx/apidom-ns-openapi-3-1';
9
- import { isAnchor, uriToAnchor, evaluate as $anchorEvaluate } from "./selectors/$anchor.mjs";
10
- import { evaluate as uriEvaluate } from "./selectors/uri.mjs";
11
- import MaximumDereferenceDepthError from "../../../errors/MaximumDereferenceDepthError.mjs";
12
- import MaximumResolveDepthError from "../../../errors/MaximumResolveDepthError.mjs";
13
- import UnresolvableReferenceError from "../../../errors/UnresolvableReferenceError.mjs";
14
- import EvaluationJsonSchemaUriError from "../../../errors/EvaluationJsonSchemaUriError.mjs";
15
- import * as url from "../../../util/url.mjs";
16
- import parse from "../../../parse/index.mjs";
17
- import Reference from "../../../Reference.mjs";
18
- import File from "../../../File.mjs";
19
- import { resolveSchema$refField, maybeRefractToSchemaElement } from "./util.mjs";
20
- import { AncestorLineage } from "../../util.mjs";
21
- /**
22
- * @public
23
- */
24
- /**
25
- * @public
26
- */
27
- class OpenAPI3_1DereferenceVisitor {
28
- indirections;
29
- reference;
30
- options;
31
- refractCache;
32
-
33
- /**
34
- * Tracks element ancestors across dive-deep traversal boundaries.
35
- * Used for cycle detection: if a referenced element is found in
36
- * the ancestor lineage, a circular reference is detected.
37
- */
38
- ancestors;
39
- constructor({
40
- reference,
41
- options,
42
- indirections = [],
43
- refractCache = new WeakMap(),
44
- ancestors = new AncestorLineage()
45
- }) {
46
- this.indirections = indirections;
47
- this.reference = reference;
48
- this.options = options;
49
- this.refractCache = refractCache;
50
- this.ancestors = new AncestorLineage(...ancestors);
51
- }
52
- toAncestorLineage(path) {
53
- const ancestorNodes = path.getAncestorNodes();
54
- const directAncestors = new Set(ancestorNodes.filter(isElement));
55
- const ancestorsLineage = new AncestorLineage(...this.ancestors, directAncestors);
56
- return [ancestorsLineage, directAncestors];
57
- }
58
- toBaseURI(uri) {
59
- return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
60
- }
61
- async toReference(uri) {
62
- // detect maximum depth of resolution
63
- if (this.reference.depth >= this.options.resolve.maxDepth) {
64
- throw new MaximumResolveDepthError(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`, {
65
- maxDepth: this.options.resolve.maxDepth,
66
- uri: this.reference.uri
67
- });
68
- }
69
- const baseURI = this.toBaseURI(uri);
70
- const {
71
- refSet
72
- } = this.reference;
73
-
74
- // we've already processed this Reference in past
75
- if (refSet.has(baseURI)) {
76
- return refSet.find(propEq(baseURI, 'uri'));
77
- }
78
- const parseResult = await parse(url.unsanitize(baseURI), {
79
- ...this.options,
80
- parse: {
81
- ...this.options.parse,
82
- mediaType: 'text/plain'
83
- }
84
- });
85
-
86
- // register new mutable reference with a refSet
87
- //
88
- // NOTE(known limitation): the mutable reference is mutated in place during traversal
89
- // (via `{ mutable: true }`). When an external document evaluates a JSON pointer back
90
- // into this document, it may receive an already-resolved element instead of the original
91
- // $ref. That resolved element was produced using the entry document's resolution context
92
- // (ancestors, indirections), which may differ from the external document's context.
93
- // This can affect cycle detection in rare cross-document circular reference patterns.
94
- //
95
- // Remediation: evaluate JSON pointers against the immutable (original) parse tree
96
- // instead of the mutable working copy. The `immutable://` reference below preserves
97
- // the original tree and could be used for pointer evaluation, ensuring every resolution
98
- // context always sees raw, unresolved elements and processes them with its own
99
- // ancestors/indirections. The trade-off is that elements referenced by multiple
100
- // documents would be resolved once per context instead of being reused.
101
- const mutableReference = new Reference({
102
- uri: baseURI,
103
- value: this.options.dereference.immutable ? cloneDeep(parseResult) : parseResult,
104
- depth: this.reference.depth + 1
105
- });
106
- refSet.add(mutableReference);
107
- if (this.options.dereference.immutable) {
108
- // register new immutable reference with original parseResult
109
- const immutableReference = new Reference({
110
- uri: `immutable://${baseURI}`,
111
- value: parseResult,
112
- depth: this.reference.depth + 1
113
- });
114
- refSet.add(immutableReference);
115
- }
116
- return mutableReference;
117
- }
118
-
119
- /**
120
- * Handles an error according to the continueOnError option.
121
- *
122
- * For new errors: wraps in UnresolvableReferenceError with structured context
123
- * (type, uri, location, codeFrame, refFieldName, refFieldValue, trace).
124
- * For errors already wrapped by a nested visitor: prepends the current hop to the trace.
125
- *
126
- * Inner/intermediate visitors always throw to let the trace accumulate.
127
- * Only the entry document visitor respects continueOnError (callback/swallow/throw).
128
- */
129
- handleError(message, error, referencingElement, refFieldName, refFieldValue, visitorPath) {
130
- const {
131
- continueOnError
132
- } = this.options.dereference;
133
- const isEntryDocument = url.stripHash(this.reference.refSet?.rootRef?.uri ?? '') === this.reference.uri;
134
- const uri = this.reference.uri;
135
- const type = referencingElement.element;
136
- const codeFrame = toYAML(referencingElement);
137
-
138
- // find element location: tree search for entry documents, visitor path for external
139
- let location;
140
- traverse(this.reference.value.result, {
141
- enter: p => {
142
- if (p.node === referencingElement || this.refractCache.get(p.node) === referencingElement) {
143
- location = p.formatPath();
144
- p.stop();
145
- }
146
- }
147
- });
148
- location ??= visitorPath.formatPath();
149
- const hop = {
150
- uri,
151
- type,
152
- refFieldName,
153
- refFieldValue,
154
- location,
155
- codeFrame
156
- };
157
-
158
- // enrich existing error from nested visitor or create new one
159
- let unresolvedError;
160
- if (error instanceof UnresolvableReferenceError) {
161
- // prefix relative locations for entries belonging to the referenced document
162
- const refBaseURI = this.toBaseURI(refFieldValue);
163
- const fragment = URIFragmentIdentifier.fromURIReference(refFieldValue);
164
- if (fragment) {
165
- if (refBaseURI === error.uri && error.location) {
166
- error.location = fragment + error.location;
167
- }
168
- for (const h of error.trace) {
169
- if (h.uri === refBaseURI && h.location) h.location = fragment + h.location;
170
- }
171
- }
172
- // @ts-ignore
173
- error.trace = [hop, ...error.trace];
174
- unresolvedError = error;
175
- } else {
176
- unresolvedError = new UnresolvableReferenceError(message, {
177
- cause: error,
178
- type,
179
- uri,
180
- location,
181
- codeFrame,
182
- refFieldName,
183
- refFieldValue,
184
- trace: []
185
- });
186
- }
187
- if (!isEntryDocument || continueOnError === false) throw unresolvedError;
188
- if (typeof continueOnError === 'function') continueOnError(unresolvedError);
189
- }
190
- async ReferenceElement(path) {
191
- const referencingElement = path.node;
192
-
193
- // skip current referencing element as it's already been accessed
194
- if (this.indirections.includes(referencingElement)) {
195
- path.skip();
196
- return;
197
- }
198
- const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref));
199
- const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
200
- const isExternalReference = !isInternalReference;
201
-
202
- // ignore resolving internal Reference Objects
203
- if (!this.options.resolve.internal && isInternalReference) {
204
- // skip traversing this reference element and all it's child elements
205
- path.skip();
206
- return;
207
- }
208
- // ignore resolving external Reference Objects
209
- if (!this.options.resolve.external && isExternalReference) {
210
- // skip traversing this reference element and all it's child elements
211
- path.skip();
212
- return;
213
- }
214
- const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
215
- const indirectionsSize = this.indirections.length;
216
- try {
217
- const reference = await this.toReference(toValue(referencingElement.$ref));
218
- this.indirections.push(referencingElement);
219
- const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI);
220
-
221
- // possibly non-semantic fragment
222
- let referencedElement = jsonPointerEvaluate(reference.value.result, jsonPointer);
223
-
224
- // applying semantics to a fragment
225
- const referencedElementType = referencingElement.meta.get('referenced-element');
226
- if (referencedElement.element !== referencedElementType && !isReferenceElement(referencedElement)) {
227
- if (this.refractCache.has(referencedElement)) {
228
- referencedElement = this.refractCache.get(referencedElement);
229
- } else if (isReferenceLikeElement(referencedElement)) {
230
- // handling generic indirect references
231
- const sourceElement = referencedElement;
232
- referencedElement = refractReference(referencedElement);
233
- referencedElement.meta.set('referenced-element', referencedElementType);
234
- this.refractCache.set(sourceElement, referencedElement);
235
- } else {
236
- // handling direct references
237
- const sourceElement = referencedElement;
238
- referencedElement = refract(referencedElement, {
239
- element: referencedElementType
240
- });
241
- this.refractCache.set(sourceElement, referencedElement);
242
- }
243
- }
244
-
245
- // detect direct or indirect reference
246
- if (referencingElement === referencedElement) {
247
- throw new ApiDOMStructuredError('Recursive Reference Object detected', {
248
- $ref: toValue(referencingElement.$ref)
249
- });
250
- }
251
-
252
- // detect maximum depth of dereferencing
253
- if (this.indirections.length > this.options.dereference.maxDepth) {
254
- throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
255
- maxDepth: this.options.dereference.maxDepth,
256
- uri: this.reference.uri
257
- });
258
- }
259
-
260
- // detect cross-boundary cycle
261
- const [ancestorsLineage, directAncestors] = this.toAncestorLineage(path);
262
- if (ancestorsLineage.includes(referencedElement)) {
263
- reference.refSet.circular = true;
264
- if (this.options.dereference.circular === 'error') {
265
- throw new ApiDOMStructuredError('Circular reference detected', {
266
- $ref: toValue(referencingElement.$ref)
267
- });
268
- } else if (this.options.dereference.circular === 'replace') {
269
- const refElement = new RefElement($refBaseURI, {
270
- type: referencingElement.element,
271
- uri: reference.uri,
272
- $ref: toValue(referencingElement.$ref)
273
- });
274
- const replacer = this.options.dereference.strategyOpts['openapi-3-1']?.circularReplacer ?? this.options.dereference.circularReplacer;
275
- const replacement = replacer(refElement);
276
- path.replaceWith(replacement);
277
- return;
278
- }
279
- }
280
-
281
- /**
282
- * Dive deep into the fragment.
283
- *
284
- * Cases to consider:
285
- * 1. We're crossing document boundary
286
- * 2. Fragment is from non-entry document
287
- * 3. Fragment is a Reference Object. We need to follow it to get the eventual value
288
- * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
289
- */
290
- const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
291
- const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
292
- if ((isExternalReference || isNonEntryDocument || isReferenceElement(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
293
- directAncestors.add(referencingElement);
294
- const visitor = new OpenAPI3_1DereferenceVisitor({
295
- reference,
296
- indirections: [...this.indirections],
297
- options: this.options,
298
- refractCache: this.refractCache,
299
- ancestors: ancestorsLineage
300
- });
301
- referencedElement = await traverseAsync(referencedElement, visitor, {
302
- mutable: true
303
- });
304
- directAncestors.delete(referencingElement);
305
- }
306
-
307
- /**
308
- * Creating a new version of referenced element to avoid modifying the original one.
309
- */
310
- const mergedElement = cloneShallow(referencedElement);
311
- // annotate fragment with info about original Reference element
312
- mergedElement.meta.set('ref-fields', {
313
- $ref: toValue(referencingElement.$ref),
314
- // @ts-ignore
315
- description: toValue(referencingElement.description),
316
- // @ts-ignore
317
- summary: toValue(referencingElement.summary)
318
- });
319
- // annotate fragment with info about origin and type
320
- mergedElement.meta.set('ref-origin', reference.uri);
321
- mergedElement.meta.set('ref-type', referencingElement.element);
322
-
323
- // override description and summary (outer has higher priority then inner)
324
- if (isObjectElement(referencedElement) && isObjectElement(mergedElement)) {
325
- const fields = fixedFields(referencedElement, {
326
- indexed: true
327
- });
328
- if (referencingElement.hasKey('description') && Object.hasOwn(fields, 'description')) {
329
- mergedElement.remove('description');
330
- mergedElement.set('description', referencingElement.get('description'));
331
- }
332
- if (referencingElement.hasKey('summary') && Object.hasOwn(fields, 'summary')) {
333
- mergedElement.remove('summary');
334
- mergedElement.set('summary', referencingElement.get('summary'));
335
- }
336
- }
337
-
338
- /**
339
- * Transclude referencing element with merged referenced element.
340
- */
341
- path.replaceWith(mergedElement);
342
- } catch (error) {
343
- const $ref = toValue(referencingElement.$ref);
344
- this.handleError(`Error while dereferencing Reference Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
345
- } finally {
346
- if (this.indirections.length > indirectionsSize) this.indirections.pop();
347
- }
348
- }
349
- async PathItemElement(path) {
350
- const referencingElement = path.node;
351
-
352
- // ignore PathItemElement without $ref field
353
- if (!isStringElement(referencingElement.$ref)) {
354
- return;
355
- }
356
-
357
- // skip current referencing element as it's already been accessed
358
- if (this.indirections.includes(referencingElement)) {
359
- path.skip();
360
- return;
361
- }
362
- const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref));
363
- const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
364
- const isExternalReference = !isInternalReference;
365
-
366
- // ignore resolving internal Path Item Objects
367
- if (!this.options.resolve.internal && isInternalReference) {
368
- // skip traversing this Path Item element but traverse all it's child elements
369
- return;
370
- }
371
- // ignore resolving external Path Item Objects
372
- if (!this.options.resolve.external && isExternalReference) {
373
- // skip traversing this Path Item element but traverse all it's child elements
374
- return;
375
- }
376
- const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
377
- const indirectionsSize = this.indirections.length;
378
- try {
379
- const reference = await this.toReference(toValue(referencingElement.$ref));
380
- this.indirections.push(referencingElement);
381
- const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI);
382
-
383
- // possibly non-semantic referenced element
384
- let referencedElement = jsonPointerEvaluate(reference.value.result, jsonPointer);
385
-
386
- // applying semantics to a referenced element
387
- if (!isPathItemElement(referencedElement)) {
388
- if (this.refractCache.has(referencedElement)) {
389
- referencedElement = this.refractCache.get(referencedElement);
390
- } else {
391
- const sourceElement = referencedElement;
392
- referencedElement = refractPathItem(referencedElement);
393
- this.refractCache.set(sourceElement, referencedElement);
394
- }
395
- }
396
-
397
- // detect direct or indirect reference
398
- if (referencingElement === referencedElement) {
399
- throw new ApiDOMStructuredError('Recursive Path Item Object reference detected', {
400
- $ref: toValue(referencingElement.$ref)
401
- });
402
- }
403
-
404
- // detect maximum depth of dereferencing
405
- if (this.indirections.length > this.options.dereference.maxDepth) {
406
- throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
407
- maxDepth: this.options.dereference.maxDepth,
408
- uri: this.reference.uri
409
- });
410
- }
411
-
412
- // detect cross-boundary cycle
413
- const [ancestorsLineage, directAncestors] = this.toAncestorLineage(path);
414
- if (ancestorsLineage.includes(referencedElement)) {
415
- reference.refSet.circular = true;
416
- if (this.options.dereference.circular === 'error') {
417
- throw new ApiDOMStructuredError('Circular reference detected', {
418
- $ref: toValue(referencingElement.$ref)
419
- });
420
- } else if (this.options.dereference.circular === 'replace') {
421
- const refElement = new RefElement($refBaseURI, {
422
- type: referencingElement.element,
423
- uri: reference.uri,
424
- $ref: toValue(referencingElement.$ref)
425
- });
426
- const replacer = this.options.dereference.strategyOpts['openapi-3-1']?.circularReplacer ?? this.options.dereference.circularReplacer;
427
- const replacement = replacer(refElement);
428
- path.replaceWith(replacement);
429
- return;
430
- }
431
- }
432
-
433
- /**
434
- * Dive deep into the fragment.
435
- *
436
- * Cases to consider:
437
- * 1. We're crossing document boundary
438
- * 2. Fragment is from non-entry document
439
- * 3. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
440
- * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
441
- */
442
- const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
443
- const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
444
- if ((isExternalReference || isNonEntryDocument || isPathItemElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
445
- directAncestors.add(referencingElement);
446
- const visitor = new OpenAPI3_1DereferenceVisitor({
447
- reference,
448
- indirections: [...this.indirections],
449
- options: this.options,
450
- refractCache: this.refractCache,
451
- ancestors: ancestorsLineage
452
- });
453
- referencedElement = await traverseAsync(referencedElement, visitor, {
454
- mutable: true
455
- });
456
- directAncestors.delete(referencingElement);
457
- }
458
-
459
- /**
460
- * Creating a new version of Path Item by merging fields from referenced Path Item with referencing one.
461
- */
462
- if (isPathItemElement(referencedElement)) {
463
- const mergedElement = cloneShallow(referencedElement);
464
- // existing keywords from referencing PathItemElement overrides ones from referenced element
465
- referencingElement.forEach((value, keyElement, item) => {
466
- mergedElement.remove(toValue(keyElement));
467
- mergedElement.content.push(item);
468
- });
469
- mergedElement.remove('$ref');
470
-
471
- // annotate referenced element with info about original referencing element
472
- mergedElement.meta.set('ref-fields', {
473
- $ref: toValue(referencingElement.$ref)
474
- });
475
- // annotate referenced element with info about origin and type
476
- mergedElement.meta.set('ref-origin', reference.uri);
477
- mergedElement.meta.set('ref-type', referencingElement.element);
478
- referencedElement = mergedElement;
479
- }
480
-
481
- /**
482
- * Transclude referencing element with merged referenced element.
483
- */
484
- path.replaceWith(referencedElement);
485
- } catch (error) {
486
- const $ref = toValue(referencingElement.$ref);
487
- this.handleError(`Error while dereferencing Path Item Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
488
- } finally {
489
- if (this.indirections.length > indirectionsSize) this.indirections.pop();
490
- }
491
- }
492
- async LinkElement(path) {
493
- const linkElement = path.node;
494
-
495
- // ignore LinkElement without operationRef or operationId field
496
- if (!isStringElement(linkElement.operationRef) && !isStringElement(linkElement.operationId)) {
497
- return;
498
- }
499
-
500
- // operationRef and operationId fields are mutually exclusive
501
- if (isStringElement(linkElement.operationRef) && isStringElement(linkElement.operationId)) {
502
- throw new ApiDOMStructuredError('LinkElement operationRef and operationId fields are mutually exclusive', {
503
- operationRef: toValue(linkElement.operationRef),
504
- operationId: toValue(linkElement.operationId)
505
- });
506
- }
507
- try {
508
- let operationElement;
509
- if (isStringElement(linkElement.operationRef)) {
510
- // possibly non-semantic referenced element
511
- const jsonPointer = URIFragmentIdentifier.fromURIReference(toValue(linkElement.operationRef));
512
- const retrievalURI = this.toBaseURI(toValue(linkElement.operationRef));
513
- const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
514
- const isExternalReference = !isInternalReference;
515
-
516
- // ignore resolving internal Operation Object reference
517
- if (!this.options.resolve.internal && isInternalReference) {
518
- // skip traversing this Link element but traverse all it's child elements
519
- return;
520
- }
521
- // ignore resolving external Operation Object reference
522
- if (!this.options.resolve.external && isExternalReference) {
523
- // skip traversing this Link element but traverse all it's child elements
524
- return;
525
- }
526
- const reference = await this.toReference(toValue(linkElement.operationRef));
527
- operationElement = jsonPointerEvaluate(reference.value.result, jsonPointer);
528
- // applying semantics to a referenced element
529
- if (!isOperationElement(operationElement)) {
530
- if (this.refractCache.has(operationElement)) {
531
- operationElement = this.refractCache.get(operationElement);
532
- } else {
533
- const sourceElement = operationElement;
534
- operationElement = refractOperation(operationElement);
535
- this.refractCache.set(sourceElement, operationElement);
536
- }
537
- }
538
- // create shallow clone to be able to annotate with metadata
539
- operationElement = cloneShallow(operationElement);
540
- // annotate operation element with info about origin and type
541
- operationElement.meta.set('ref-origin', reference.uri);
542
- operationElement.meta.set('ref-type', linkElement.element);
543
- const linkElementCopy = cloneShallow(linkElement);
544
- linkElementCopy.operationRef?.meta.set('operation', operationElement);
545
-
546
- /**
547
- * Transclude Link Object containing Operation Object in its meta.
548
- */
549
- path.replaceWith(linkElementCopy);
550
- return;
551
- }
552
- if (isStringElement(linkElement.operationId)) {
553
- const operationId = toValue(linkElement.operationId);
554
- const reference = await this.toReference(url.unsanitize(this.reference.uri));
555
- operationElement = find(reference.value.result, e => isOperationElement(e) && isElement(e.operationId) && e.operationId.equals(operationId));
556
- // OperationElement not found by its operationId
557
- if (isUndefined(operationElement)) {
558
- throw new ApiDOMStructuredError(`OperationElement(operationId=${operationId}) not found`, {
559
- operationId
560
- });
561
- }
562
- const linkElementCopy = cloneShallow(linkElement);
563
- linkElementCopy.operationId?.meta.set('operation', operationElement);
564
-
565
- /**
566
- * Transclude Link Object containing Operation Object in its meta.
567
- */
568
- path.replaceWith(linkElementCopy);
569
- }
570
- } catch (error) {
571
- const refFieldName = isStringElement(linkElement.operationRef) ? 'operationRef' : 'operationId';
572
- const refFieldValue = isStringElement(linkElement.operationRef) ? toValue(linkElement.operationRef) : toValue(linkElement.operationId);
573
- this.handleError(`Error while dereferencing Link Object. Cannot resolve ${refFieldName} "${refFieldValue}": ${error.message}`, error, linkElement, refFieldName, refFieldValue, path);
574
- }
575
- }
576
- async ExampleElement(path) {
577
- const exampleElement = path.node;
578
-
579
- // ignore ExampleElement without externalValue field
580
- if (!isStringElement(exampleElement.externalValue)) {
581
- return;
582
- }
583
-
584
- // value and externalValue fields are mutually exclusive
585
- if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) {
586
- throw new ApiDOMStructuredError('ExampleElement value and externalValue fields are mutually exclusive', {
587
- value: toValue(exampleElement.value),
588
- externalValue: toValue(exampleElement.externalValue)
589
- });
590
- }
591
- const retrievalURI = this.toBaseURI(toValue(exampleElement.externalValue));
592
- const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
593
- const isExternalReference = !isInternalReference;
594
-
595
- // ignore resolving internal Example Objects
596
- if (!this.options.resolve.internal && isInternalReference) {
597
- // skip traversing this Example element but traverse all it's child elements
598
- return;
599
- }
600
- // ignore resolving external Example Objects
601
- if (!this.options.resolve.external && isExternalReference) {
602
- // skip traversing this Example element but traverse all it's child elements
603
- return;
604
- }
605
- try {
606
- const reference = await this.toReference(toValue(exampleElement.externalValue));
607
-
608
- // shallow clone of the referenced element
609
- const valueElement = cloneShallow(reference.value.result);
610
- // annotate element with info about origin and type
611
- valueElement.meta.set('ref-origin', reference.uri);
612
- valueElement.meta.set('ref-type', exampleElement.element);
613
- const exampleElementCopy = cloneShallow(exampleElement);
614
- exampleElementCopy.value = valueElement;
615
-
616
- /**
617
- * Transclude Example Object containing external value.
618
- */
619
- path.replaceWith(exampleElementCopy);
620
- } catch (error) {
621
- const externalValue = toValue(exampleElement.externalValue);
622
- this.handleError(`Error while dereferencing Example Object. Cannot resolve externalValue "${externalValue}": ${error.message}`, error, exampleElement, 'externalValue', externalValue, path);
623
- }
624
- }
625
- async SchemaElement(path) {
626
- const referencingElement = path.node;
627
-
628
- // skip current referencing schema as $ref keyword was not defined
629
- if (!isStringElement(referencingElement.$ref)) {
630
- return;
631
- }
632
-
633
- // skip current referencing element as it's already been accessed
634
- if (this.indirections.includes(referencingElement)) {
635
- path.skip();
636
- return;
637
- }
638
- const indirectionsSize = this.indirections.length;
639
- try {
640
- // compute baseURI using rules around $id and $ref keywords
641
- let reference = await this.toReference(url.unsanitize(this.reference.uri));
642
- let {
643
- uri: retrievalURI
644
- } = reference;
645
- const $refBaseURI = resolveSchema$refField(retrievalURI, referencingElement);
646
- const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
647
- const file = new File({
648
- uri: $refBaseURIStrippedHash
649
- });
650
- const isUnknownURI = none(r => r.canRead(file), this.options.resolve.resolvers);
651
- const isURL = !isUnknownURI;
652
- let isInternalReference = url.stripHash(this.reference.uri) === $refBaseURI;
653
- let isExternalReference = !isInternalReference;
654
-
655
- // determining reference, proper evaluation and selection mechanism
656
- let referencedElement;
657
- try {
658
- if (isUnknownURI || isURL) {
659
- // we're dealing with canonical URI or URL with possible fragment
660
- retrievalURI = this.toBaseURI($refBaseURI);
661
- const selector = $refBaseURI;
662
- const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
663
- referencedElement = uriEvaluate(selector, referenceAsSchema);
664
- referencedElement = maybeRefractToSchemaElement(referencedElement);
665
-
666
- // ignore resolving internal Schema Objects
667
- if (!this.options.resolve.internal && isInternalReference) {
668
- // skip traversing this schema element but traverse all it's child elements
669
- return;
670
- }
671
- // ignore resolving external Schema Objects
672
- if (!this.options.resolve.external && isExternalReference) {
673
- // skip traversing this schema element but traverse all it's child elements
674
- return;
675
- }
676
- } else {
677
- // we're assuming here that we're dealing with JSON Pointer here
678
- retrievalURI = this.toBaseURI($refBaseURI);
679
- isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
680
- isExternalReference = !isInternalReference;
681
-
682
- // ignore resolving internal Schema Objects
683
- if (!this.options.resolve.internal && isInternalReference) {
684
- // skip traversing this schema element but traverse all it's child elements
685
- return;
686
- }
687
- // ignore resolving external Schema Objects
688
- if (!this.options.resolve.external && isExternalReference) {
689
- // skip traversing this schema element but traverse all it's child elements
690
- return;
691
- }
692
- reference = await this.toReference(url.unsanitize($refBaseURI));
693
- const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
694
- const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
695
- referencedElement = jsonPointerEvaluate(referenceAsSchema, selector);
696
- referencedElement = maybeRefractToSchemaElement(referencedElement);
697
- }
698
- } catch (error) {
699
- /**
700
- * SchemaElement($id=URL) was not found, so we're going to try to resolve
701
- * the URL and assume the returned response is a JSON Schema.
702
- */
703
- if (isURL && error instanceof EvaluationJsonSchemaUriError) {
704
- if (isAnchor(uriToAnchor($refBaseURI))) {
705
- // we're dealing with JSON Schema $anchor here
706
- isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
707
- isExternalReference = !isInternalReference;
708
-
709
- // ignore resolving internal Schema Objects
710
- if (!this.options.resolve.internal && isInternalReference) {
711
- // skip traversing this schema element but traverse all it's child elements
712
- return;
713
- }
714
- // ignore resolving external Schema Objects
715
- if (!this.options.resolve.external && isExternalReference) {
716
- // skip traversing this schema element but traverse all it's child elements
717
- return;
718
- }
719
- reference = await this.toReference(url.unsanitize($refBaseURI));
720
- const selector = uriToAnchor($refBaseURI);
721
- const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
722
- referencedElement = $anchorEvaluate(selector, referenceAsSchema);
723
- referencedElement = maybeRefractToSchemaElement(referencedElement);
724
- } else {
725
- // we're assuming here that we're dealing with JSON Pointer here
726
- retrievalURI = this.toBaseURI($refBaseURI);
727
- isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
728
- isExternalReference = !isInternalReference;
729
-
730
- // ignore resolving internal Schema Objects
731
- if (!this.options.resolve.internal && isInternalReference) {
732
- // skip traversing this schema element but traverse all it's child elements
733
- return;
734
- }
735
- // ignore resolving external Schema Objects
736
- if (!this.options.resolve.external && isExternalReference) {
737
- // skip traversing this schema element but traverse all it's child elements
738
- return;
739
- }
740
- reference = await this.toReference(url.unsanitize($refBaseURI));
741
- const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
742
- const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
743
- referencedElement = jsonPointerEvaluate(referenceAsSchema, selector);
744
- referencedElement = maybeRefractToSchemaElement(referencedElement);
745
- }
746
- } else {
747
- throw error;
748
- }
749
- }
750
- this.indirections.push(referencingElement);
751
-
752
- // detect direct or indirect reference
753
- if (referencingElement === referencedElement) {
754
- throw new ApiDOMStructuredError('Recursive Schema Object reference detected', {
755
- $ref: toValue(referencingElement.$ref)
756
- });
757
- }
758
-
759
- // detect maximum depth of dereferencing
760
- if (this.indirections.length > this.options.dereference.maxDepth) {
761
- throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
762
- maxDepth: this.options.dereference.maxDepth,
763
- uri: this.reference.uri
764
- });
765
- }
766
-
767
- // detect cross-boundary cycle
768
- const [ancestorsLineage, directAncestors] = this.toAncestorLineage(path);
769
- if (ancestorsLineage.includes(referencedElement)) {
770
- reference.refSet.circular = true;
771
- if (this.options.dereference.circular === 'error') {
772
- throw new ApiDOMStructuredError('Circular reference detected', {
773
- $ref: toValue(referencingElement.$ref)
774
- });
775
- } else if (this.options.dereference.circular === 'replace') {
776
- const refElement = new RefElement($refBaseURI, {
777
- type: referencingElement.element,
778
- uri: reference.uri,
779
- $ref: toValue(referencingElement.$ref)
780
- });
781
- const replacer = this.options.dereference.strategyOpts['openapi-3-1']?.circularReplacer ?? this.options.dereference.circularReplacer;
782
- const replacement = replacer(refElement);
783
- path.replaceWith(replacement);
784
- return;
785
- }
786
- }
787
-
788
- /**
789
- * Dive deep into the fragment.
790
- *
791
- * Cases to consider:
792
- * 1. We're crossing document boundary
793
- * 2. Fragment is from non-entry document
794
- * 3. Fragment is a Schema Object with $ref field. We need to follow it to get the eventual value
795
- * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
796
- */
797
- const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
798
- const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
799
- if ((isExternalReference || isNonEntryDocument || isSchemaElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
800
- directAncestors.add(referencingElement);
801
- const visitor = new OpenAPI3_1DereferenceVisitor({
802
- reference,
803
- indirections: [...this.indirections],
804
- options: this.options,
805
- refractCache: this.refractCache,
806
- ancestors: ancestorsLineage
807
- });
808
- referencedElement = await traverseAsync(referencedElement, visitor, {
809
- mutable: true
810
- });
811
- directAncestors.delete(referencingElement);
812
- }
813
-
814
- // Boolean JSON Schemas
815
- if (isBooleanJSONSchemaElement(referencedElement)) {
816
- const booleanJsonSchemaElement = cloneDeep(referencedElement);
817
- // annotate referenced element with info about original referencing element
818
- booleanJsonSchemaElement.meta.set('ref-fields', {
819
- $ref: toValue(referencingElement.$ref)
820
- });
821
- // annotate referenced element with info about origin and type
822
- booleanJsonSchemaElement.meta.set('ref-origin', reference.uri);
823
- booleanJsonSchemaElement.meta.set('ref-type', referencingElement.element);
824
- path.replaceWith(booleanJsonSchemaElement);
825
- return;
826
- }
827
-
828
- /**
829
- * Creating a new version of Schema Object by merging fields from referenced Schema Object with referencing one.
830
- */
831
- if (isSchemaElement(referencedElement)) {
832
- const mergedElement = cloneShallow(referencedElement);
833
- // existing keywords from referencing schema overrides ones from referenced schema
834
- referencingElement.forEach((value, keyElement, item) => {
835
- mergedElement.remove(toValue(keyElement));
836
- mergedElement.content.push(item);
837
- });
838
- mergedElement.remove('$ref');
839
- // annotate referenced element with info about original referencing element
840
- mergedElement.meta.set('ref-fields', {
841
- $ref: toValue(referencingElement.$ref)
842
- });
843
- // annotate fragment with info about origin and type
844
- mergedElement.meta.set('ref-origin', reference.uri);
845
- mergedElement.meta.set('ref-type', referencingElement.element);
846
- referencedElement = mergedElement;
847
- }
848
- /**
849
- * Transclude referencing element with merged referenced element.
850
- */
851
- path.replaceWith(referencedElement);
852
- } catch (error) {
853
- const $ref = toValue(referencingElement.$ref);
854
- this.handleError(`Error while dereferencing Schema Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
855
- } finally {
856
- if (this.indirections.length > indirectionsSize) this.indirections.pop();
857
- }
858
- }
859
- }
860
- export default OpenAPI3_1DereferenceVisitor;