@speclynx/apidom-reference 6.1.2 → 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 +63 -0
- package/README.md +57 -0
- package/dist/apidom-reference.browser.js +5783 -3741
- package/dist/apidom-reference.browser.min.js +1 -1
- package/package.json +31 -30
- package/src/File.cjs +5 -2
- package/src/File.mjs +5 -2
- package/src/bundle/index.cjs +6 -4
- package/src/bundle/index.mjs +6 -4
- 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/index.cjs +8 -5
- package/src/dereference/index.mjs +8 -5
- 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/options/index.cjs +4 -0
- package/src/options/index.mjs +4 -0
- package/src/options/util.cjs +7 -1
- package/src/options/util.mjs +8 -2
- 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/src/resolve/index.cjs +6 -3
- package/src/resolve/index.mjs +6 -3
- package/src/util/url.cjs +38 -8
- package/src/util/url.mjs +35 -6
- package/types/apidom-reference.d.ts +13 -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/types/util/url.d.ts +11 -0
- package/LICENSES/AFL-3.0.txt +0 -182
- package/LICENSES/BSD-3-Clause.txt +0 -26
- package/src/dereference/util.cjs +0 -33
- package/src/dereference/util.mjs +0 -27
- package/types/dereference/util.d.ts +0 -9
|
@@ -17,399 +17,247 @@ var _apidomNsOpenapi = require("@speclynx/apidom-ns-openapi-3-1");
|
|
|
17
17
|
var _$anchor = require("./selectors/$anchor.cjs");
|
|
18
18
|
var _uri = require("./selectors/uri.cjs");
|
|
19
19
|
var _jsonPointer = require("./selectors/json-pointer.cjs");
|
|
20
|
-
var _MaximumDereferenceDepthError = _interopRequireDefault(require("../../../errors/MaximumDereferenceDepthError.cjs"));
|
|
21
|
-
var _MaximumResolveDepthError = _interopRequireDefault(require("../../../errors/MaximumResolveDepthError.cjs"));
|
|
22
|
-
var _UnresolvableReferenceError = _interopRequireDefault(require("../../../errors/UnresolvableReferenceError.cjs"));
|
|
23
20
|
var _EvaluationJsonSchemaUriError = _interopRequireDefault(require("../../../errors/EvaluationJsonSchemaUriError.cjs"));
|
|
21
|
+
var _DereferenceVisitor = _interopRequireDefault(require("../DereferenceVisitor.cjs"));
|
|
24
22
|
var url = _interopRequireWildcard(require("../../../util/url.cjs"));
|
|
25
|
-
var _index = _interopRequireDefault(require("../../../parse/index.cjs"));
|
|
26
|
-
var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
|
|
27
23
|
var _File = _interopRequireDefault(require("../../../File.cjs"));
|
|
28
24
|
var _util = require("./util.cjs");
|
|
29
|
-
var _util2 = require("../../util.cjs");
|
|
30
25
|
/**
|
|
31
|
-
*
|
|
26
|
+
* Type a Schema Object `$ref` expects its target to have.
|
|
27
|
+
*/
|
|
28
|
+
const SCHEMA_TYPE = 'schema';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Context of a source being dereferenced: the `$id`s of the Schema Objects enclosing it.
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* @public
|
|
36
36
|
*/
|
|
37
|
-
class OpenAPI3_1DereferenceVisitor {
|
|
38
|
-
indirections;
|
|
39
|
-
reference;
|
|
40
|
-
options;
|
|
41
|
-
refractCache;
|
|
42
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Dereferences an OpenAPI 3.1 document. Reference Objects expect the type of their
|
|
40
|
+
* `referenced-element` meta, Path Item `$ref`s a Path Item, Schema Object `$ref`s a Schema Object.
|
|
41
|
+
* Link Objects get the Operation Object they point at attached, Example Objects
|
|
42
|
+
* the external value.
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
class OpenAPI3_1DereferenceVisitor extends _DereferenceVisitor.default {
|
|
43
47
|
/**
|
|
44
|
-
* Index of `$id`-bearing schemas per document, shared across
|
|
45
|
-
* of a single dereference run
|
|
48
|
+
* Index of `$id`-bearing schemas per document, shared across the visitors
|
|
49
|
+
* of a single dereference run.
|
|
46
50
|
*/
|
|
47
51
|
schema$idIndex;
|
|
48
|
-
visited;
|
|
49
52
|
|
|
50
53
|
/**
|
|
51
54
|
* `$id`s of the JSON Schema elements enclosing the traversal root within its
|
|
52
55
|
* document (outermost first). Empty when the root is the document itself; a
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
+
* visitor traversing a fragment is seeded with the `$id`s of the schemas the
|
|
57
|
+
* fragment sits under, so the `$ref`s and `$id`s within resolve against the
|
|
58
|
+
* same base as they would from the document root.
|
|
56
59
|
*/
|
|
57
60
|
ancestorSchema$ids;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Tracks element ancestors across dive-deep traversal boundaries.
|
|
61
|
-
* Used for cycle detection: if a referenced element is found in
|
|
62
|
-
* the ancestor lineage, a circular reference is detected.
|
|
63
|
-
*/
|
|
64
|
-
ancestors;
|
|
65
61
|
constructor({
|
|
66
62
|
reference,
|
|
67
63
|
options,
|
|
68
|
-
indirections = [],
|
|
69
|
-
refractCache = new WeakMap(),
|
|
70
64
|
schema$idIndex = new WeakMap(),
|
|
71
|
-
ancestors = new _util2.AncestorLineage(),
|
|
72
|
-
visited = new WeakSet(),
|
|
73
65
|
ancestorSchema$ids = []
|
|
74
66
|
}) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
67
|
+
super({
|
|
68
|
+
reference,
|
|
69
|
+
options
|
|
70
|
+
});
|
|
79
71
|
this.schema$idIndex = schema$idIndex;
|
|
80
|
-
this.ancestors = new _util2.AncestorLineage(...ancestors);
|
|
81
|
-
this.visited = visited;
|
|
82
72
|
this.ancestorSchema$ids = ancestorSchema$ids;
|
|
83
73
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const directAncestors = new Set(ancestorNodes.filter(_apidomDatamodel.isElement));
|
|
87
|
-
const ancestorsLineage = new _util2.AncestorLineage(...this.ancestors, directAncestors);
|
|
88
|
-
return [ancestorsLineage, directAncestors];
|
|
74
|
+
get strategyName() {
|
|
75
|
+
return 'openapi-3-1';
|
|
89
76
|
}
|
|
90
|
-
|
|
91
|
-
|
|
77
|
+
createVisitor(reference, {
|
|
78
|
+
ancestorSchema$ids
|
|
79
|
+
}) {
|
|
80
|
+
return new OpenAPI3_1DereferenceVisitor({
|
|
81
|
+
reference,
|
|
82
|
+
options: this.options,
|
|
83
|
+
schema$idIndex: this.schema$idIndex,
|
|
84
|
+
ancestorSchema$ids
|
|
85
|
+
});
|
|
92
86
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A Reference Object stands for the type its reference expects: it's dereferenced as that type.
|
|
90
|
+
*/
|
|
91
|
+
isOfType(element, type) {
|
|
92
|
+
if ((0, _apidomNsOpenapi.isReferenceElement)(element)) return true;
|
|
93
|
+
return type === SCHEMA_TYPE ? (0, _apidomNsOpenapi.isSchemaElement)(element) || (0, _apidomNsOpenapi.isBooleanJSONSchemaElement)(element) : element.element === type;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* A generic structure holding a `$ref` is a Reference Object to the expected type,
|
|
98
|
+
* unless the type has its own `$ref` field (Path Item Object, Schema Object).
|
|
99
|
+
*/
|
|
100
|
+
refractTo(element, type) {
|
|
101
|
+
if (type !== SCHEMA_TYPE && type !== 'pathItem' && (0, _apidomNsOpenapi.isReferenceLikeElement)(element)) {
|
|
102
|
+
const referenceElement = (0, _apidomNsOpenapi.refractReference)(element);
|
|
103
|
+
referenceElement.meta.set('referenced-element', type);
|
|
104
|
+
return referenceElement;
|
|
109
105
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
parse: {
|
|
113
|
-
...this.options.parse,
|
|
114
|
-
mediaType: 'text/plain'
|
|
115
|
-
}
|
|
106
|
+
return (0, _apidomNsOpenapi.refract)(element, {
|
|
107
|
+
element: type
|
|
116
108
|
});
|
|
109
|
+
}
|
|
117
110
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// instead of the mutable working copy. The `immutable://` reference below preserves
|
|
129
|
-
// the original tree and could be used for pointer evaluation, ensuring every resolution
|
|
130
|
-
// context always sees raw, unresolved elements and processes them with its own
|
|
131
|
-
// ancestors/indirections. The trade-off is that elements referenced by multiple
|
|
132
|
-
// documents would be resolved once per context instead of being reused.
|
|
133
|
-
const mutableReference = new _Reference.default({
|
|
134
|
-
uri: baseURI,
|
|
135
|
-
value: this.options.dereference.immutable ? (0, _apidomDatamodel.cloneDeep)(parseResult) : parseResult,
|
|
136
|
-
depth: this.reference.depth + 1
|
|
137
|
-
});
|
|
138
|
-
refSet.add(mutableReference);
|
|
139
|
-
if (this.options.dereference.immutable) {
|
|
140
|
-
// register new immutable reference with original parseResult
|
|
141
|
-
const immutableReference = new _Reference.default({
|
|
142
|
-
uri: `immutable://${baseURI}`,
|
|
143
|
-
value: parseResult,
|
|
144
|
-
depth: this.reference.depth + 1
|
|
145
|
-
});
|
|
146
|
-
refSet.add(immutableReference);
|
|
111
|
+
/**
|
|
112
|
+
* A document as Schema Object for locating schemas within it: a generic document is
|
|
113
|
+
* refracted to Schema Object once per dereference run, a semantic one is used as it is.
|
|
114
|
+
*/
|
|
115
|
+
toSchemaDocument(document) {
|
|
116
|
+
if (!(0, _apidomDatamodel.isPrimitiveElement)(document)) return document;
|
|
117
|
+
let byType = this.run.refracted.get(document);
|
|
118
|
+
if (byType === undefined) {
|
|
119
|
+
byType = new Map();
|
|
120
|
+
this.run.refracted.set(document, byType);
|
|
147
121
|
}
|
|
148
|
-
|
|
122
|
+
let refracted = byType.get(SCHEMA_TYPE);
|
|
123
|
+
if (refracted === undefined) {
|
|
124
|
+
refracted = (0, _apidomNsOpenapi.refractSchema)(document);
|
|
125
|
+
byType.set(SCHEMA_TYPE, refracted);
|
|
126
|
+
}
|
|
127
|
+
return refracted;
|
|
149
128
|
}
|
|
150
129
|
|
|
151
130
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* (type, uri, location, codeFrame, refFieldName, refFieldValue, trace).
|
|
156
|
-
* For errors already wrapped by a nested visitor: prepends the current hop to the trace.
|
|
157
|
-
*
|
|
158
|
-
* Inner/intermediate visitors always throw to let the trace accumulate.
|
|
159
|
-
* Only the entry document visitor respects continueOnError (callback/swallow/throw).
|
|
131
|
+
* Indexes the `$id`-bearing schemas of an element for the run. Transcluded elements aren't
|
|
132
|
+
* indexed: they're copies of structures indexed where they're defined,
|
|
133
|
+
* and in a document dereferenced in place their shared structure would be walked once per path.
|
|
160
134
|
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// dereferenceApiDOM is called with a fragment (cloneShallow creates a new root identity).
|
|
174
|
-
let location;
|
|
175
|
-
const root = this.reference.value.result;
|
|
176
|
-
if ((0, _apidomDatamodel.isElement)(root)) {
|
|
177
|
-
(0, _apidomTraverse.traverse)(root, {
|
|
178
|
-
enter: p => {
|
|
179
|
-
if (p.node === referencingElement || this.refractCache.get(p.node) === referencingElement) {
|
|
180
|
-
location = p.formatPath();
|
|
181
|
-
p.stop();
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
location ??= visitorPath.formatPath();
|
|
187
|
-
const hop = {
|
|
188
|
-
uri,
|
|
189
|
-
type,
|
|
190
|
-
refFieldName,
|
|
191
|
-
refFieldValue,
|
|
192
|
-
location,
|
|
193
|
-
codeFrame
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
// enrich existing error from nested visitor or create new one
|
|
197
|
-
let unresolvedError;
|
|
198
|
-
if (error instanceof _UnresolvableReferenceError.default) {
|
|
199
|
-
// prefix relative locations for entries belonging to the referenced document
|
|
200
|
-
const refBaseURI = this.toBaseURI(refFieldValue);
|
|
201
|
-
const fragment = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference(refFieldValue);
|
|
202
|
-
if (fragment) {
|
|
203
|
-
if (refBaseURI === error.uri && error.location) {
|
|
204
|
-
error.location = fragment + error.location;
|
|
205
|
-
}
|
|
206
|
-
for (const h of error.trace) {
|
|
207
|
-
if (h.uri === refBaseURI && h.location) h.location = fragment + h.location;
|
|
135
|
+
index$ids(element) {
|
|
136
|
+
if (this.schema$idIndex.has(element)) return;
|
|
137
|
+
const entries = [];
|
|
138
|
+
(0, _apidomTraverse.traverse)(element, {
|
|
139
|
+
enter: path => {
|
|
140
|
+
if (this.isTranscluded(path.node)) {
|
|
141
|
+
path.skip();
|
|
142
|
+
} else if ((0, _util.isJSONSchemaElementWith$id)(path.node)) {
|
|
143
|
+
entries.push({
|
|
144
|
+
schema: path.node,
|
|
145
|
+
$ids: (0, _util.collectSchema$ids)(path)
|
|
146
|
+
});
|
|
208
147
|
}
|
|
209
148
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
unresolvedError = error;
|
|
213
|
-
} else {
|
|
214
|
-
unresolvedError = new _UnresolvableReferenceError.default(message, {
|
|
215
|
-
cause: error,
|
|
216
|
-
type,
|
|
217
|
-
uri,
|
|
218
|
-
location,
|
|
219
|
-
codeFrame,
|
|
220
|
-
refFieldName,
|
|
221
|
-
refFieldValue,
|
|
222
|
-
trace: []
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
if (!isEntryDocument || continueOnError === false) throw unresolvedError;
|
|
226
|
-
if (typeof continueOnError === 'function') continueOnError(unresolvedError);
|
|
149
|
+
});
|
|
150
|
+
this.schema$idIndex.set(element, entries);
|
|
227
151
|
}
|
|
228
152
|
async ReferenceElement(path) {
|
|
229
153
|
const referencingElement = path.node;
|
|
230
154
|
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
155
|
+
// the fields of a Reference Object hold no references
|
|
156
|
+
path.skip();
|
|
235
157
|
|
|
236
|
-
//
|
|
237
|
-
if (this.
|
|
238
|
-
path.skip();
|
|
158
|
+
// ignore Reference Object without a string $ref, e.g. an empty YAML value
|
|
159
|
+
if (this.isTranscluded(referencingElement) || !(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
239
160
|
return;
|
|
240
161
|
}
|
|
241
|
-
const
|
|
162
|
+
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
163
|
+
const retrievalURI = this.toBaseURI($ref);
|
|
242
164
|
const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
|
|
243
165
|
const isExternalReference = !isInternalReference;
|
|
244
166
|
|
|
245
167
|
// ignore resolving internal Reference Objects
|
|
246
168
|
if (!this.options.resolve.internal && isInternalReference) {
|
|
247
|
-
// skip traversing this reference element and all it's child elements
|
|
248
|
-
path.skip();
|
|
249
169
|
return;
|
|
250
170
|
}
|
|
251
171
|
// ignore resolving external Reference Objects
|
|
252
172
|
if (!this.options.resolve.external && isExternalReference) {
|
|
253
|
-
// skip traversing this reference element and all it's child elements
|
|
254
|
-
path.skip();
|
|
255
173
|
return;
|
|
256
174
|
}
|
|
257
|
-
const $refBaseURI = url.resolve(retrievalURI,
|
|
258
|
-
const indirectionsSize = this.indirections.length;
|
|
175
|
+
const $refBaseURI = url.resolve(retrievalURI, $ref);
|
|
259
176
|
try {
|
|
260
|
-
const reference = await this.toReference(
|
|
261
|
-
this.indirections.push(referencingElement);
|
|
177
|
+
const reference = await this.toReference($ref);
|
|
262
178
|
const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
263
|
-
|
|
264
179
|
// possibly non-semantic fragment
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
// applying semantics to a fragment
|
|
268
|
-
const referencedElementType = referencingElement.meta.get('referenced-element');
|
|
269
|
-
if (referencedElement.element !== referencedElementType && !(0, _apidomNsOpenapi.isReferenceElement)(referencedElement)) {
|
|
270
|
-
if (this.refractCache.has(referencedElement)) {
|
|
271
|
-
referencedElement = this.refractCache.get(referencedElement);
|
|
272
|
-
} else if ((0, _apidomNsOpenapi.isReferenceLikeElement)(referencedElement)) {
|
|
273
|
-
// handling generic indirect references
|
|
274
|
-
const sourceElement = referencedElement;
|
|
275
|
-
referencedElement = (0, _apidomNsOpenapi.refractReference)(referencedElement);
|
|
276
|
-
referencedElement.meta.set('referenced-element', referencedElementType);
|
|
277
|
-
this.refractCache.set(sourceElement, referencedElement);
|
|
278
|
-
} else {
|
|
279
|
-
// handling direct references
|
|
280
|
-
const sourceElement = referencedElement;
|
|
281
|
-
referencedElement = (0, _apidomNsOpenapi.refract)(referencedElement, {
|
|
282
|
-
element: referencedElementType
|
|
283
|
-
});
|
|
284
|
-
this.refractCache.set(sourceElement, referencedElement);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
180
|
+
const referencedElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
|
|
287
181
|
|
|
288
|
-
// detect direct
|
|
182
|
+
// detect direct reference
|
|
289
183
|
if (referencingElement === referencedElement) {
|
|
290
184
|
throw new _apidomError.ApiDOMStructuredError('Recursive Reference Object detected', {
|
|
291
|
-
$ref
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// detect maximum depth of dereferencing
|
|
296
|
-
if (this.indirections.length > this.options.dereference.maxDepth) {
|
|
297
|
-
throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
|
|
298
|
-
maxDepth: this.options.dereference.maxDepth,
|
|
299
|
-
uri: this.reference.uri
|
|
185
|
+
$ref
|
|
300
186
|
});
|
|
301
187
|
}
|
|
302
188
|
|
|
303
|
-
//
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
189
|
+
// a reference without an expected type reads its target as its own type
|
|
190
|
+
const type = (0, _apidomCore.toValue)(referencingElement.meta.get('referenced-element')) ?? referencedElement.element;
|
|
191
|
+
await this.addTransclusion(path, {
|
|
192
|
+
source: referencedElement,
|
|
193
|
+
type,
|
|
194
|
+
reference,
|
|
195
|
+
context: {
|
|
196
|
+
ancestorSchema$ids: []
|
|
197
|
+
}
|
|
198
|
+
}, {
|
|
199
|
+
toTranscludedElement: (element, target) => {
|
|
200
|
+
// a chain of Reference Objects leading back to itself never reaches a value
|
|
201
|
+
if ((0, _apidomNsOpenapi.isReferenceElement)(target)) return element;
|
|
202
|
+
const reusingElement = element;
|
|
203
|
+
/**
|
|
204
|
+
* Creating a new version of referenced element to avoid modifying the original one.
|
|
205
|
+
*/
|
|
206
|
+
const mergedElement = (0, _apidomDatamodel.cloneShallow)(target);
|
|
207
|
+
// annotate fragment with info about original Reference element
|
|
208
|
+
mergedElement.meta.set('ref-fields', {
|
|
209
|
+
$ref,
|
|
210
|
+
description: (0, _apidomCore.toValue)(reusingElement.get('description')),
|
|
211
|
+
summary: (0, _apidomCore.toValue)(reusingElement.get('summary'))
|
|
310
212
|
});
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
213
|
+
// annotate fragment with info about origin and type
|
|
214
|
+
mergedElement.meta.set('ref-origin', reference.uri);
|
|
215
|
+
mergedElement.meta.set('ref-type', element.element);
|
|
216
|
+
|
|
217
|
+
// override description and summary (outer has higher priority then inner)
|
|
218
|
+
if ((0, _apidomDatamodel.isObjectElement)(target) && (0, _apidomDatamodel.isObjectElement)(mergedElement)) {
|
|
219
|
+
const fields = (0, _apidomCore.fixedFields)(target, {
|
|
220
|
+
indexed: true
|
|
221
|
+
});
|
|
222
|
+
if (reusingElement.hasKey('description') && Object.hasOwn(fields, 'description')) {
|
|
223
|
+
mergedElement.remove('description');
|
|
224
|
+
mergedElement.set('description', reusingElement.get('description'));
|
|
225
|
+
}
|
|
226
|
+
if (reusingElement.hasKey('summary') && Object.hasOwn(fields, 'summary')) {
|
|
227
|
+
mergedElement.remove('summary');
|
|
228
|
+
mergedElement.set('summary', reusingElement.get('summary'));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return mergedElement;
|
|
232
|
+
},
|
|
233
|
+
circular: {
|
|
234
|
+
uri: $refBaseURI,
|
|
235
|
+
meta: {
|
|
314
236
|
uri: reference.uri,
|
|
315
|
-
$ref
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
path.skip();
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Dive deep into the fragment.
|
|
327
|
-
*
|
|
328
|
-
* Cases to consider:
|
|
329
|
-
* 1. We're crossing document boundary
|
|
330
|
-
* 2. Fragment is from non-entry document
|
|
331
|
-
* 3. Fragment is a Reference Object. We need to follow it to get the eventual value
|
|
332
|
-
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
|
|
333
|
-
*/
|
|
334
|
-
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
335
|
-
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
336
|
-
if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
337
|
-
if (shouldDetectCircular) {
|
|
338
|
-
this.visited.add(referencedElement);
|
|
237
|
+
$ref
|
|
238
|
+
},
|
|
239
|
+
details: {
|
|
240
|
+
$ref
|
|
241
|
+
}
|
|
339
242
|
}
|
|
340
|
-
directAncestors.add(referencingElement);
|
|
341
|
-
const visitor = new OpenAPI3_1DereferenceVisitor({
|
|
342
|
-
reference,
|
|
343
|
-
indirections: [...this.indirections],
|
|
344
|
-
options: this.options,
|
|
345
|
-
refractCache: this.refractCache,
|
|
346
|
-
schema$idIndex: this.schema$idIndex,
|
|
347
|
-
visited: this.visited,
|
|
348
|
-
ancestors: ancestorsLineage
|
|
349
|
-
});
|
|
350
|
-
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
351
|
-
mutable: true,
|
|
352
|
-
skipVisited: 'skip'
|
|
353
|
-
});
|
|
354
|
-
directAncestors.delete(referencingElement);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Creating a new version of referenced element to avoid modifying the original one.
|
|
359
|
-
*/
|
|
360
|
-
const mergedElement = (0, _apidomDatamodel.cloneShallow)(referencedElement);
|
|
361
|
-
// annotate fragment with info about original Reference element
|
|
362
|
-
mergedElement.meta.set('ref-fields', {
|
|
363
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref),
|
|
364
|
-
// @ts-ignore
|
|
365
|
-
description: (0, _apidomCore.toValue)(referencingElement.description),
|
|
366
|
-
// @ts-ignore
|
|
367
|
-
summary: (0, _apidomCore.toValue)(referencingElement.summary)
|
|
368
243
|
});
|
|
369
|
-
// annotate fragment with info about origin and type
|
|
370
|
-
mergedElement.meta.set('ref-origin', reference.uri);
|
|
371
|
-
mergedElement.meta.set('ref-type', referencingElement.element);
|
|
372
|
-
|
|
373
|
-
// override description and summary (outer has higher priority then inner)
|
|
374
|
-
if ((0, _apidomDatamodel.isObjectElement)(referencedElement) && (0, _apidomDatamodel.isObjectElement)(mergedElement)) {
|
|
375
|
-
const fields = (0, _apidomCore.fixedFields)(referencedElement, {
|
|
376
|
-
indexed: true
|
|
377
|
-
});
|
|
378
|
-
if (referencingElement.hasKey('description') && Object.hasOwn(fields, 'description')) {
|
|
379
|
-
mergedElement.remove('description');
|
|
380
|
-
mergedElement.set('description', referencingElement.get('description'));
|
|
381
|
-
}
|
|
382
|
-
if (referencingElement.hasKey('summary') && Object.hasOwn(fields, 'summary')) {
|
|
383
|
-
mergedElement.remove('summary');
|
|
384
|
-
mergedElement.set('summary', referencingElement.get('summary'));
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Transclude referencing element with merged referenced element.
|
|
390
|
-
*/
|
|
391
|
-
path.replaceWith(mergedElement);
|
|
392
244
|
} catch (error) {
|
|
393
|
-
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
394
245
|
this.handleError(`Error while dereferencing Reference Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
|
|
395
|
-
} finally {
|
|
396
|
-
if (this.indirections.length > indirectionsSize) this.indirections.pop();
|
|
397
246
|
}
|
|
398
247
|
}
|
|
399
248
|
async PathItemElement(path) {
|
|
400
249
|
const referencingElement = path.node;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
250
|
+
if (this.isTranscluded(referencingElement)) {
|
|
251
|
+
path.skip();
|
|
404
252
|
return;
|
|
405
253
|
}
|
|
406
254
|
|
|
407
|
-
//
|
|
408
|
-
if (
|
|
409
|
-
path.skip();
|
|
255
|
+
// ignore PathItemElement without $ref field
|
|
256
|
+
if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
410
257
|
return;
|
|
411
258
|
}
|
|
412
|
-
const
|
|
259
|
+
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
260
|
+
const retrievalURI = this.toBaseURI($ref);
|
|
413
261
|
const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
|
|
414
262
|
const isExternalReference = !isInternalReference;
|
|
415
263
|
|
|
@@ -423,131 +271,72 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
423
271
|
// skip traversing this Path Item element but traverse all it's child elements
|
|
424
272
|
return;
|
|
425
273
|
}
|
|
426
|
-
const $refBaseURI = url.resolve(retrievalURI,
|
|
427
|
-
const indirectionsSize = this.indirections.length;
|
|
274
|
+
const $refBaseURI = url.resolve(retrievalURI, $ref);
|
|
428
275
|
try {
|
|
429
|
-
const reference = await this.toReference(
|
|
430
|
-
this.indirections.push(referencingElement);
|
|
276
|
+
const reference = await this.toReference($ref);
|
|
431
277
|
const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
432
|
-
|
|
433
278
|
// possibly non-semantic referenced element
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
// applying semantics to a referenced element
|
|
437
|
-
if (!(0, _apidomNsOpenapi.isPathItemElement)(referencedElement)) {
|
|
438
|
-
if (this.refractCache.has(referencedElement)) {
|
|
439
|
-
referencedElement = this.refractCache.get(referencedElement);
|
|
440
|
-
} else {
|
|
441
|
-
const sourceElement = referencedElement;
|
|
442
|
-
referencedElement = (0, _apidomNsOpenapi.refractPathItem)(referencedElement);
|
|
443
|
-
this.refractCache.set(sourceElement, referencedElement);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
279
|
+
const referencedElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
|
|
446
280
|
|
|
447
|
-
// detect direct
|
|
281
|
+
// detect direct reference
|
|
448
282
|
if (referencingElement === referencedElement) {
|
|
449
283
|
throw new _apidomError.ApiDOMStructuredError('Recursive Path Item Object reference detected', {
|
|
450
|
-
$ref
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// detect maximum depth of dereferencing
|
|
455
|
-
if (this.indirections.length > this.options.dereference.maxDepth) {
|
|
456
|
-
throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
|
|
457
|
-
maxDepth: this.options.dereference.maxDepth,
|
|
458
|
-
uri: this.reference.uri
|
|
284
|
+
$ref
|
|
459
285
|
});
|
|
460
286
|
}
|
|
287
|
+
await this.addTransclusion(path, {
|
|
288
|
+
source: referencedElement,
|
|
289
|
+
type: 'pathItem',
|
|
290
|
+
reference,
|
|
291
|
+
context: {
|
|
292
|
+
ancestorSchema$ids: []
|
|
293
|
+
}
|
|
294
|
+
}, {
|
|
295
|
+
toTranscludedElement: (element, target) => {
|
|
296
|
+
if (!(0, _apidomNsOpenapi.isPathItemElement)(target)) return target;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Creating a new version of Path Item by merging fields from referenced Path Item with referencing one.
|
|
300
|
+
*/
|
|
301
|
+
const mergedElement = (0, _apidomDatamodel.cloneShallow)(target);
|
|
302
|
+
// existing keywords from referencing PathItemElement overrides ones from referenced element
|
|
303
|
+
element.forEach((value, keyElement, item) => {
|
|
304
|
+
mergedElement.remove((0, _apidomCore.toValue)(keyElement));
|
|
305
|
+
mergedElement.content.push(item);
|
|
306
|
+
});
|
|
307
|
+
mergedElement.remove('$ref');
|
|
461
308
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
reference.refSet.circular = true;
|
|
466
|
-
if (this.options.dereference.circular === 'error') {
|
|
467
|
-
throw new _apidomError.ApiDOMStructuredError('Circular reference detected', {
|
|
468
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref)
|
|
309
|
+
// annotate referenced element with info about original referencing element
|
|
310
|
+
mergedElement.meta.set('ref-fields', {
|
|
311
|
+
$ref
|
|
469
312
|
});
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
313
|
+
// annotate referenced element with info about origin and type
|
|
314
|
+
mergedElement.meta.set('ref-origin', reference.uri);
|
|
315
|
+
mergedElement.meta.set('ref-type', element.element);
|
|
316
|
+
return mergedElement;
|
|
317
|
+
},
|
|
318
|
+
aliasField: '$ref',
|
|
319
|
+
circular: {
|
|
320
|
+
uri: $refBaseURI,
|
|
321
|
+
meta: {
|
|
473
322
|
uri: reference.uri,
|
|
474
|
-
$ref
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
path.skip();
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* Dive deep into the fragment.
|
|
486
|
-
*
|
|
487
|
-
* Cases to consider:
|
|
488
|
-
* 1. We're crossing document boundary
|
|
489
|
-
* 2. Fragment is from non-entry document
|
|
490
|
-
* 3. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
|
|
491
|
-
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
|
|
492
|
-
*/
|
|
493
|
-
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
494
|
-
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
495
|
-
if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isPathItemElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
496
|
-
if (shouldDetectCircular) {
|
|
497
|
-
this.visited.add(referencedElement);
|
|
323
|
+
$ref
|
|
324
|
+
},
|
|
325
|
+
details: {
|
|
326
|
+
$ref
|
|
327
|
+
}
|
|
498
328
|
}
|
|
499
|
-
|
|
500
|
-
const visitor = new OpenAPI3_1DereferenceVisitor({
|
|
501
|
-
reference,
|
|
502
|
-
indirections: [...this.indirections],
|
|
503
|
-
options: this.options,
|
|
504
|
-
refractCache: this.refractCache,
|
|
505
|
-
schema$idIndex: this.schema$idIndex,
|
|
506
|
-
visited: this.visited,
|
|
507
|
-
ancestors: ancestorsLineage
|
|
508
|
-
});
|
|
509
|
-
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
510
|
-
mutable: true,
|
|
511
|
-
skipVisited: 'skip'
|
|
512
|
-
});
|
|
513
|
-
directAncestors.delete(referencingElement);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Creating a new version of Path Item by merging fields from referenced Path Item with referencing one.
|
|
518
|
-
*/
|
|
519
|
-
if ((0, _apidomNsOpenapi.isPathItemElement)(referencedElement)) {
|
|
520
|
-
const mergedElement = (0, _apidomDatamodel.cloneShallow)(referencedElement);
|
|
521
|
-
// existing keywords from referencing PathItemElement overrides ones from referenced element
|
|
522
|
-
referencingElement.forEach((value, keyElement, item) => {
|
|
523
|
-
mergedElement.remove((0, _apidomCore.toValue)(keyElement));
|
|
524
|
-
mergedElement.content.push(item);
|
|
525
|
-
});
|
|
526
|
-
mergedElement.remove('$ref');
|
|
527
|
-
|
|
528
|
-
// annotate referenced element with info about original referencing element
|
|
529
|
-
mergedElement.meta.set('ref-fields', {
|
|
530
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref)
|
|
531
|
-
});
|
|
532
|
-
// annotate referenced element with info about origin and type
|
|
533
|
-
mergedElement.meta.set('ref-origin', reference.uri);
|
|
534
|
-
mergedElement.meta.set('ref-type', referencingElement.element);
|
|
535
|
-
referencedElement = mergedElement;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Transclude referencing element with merged referenced element.
|
|
540
|
-
*/
|
|
541
|
-
path.replaceWith(referencedElement);
|
|
329
|
+
});
|
|
542
330
|
} catch (error) {
|
|
543
|
-
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
544
331
|
this.handleError(`Error while dereferencing Path Item Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
|
|
545
|
-
} finally {
|
|
546
|
-
if (this.indirections.length > indirectionsSize) this.indirections.pop();
|
|
547
332
|
}
|
|
548
333
|
}
|
|
549
334
|
async LinkElement(path) {
|
|
550
335
|
const linkElement = path.node;
|
|
336
|
+
if (this.isTranscluded(linkElement)) {
|
|
337
|
+
path.skip();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
551
340
|
|
|
552
341
|
// ignore LinkElement without operationRef or operationId field
|
|
553
342
|
if (!(0, _apidomDatamodel.isStringElement)(linkElement.operationRef) && !(0, _apidomDatamodel.isStringElement)(linkElement.operationId)) {
|
|
@@ -562,11 +351,11 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
562
351
|
});
|
|
563
352
|
}
|
|
564
353
|
try {
|
|
565
|
-
let operationElement;
|
|
566
354
|
if ((0, _apidomDatamodel.isStringElement)(linkElement.operationRef)) {
|
|
355
|
+
const operationRef = (0, _apidomCore.toValue)(linkElement.operationRef);
|
|
567
356
|
// possibly non-semantic referenced element
|
|
568
|
-
const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference(
|
|
569
|
-
const retrievalURI = this.toBaseURI(
|
|
357
|
+
const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference(operationRef);
|
|
358
|
+
const retrievalURI = this.toBaseURI(operationRef);
|
|
570
359
|
const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
|
|
571
360
|
const isExternalReference = !isInternalReference;
|
|
572
361
|
|
|
@@ -580,51 +369,56 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
580
369
|
// skip traversing this Link element but traverse all it's child elements
|
|
581
370
|
return;
|
|
582
371
|
}
|
|
583
|
-
const reference = await this.toReference(
|
|
584
|
-
operationElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
operationElement = (0, _apidomNsOpenapi.refractOperation)(operationElement);
|
|
592
|
-
this.refractCache.set(sourceElement, operationElement);
|
|
372
|
+
const reference = await this.toReference(operationRef);
|
|
373
|
+
const operationElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
|
|
374
|
+
await this.addAttachment(path, {
|
|
375
|
+
source: operationElement,
|
|
376
|
+
type: 'operation',
|
|
377
|
+
reference,
|
|
378
|
+
context: {
|
|
379
|
+
ancestorSchema$ids: []
|
|
593
380
|
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
381
|
+
}, (element, target) => {
|
|
382
|
+
// create shallow clone to be able to annotate with metadata
|
|
383
|
+
const operationCopy = (0, _apidomDatamodel.cloneShallow)(target);
|
|
384
|
+
// annotate operation element with info about origin and type
|
|
385
|
+
operationCopy.meta.set('ref-origin', reference.uri);
|
|
386
|
+
operationCopy.meta.set('ref-type', element.element);
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Transclude Link Object containing Operation Object in its meta.
|
|
390
|
+
*/
|
|
391
|
+
const linkElementCopy = (0, _apidomDatamodel.cloneShallow)(element);
|
|
392
|
+
linkElementCopy.operationRef?.meta.set('operation', operationCopy);
|
|
393
|
+
return linkElementCopy;
|
|
394
|
+
});
|
|
607
395
|
return;
|
|
608
396
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
397
|
+
const operationId = (0, _apidomCore.toValue)(linkElement.operationId);
|
|
398
|
+
const reference = await this.toReference(url.unsanitize(this.reference.uri));
|
|
399
|
+
const operationPath = (0, _apidomTraverse.find)(reference.value.result, p => (0, _apidomNsOpenapi.isOperationElement)(p.node) && (0, _apidomDatamodel.isElement)(p.node.operationId) && p.node.operationId.equals(operationId));
|
|
400
|
+
const operationElement = operationPath?.node;
|
|
401
|
+
// OperationElement not found by its operationId
|
|
402
|
+
if ((0, _ramdaAdjunct.isUndefined)(operationElement)) {
|
|
403
|
+
throw new _apidomError.ApiDOMStructuredError(`OperationElement(operationId=${operationId}) not found`, {
|
|
404
|
+
operationId
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
await this.addAttachment(path, {
|
|
408
|
+
source: operationElement,
|
|
409
|
+
type: 'operation',
|
|
410
|
+
reference,
|
|
411
|
+
context: {
|
|
412
|
+
ancestorSchema$ids: []
|
|
619
413
|
}
|
|
620
|
-
|
|
621
|
-
linkElementCopy.operationId?.meta.set('operation', operationElement);
|
|
622
|
-
|
|
414
|
+
}, (element, target) => {
|
|
623
415
|
/**
|
|
624
416
|
* Transclude Link Object containing Operation Object in its meta.
|
|
625
417
|
*/
|
|
626
|
-
|
|
627
|
-
|
|
418
|
+
const linkElementCopy = (0, _apidomDatamodel.cloneShallow)(element);
|
|
419
|
+
linkElementCopy.operationId?.meta.set('operation', target);
|
|
420
|
+
return linkElementCopy;
|
|
421
|
+
});
|
|
628
422
|
} catch (error) {
|
|
629
423
|
const refFieldName = (0, _apidomDatamodel.isStringElement)(linkElement.operationRef) ? 'operationRef' : 'operationId';
|
|
630
424
|
const refFieldValue = (0, _apidomDatamodel.isStringElement)(linkElement.operationRef) ? (0, _apidomCore.toValue)(linkElement.operationRef) : (0, _apidomCore.toValue)(linkElement.operationId);
|
|
@@ -633,6 +427,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
633
427
|
}
|
|
634
428
|
async ExampleElement(path) {
|
|
635
429
|
const exampleElement = path.node;
|
|
430
|
+
if (this.isTranscluded(exampleElement)) {
|
|
431
|
+
path.skip();
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
636
434
|
|
|
637
435
|
// ignore ExampleElement without externalValue field
|
|
638
436
|
if (!(0, _apidomDatamodel.isStringElement)(exampleElement.externalValue)) {
|
|
@@ -646,7 +444,8 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
646
444
|
externalValue: (0, _apidomCore.toValue)(exampleElement.externalValue)
|
|
647
445
|
});
|
|
648
446
|
}
|
|
649
|
-
const
|
|
447
|
+
const externalValue = (0, _apidomCore.toValue)(exampleElement.externalValue);
|
|
448
|
+
const retrievalURI = this.toBaseURI(externalValue);
|
|
650
449
|
const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
|
|
651
450
|
const isExternalReference = !isInternalReference;
|
|
652
451
|
|
|
@@ -661,39 +460,44 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
661
460
|
return;
|
|
662
461
|
}
|
|
663
462
|
try {
|
|
664
|
-
const reference = await this.toReference(
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
463
|
+
const reference = await this.toReference(externalValue);
|
|
464
|
+
const valueSource = reference.value.result;
|
|
465
|
+
await this.addAttachment(path, {
|
|
466
|
+
source: valueSource,
|
|
467
|
+
type: valueSource.element,
|
|
468
|
+
reference,
|
|
469
|
+
context: {
|
|
470
|
+
ancestorSchema$ids: []
|
|
471
|
+
}
|
|
472
|
+
}, (element, target) => {
|
|
473
|
+
// shallow clone of the referenced element
|
|
474
|
+
const valueElement = (0, _apidomDatamodel.cloneShallow)(target);
|
|
475
|
+
// annotate element with info about origin and type
|
|
476
|
+
valueElement.meta.set('ref-origin', reference.uri);
|
|
477
|
+
valueElement.meta.set('ref-type', element.element);
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Transclude Example Object containing external value.
|
|
481
|
+
*/
|
|
482
|
+
const exampleElementCopy = (0, _apidomDatamodel.cloneShallow)(element);
|
|
483
|
+
exampleElementCopy.value = valueElement;
|
|
484
|
+
return exampleElementCopy;
|
|
485
|
+
});
|
|
678
486
|
} catch (error) {
|
|
679
|
-
const externalValue = (0, _apidomCore.toValue)(exampleElement.externalValue);
|
|
680
487
|
this.handleError(`Error while dereferencing Example Object. Cannot resolve externalValue "${externalValue}": ${error.message}`, error, exampleElement, 'externalValue', externalValue, path);
|
|
681
488
|
}
|
|
682
489
|
}
|
|
683
490
|
async SchemaElement(path) {
|
|
684
491
|
const referencingElement = path.node;
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
492
|
+
if (this.isTranscluded(referencingElement)) {
|
|
493
|
+
path.skip();
|
|
688
494
|
return;
|
|
689
495
|
}
|
|
690
496
|
|
|
691
|
-
// skip current referencing
|
|
692
|
-
if (
|
|
693
|
-
path.skip();
|
|
497
|
+
// skip current referencing schema as $ref keyword was not defined
|
|
498
|
+
if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
694
499
|
return;
|
|
695
500
|
}
|
|
696
|
-
const indirectionsSize = this.indirections.length;
|
|
697
501
|
try {
|
|
698
502
|
// compute baseURI using rules around $id and $ref keywords
|
|
699
503
|
let reference = await this.toReference(url.unsanitize(this.reference.uri));
|
|
@@ -736,10 +540,12 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
736
540
|
try {
|
|
737
541
|
// the fragment's $ids resolve against the base its enclosing chain
|
|
738
542
|
// leaves in effect, which then also prefixes the located schema's chain
|
|
543
|
+
const fragmentAsSchema = this.toSchemaDocument(fragmentElement);
|
|
544
|
+
this.index$ids(fragmentAsSchema);
|
|
739
545
|
({
|
|
740
546
|
element: referencedElement,
|
|
741
547
|
ancestorSchema$ids
|
|
742
|
-
} = (0, _uri.locate)(selector,
|
|
548
|
+
} = (0, _uri.locate)(selector, fragmentAsSchema, {
|
|
743
549
|
baseURI: rootBaseURI,
|
|
744
550
|
index: this.schema$idIndex
|
|
745
551
|
}));
|
|
@@ -750,15 +556,16 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
750
556
|
}
|
|
751
557
|
// the document index is rooted at the document, so the located chain
|
|
752
558
|
// is already document-relative and takes no prefix
|
|
559
|
+
const documentAsSchema = this.toSchemaDocument(documentElement);
|
|
560
|
+
this.index$ids(documentAsSchema);
|
|
753
561
|
({
|
|
754
562
|
element: referencedElement,
|
|
755
563
|
ancestorSchema$ids
|
|
756
|
-
} = (0, _uri.locate)(selector,
|
|
564
|
+
} = (0, _uri.locate)(selector, documentAsSchema, {
|
|
757
565
|
baseURI: reference.uri,
|
|
758
566
|
index: this.schema$idIndex
|
|
759
567
|
}));
|
|
760
568
|
}
|
|
761
|
-
referencedElement = (0, _util.maybeRefractToSchemaElement)(referencedElement);
|
|
762
569
|
|
|
763
570
|
// ignore resolving internal Schema Objects
|
|
764
571
|
if (!this.options.resolve.internal && isInternalReference) {
|
|
@@ -788,12 +595,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
788
595
|
}
|
|
789
596
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
790
597
|
const selector = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
791
|
-
const referenceAsSchema = (0, _util.maybeRefractToSchemaElement)(reference.value.result);
|
|
792
598
|
({
|
|
793
599
|
element: referencedElement,
|
|
794
600
|
ancestorSchema$ids
|
|
795
|
-
} = (0, _jsonPointer.locate)(
|
|
796
|
-
referencedElement = (0, _util.maybeRefractToSchemaElement)(referencedElement);
|
|
601
|
+
} = (0, _jsonPointer.locate)(this.toSchemaDocument(reference.value.result), selector));
|
|
797
602
|
}
|
|
798
603
|
} catch (error) {
|
|
799
604
|
/**
|
|
@@ -818,12 +623,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
818
623
|
}
|
|
819
624
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
820
625
|
const selector = (0, _$anchor.uriToAnchor)($refBaseURI);
|
|
821
|
-
const referenceAsSchema = (0, _util.maybeRefractToSchemaElement)(reference.value.result);
|
|
822
626
|
({
|
|
823
627
|
element: referencedElement,
|
|
824
628
|
ancestorSchema$ids
|
|
825
|
-
} = (0, _$anchor.locate)(selector,
|
|
826
|
-
referencedElement = (0, _util.maybeRefractToSchemaElement)(referencedElement);
|
|
629
|
+
} = (0, _$anchor.locate)(selector, this.toSchemaDocument(reference.value.result)));
|
|
827
630
|
} else {
|
|
828
631
|
// we're assuming here that we're dealing with JSON Pointer here
|
|
829
632
|
retrievalURI = this.toBaseURI($refBaseURI);
|
|
@@ -842,137 +645,82 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
842
645
|
}
|
|
843
646
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
844
647
|
const selector = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
845
|
-
const referenceAsSchema = (0, _util.maybeRefractToSchemaElement)(reference.value.result);
|
|
846
648
|
({
|
|
847
649
|
element: referencedElement,
|
|
848
650
|
ancestorSchema$ids
|
|
849
|
-
} = (0, _jsonPointer.locate)(
|
|
850
|
-
referencedElement = (0, _util.maybeRefractToSchemaElement)(referencedElement);
|
|
651
|
+
} = (0, _jsonPointer.locate)(this.toSchemaDocument(reference.value.result), selector));
|
|
851
652
|
}
|
|
852
653
|
} else {
|
|
853
654
|
throw error;
|
|
854
655
|
}
|
|
855
656
|
}
|
|
856
|
-
|
|
657
|
+
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
857
658
|
|
|
858
|
-
// detect direct
|
|
659
|
+
// detect direct reference
|
|
859
660
|
if (referencingElement === referencedElement) {
|
|
860
661
|
throw new _apidomError.ApiDOMStructuredError('Recursive Schema Object reference detected', {
|
|
861
|
-
$ref
|
|
662
|
+
$ref
|
|
862
663
|
});
|
|
863
664
|
}
|
|
864
|
-
|
|
865
|
-
//
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
665
|
+
const origin = reference.uri;
|
|
666
|
+
// a Schema Object $ref reads whatever it points at as a Schema Object
|
|
667
|
+
await this.addTransclusion(path, {
|
|
668
|
+
source: referencedElement,
|
|
669
|
+
type: SCHEMA_TYPE,
|
|
670
|
+
reference,
|
|
671
|
+
context: {
|
|
672
|
+
ancestorSchema$ids
|
|
673
|
+
}
|
|
674
|
+
}, {
|
|
675
|
+
toTranscludedElement: (element, target) => {
|
|
676
|
+
// Boolean JSON Schemas
|
|
677
|
+
if ((0, _apidomNsOpenapi.isBooleanJSONSchemaElement)(target)) {
|
|
678
|
+
const booleanJsonSchemaElement = (0, _apidomDatamodel.cloneDeep)(target);
|
|
679
|
+
// annotate referenced element with info about original referencing element
|
|
680
|
+
booleanJsonSchemaElement.meta.set('ref-fields', {
|
|
681
|
+
$ref
|
|
682
|
+
});
|
|
683
|
+
// annotate referenced element with info about origin and type
|
|
684
|
+
booleanJsonSchemaElement.meta.set('ref-origin', origin);
|
|
685
|
+
booleanJsonSchemaElement.meta.set('ref-type', element.element);
|
|
686
|
+
return booleanJsonSchemaElement;
|
|
687
|
+
}
|
|
688
|
+
if (!(0, _apidomNsOpenapi.isSchemaElement)(target)) return target;
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Creating a new version of Schema Object by merging fields from referenced Schema Object with referencing one.
|
|
692
|
+
*/
|
|
693
|
+
const mergedElement = (0, _apidomDatamodel.cloneShallow)(target);
|
|
694
|
+
// existing keywords from referencing schema overrides ones from referenced schema
|
|
695
|
+
element.forEach((value, keyElement, item) => {
|
|
696
|
+
mergedElement.remove((0, _apidomCore.toValue)(keyElement));
|
|
697
|
+
mergedElement.content.push(item);
|
|
880
698
|
});
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref)
|
|
699
|
+
mergedElement.remove('$ref');
|
|
700
|
+
// annotate referenced element with info about original referencing element
|
|
701
|
+
mergedElement.meta.set('ref-fields', {
|
|
702
|
+
$ref
|
|
886
703
|
});
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
|
|
903
|
-
*/
|
|
904
|
-
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
905
|
-
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
906
|
-
// content transplanted into the referencing site is traversed under that
|
|
907
|
-
// site's $id chain; a fragment enclosed by a different chain has its own
|
|
908
|
-
// $refs resolved in place first, by a nested visitor seeded with that chain
|
|
909
|
-
const hasDifferentEnclosingBase = (0, _util.resolveSchema$ids)(reference.uri, ancestorSchema$ids) !== (0, _util.resolveSchema$ids)(rootBaseURI, (0, _util.schema$idsOf)(path.getAncestorNodes().reverse()));
|
|
910
|
-
if ((isExternalReference || isNonEntryDocument || hasDifferentEnclosingBase || (0, _apidomNsOpenapi.isSchemaElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
911
|
-
if (shouldDetectCircular) {
|
|
912
|
-
this.visited.add(referencedElement);
|
|
704
|
+
// annotate fragment with info about origin and type
|
|
705
|
+
mergedElement.meta.set('ref-origin', origin);
|
|
706
|
+
mergedElement.meta.set('ref-type', element.element);
|
|
707
|
+
return mergedElement;
|
|
708
|
+
},
|
|
709
|
+
aliasField: '$ref',
|
|
710
|
+
circular: {
|
|
711
|
+
uri: $refBaseURI,
|
|
712
|
+
meta: {
|
|
713
|
+
uri: origin,
|
|
714
|
+
$ref
|
|
715
|
+
},
|
|
716
|
+
details: {
|
|
717
|
+
$ref
|
|
718
|
+
}
|
|
913
719
|
}
|
|
914
|
-
|
|
915
|
-
const visitor = new OpenAPI3_1DereferenceVisitor({
|
|
916
|
-
reference,
|
|
917
|
-
indirections: [...this.indirections],
|
|
918
|
-
options: this.options,
|
|
919
|
-
refractCache: this.refractCache,
|
|
920
|
-
schema$idIndex: this.schema$idIndex,
|
|
921
|
-
visited: this.visited,
|
|
922
|
-
ancestors: ancestorsLineage,
|
|
923
|
-
ancestorSchema$ids
|
|
924
|
-
});
|
|
925
|
-
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
926
|
-
mutable: true,
|
|
927
|
-
skipVisited: 'skip'
|
|
928
|
-
});
|
|
929
|
-
directAncestors.delete(referencingElement);
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
// Boolean JSON Schemas
|
|
933
|
-
if ((0, _apidomNsOpenapi.isBooleanJSONSchemaElement)(referencedElement)) {
|
|
934
|
-
const booleanJsonSchemaElement = (0, _apidomDatamodel.cloneDeep)(referencedElement);
|
|
935
|
-
// annotate referenced element with info about original referencing element
|
|
936
|
-
booleanJsonSchemaElement.meta.set('ref-fields', {
|
|
937
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref)
|
|
938
|
-
});
|
|
939
|
-
// annotate referenced element with info about origin and type
|
|
940
|
-
booleanJsonSchemaElement.meta.set('ref-origin', reference.uri);
|
|
941
|
-
booleanJsonSchemaElement.meta.set('ref-type', referencingElement.element);
|
|
942
|
-
path.replaceWith(booleanJsonSchemaElement);
|
|
943
|
-
path.skip();
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
/**
|
|
948
|
-
* Creating a new version of Schema Object by merging fields from referenced Schema Object with referencing one.
|
|
949
|
-
*/
|
|
950
|
-
if ((0, _apidomNsOpenapi.isSchemaElement)(referencedElement)) {
|
|
951
|
-
const mergedElement = (0, _apidomDatamodel.cloneShallow)(referencedElement);
|
|
952
|
-
// existing keywords from referencing schema overrides ones from referenced schema
|
|
953
|
-
referencingElement.forEach((value, keyElement, item) => {
|
|
954
|
-
mergedElement.remove((0, _apidomCore.toValue)(keyElement));
|
|
955
|
-
mergedElement.content.push(item);
|
|
956
|
-
});
|
|
957
|
-
mergedElement.remove('$ref');
|
|
958
|
-
// annotate referenced element with info about original referencing element
|
|
959
|
-
mergedElement.meta.set('ref-fields', {
|
|
960
|
-
$ref: (0, _apidomCore.toValue)(referencingElement.$ref)
|
|
961
|
-
});
|
|
962
|
-
// annotate fragment with info about origin and type
|
|
963
|
-
mergedElement.meta.set('ref-origin', reference.uri);
|
|
964
|
-
mergedElement.meta.set('ref-type', referencingElement.element);
|
|
965
|
-
referencedElement = mergedElement;
|
|
966
|
-
}
|
|
967
|
-
/**
|
|
968
|
-
* Transclude referencing element with merged referenced element.
|
|
969
|
-
*/
|
|
970
|
-
path.replaceWith(referencedElement);
|
|
720
|
+
});
|
|
971
721
|
} catch (error) {
|
|
972
722
|
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
973
723
|
this.handleError(`Error while dereferencing Schema Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
|
|
974
|
-
} finally {
|
|
975
|
-
if (this.indirections.length > indirectionsSize) this.indirections.pop();
|
|
976
724
|
}
|
|
977
725
|
}
|
|
978
726
|
}
|