@speclynx/apidom-reference 6.2.0 → 7.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.
- package/CHANGELOG.md +49 -0
- package/README.md +45 -0
- package/dist/apidom-reference.browser.js +4701 -3478
- package/dist/apidom-reference.browser.min.js +1 -1
- package/package.json +29 -28
- package/src/File.cjs +5 -2
- package/src/File.mjs +5 -2
- package/src/bundle/strategies/arazzo-1/index.cjs +3 -2
- package/src/bundle/strategies/arazzo-1/index.mjs +3 -2
- package/src/bundle/strategies/asyncapi-2/index.cjs +3 -2
- package/src/bundle/strategies/asyncapi-2/index.mjs +3 -2
- package/src/bundle/strategies/openapi-2/index.cjs +3 -2
- package/src/bundle/strategies/openapi-2/index.mjs +3 -2
- package/src/bundle/strategies/openapi-3-0/index.cjs +3 -2
- package/src/bundle/strategies/openapi-3-0/index.mjs +3 -2
- package/src/bundle/strategies/openapi-3-1/index.cjs +3 -2
- package/src/bundle/strategies/openapi-3-1/index.mjs +3 -2
- package/src/dereference/strategies/DereferenceVisitor.cjs +588 -0
- package/src/dereference/strategies/DereferenceVisitor.mjs +580 -0
- package/src/dereference/strategies/arazzo-1/index.cjs +14 -39
- package/src/dereference/strategies/arazzo-1/index.mjs +15 -40
- package/src/dereference/strategies/arazzo-1/visitor.cjs +207 -331
- package/src/dereference/strategies/arazzo-1/visitor.mjs +215 -338
- package/src/dereference/strategies/asyncapi-2/index.cjs +11 -39
- package/src/dereference/strategies/asyncapi-2/index.mjs +11 -39
- package/src/dereference/strategies/asyncapi-2/visitor.cjs +133 -425
- package/src/dereference/strategies/asyncapi-2/visitor.mjs +137 -429
- package/src/dereference/strategies/openapi-2/index.cjs +12 -42
- package/src/dereference/strategies/openapi-2/index.mjs +12 -42
- package/src/dereference/strategies/openapi-2/visitor.cjs +168 -532
- package/src/dereference/strategies/openapi-2/visitor.mjs +172 -536
- package/src/dereference/strategies/openapi-3-0/index.cjs +12 -42
- package/src/dereference/strategies/openapi-3-0/index.mjs +12 -42
- package/src/dereference/strategies/openapi-3-0/visitor.cjs +194 -468
- package/src/dereference/strategies/openapi-3-0/visitor.mjs +199 -473
- package/src/dereference/strategies/openapi-3-1/index.cjs +16 -42
- package/src/dereference/strategies/openapi-3-1/index.mjs +16 -42
- package/src/dereference/strategies/openapi-3-1/visitor.cjs +350 -602
- package/src/dereference/strategies/openapi-3-1/visitor.mjs +357 -608
- package/src/dereference/strategies/overlay-1/index.cjs +3 -2
- package/src/dereference/strategies/overlay-1/index.mjs +3 -2
- package/src/index.cjs +0 -7
- package/src/index.mjs +0 -1
- package/src/parse/index.cjs +9 -1
- package/src/parse/index.mjs +9 -1
- package/src/parse/parsers/apidom-json/index.cjs +2 -1
- package/src/parse/parsers/apidom-json/index.mjs +2 -1
- package/src/parse/parsers/arazzo-json-1/index.cjs +2 -1
- package/src/parse/parsers/arazzo-json-1/index.mjs +2 -1
- package/src/parse/parsers/arazzo-yaml-1/index.cjs +2 -1
- package/src/parse/parsers/arazzo-yaml-1/index.mjs +2 -1
- package/src/parse/parsers/asyncapi-json-2/index.cjs +2 -1
- package/src/parse/parsers/asyncapi-json-2/index.mjs +2 -1
- package/src/parse/parsers/asyncapi-yaml-2/index.cjs +2 -1
- package/src/parse/parsers/asyncapi-yaml-2/index.mjs +2 -1
- package/src/parse/parsers/json/index.cjs +2 -1
- package/src/parse/parsers/json/index.mjs +2 -1
- package/src/parse/parsers/openapi-json-2/index.cjs +2 -1
- package/src/parse/parsers/openapi-json-2/index.mjs +2 -1
- package/src/parse/parsers/openapi-json-3-0/index.cjs +2 -1
- package/src/parse/parsers/openapi-json-3-0/index.mjs +2 -1
- package/src/parse/parsers/openapi-json-3-1/index.cjs +2 -1
- package/src/parse/parsers/openapi-json-3-1/index.mjs +2 -1
- package/src/parse/parsers/openapi-yaml-2/index.cjs +2 -1
- package/src/parse/parsers/openapi-yaml-2/index.mjs +2 -1
- package/src/parse/parsers/openapi-yaml-3-0/index.cjs +2 -1
- package/src/parse/parsers/openapi-yaml-3-0/index.mjs +2 -1
- package/src/parse/parsers/openapi-yaml-3-1/index.cjs +2 -1
- package/src/parse/parsers/openapi-yaml-3-1/index.mjs +2 -1
- package/src/parse/parsers/overlay-json-1/index.cjs +2 -1
- package/src/parse/parsers/overlay-json-1/index.mjs +2 -1
- package/src/parse/parsers/overlay-yaml-1/index.cjs +2 -1
- package/src/parse/parsers/overlay-yaml-1/index.mjs +2 -1
- package/src/parse/parsers/yaml-1-2/index.cjs +2 -1
- package/src/parse/parsers/yaml-1-2/index.mjs +2 -1
- package/types/apidom-reference.d.ts +0 -9
- package/types/dereference/strategies/DereferenceVisitor.d.ts +224 -0
- package/types/dereference/strategies/arazzo-1/index.d.ts +0 -1
- package/types/dereference/strategies/arazzo-1/visitor.d.ts +38 -38
- package/types/dereference/strategies/asyncapi-2/index.d.ts +0 -1
- package/types/dereference/strategies/asyncapi-2/visitor.d.ts +13 -29
- package/types/dereference/strategies/openapi-2/index.d.ts +0 -1
- package/types/dereference/strategies/openapi-2/visitor.d.ts +14 -29
- package/types/dereference/strategies/openapi-3-0/index.d.ts +0 -1
- package/types/dereference/strategies/openapi-3-0/visitor.d.ts +13 -28
- package/types/dereference/strategies/openapi-3-1/index.d.ts +0 -1
- package/types/dereference/strategies/openapi-3-1/visitor.d.ts +37 -33
- package/types/index.d.ts +0 -1
- package/src/dereference/util.cjs +0 -33
- package/src/dereference/util.mjs +0 -27
- package/types/dereference/util.d.ts +0 -9
|
@@ -6,367 +6,151 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
8
|
exports.default = void 0;
|
|
9
|
-
var _ramda = require("ramda");
|
|
10
9
|
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
11
10
|
var _apidomCore = require("@speclynx/apidom-core");
|
|
12
11
|
var _apidomError = require("@speclynx/apidom-error");
|
|
13
|
-
var _apidomTraverse = require("@speclynx/apidom-traverse");
|
|
14
12
|
var _apidomJsonPointer = require("@speclynx/apidom-json-pointer");
|
|
15
13
|
var _apidomNsOpenapi = require("@speclynx/apidom-ns-openapi-2");
|
|
16
|
-
var
|
|
17
|
-
var _MaximumDereferenceDepthError = _interopRequireDefault(require("../../../errors/MaximumDereferenceDepthError.cjs"));
|
|
18
|
-
var _MaximumResolveDepthError = _interopRequireDefault(require("../../../errors/MaximumResolveDepthError.cjs"));
|
|
19
|
-
var _util = require("../../util.cjs");
|
|
14
|
+
var _DereferenceVisitor = _interopRequireDefault(require("../DereferenceVisitor.cjs"));
|
|
20
15
|
var url = _interopRequireWildcard(require("../../../util/url.cjs"));
|
|
21
|
-
var _index = _interopRequireDefault(require("../../../parse/index.cjs"));
|
|
22
|
-
var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
|
|
23
16
|
/**
|
|
24
17
|
* @public
|
|
25
18
|
*/
|
|
26
19
|
|
|
27
20
|
/**
|
|
21
|
+
* Dereferences an OpenAPI 2.0 document. Reference Objects and JSON Reference Objects expect
|
|
22
|
+
* the type of their `referenced-element` meta, Path Item `$ref`s a Path Item Object.
|
|
23
|
+
*
|
|
28
24
|
* @public
|
|
29
25
|
*/
|
|
30
|
-
class OpenAPI2DereferenceVisitor {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
options;
|
|
34
|
-
refractCache;
|
|
35
|
-
visited;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Tracks element ancestors across dive-deep traversal boundaries.
|
|
39
|
-
* Used for cycle detection: if a referenced element is found in
|
|
40
|
-
* the ancestor lineage, a circular reference is detected.
|
|
41
|
-
*/
|
|
42
|
-
ancestors;
|
|
43
|
-
constructor({
|
|
44
|
-
reference,
|
|
45
|
-
options,
|
|
46
|
-
indirections = [],
|
|
47
|
-
ancestors = new _util.AncestorLineage(),
|
|
48
|
-
refractCache = new WeakMap(),
|
|
49
|
-
visited = new WeakSet()
|
|
50
|
-
}) {
|
|
51
|
-
this.indirections = indirections;
|
|
52
|
-
this.reference = reference;
|
|
53
|
-
this.options = options;
|
|
54
|
-
this.refractCache = refractCache;
|
|
55
|
-
this.ancestors = new _util.AncestorLineage(...ancestors);
|
|
56
|
-
this.visited = visited;
|
|
57
|
-
}
|
|
58
|
-
toAncestorLineage(path) {
|
|
59
|
-
const ancestorNodes = path.getAncestorNodes();
|
|
60
|
-
const directAncestors = new Set(ancestorNodes.filter(_apidomDatamodel.isElement));
|
|
61
|
-
const ancestorsLineage = new _util.AncestorLineage(...this.ancestors, directAncestors);
|
|
62
|
-
return [ancestorsLineage, directAncestors];
|
|
26
|
+
class OpenAPI2DereferenceVisitor extends _DereferenceVisitor.default {
|
|
27
|
+
get strategyName() {
|
|
28
|
+
return 'openapi-2';
|
|
63
29
|
}
|
|
64
|
-
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// detect maximum depth of resolution
|
|
69
|
-
if (this.reference.depth >= this.options.resolve.maxDepth) {
|
|
70
|
-
throw new _MaximumResolveDepthError.default(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`, {
|
|
71
|
-
maxDepth: this.options.resolve.maxDepth,
|
|
72
|
-
uri: this.reference.uri
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
const baseURI = this.toBaseURI(uri);
|
|
76
|
-
const {
|
|
77
|
-
refSet
|
|
78
|
-
} = this.reference;
|
|
79
|
-
|
|
80
|
-
// we've already processed this Reference in past
|
|
81
|
-
if (refSet.has(baseURI)) {
|
|
82
|
-
return refSet.find((0, _ramda.propEq)(baseURI, 'uri'));
|
|
83
|
-
}
|
|
84
|
-
const parseResult = await (0, _index.default)(url.unsanitize(baseURI), {
|
|
85
|
-
...this.options,
|
|
86
|
-
parse: {
|
|
87
|
-
...this.options.parse,
|
|
88
|
-
mediaType: 'text/plain'
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// register new mutable reference with a refSet
|
|
93
|
-
//
|
|
94
|
-
// NOTE(known limitation): the mutable reference is mutated in place during traversal
|
|
95
|
-
// (via `{ mutable: true }`). When an external document evaluates a JSON pointer back
|
|
96
|
-
// into this document, it may receive an already-resolved element instead of the original
|
|
97
|
-
// $ref. That resolved element was produced using the entry document's resolution context
|
|
98
|
-
// (ancestors, indirections), which may differ from the external document's context.
|
|
99
|
-
// This can affect cycle detection in rare cross-document circular reference patterns.
|
|
100
|
-
//
|
|
101
|
-
// Remediation: evaluate JSON pointers against the immutable (original) parse tree
|
|
102
|
-
// instead of the mutable working copy. The `immutable://` reference below preserves
|
|
103
|
-
// the original tree and could be used for pointer evaluation, ensuring every resolution
|
|
104
|
-
// context always sees raw, unresolved elements and processes them with its own
|
|
105
|
-
// ancestors/indirections. The trade-off is that elements referenced by multiple
|
|
106
|
-
// documents would be resolved once per context instead of being reused.
|
|
107
|
-
const mutableReference = new _Reference.default({
|
|
108
|
-
uri: baseURI,
|
|
109
|
-
value: this.options.dereference.immutable ? (0, _apidomDatamodel.cloneDeep)(parseResult) : parseResult,
|
|
110
|
-
depth: this.reference.depth + 1
|
|
30
|
+
createVisitor(reference) {
|
|
31
|
+
return new OpenAPI2DereferenceVisitor({
|
|
32
|
+
reference,
|
|
33
|
+
options: this.options
|
|
111
34
|
});
|
|
112
|
-
refSet.add(mutableReference);
|
|
113
|
-
if (this.options.dereference.immutable) {
|
|
114
|
-
// register new immutable reference with a refSet
|
|
115
|
-
const immutableReference = new _Reference.default({
|
|
116
|
-
uri: `immutable://${baseURI}`,
|
|
117
|
-
value: parseResult,
|
|
118
|
-
depth: this.reference.depth + 1
|
|
119
|
-
});
|
|
120
|
-
refSet.add(immutableReference);
|
|
121
|
-
}
|
|
122
|
-
return mutableReference;
|
|
123
35
|
}
|
|
124
36
|
|
|
125
37
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* For new errors: wraps in UnresolvableReferenceError with structured context
|
|
129
|
-
* (type, uri, location, codeFrame, refFieldName, refFieldValue, trace).
|
|
130
|
-
* For errors already wrapped by a nested visitor: prepends the current hop to the trace.
|
|
131
|
-
*
|
|
132
|
-
* Inner/intermediate visitors always throw to let the trace accumulate.
|
|
133
|
-
* Only the entry document visitor respects continueOnError (callback/swallow/throw).
|
|
38
|
+
* A (JSON) Reference Object stands for the type its reference expects: it's dereferenced as that type.
|
|
134
39
|
*/
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
} = this.options.dereference;
|
|
139
|
-
const isEntryDocument = url.stripHash(this.reference.refSet?.rootRef?.uri ?? '') === this.reference.uri;
|
|
140
|
-
const uri = this.reference.uri;
|
|
141
|
-
const type = referencingElement.element;
|
|
142
|
-
const codeFrame = (0, _apidomCore.toYAML)(referencingElement);
|
|
40
|
+
isOfType(element, type) {
|
|
41
|
+
return (0, _apidomNsOpenapi.isReferenceElement)(element) || (0, _apidomNsOpenapi.isJSONReferenceElement)(element) || element.element === type;
|
|
42
|
+
}
|
|
143
43
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if ((0,
|
|
151
|
-
(0,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
location = p.formatPath();
|
|
155
|
-
p.stop();
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
});
|
|
44
|
+
/**
|
|
45
|
+
* A generic structure holding a `$ref` is a reference to the expected type: a JSON Reference
|
|
46
|
+
* Object for a Schema Object, a Reference Object otherwise, unless the type has its own `$ref`
|
|
47
|
+
* field (Path Item Object).
|
|
48
|
+
*/
|
|
49
|
+
refractTo(element, type) {
|
|
50
|
+
if (type === 'schema' && (0, _apidomNsOpenapi.isJSONReferenceLikeElement)(element)) {
|
|
51
|
+
const referenceElement = (0, _apidomNsOpenapi.refractJSONReference)(element);
|
|
52
|
+
referenceElement.meta.set('referenced-element', type);
|
|
53
|
+
return referenceElement;
|
|
159
54
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
refFieldName,
|
|
165
|
-
refFieldValue,
|
|
166
|
-
location,
|
|
167
|
-
codeFrame
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
// enrich existing error from nested visitor or create new one
|
|
171
|
-
let unresolvedError;
|
|
172
|
-
if (error instanceof _UnresolvableReferenceError.default) {
|
|
173
|
-
// prefix relative locations for entries belonging to the referenced document
|
|
174
|
-
const refBaseURI = this.toBaseURI(refFieldValue);
|
|
175
|
-
const fragment = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference(refFieldValue);
|
|
176
|
-
if (fragment) {
|
|
177
|
-
if (refBaseURI === error.uri && error.location) {
|
|
178
|
-
error.location = fragment + error.location;
|
|
179
|
-
}
|
|
180
|
-
for (const h of error.trace) {
|
|
181
|
-
if (h.uri === refBaseURI && h.location) h.location = fragment + h.location;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// @ts-ignore
|
|
185
|
-
error.trace = [hop, ...error.trace];
|
|
186
|
-
unresolvedError = error;
|
|
187
|
-
} else {
|
|
188
|
-
unresolvedError = new _UnresolvableReferenceError.default(message, {
|
|
189
|
-
cause: error,
|
|
190
|
-
type,
|
|
191
|
-
uri,
|
|
192
|
-
location,
|
|
193
|
-
codeFrame,
|
|
194
|
-
refFieldName,
|
|
195
|
-
refFieldValue,
|
|
196
|
-
trace: []
|
|
197
|
-
});
|
|
55
|
+
if (type !== 'schema' && type !== 'pathItem' && (0, _apidomNsOpenapi.isReferenceLikeElement)(element)) {
|
|
56
|
+
const referenceElement = (0, _apidomNsOpenapi.refractReference)(element);
|
|
57
|
+
referenceElement.meta.set('referenced-element', type);
|
|
58
|
+
return referenceElement;
|
|
198
59
|
}
|
|
199
|
-
|
|
200
|
-
|
|
60
|
+
return (0, _apidomNsOpenapi.refract)(element, {
|
|
61
|
+
element: type
|
|
62
|
+
});
|
|
201
63
|
}
|
|
202
64
|
async ReferenceElement(path) {
|
|
203
65
|
const referencingElement = path.node;
|
|
204
66
|
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
67
|
+
// the fields of a Reference Object hold no references
|
|
68
|
+
path.skip();
|
|
209
69
|
|
|
210
|
-
//
|
|
211
|
-
if (this.
|
|
212
|
-
path.skip();
|
|
70
|
+
// ignore Reference Object without a string $ref, e.g. an empty YAML value
|
|
71
|
+
if (this.isTranscluded(referencingElement) || !(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
213
72
|
return;
|
|
214
73
|
}
|
|
215
|
-
const
|
|
74
|
+
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
75
|
+
const retrievalURI = this.toBaseURI($ref);
|
|
216
76
|
const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
|
|
217
77
|
const isExternalReference = !isInternalReference;
|
|
218
78
|
|
|
219
79
|
// ignore resolving internal Reference Objects
|
|
220
80
|
if (!this.options.resolve.internal && isInternalReference) {
|
|
221
|
-
// skip traversing this reference element and all it's child elements
|
|
222
|
-
path.skip();
|
|
223
81
|
return;
|
|
224
82
|
}
|
|
225
83
|
// ignore resolving external Reference Objects
|
|
226
84
|
if (!this.options.resolve.external && isExternalReference) {
|
|
227
|
-
// skip traversing this reference element and all it's child elements
|
|
228
|
-
path.skip();
|
|
229
85
|
return;
|
|
230
86
|
}
|
|
231
|
-
const $refBaseURI = url.resolve(retrievalURI,
|
|
232
|
-
const indirectionsSize = this.indirections.length;
|
|
87
|
+
const $refBaseURI = url.resolve(retrievalURI, $ref);
|
|
233
88
|
try {
|
|
234
|
-
const reference = await this.toReference(
|
|
235
|
-
this.indirections.push(referencingElement);
|
|
89
|
+
const reference = await this.toReference($ref);
|
|
236
90
|
const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
237
|
-
|
|
238
91
|
// possibly non-semantic fragment
|
|
239
|
-
|
|
92
|
+
const referencedElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
|
|
240
93
|
|
|
241
|
-
//
|
|
242
|
-
const referencedElementType = referencingElement.meta.get('referenced-element');
|
|
243
|
-
if (referencedElement.element !== referencedElementType && !(0, _apidomNsOpenapi.isReferenceElement)(referencedElement)) {
|
|
244
|
-
if (this.refractCache.has(referencedElement)) {
|
|
245
|
-
referencedElement = this.refractCache.get(referencedElement);
|
|
246
|
-
} else if ((0, _apidomNsOpenapi.isReferenceLikeElement)(referencedElement)) {
|
|
247
|
-
// handling generic indirect references
|
|
248
|
-
const sourceElement = referencedElement;
|
|
249
|
-
referencedElement = (0, _apidomNsOpenapi.refractReference)(referencedElement);
|
|
250
|
-
referencedElement.meta.set('referenced-element', referencedElementType);
|
|
251
|
-
this.refractCache.set(sourceElement, referencedElement);
|
|
252
|
-
} else {
|
|
253
|
-
// handling direct references
|
|
254
|
-
const sourceElement = referencedElement;
|
|
255
|
-
referencedElement = (0, _apidomNsOpenapi.refract)(referencedElement, {
|
|
256
|
-
element: referencedElementType
|
|
257
|
-
});
|
|
258
|
-
this.refractCache.set(sourceElement, referencedElement);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// detect direct or indirect reference
|
|
94
|
+
// detect direct reference
|
|
263
95
|
if (referencingElement === referencedElement) {
|
|
264
96
|
throw new _apidomError.ApiDOMStructuredError('Recursive Reference Object detected', {
|
|
265
|
-
$ref
|
|
97
|
+
$ref
|
|
266
98
|
});
|
|
267
99
|
}
|
|
268
100
|
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
101
|
+
// a reference without an expected type reads its target as its own type
|
|
102
|
+
const type = (0, _apidomCore.toValue)(referencingElement.meta.get('referenced-element')) ?? referencedElement.element;
|
|
103
|
+
await this.addTransclusion(path, {
|
|
104
|
+
source: referencedElement,
|
|
105
|
+
type,
|
|
106
|
+
reference,
|
|
107
|
+
context: undefined
|
|
108
|
+
}, {
|
|
109
|
+
toTranscludedElement: (element, target) => {
|
|
110
|
+
// a chain of Reference Objects leading back to itself never reaches a value
|
|
111
|
+
if ((0, _apidomNsOpenapi.isReferenceElement)(target)) return element;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Creating a new version of referenced element to avoid modifying the original one.
|
|
115
|
+
*/
|
|
116
|
+
const mergedElement = (0, _apidomDatamodel.cloneShallow)(target);
|
|
117
|
+
// annotate referenced element with info about original referencing element
|
|
118
|
+
mergedElement.meta.set('ref-fields', {
|
|
119
|
+
$ref
|
|
284
120
|
});
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
121
|
+
// annotate fragment with info about origin
|
|
122
|
+
mergedElement.meta.set('ref-origin', reference.uri);
|
|
123
|
+
mergedElement.meta.set('ref-type', element.element);
|
|
124
|
+
return mergedElement;
|
|
125
|
+
},
|
|
126
|
+
circular: {
|
|
127
|
+
uri: $refBaseURI,
|
|
128
|
+
meta: {
|
|
288
129
|
uri: reference.uri,
|
|
289
|
-
$ref
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
path.skip();
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Dive deep into the fragment.
|
|
301
|
-
*
|
|
302
|
-
* Cases to consider:
|
|
303
|
-
* 1. We're crossing document boundary
|
|
304
|
-
* 2. Fragment is from non-entry document
|
|
305
|
-
* 3. Fragment is a Reference Object. We need to follow it to get the eventual value
|
|
306
|
-
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
|
|
307
|
-
*/
|
|
308
|
-
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
309
|
-
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
310
|
-
if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
311
|
-
if (shouldDetectCircular) {
|
|
312
|
-
this.visited.add(referencedElement);
|
|
130
|
+
$ref
|
|
131
|
+
},
|
|
132
|
+
details: {
|
|
133
|
+
$ref
|
|
134
|
+
}
|
|
313
135
|
}
|
|
314
|
-
|
|
315
|
-
// append referencing reference to ancestors lineage
|
|
316
|
-
directAncestors.add(referencingElement);
|
|
317
|
-
const visitor = new OpenAPI2DereferenceVisitor({
|
|
318
|
-
reference,
|
|
319
|
-
indirections: [...this.indirections],
|
|
320
|
-
options: this.options,
|
|
321
|
-
refractCache: this.refractCache,
|
|
322
|
-
visited: this.visited,
|
|
323
|
-
ancestors: ancestorsLineage
|
|
324
|
-
});
|
|
325
|
-
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
326
|
-
mutable: true,
|
|
327
|
-
skipVisited: 'skip'
|
|
328
|
-
});
|
|
329
|
-
directAncestors.delete(referencingElement);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Creating a new version of referenced element to avoid modifying the original one.
|
|
334
|
-
*/
|
|
335
|
-
const mergedElement = (0, _apidomDatamodel.cloneShallow)(referencedElement);
|
|
336
|
-
// annotate referenced element with info about original referencing element
|
|
337
|
-
mergedElement.meta.set('ref-fields', {
|
|
338
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref)
|
|
339
136
|
});
|
|
340
|
-
// annotate fragment with info about origin
|
|
341
|
-
mergedElement.meta.set('ref-origin', reference.uri);
|
|
342
|
-
mergedElement.meta.set('ref-type', referencingElement.element);
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Transclude referencing element with merged referenced element.
|
|
346
|
-
*/
|
|
347
|
-
path.replaceWith(mergedElement);
|
|
348
|
-
path.skip();
|
|
349
137
|
} catch (error) {
|
|
350
|
-
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
351
138
|
this.handleError(`Error while dereferencing Reference Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
|
|
352
|
-
} finally {
|
|
353
|
-
if (this.indirections.length > indirectionsSize) this.indirections.pop();
|
|
354
139
|
}
|
|
355
140
|
}
|
|
356
141
|
async PathItemElement(path) {
|
|
357
142
|
const referencingElement = path.node;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
143
|
+
if (this.isTranscluded(referencingElement)) {
|
|
144
|
+
path.skip();
|
|
361
145
|
return;
|
|
362
146
|
}
|
|
363
147
|
|
|
364
|
-
//
|
|
365
|
-
if (
|
|
366
|
-
path.skip();
|
|
148
|
+
// ignore PathItemElement without $ref field
|
|
149
|
+
if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
367
150
|
return;
|
|
368
151
|
}
|
|
369
|
-
const
|
|
152
|
+
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
153
|
+
const retrievalURI = this.toBaseURI($ref);
|
|
370
154
|
const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
|
|
371
155
|
const isExternalReference = !isInternalReference;
|
|
372
156
|
|
|
@@ -380,287 +164,139 @@ class OpenAPI2DereferenceVisitor {
|
|
|
380
164
|
// skip traversing this Path Item element but traverse all it's child elements
|
|
381
165
|
return;
|
|
382
166
|
}
|
|
383
|
-
const $refBaseURI = url.resolve(retrievalURI,
|
|
384
|
-
const indirectionsSize = this.indirections.length;
|
|
167
|
+
const $refBaseURI = url.resolve(retrievalURI, $ref);
|
|
385
168
|
try {
|
|
386
|
-
const reference = await this.toReference(
|
|
387
|
-
this.indirections.push(referencingElement);
|
|
169
|
+
const reference = await this.toReference($ref);
|
|
388
170
|
const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
389
|
-
|
|
390
171
|
// possibly non-semantic referenced element
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
// applying semantics to a referenced element
|
|
394
|
-
if (!(0, _apidomNsOpenapi.isPathItemElement)(referencedElement)) {
|
|
395
|
-
if (this.refractCache.has(referencedElement)) {
|
|
396
|
-
referencedElement = this.refractCache.get(referencedElement);
|
|
397
|
-
} else {
|
|
398
|
-
const sourceElement = referencedElement;
|
|
399
|
-
referencedElement = (0, _apidomNsOpenapi.refractPathItem)(referencedElement);
|
|
400
|
-
this.refractCache.set(sourceElement, referencedElement);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
172
|
+
const referencedElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
|
|
403
173
|
|
|
404
|
-
// detect direct
|
|
174
|
+
// detect direct reference
|
|
405
175
|
if (referencingElement === referencedElement) {
|
|
406
176
|
throw new _apidomError.ApiDOMStructuredError('Recursive Path Item Object reference detected', {
|
|
407
|
-
$ref
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// detect maximum depth of dereferencing
|
|
412
|
-
if (this.indirections.length > this.options.dereference.maxDepth) {
|
|
413
|
-
throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
|
|
414
|
-
maxDepth: this.options.dereference.maxDepth,
|
|
415
|
-
uri: this.reference.uri
|
|
177
|
+
$ref
|
|
416
178
|
});
|
|
417
179
|
}
|
|
180
|
+
await this.addTransclusion(path, {
|
|
181
|
+
source: referencedElement,
|
|
182
|
+
type: 'pathItem',
|
|
183
|
+
reference,
|
|
184
|
+
context: undefined
|
|
185
|
+
}, {
|
|
186
|
+
toTranscludedElement: (element, target) => {
|
|
187
|
+
if (!(0, _apidomNsOpenapi.isPathItemElement)(target)) return target;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Creating a new version of Path Item by merging fields from referenced Path Item with referencing one.
|
|
191
|
+
*/
|
|
192
|
+
const mergedElement = (0, _apidomDatamodel.cloneShallow)(target);
|
|
193
|
+
// existing keywords from referencing PathItemElement overrides ones from referenced element
|
|
194
|
+
element.forEach((value, keyElement, item) => {
|
|
195
|
+
mergedElement.remove((0, _apidomCore.toValue)(keyElement));
|
|
196
|
+
mergedElement.content.push(item);
|
|
197
|
+
});
|
|
198
|
+
mergedElement.remove('$ref');
|
|
418
199
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
reference.refSet.circular = true;
|
|
423
|
-
if (this.options.dereference.circular === 'error') {
|
|
424
|
-
throw new _apidomError.ApiDOMStructuredError('Circular reference detected', {
|
|
425
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref)
|
|
200
|
+
// annotate referenced element with info about original referencing element
|
|
201
|
+
mergedElement.meta.set('ref-fields', {
|
|
202
|
+
$ref
|
|
426
203
|
});
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
204
|
+
// annotate referenced element with info about origin and type
|
|
205
|
+
mergedElement.meta.set('ref-origin', reference.uri);
|
|
206
|
+
mergedElement.meta.set('ref-type', element.element);
|
|
207
|
+
return mergedElement;
|
|
208
|
+
},
|
|
209
|
+
aliasField: '$ref',
|
|
210
|
+
circular: {
|
|
211
|
+
uri: $refBaseURI,
|
|
212
|
+
meta: {
|
|
430
213
|
uri: reference.uri,
|
|
431
|
-
$ref
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
path.skip();
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Dive deep into the fragment.
|
|
443
|
-
*
|
|
444
|
-
* Cases to consider:
|
|
445
|
-
* 1. We're crossing document boundary
|
|
446
|
-
* 2. Fragment is from non-entry document
|
|
447
|
-
* 3. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
|
|
448
|
-
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
|
|
449
|
-
*/
|
|
450
|
-
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
451
|
-
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
452
|
-
if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isPathItemElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
453
|
-
if (shouldDetectCircular) {
|
|
454
|
-
this.visited.add(referencedElement);
|
|
214
|
+
$ref
|
|
215
|
+
},
|
|
216
|
+
details: {
|
|
217
|
+
$ref
|
|
218
|
+
}
|
|
455
219
|
}
|
|
456
|
-
|
|
457
|
-
// append referencing reference to ancestors lineage
|
|
458
|
-
directAncestors.add(referencingElement);
|
|
459
|
-
const visitor = new OpenAPI2DereferenceVisitor({
|
|
460
|
-
reference,
|
|
461
|
-
indirections: [...this.indirections],
|
|
462
|
-
options: this.options,
|
|
463
|
-
refractCache: this.refractCache,
|
|
464
|
-
visited: this.visited,
|
|
465
|
-
ancestors: ancestorsLineage
|
|
466
|
-
});
|
|
467
|
-
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
468
|
-
mutable: true,
|
|
469
|
-
skipVisited: 'skip'
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
// remove referencing reference from ancestors lineage
|
|
473
|
-
directAncestors.delete(referencingElement);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Creating a new version of Path Item by merging fields from referenced Path Item with referencing one.
|
|
478
|
-
*/
|
|
479
|
-
if ((0, _apidomNsOpenapi.isPathItemElement)(referencedElement)) {
|
|
480
|
-
const mergedElement = (0, _apidomDatamodel.cloneShallow)(referencedElement);
|
|
481
|
-
// existing keywords from referencing PathItemElement overrides ones from referenced element
|
|
482
|
-
referencingElement.forEach((value, keyElement, item) => {
|
|
483
|
-
mergedElement.remove((0, _apidomCore.toValue)(keyElement));
|
|
484
|
-
mergedElement.content.push(item);
|
|
485
|
-
});
|
|
486
|
-
mergedElement.remove('$ref');
|
|
487
|
-
|
|
488
|
-
// annotate referenced element with info about original referencing element
|
|
489
|
-
mergedElement.meta.set('ref-fields', {
|
|
490
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref)
|
|
491
|
-
});
|
|
492
|
-
// annotate referenced element with info about origin and type
|
|
493
|
-
mergedElement.meta.set('ref-origin', reference.uri);
|
|
494
|
-
mergedElement.meta.set('ref-type', referencingElement.element);
|
|
495
|
-
referencedElement = mergedElement;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Transclude referencing element with merged referenced element.
|
|
500
|
-
*/
|
|
501
|
-
path.replaceWith(referencedElement);
|
|
502
|
-
return;
|
|
220
|
+
});
|
|
503
221
|
} catch (error) {
|
|
504
|
-
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
505
222
|
this.handleError(`Error while dereferencing Path Item Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
|
|
506
|
-
} finally {
|
|
507
|
-
if (this.indirections.length > indirectionsSize) this.indirections.pop();
|
|
508
223
|
}
|
|
509
224
|
}
|
|
510
225
|
async JSONReferenceElement(path) {
|
|
511
226
|
const referencingElement = path.node;
|
|
512
227
|
|
|
513
|
-
//
|
|
514
|
-
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
228
|
+
// the fields of a JSON Reference Object hold no references
|
|
229
|
+
path.skip();
|
|
517
230
|
|
|
518
|
-
//
|
|
519
|
-
if (this.
|
|
520
|
-
path.skip();
|
|
231
|
+
// ignore JSON Reference Object without a string $ref, e.g. an empty YAML value
|
|
232
|
+
if (this.isTranscluded(referencingElement) || !(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
521
233
|
return;
|
|
522
234
|
}
|
|
523
|
-
const
|
|
235
|
+
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
236
|
+
const retrievalURI = this.toBaseURI($ref);
|
|
524
237
|
const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
|
|
525
238
|
const isExternalReference = !isInternalReference;
|
|
526
239
|
|
|
527
|
-
// ignore resolving internal
|
|
240
|
+
// ignore resolving internal JSON Reference Objects
|
|
528
241
|
if (!this.options.resolve.internal && isInternalReference) {
|
|
529
|
-
// skip traversing this JSONReference element and all it's child elements
|
|
530
|
-
path.skip();
|
|
531
242
|
return;
|
|
532
243
|
}
|
|
533
|
-
// ignore resolving external
|
|
244
|
+
// ignore resolving external JSON Reference Objects
|
|
534
245
|
if (!this.options.resolve.external && isExternalReference) {
|
|
535
|
-
// skip traversing this JSONReference element and all it's child elements
|
|
536
|
-
path.skip();
|
|
537
246
|
return;
|
|
538
247
|
}
|
|
539
|
-
const $refBaseURI = url.resolve(retrievalURI,
|
|
540
|
-
const indirectionsSize = this.indirections.length;
|
|
248
|
+
const $refBaseURI = url.resolve(retrievalURI, $ref);
|
|
541
249
|
try {
|
|
542
|
-
const reference = await this.toReference(
|
|
543
|
-
this.indirections.push(referencingElement);
|
|
250
|
+
const reference = await this.toReference($ref);
|
|
544
251
|
const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
545
|
-
|
|
546
252
|
// possibly non-semantic fragment
|
|
547
|
-
|
|
253
|
+
const referencedElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
|
|
548
254
|
|
|
549
|
-
//
|
|
550
|
-
const referencedElementType = referencingElement.meta.get('referenced-element');
|
|
551
|
-
if (referencedElement.element !== referencedElementType && !(0, _apidomNsOpenapi.isJSONReferenceElement)(referencedElement)) {
|
|
552
|
-
if (this.refractCache.has(referencedElement)) {
|
|
553
|
-
referencedElement = this.refractCache.get(referencedElement);
|
|
554
|
-
} else if ((0, _apidomNsOpenapi.isJSONReferenceLikeElement)(referencedElement)) {
|
|
555
|
-
// handling generic indirect references
|
|
556
|
-
const sourceElement = referencedElement;
|
|
557
|
-
referencedElement = (0, _apidomNsOpenapi.refractJSONReference)(referencedElement);
|
|
558
|
-
referencedElement.meta.set('referenced-element', referencedElementType);
|
|
559
|
-
this.refractCache.set(sourceElement, referencedElement);
|
|
560
|
-
} else {
|
|
561
|
-
// handling direct references
|
|
562
|
-
const sourceElement = referencedElement;
|
|
563
|
-
referencedElement = (0, _apidomNsOpenapi.refract)(referencedElement, {
|
|
564
|
-
element: referencedElementType
|
|
565
|
-
});
|
|
566
|
-
this.refractCache.set(sourceElement, referencedElement);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// detect direct or indirect reference
|
|
255
|
+
// detect direct reference
|
|
571
256
|
if (referencingElement === referencedElement) {
|
|
572
257
|
throw new _apidomError.ApiDOMStructuredError('Recursive JSON Reference Object detected', {
|
|
573
|
-
$ref
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// detect maximum depth of dereferencing
|
|
578
|
-
if (this.indirections.length > this.options.dereference.maxDepth) {
|
|
579
|
-
throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
|
|
580
|
-
maxDepth: this.options.dereference.maxDepth,
|
|
581
|
-
uri: this.reference.uri
|
|
258
|
+
$ref
|
|
582
259
|
});
|
|
583
260
|
}
|
|
584
261
|
|
|
585
|
-
//
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
262
|
+
// a reference without an expected type reads its target as its own type
|
|
263
|
+
const type = (0, _apidomCore.toValue)(referencingElement.meta.get('referenced-element')) ?? referencedElement.element;
|
|
264
|
+
await this.addTransclusion(path, {
|
|
265
|
+
source: referencedElement,
|
|
266
|
+
type,
|
|
267
|
+
reference,
|
|
268
|
+
context: undefined
|
|
269
|
+
}, {
|
|
270
|
+
toTranscludedElement: (element, target) => {
|
|
271
|
+
// a chain of JSON Reference Objects leading back to itself never reaches a value
|
|
272
|
+
if ((0, _apidomNsOpenapi.isJSONReferenceElement)(target)) return element;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Creating a new version of referenced element to avoid modifying the original one.
|
|
276
|
+
*/
|
|
277
|
+
const mergedElement = (0, _apidomDatamodel.cloneShallow)(target);
|
|
278
|
+
// annotate referenced element with info about original referencing element
|
|
279
|
+
mergedElement.meta.set('ref-fields', {
|
|
280
|
+
$ref
|
|
592
281
|
});
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
282
|
+
// annotate fragment with info about origin
|
|
283
|
+
mergedElement.meta.set('ref-origin', reference.uri);
|
|
284
|
+
mergedElement.meta.set('ref-type', element.element);
|
|
285
|
+
return mergedElement;
|
|
286
|
+
},
|
|
287
|
+
circular: {
|
|
288
|
+
uri: $refBaseURI,
|
|
289
|
+
meta: {
|
|
596
290
|
uri: reference.uri,
|
|
597
|
-
$ref
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
path.skip();
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
/**
|
|
608
|
-
* Dive deep into the fragment.
|
|
609
|
-
*
|
|
610
|
-
* Cases to consider:
|
|
611
|
-
* 1. We're crossing document boundary
|
|
612
|
-
2. Fragment is from non-entry document
|
|
613
|
-
* 3. Fragment is a JSON Reference Object. We need to follow it to get the eventual value
|
|
614
|
-
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
|
|
615
|
-
*/
|
|
616
|
-
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
617
|
-
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
618
|
-
if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isJSONReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
619
|
-
if (shouldDetectCircular) {
|
|
620
|
-
this.visited.add(referencedElement);
|
|
291
|
+
$ref
|
|
292
|
+
},
|
|
293
|
+
details: {
|
|
294
|
+
$ref
|
|
295
|
+
}
|
|
621
296
|
}
|
|
622
|
-
|
|
623
|
-
// append referencing reference to ancestors lineage
|
|
624
|
-
directAncestors.add(referencingElement);
|
|
625
|
-
const visitor = new OpenAPI2DereferenceVisitor({
|
|
626
|
-
reference,
|
|
627
|
-
indirections: [...this.indirections],
|
|
628
|
-
options: this.options,
|
|
629
|
-
refractCache: this.refractCache,
|
|
630
|
-
visited: this.visited,
|
|
631
|
-
ancestors: ancestorsLineage
|
|
632
|
-
});
|
|
633
|
-
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
634
|
-
mutable: true,
|
|
635
|
-
skipVisited: 'skip'
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
// remove referencing reference from ancestors lineage
|
|
639
|
-
directAncestors.delete(referencingElement);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* Creating a new version of referenced element to avoid modifying the original one.
|
|
644
|
-
*/
|
|
645
|
-
const mergedElement = (0, _apidomDatamodel.cloneShallow)(referencedElement);
|
|
646
|
-
// annotate referenced element with info about original referencing element
|
|
647
|
-
mergedElement.meta.set('ref-fields', {
|
|
648
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref)
|
|
649
297
|
});
|
|
650
|
-
// annotate fragment with info about origin
|
|
651
|
-
mergedElement.meta.set('ref-origin', reference.uri);
|
|
652
|
-
mergedElement.meta.set('ref-type', referencingElement.element);
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* Transclude referencing element with merged referenced element.
|
|
656
|
-
*/
|
|
657
|
-
path.replaceWith(mergedElement);
|
|
658
|
-
path.skip();
|
|
659
298
|
} catch (error) {
|
|
660
|
-
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
661
299
|
this.handleError(`Error while dereferencing JSON Reference Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
|
|
662
|
-
} finally {
|
|
663
|
-
if (this.indirections.length > indirectionsSize) this.indirections.pop();
|
|
664
300
|
}
|
|
665
301
|
}
|
|
666
302
|
}
|