@speclynx/apidom-reference 3.0.0 → 3.2.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 +16 -0
- package/README.md +97 -0
- package/dist/apidom-reference.browser.js +3862 -3241
- package/dist/apidom-reference.browser.min.js +1 -1
- package/package.json +48 -25
- package/src/dereference/index.cjs +4 -0
- package/src/dereference/index.mjs +4 -0
- package/src/dereference/strategies/apidom/visitor.cjs +139 -59
- package/src/dereference/strategies/apidom/visitor.mjs +142 -62
- package/src/dereference/strategies/arazzo-1/index.cjs +1 -4
- package/src/dereference/strategies/arazzo-1/index.mjs +2 -4
- package/src/dereference/strategies/arazzo-1/visitor.cjs +289 -199
- package/src/dereference/strategies/arazzo-1/visitor.mjs +292 -203
- package/src/dereference/strategies/asyncapi-2/index.cjs +1 -4
- package/src/dereference/strategies/asyncapi-2/index.mjs +2 -4
- package/src/dereference/strategies/asyncapi-2/visitor.cjs +325 -229
- package/src/dereference/strategies/asyncapi-2/visitor.mjs +328 -233
- package/src/dereference/strategies/openapi-2/index.cjs +1 -4
- package/src/dereference/strategies/openapi-2/index.mjs +2 -4
- package/src/dereference/strategies/openapi-2/visitor.cjs +420 -318
- package/src/dereference/strategies/openapi-2/visitor.mjs +425 -324
- package/src/dereference/strategies/openapi-3-0/index.cjs +1 -4
- package/src/dereference/strategies/openapi-3-0/index.mjs +2 -4
- package/src/dereference/strategies/openapi-3-0/visitor.cjs +405 -286
- package/src/dereference/strategies/openapi-3-0/visitor.mjs +409 -291
- package/src/dereference/strategies/openapi-3-1/index.cjs +1 -4
- package/src/dereference/strategies/openapi-3-1/index.mjs +2 -4
- package/src/dereference/strategies/openapi-3-1/visitor.cjs +598 -484
- package/src/dereference/strategies/openapi-3-1/visitor.mjs +602 -489
- package/src/errors/DereferenceError.cjs +1 -1
- package/src/errors/DereferenceError.mjs +2 -2
- package/src/errors/ResolveError.cjs +1 -1
- package/src/errors/ResolveError.mjs +2 -2
- package/src/errors/UnresolvableReferenceError.cjs +11 -0
- package/src/errors/UnresolvableReferenceError.mjs +6 -0
- package/src/index.cjs +3 -1
- package/src/index.mjs +1 -0
- package/src/options/index.cjs +10 -1
- package/src/options/index.mjs +10 -1
- package/src/util/plugins.cjs +1 -6
- package/src/util/plugins.mjs +2 -5
- package/types/apidom-reference.d.ts +10 -2
- package/types/dereference/strategies/apidom/visitor.d.ts +10 -0
- package/types/dereference/strategies/arazzo-1/visitor.d.ts +19 -5
- package/types/dereference/strategies/asyncapi-2/visitor.d.ts +21 -7
- package/types/dereference/strategies/openapi-2/visitor.d.ts +21 -8
- package/types/dereference/strategies/openapi-3-0/visitor.d.ts +21 -7
- package/types/dereference/strategies/openapi-3-1/visitor.d.ts +21 -7
- package/types/errors/DereferenceError.d.ts +2 -2
- package/types/errors/ResolveError.d.ts +2 -2
- package/types/errors/UnresolvableReferenceError.d.ts +7 -0
- package/types/index.d.ts +1 -0
- package/types/options/index.d.ts +2 -0
package/package.json
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speclynx/apidom-reference",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Advanced algorithms for semantic ApiDOM manipulations like dereferencing or resolution.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"apidom",
|
|
7
|
+
"api",
|
|
8
|
+
"$ref",
|
|
9
|
+
"dereference",
|
|
10
|
+
"reference",
|
|
11
|
+
"resolve",
|
|
12
|
+
"resolver",
|
|
13
|
+
"bundle",
|
|
14
|
+
"openapi",
|
|
15
|
+
"swagger",
|
|
16
|
+
"oas",
|
|
17
|
+
"asyncapi",
|
|
18
|
+
"arazzo",
|
|
19
|
+
"json-schema",
|
|
20
|
+
"json",
|
|
21
|
+
"yaml",
|
|
22
|
+
"spec",
|
|
23
|
+
"specification",
|
|
24
|
+
"parse",
|
|
25
|
+
"typescript",
|
|
26
|
+
"javascript"
|
|
27
|
+
],
|
|
5
28
|
"publishConfig": {
|
|
6
29
|
"access": "public",
|
|
7
30
|
"registry": "https://registry.npmjs.org",
|
|
@@ -231,29 +254,29 @@
|
|
|
231
254
|
"license": "Apache-2.0",
|
|
232
255
|
"dependencies": {
|
|
233
256
|
"@babel/runtime-corejs3": "^7.28.4",
|
|
234
|
-
"@speclynx/apidom-core": "3.
|
|
235
|
-
"@speclynx/apidom-datamodel": "3.
|
|
236
|
-
"@speclynx/apidom-error": "3.
|
|
237
|
-
"@speclynx/apidom-json-pointer": "3.
|
|
238
|
-
"@speclynx/apidom-ns-arazzo-1": "3.
|
|
239
|
-
"@speclynx/apidom-ns-asyncapi-2": "3.
|
|
240
|
-
"@speclynx/apidom-ns-json-schema-2020-12": "3.
|
|
241
|
-
"@speclynx/apidom-ns-openapi-2": "3.
|
|
242
|
-
"@speclynx/apidom-ns-openapi-3-0": "3.
|
|
243
|
-
"@speclynx/apidom-ns-openapi-3-1": "3.
|
|
244
|
-
"@speclynx/apidom-parser-adapter-arazzo-json-1": "3.
|
|
245
|
-
"@speclynx/apidom-parser-adapter-arazzo-yaml-1": "3.
|
|
246
|
-
"@speclynx/apidom-parser-adapter-asyncapi-json-2": "3.
|
|
247
|
-
"@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "3.
|
|
248
|
-
"@speclynx/apidom-parser-adapter-json": "3.
|
|
249
|
-
"@speclynx/apidom-parser-adapter-openapi-json-2": "3.
|
|
250
|
-
"@speclynx/apidom-parser-adapter-openapi-json-3-0": "3.
|
|
251
|
-
"@speclynx/apidom-parser-adapter-openapi-json-3-1": "3.
|
|
252
|
-
"@speclynx/apidom-parser-adapter-openapi-yaml-2": "3.
|
|
253
|
-
"@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "3.
|
|
254
|
-
"@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "3.
|
|
255
|
-
"@speclynx/apidom-parser-adapter-yaml-1-2": "3.
|
|
256
|
-
"@speclynx/apidom-traverse": "3.
|
|
257
|
+
"@speclynx/apidom-core": "3.2.0",
|
|
258
|
+
"@speclynx/apidom-datamodel": "3.2.0",
|
|
259
|
+
"@speclynx/apidom-error": "3.2.0",
|
|
260
|
+
"@speclynx/apidom-json-pointer": "3.2.0",
|
|
261
|
+
"@speclynx/apidom-ns-arazzo-1": "3.2.0",
|
|
262
|
+
"@speclynx/apidom-ns-asyncapi-2": "3.2.0",
|
|
263
|
+
"@speclynx/apidom-ns-json-schema-2020-12": "3.2.0",
|
|
264
|
+
"@speclynx/apidom-ns-openapi-2": "3.2.0",
|
|
265
|
+
"@speclynx/apidom-ns-openapi-3-0": "3.2.0",
|
|
266
|
+
"@speclynx/apidom-ns-openapi-3-1": "3.2.0",
|
|
267
|
+
"@speclynx/apidom-parser-adapter-arazzo-json-1": "3.2.0",
|
|
268
|
+
"@speclynx/apidom-parser-adapter-arazzo-yaml-1": "3.2.0",
|
|
269
|
+
"@speclynx/apidom-parser-adapter-asyncapi-json-2": "3.2.0",
|
|
270
|
+
"@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "3.2.0",
|
|
271
|
+
"@speclynx/apidom-parser-adapter-json": "3.2.0",
|
|
272
|
+
"@speclynx/apidom-parser-adapter-openapi-json-2": "3.2.0",
|
|
273
|
+
"@speclynx/apidom-parser-adapter-openapi-json-3-0": "3.2.0",
|
|
274
|
+
"@speclynx/apidom-parser-adapter-openapi-json-3-1": "3.2.0",
|
|
275
|
+
"@speclynx/apidom-parser-adapter-openapi-yaml-2": "3.2.0",
|
|
276
|
+
"@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "3.2.0",
|
|
277
|
+
"@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "3.2.0",
|
|
278
|
+
"@speclynx/apidom-parser-adapter-yaml-1-2": "3.2.0",
|
|
279
|
+
"@speclynx/apidom-traverse": "3.2.0",
|
|
257
280
|
"@swaggerexpert/arazzo-runtime-expression": "^2.0.3",
|
|
258
281
|
"axios": "^1.13.5",
|
|
259
282
|
"picomatch": "^4.0.3",
|
|
@@ -275,5 +298,5 @@
|
|
|
275
298
|
"README.md",
|
|
276
299
|
"CHANGELOG.md"
|
|
277
300
|
],
|
|
278
|
-
"gitHead": "
|
|
301
|
+
"gitHead": "eeb2f01cd34fb0a95617f02bc1b6683aefc028f2"
|
|
279
302
|
}
|
|
@@ -10,6 +10,7 @@ var _File = _interopRequireDefault(require("../File.cjs"));
|
|
|
10
10
|
var plugins = _interopRequireWildcard(require("../util/plugins.cjs"));
|
|
11
11
|
var _UnmatchedDereferenceStrategyError = _interopRequireDefault(require("../errors/UnmatchedDereferenceStrategyError.cjs"));
|
|
12
12
|
var _DereferenceError = _interopRequireDefault(require("../errors/DereferenceError.cjs"));
|
|
13
|
+
var _UnresolvableReferenceError = _interopRequireDefault(require("../errors/UnresolvableReferenceError.cjs"));
|
|
13
14
|
var _index = _interopRequireDefault(require("../parse/index.cjs"));
|
|
14
15
|
var _util = require("../options/util.cjs");
|
|
15
16
|
var url = _interopRequireWildcard(require("../util/url.cjs"));
|
|
@@ -46,6 +47,9 @@ const dereferenceApiDOM = async (element, options) => {
|
|
|
46
47
|
// unwrap the element from ParseResult assuming first element is the actual result
|
|
47
48
|
return surrogateWrapping ? result.get(0) : result;
|
|
48
49
|
} catch (error) {
|
|
50
|
+
if (error instanceof _UnresolvableReferenceError.default) {
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
49
53
|
throw new _DereferenceError.default(`Error while dereferencing file "${file.uri}"`, {
|
|
50
54
|
cause: error
|
|
51
55
|
});
|
|
@@ -4,6 +4,7 @@ import File from "../File.mjs";
|
|
|
4
4
|
import * as plugins from "../util/plugins.mjs";
|
|
5
5
|
import UnmatchedDereferenceStrategyError from "../errors/UnmatchedDereferenceStrategyError.mjs";
|
|
6
6
|
import DereferenceError from "../errors/DereferenceError.mjs";
|
|
7
|
+
import UnresolvableReferenceError from "../errors/UnresolvableReferenceError.mjs";
|
|
7
8
|
import parse from "../parse/index.mjs";
|
|
8
9
|
import { merge as mergeOptions } from "../options/util.mjs";
|
|
9
10
|
import * as url from "../util/url.mjs";
|
|
@@ -40,6 +41,9 @@ export const dereferenceApiDOM = async (element, options) => {
|
|
|
40
41
|
// unwrap the element from ParseResult assuming first element is the actual result
|
|
41
42
|
return surrogateWrapping ? result.get(0) : result;
|
|
42
43
|
} catch (error) {
|
|
44
|
+
if (error instanceof UnresolvableReferenceError) {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
43
47
|
throw new DereferenceError(`Error while dereferencing file "${file.uri}"`, {
|
|
44
48
|
cause: error
|
|
45
49
|
});
|
|
@@ -11,6 +11,7 @@ var _apidomCore = require("@speclynx/apidom-core");
|
|
|
11
11
|
var _apidomTraverse = require("@speclynx/apidom-traverse");
|
|
12
12
|
var _apidomJsonPointer = require("@speclynx/apidom-json-pointer");
|
|
13
13
|
var _MaximumResolveDepthError = _interopRequireDefault(require("../../../errors/MaximumResolveDepthError.cjs"));
|
|
14
|
+
var _UnresolvableReferenceError = _interopRequireDefault(require("../../../errors/UnresolvableReferenceError.cjs"));
|
|
14
15
|
var url = _interopRequireWildcard(require("../../../util/url.cjs"));
|
|
15
16
|
var _index = _interopRequireDefault(require("../../../parse/index.cjs"));
|
|
16
17
|
var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
|
|
@@ -48,7 +49,10 @@ class ApiDOMDereferenceVisitor {
|
|
|
48
49
|
async toReference(uri) {
|
|
49
50
|
// detect maximum depth of resolution
|
|
50
51
|
if (this.reference.depth >= this.options.resolve.maxDepth) {
|
|
51
|
-
throw new _MaximumResolveDepthError.default(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"
|
|
52
|
+
throw new _MaximumResolveDepthError.default(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`, {
|
|
53
|
+
maxDepth: this.options.resolve.maxDepth,
|
|
54
|
+
uri: this.reference.uri
|
|
55
|
+
});
|
|
52
56
|
}
|
|
53
57
|
const baseURI = this.toBaseURI(uri);
|
|
54
58
|
const {
|
|
@@ -85,6 +89,72 @@ class ApiDOMDereferenceVisitor {
|
|
|
85
89
|
}
|
|
86
90
|
return mutableReference;
|
|
87
91
|
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Handles an error according to the continueOnError option.
|
|
95
|
+
*
|
|
96
|
+
* For new errors: wraps in UnresolvableReferenceError with structured context.
|
|
97
|
+
* For errors already wrapped by a nested visitor: prepends the current hop to the trace.
|
|
98
|
+
*
|
|
99
|
+
* Inner/intermediate visitors always throw to let the trace accumulate.
|
|
100
|
+
* Only the entry document visitor respects continueOnError (callback/swallow/throw).
|
|
101
|
+
*/
|
|
102
|
+
handleError(message, error, referencingElement, refFieldName, refFieldValue, visitorPath) {
|
|
103
|
+
const {
|
|
104
|
+
continueOnError
|
|
105
|
+
} = this.options.dereference;
|
|
106
|
+
const isEntryDocument = url.stripHash(this.reference.refSet?.rootRef?.uri ?? '') === this.reference.uri;
|
|
107
|
+
const uri = this.reference.uri;
|
|
108
|
+
const type = referencingElement.element;
|
|
109
|
+
let location;
|
|
110
|
+
(0, _apidomTraverse.traverse)(this.reference.value.result, {
|
|
111
|
+
enter(p) {
|
|
112
|
+
if (p.node === referencingElement) {
|
|
113
|
+
location = p.formatPath();
|
|
114
|
+
p.stop();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
location ??= visitorPath.formatPath();
|
|
119
|
+
const codeFrame = (0, _apidomCore.toYAML)(referencingElement);
|
|
120
|
+
const hop = {
|
|
121
|
+
uri,
|
|
122
|
+
type,
|
|
123
|
+
refFieldName,
|
|
124
|
+
refFieldValue,
|
|
125
|
+
location,
|
|
126
|
+
codeFrame
|
|
127
|
+
};
|
|
128
|
+
let unresolvedError;
|
|
129
|
+
if (error instanceof _UnresolvableReferenceError.default) {
|
|
130
|
+
const refBaseURI = this.toBaseURI(refFieldValue);
|
|
131
|
+
const fragment = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference(refFieldValue);
|
|
132
|
+
if (fragment) {
|
|
133
|
+
if (refBaseURI === error.uri && error.location) {
|
|
134
|
+
error.location = fragment + error.location;
|
|
135
|
+
}
|
|
136
|
+
for (const h of error.trace) {
|
|
137
|
+
if (h.uri === refBaseURI && h.location) h.location = fragment + h.location;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// @ts-ignore
|
|
141
|
+
error.trace = [hop, ...error.trace];
|
|
142
|
+
unresolvedError = error;
|
|
143
|
+
} else {
|
|
144
|
+
unresolvedError = new _UnresolvableReferenceError.default(message, {
|
|
145
|
+
cause: error,
|
|
146
|
+
type,
|
|
147
|
+
uri,
|
|
148
|
+
location,
|
|
149
|
+
codeFrame,
|
|
150
|
+
refFieldName,
|
|
151
|
+
refFieldValue,
|
|
152
|
+
trace: []
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (!isEntryDocument || continueOnError === false) throw unresolvedError;
|
|
156
|
+
if (typeof continueOnError === 'function') continueOnError(unresolvedError);
|
|
157
|
+
}
|
|
88
158
|
async RefElement(path) {
|
|
89
159
|
const refElement = path.node;
|
|
90
160
|
const {
|
|
@@ -110,69 +180,79 @@ class ApiDOMDereferenceVisitor {
|
|
|
110
180
|
path.skip();
|
|
111
181
|
return;
|
|
112
182
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
reference
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
referencedElement = (0, _apidomDatamodel.refract)(referencedElement[referencedElementPath]);
|
|
144
|
-
}
|
|
183
|
+
try {
|
|
184
|
+
const reference = await this.toReference(refNormalizedURI);
|
|
185
|
+
const refBaseURI = url.resolve(retrievalURI, refNormalizedURI);
|
|
186
|
+
const elementID = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference(refBaseURI);
|
|
187
|
+
let referencedElement = (0, _elementId.evaluate)(elementID, reference.value.result);
|
|
188
|
+
if (!(0, _apidomDatamodel.isElement)(referencedElement)) {
|
|
189
|
+
throw new _apidomError.ApiDOMStructuredError(`Referenced element with id="${elementID}" was not found`, {
|
|
190
|
+
elementID
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (refElement === referencedElement) {
|
|
194
|
+
throw new _apidomError.ApiDOMStructuredError(`RefElement with id="${elementID}" cannot reference itself`, {
|
|
195
|
+
elementID
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if ((0, _apidomDatamodel.isRefElement)(referencedElement)) {
|
|
199
|
+
throw new _apidomError.ApiDOMStructuredError(`RefElement with id="${elementID}" cannot reference another RefElement`, {
|
|
200
|
+
elementID
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (isExternalReference) {
|
|
204
|
+
// dive deep into the fragment
|
|
205
|
+
const visitor = new ApiDOMDereferenceVisitor({
|
|
206
|
+
reference,
|
|
207
|
+
options: this.options
|
|
208
|
+
});
|
|
209
|
+
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
210
|
+
mutable: true
|
|
211
|
+
});
|
|
212
|
+
}
|
|
145
213
|
|
|
146
|
-
/**
|
|
147
|
-
* Transclusion of a Ref Element SHALL be defined in the if/else block below.
|
|
148
|
-
*/
|
|
149
|
-
// ancestors[0] is the grandparent (nearest ancestor from getAncestorNodes())
|
|
150
|
-
const grandparent = ancestors[0];
|
|
151
|
-
if ((0, _apidomDatamodel.isObjectElement)(referencedElement) && (0, _apidomDatamodel.isObjectElement)(grandparent) && Array.isArray(parent) && typeof key === 'number') {
|
|
152
|
-
/**
|
|
153
|
-
* If the Ref Element is held by an Object Element and references an Object Element,
|
|
154
|
-
* its content entries SHALL be inserted in place of the Ref Element.
|
|
155
|
-
*/
|
|
156
|
-
parent.splice(key, 1, ...referencedElement.content);
|
|
157
|
-
} else if ((0, _apidomDatamodel.isArrayElement)(referencedElement) && Array.isArray(parent) && typeof key === 'number') {
|
|
158
214
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
215
|
+
* When path is used, it references the given property of the referenced element.
|
|
216
|
+
* Valid paths are: 'element', 'content', 'meta', 'attributes'.
|
|
161
217
|
*/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
parent.value = referencedElement;
|
|
168
|
-
} else if (Array.isArray(parent)) {
|
|
218
|
+
const referencedElementPath = (0, _apidomCore.toValue)(refElement.path);
|
|
219
|
+
if (referencedElementPath !== 'element' && (0, _apidomDatamodel.isElement)(referencedElement)) {
|
|
220
|
+
referencedElement = (0, _apidomDatamodel.refract)(referencedElement[referencedElementPath]);
|
|
221
|
+
}
|
|
222
|
+
|
|
169
223
|
/**
|
|
170
|
-
*
|
|
224
|
+
* Transclusion of a Ref Element SHALL be defined in the if/else block below.
|
|
171
225
|
*/
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
226
|
+
// ancestors[0] is the grandparent (nearest ancestor from getAncestorNodes())
|
|
227
|
+
const grandparent = ancestors[0];
|
|
228
|
+
if ((0, _apidomDatamodel.isObjectElement)(referencedElement) && (0, _apidomDatamodel.isObjectElement)(grandparent) && Array.isArray(parent) && typeof key === 'number') {
|
|
229
|
+
/**
|
|
230
|
+
* If the Ref Element is held by an Object Element and references an Object Element,
|
|
231
|
+
* its content entries SHALL be inserted in place of the Ref Element.
|
|
232
|
+
*/
|
|
233
|
+
parent.splice(key, 1, ...referencedElement.content);
|
|
234
|
+
} else if ((0, _apidomDatamodel.isArrayElement)(referencedElement) && Array.isArray(parent) && typeof key === 'number') {
|
|
235
|
+
/**
|
|
236
|
+
* If the Ref Element is held by an Array Element and references an Array Element,
|
|
237
|
+
* its content entries SHALL be inserted in place of the Ref Element.
|
|
238
|
+
*/
|
|
239
|
+
parent.splice(key, 1, ...referencedElement.content);
|
|
240
|
+
} else if ((0, _apidomDatamodel.isMemberElement)(parent)) {
|
|
241
|
+
/**
|
|
242
|
+
* The Ref Element is substituted by the Element it references.
|
|
243
|
+
*/
|
|
244
|
+
parent.value = referencedElement;
|
|
245
|
+
} else if (Array.isArray(parent)) {
|
|
246
|
+
/**
|
|
247
|
+
* The Ref Element is substituted by the Element it references.
|
|
248
|
+
*/
|
|
249
|
+
parent[key] = referencedElement;
|
|
250
|
+
}
|
|
251
|
+
if (!parent) {
|
|
252
|
+
path.replaceWith(referencedElement);
|
|
253
|
+
}
|
|
254
|
+
} catch (error) {
|
|
255
|
+
this.handleError(`Error while dereferencing Ref Element. Cannot resolve ref "${refURI}": ${error.message}`, error, refElement, null, refURI, path);
|
|
176
256
|
}
|
|
177
257
|
}
|
|
178
258
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { propEq } from 'ramda';
|
|
2
|
-
import {
|
|
2
|
+
import { ApiDOMStructuredError } from '@speclynx/apidom-error';
|
|
3
3
|
import { isElement, isMemberElement, isArrayElement, isObjectElement, isRefElement, refract, cloneDeep } from '@speclynx/apidom-datamodel';
|
|
4
|
-
import { toValue } from '@speclynx/apidom-core';
|
|
5
|
-
import { traverseAsync } from '@speclynx/apidom-traverse';
|
|
4
|
+
import { toValue, toYAML } from '@speclynx/apidom-core';
|
|
5
|
+
import { traverse, traverseAsync } from '@speclynx/apidom-traverse';
|
|
6
6
|
import { URIFragmentIdentifier } from '@speclynx/apidom-json-pointer';
|
|
7
7
|
import MaximumResolveDepthError from "../../../errors/MaximumResolveDepthError.mjs";
|
|
8
|
+
import UnresolvableReferenceError from "../../../errors/UnresolvableReferenceError.mjs";
|
|
8
9
|
import * as url from "../../../util/url.mjs";
|
|
9
10
|
import parse from "../../../parse/index.mjs";
|
|
10
11
|
import Reference from "../../../Reference.mjs";
|
|
@@ -41,7 +42,10 @@ class ApiDOMDereferenceVisitor {
|
|
|
41
42
|
async toReference(uri) {
|
|
42
43
|
// detect maximum depth of resolution
|
|
43
44
|
if (this.reference.depth >= this.options.resolve.maxDepth) {
|
|
44
|
-
throw new MaximumResolveDepthError(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"
|
|
45
|
+
throw new MaximumResolveDepthError(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`, {
|
|
46
|
+
maxDepth: this.options.resolve.maxDepth,
|
|
47
|
+
uri: this.reference.uri
|
|
48
|
+
});
|
|
45
49
|
}
|
|
46
50
|
const baseURI = this.toBaseURI(uri);
|
|
47
51
|
const {
|
|
@@ -78,6 +82,72 @@ class ApiDOMDereferenceVisitor {
|
|
|
78
82
|
}
|
|
79
83
|
return mutableReference;
|
|
80
84
|
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Handles an error according to the continueOnError option.
|
|
88
|
+
*
|
|
89
|
+
* For new errors: wraps in UnresolvableReferenceError with structured context.
|
|
90
|
+
* For errors already wrapped by a nested visitor: prepends the current hop to the trace.
|
|
91
|
+
*
|
|
92
|
+
* Inner/intermediate visitors always throw to let the trace accumulate.
|
|
93
|
+
* Only the entry document visitor respects continueOnError (callback/swallow/throw).
|
|
94
|
+
*/
|
|
95
|
+
handleError(message, error, referencingElement, refFieldName, refFieldValue, visitorPath) {
|
|
96
|
+
const {
|
|
97
|
+
continueOnError
|
|
98
|
+
} = this.options.dereference;
|
|
99
|
+
const isEntryDocument = url.stripHash(this.reference.refSet?.rootRef?.uri ?? '') === this.reference.uri;
|
|
100
|
+
const uri = this.reference.uri;
|
|
101
|
+
const type = referencingElement.element;
|
|
102
|
+
let location;
|
|
103
|
+
traverse(this.reference.value.result, {
|
|
104
|
+
enter(p) {
|
|
105
|
+
if (p.node === referencingElement) {
|
|
106
|
+
location = p.formatPath();
|
|
107
|
+
p.stop();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
location ??= visitorPath.formatPath();
|
|
112
|
+
const codeFrame = toYAML(referencingElement);
|
|
113
|
+
const hop = {
|
|
114
|
+
uri,
|
|
115
|
+
type,
|
|
116
|
+
refFieldName,
|
|
117
|
+
refFieldValue,
|
|
118
|
+
location,
|
|
119
|
+
codeFrame
|
|
120
|
+
};
|
|
121
|
+
let unresolvedError;
|
|
122
|
+
if (error instanceof UnresolvableReferenceError) {
|
|
123
|
+
const refBaseURI = this.toBaseURI(refFieldValue);
|
|
124
|
+
const fragment = URIFragmentIdentifier.fromURIReference(refFieldValue);
|
|
125
|
+
if (fragment) {
|
|
126
|
+
if (refBaseURI === error.uri && error.location) {
|
|
127
|
+
error.location = fragment + error.location;
|
|
128
|
+
}
|
|
129
|
+
for (const h of error.trace) {
|
|
130
|
+
if (h.uri === refBaseURI && h.location) h.location = fragment + h.location;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// @ts-ignore
|
|
134
|
+
error.trace = [hop, ...error.trace];
|
|
135
|
+
unresolvedError = error;
|
|
136
|
+
} else {
|
|
137
|
+
unresolvedError = new UnresolvableReferenceError(message, {
|
|
138
|
+
cause: error,
|
|
139
|
+
type,
|
|
140
|
+
uri,
|
|
141
|
+
location,
|
|
142
|
+
codeFrame,
|
|
143
|
+
refFieldName,
|
|
144
|
+
refFieldValue,
|
|
145
|
+
trace: []
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (!isEntryDocument || continueOnError === false) throw unresolvedError;
|
|
149
|
+
if (typeof continueOnError === 'function') continueOnError(unresolvedError);
|
|
150
|
+
}
|
|
81
151
|
async RefElement(path) {
|
|
82
152
|
const refElement = path.node;
|
|
83
153
|
const {
|
|
@@ -103,69 +173,79 @@ class ApiDOMDereferenceVisitor {
|
|
|
103
173
|
path.skip();
|
|
104
174
|
return;
|
|
105
175
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
reference
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
referencedElement = refract(referencedElement[referencedElementPath]);
|
|
137
|
-
}
|
|
176
|
+
try {
|
|
177
|
+
const reference = await this.toReference(refNormalizedURI);
|
|
178
|
+
const refBaseURI = url.resolve(retrievalURI, refNormalizedURI);
|
|
179
|
+
const elementID = URIFragmentIdentifier.fromURIReference(refBaseURI);
|
|
180
|
+
let referencedElement = evaluate(elementID, reference.value.result);
|
|
181
|
+
if (!isElement(referencedElement)) {
|
|
182
|
+
throw new ApiDOMStructuredError(`Referenced element with id="${elementID}" was not found`, {
|
|
183
|
+
elementID
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (refElement === referencedElement) {
|
|
187
|
+
throw new ApiDOMStructuredError(`RefElement with id="${elementID}" cannot reference itself`, {
|
|
188
|
+
elementID
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
if (isRefElement(referencedElement)) {
|
|
192
|
+
throw new ApiDOMStructuredError(`RefElement with id="${elementID}" cannot reference another RefElement`, {
|
|
193
|
+
elementID
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (isExternalReference) {
|
|
197
|
+
// dive deep into the fragment
|
|
198
|
+
const visitor = new ApiDOMDereferenceVisitor({
|
|
199
|
+
reference,
|
|
200
|
+
options: this.options
|
|
201
|
+
});
|
|
202
|
+
referencedElement = await traverseAsync(referencedElement, visitor, {
|
|
203
|
+
mutable: true
|
|
204
|
+
});
|
|
205
|
+
}
|
|
138
206
|
|
|
139
|
-
/**
|
|
140
|
-
* Transclusion of a Ref Element SHALL be defined in the if/else block below.
|
|
141
|
-
*/
|
|
142
|
-
// ancestors[0] is the grandparent (nearest ancestor from getAncestorNodes())
|
|
143
|
-
const grandparent = ancestors[0];
|
|
144
|
-
if (isObjectElement(referencedElement) && isObjectElement(grandparent) && Array.isArray(parent) && typeof key === 'number') {
|
|
145
|
-
/**
|
|
146
|
-
* If the Ref Element is held by an Object Element and references an Object Element,
|
|
147
|
-
* its content entries SHALL be inserted in place of the Ref Element.
|
|
148
|
-
*/
|
|
149
|
-
parent.splice(key, 1, ...referencedElement.content);
|
|
150
|
-
} else if (isArrayElement(referencedElement) && Array.isArray(parent) && typeof key === 'number') {
|
|
151
207
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
208
|
+
* When path is used, it references the given property of the referenced element.
|
|
209
|
+
* Valid paths are: 'element', 'content', 'meta', 'attributes'.
|
|
154
210
|
*/
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
parent.value = referencedElement;
|
|
161
|
-
} else if (Array.isArray(parent)) {
|
|
211
|
+
const referencedElementPath = toValue(refElement.path);
|
|
212
|
+
if (referencedElementPath !== 'element' && isElement(referencedElement)) {
|
|
213
|
+
referencedElement = refract(referencedElement[referencedElementPath]);
|
|
214
|
+
}
|
|
215
|
+
|
|
162
216
|
/**
|
|
163
|
-
*
|
|
217
|
+
* Transclusion of a Ref Element SHALL be defined in the if/else block below.
|
|
164
218
|
*/
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
219
|
+
// ancestors[0] is the grandparent (nearest ancestor from getAncestorNodes())
|
|
220
|
+
const grandparent = ancestors[0];
|
|
221
|
+
if (isObjectElement(referencedElement) && isObjectElement(grandparent) && Array.isArray(parent) && typeof key === 'number') {
|
|
222
|
+
/**
|
|
223
|
+
* If the Ref Element is held by an Object Element and references an Object Element,
|
|
224
|
+
* its content entries SHALL be inserted in place of the Ref Element.
|
|
225
|
+
*/
|
|
226
|
+
parent.splice(key, 1, ...referencedElement.content);
|
|
227
|
+
} else if (isArrayElement(referencedElement) && Array.isArray(parent) && typeof key === 'number') {
|
|
228
|
+
/**
|
|
229
|
+
* If the Ref Element is held by an Array Element and references an Array Element,
|
|
230
|
+
* its content entries SHALL be inserted in place of the Ref Element.
|
|
231
|
+
*/
|
|
232
|
+
parent.splice(key, 1, ...referencedElement.content);
|
|
233
|
+
} else if (isMemberElement(parent)) {
|
|
234
|
+
/**
|
|
235
|
+
* The Ref Element is substituted by the Element it references.
|
|
236
|
+
*/
|
|
237
|
+
parent.value = referencedElement;
|
|
238
|
+
} else if (Array.isArray(parent)) {
|
|
239
|
+
/**
|
|
240
|
+
* The Ref Element is substituted by the Element it references.
|
|
241
|
+
*/
|
|
242
|
+
parent[key] = referencedElement;
|
|
243
|
+
}
|
|
244
|
+
if (!parent) {
|
|
245
|
+
path.replaceWith(referencedElement);
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
this.handleError(`Error while dereferencing Ref Element. Cannot resolve ref "${refURI}": ${error.message}`, error, refElement, null, refURI, path);
|
|
169
249
|
}
|
|
170
250
|
}
|
|
171
251
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
4
|
-
var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopRequireWildcard").default;
|
|
5
4
|
exports.__esModule = true;
|
|
6
5
|
exports.resolveSchema$refField = exports.resolveSchema$idField = exports.maybeRefractToJSONSchemaElement = exports.default = void 0;
|
|
7
6
|
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
8
7
|
var _apidomTraverse = require("@speclynx/apidom-traverse");
|
|
9
|
-
var _apidomNsArazzo =
|
|
8
|
+
var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
|
|
10
9
|
var _DereferenceStrategy = _interopRequireDefault(require("../DereferenceStrategy.cjs"));
|
|
11
10
|
var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
|
|
12
11
|
var _ReferenceSet = _interopRequireDefault(require("../../../ReferenceSet.cjs"));
|
|
@@ -42,7 +41,6 @@ class Arazzo1DereferenceStrategy extends _DereferenceStrategy.default {
|
|
|
42
41
|
return (0, _apidomNsArazzo.isArazzoSpecification1Element)(file.parseResult?.result);
|
|
43
42
|
}
|
|
44
43
|
async dereference(file, options) {
|
|
45
|
-
const namespace = new _apidomDatamodel.Namespace().use(_apidomNsArazzo.default);
|
|
46
44
|
const immutableRefSet = options.dereference.refSet ?? new _ReferenceSet.default();
|
|
47
45
|
const mutableRefSet = new _ReferenceSet.default();
|
|
48
46
|
let refSet = immutableRefSet;
|
|
@@ -72,7 +70,6 @@ class Arazzo1DereferenceStrategy extends _DereferenceStrategy.default {
|
|
|
72
70
|
}
|
|
73
71
|
const visitor = new _visitor.default({
|
|
74
72
|
reference,
|
|
75
|
-
namespace,
|
|
76
73
|
options
|
|
77
74
|
});
|
|
78
75
|
const dereferencedElement = await (0, _apidomTraverse.traverseAsync)(refSet.rootRef.value, visitor, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cloneDeep } from '@speclynx/apidom-datamodel';
|
|
2
2
|
import { traverseAsync } from '@speclynx/apidom-traverse';
|
|
3
|
-
import
|
|
3
|
+
import { isArazzoSpecification1Element, mediaTypes } from '@speclynx/apidom-ns-arazzo-1';
|
|
4
4
|
import DereferenceStrategy from "../DereferenceStrategy.mjs";
|
|
5
5
|
import Reference from "../../../Reference.mjs";
|
|
6
6
|
import ReferenceSet from "../../../ReferenceSet.mjs";
|
|
@@ -29,7 +29,6 @@ class Arazzo1DereferenceStrategy extends DereferenceStrategy {
|
|
|
29
29
|
return isArazzoSpecification1Element(file.parseResult?.result);
|
|
30
30
|
}
|
|
31
31
|
async dereference(file, options) {
|
|
32
|
-
const namespace = new Namespace().use(arazzo1Namespace);
|
|
33
32
|
const immutableRefSet = options.dereference.refSet ?? new ReferenceSet();
|
|
34
33
|
const mutableRefSet = new ReferenceSet();
|
|
35
34
|
let refSet = immutableRefSet;
|
|
@@ -59,7 +58,6 @@ class Arazzo1DereferenceStrategy extends DereferenceStrategy {
|
|
|
59
58
|
}
|
|
60
59
|
const visitor = new Arazzo1DereferenceVisitor({
|
|
61
60
|
reference,
|
|
62
|
-
namespace,
|
|
63
61
|
options
|
|
64
62
|
});
|
|
65
63
|
const dereferencedElement = await traverseAsync(refSet.rootRef.value, visitor, {
|