@speclynx/apidom-reference 4.14.0 → 4.16.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 +12 -0
- package/README.md +133 -1
- package/dist/apidom-reference.browser.js +2043 -49
- package/dist/apidom-reference.browser.min.js +1 -1
- package/package.json +38 -28
- package/src/bundle/strategies/arazzo-1/index.cjs +94 -0
- package/src/bundle/strategies/arazzo-1/index.mjs +81 -0
- package/src/bundle/strategies/arazzo-1/visitor.cjs +417 -0
- package/src/bundle/strategies/arazzo-1/visitor.mjs +409 -0
- package/src/bundle/strategies/asyncapi-2/index.cjs +94 -0
- package/src/bundle/strategies/asyncapi-2/index.mjs +81 -0
- package/src/bundle/strategies/asyncapi-2/visitor.cjs +571 -0
- package/src/bundle/strategies/asyncapi-2/visitor.mjs +563 -0
- package/src/configuration/saturated.cjs +11 -9
- package/src/configuration/saturated.mjs +3 -1
- package/types/bundle/strategies/arazzo-1/index.d.ts +31 -0
- package/types/bundle/strategies/arazzo-1/visitor.d.ts +90 -0
- package/types/bundle/strategies/asyncapi-2/index.d.ts +31 -0
- package/types/bundle/strategies/asyncapi-2/visitor.d.ts +116 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speclynx/apidom-reference",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.16.0",
|
|
4
4
|
"description": "Advanced algorithms for semantic ApiDOM manipulations like dereferencing or resolution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"apidom",
|
|
@@ -263,6 +263,16 @@
|
|
|
263
263
|
"import": "./src/bundle/strategies/openapi-3-1/index.mjs",
|
|
264
264
|
"require": "./src/bundle/strategies/openapi-3-1/index.cjs",
|
|
265
265
|
"types": "./types/bundle/strategies/openapi-3-1/index.d.ts"
|
|
266
|
+
},
|
|
267
|
+
"./bundle/strategies/asyncapi-2": {
|
|
268
|
+
"import": "./src/bundle/strategies/asyncapi-2/index.mjs",
|
|
269
|
+
"require": "./src/bundle/strategies/asyncapi-2/index.cjs",
|
|
270
|
+
"types": "./types/bundle/strategies/asyncapi-2/index.d.ts"
|
|
271
|
+
},
|
|
272
|
+
"./bundle/strategies/arazzo-1": {
|
|
273
|
+
"import": "./src/bundle/strategies/arazzo-1/index.mjs",
|
|
274
|
+
"require": "./src/bundle/strategies/arazzo-1/index.cjs",
|
|
275
|
+
"types": "./types/bundle/strategies/arazzo-1/index.d.ts"
|
|
266
276
|
}
|
|
267
277
|
},
|
|
268
278
|
"scripts": {
|
|
@@ -289,32 +299,32 @@
|
|
|
289
299
|
"license": "Apache-2.0",
|
|
290
300
|
"dependencies": {
|
|
291
301
|
"@babel/runtime-corejs3": "^8.0.0",
|
|
292
|
-
"@speclynx/apidom-core": "4.
|
|
293
|
-
"@speclynx/apidom-datamodel": "4.
|
|
294
|
-
"@speclynx/apidom-error": "4.
|
|
295
|
-
"@speclynx/apidom-json-pointer": "4.
|
|
296
|
-
"@speclynx/apidom-ns-arazzo-1": "4.
|
|
297
|
-
"@speclynx/apidom-ns-asyncapi-2": "4.
|
|
298
|
-
"@speclynx/apidom-ns-json-schema-2020-12": "4.
|
|
299
|
-
"@speclynx/apidom-ns-openapi-2": "4.
|
|
300
|
-
"@speclynx/apidom-ns-openapi-3-0": "4.
|
|
301
|
-
"@speclynx/apidom-ns-openapi-3-1": "4.
|
|
302
|
-
"@speclynx/apidom-ns-overlay-1": "4.
|
|
303
|
-
"@speclynx/apidom-parser-adapter-arazzo-json-1": "4.
|
|
304
|
-
"@speclynx/apidom-parser-adapter-arazzo-yaml-1": "4.
|
|
305
|
-
"@speclynx/apidom-parser-adapter-asyncapi-json-2": "4.
|
|
306
|
-
"@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "4.
|
|
307
|
-
"@speclynx/apidom-parser-adapter-json": "4.
|
|
308
|
-
"@speclynx/apidom-parser-adapter-openapi-json-2": "4.
|
|
309
|
-
"@speclynx/apidom-parser-adapter-openapi-json-3-0": "4.
|
|
310
|
-
"@speclynx/apidom-parser-adapter-openapi-json-3-1": "4.
|
|
311
|
-
"@speclynx/apidom-parser-adapter-openapi-yaml-2": "4.
|
|
312
|
-
"@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "4.
|
|
313
|
-
"@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "4.
|
|
314
|
-
"@speclynx/apidom-parser-adapter-overlay-json-1": "4.
|
|
315
|
-
"@speclynx/apidom-parser-adapter-overlay-yaml-1": "4.
|
|
316
|
-
"@speclynx/apidom-parser-adapter-yaml-1-2": "4.
|
|
317
|
-
"@speclynx/apidom-traverse": "4.
|
|
302
|
+
"@speclynx/apidom-core": "4.16.0",
|
|
303
|
+
"@speclynx/apidom-datamodel": "4.16.0",
|
|
304
|
+
"@speclynx/apidom-error": "4.16.0",
|
|
305
|
+
"@speclynx/apidom-json-pointer": "4.16.0",
|
|
306
|
+
"@speclynx/apidom-ns-arazzo-1": "4.16.0",
|
|
307
|
+
"@speclynx/apidom-ns-asyncapi-2": "4.16.0",
|
|
308
|
+
"@speclynx/apidom-ns-json-schema-2020-12": "4.16.0",
|
|
309
|
+
"@speclynx/apidom-ns-openapi-2": "4.16.0",
|
|
310
|
+
"@speclynx/apidom-ns-openapi-3-0": "4.16.0",
|
|
311
|
+
"@speclynx/apidom-ns-openapi-3-1": "4.16.0",
|
|
312
|
+
"@speclynx/apidom-ns-overlay-1": "4.16.0",
|
|
313
|
+
"@speclynx/apidom-parser-adapter-arazzo-json-1": "4.16.0",
|
|
314
|
+
"@speclynx/apidom-parser-adapter-arazzo-yaml-1": "4.16.0",
|
|
315
|
+
"@speclynx/apidom-parser-adapter-asyncapi-json-2": "4.16.0",
|
|
316
|
+
"@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "4.16.0",
|
|
317
|
+
"@speclynx/apidom-parser-adapter-json": "4.16.0",
|
|
318
|
+
"@speclynx/apidom-parser-adapter-openapi-json-2": "4.16.0",
|
|
319
|
+
"@speclynx/apidom-parser-adapter-openapi-json-3-0": "4.16.0",
|
|
320
|
+
"@speclynx/apidom-parser-adapter-openapi-json-3-1": "4.16.0",
|
|
321
|
+
"@speclynx/apidom-parser-adapter-openapi-yaml-2": "4.16.0",
|
|
322
|
+
"@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "4.16.0",
|
|
323
|
+
"@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "4.16.0",
|
|
324
|
+
"@speclynx/apidom-parser-adapter-overlay-json-1": "4.16.0",
|
|
325
|
+
"@speclynx/apidom-parser-adapter-overlay-yaml-1": "4.16.0",
|
|
326
|
+
"@speclynx/apidom-parser-adapter-yaml-1-2": "4.16.0",
|
|
327
|
+
"@speclynx/apidom-traverse": "4.16.0",
|
|
318
328
|
"@swaggerexpert/arazzo-runtime-expression": "^2.0.3",
|
|
319
329
|
"axios": "^1.18.0",
|
|
320
330
|
"picomatch": "^4.0.4",
|
|
@@ -336,5 +346,5 @@
|
|
|
336
346
|
"README.md",
|
|
337
347
|
"CHANGELOG.md"
|
|
338
348
|
],
|
|
339
|
-
"gitHead": "
|
|
349
|
+
"gitHead": "252461983339fe06eb64db4e60f8ac8087065f06"
|
|
340
350
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
Object.defineProperty(exports, "Arazzo1BundleVisitor", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _visitor.default;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
exports.default = void 0;
|
|
14
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
15
|
+
var _apidomTraverse = require("@speclynx/apidom-traverse");
|
|
16
|
+
var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
|
|
17
|
+
var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
|
|
18
|
+
var _ReferenceSet = _interopRequireDefault(require("../../../ReferenceSet.cjs"));
|
|
19
|
+
var _BundleStrategy = _interopRequireDefault(require("../BundleStrategy.cjs"));
|
|
20
|
+
var _visitor = _interopRequireDefault(require("./visitor.cjs"));
|
|
21
|
+
/**
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
class Arazzo1BundleStrategy extends _BundleStrategy.default {
|
|
29
|
+
constructor(options) {
|
|
30
|
+
super({
|
|
31
|
+
...(options ?? {}),
|
|
32
|
+
name: 'arazzo-1'
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
canBundle(file) {
|
|
36
|
+
// assert by media type
|
|
37
|
+
if (file.mediaType !== 'text/plain') {
|
|
38
|
+
return _apidomNsArazzo.mediaTypes.includes(file.mediaType);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// assert by inspecting ApiDOM
|
|
42
|
+
return (0, _apidomNsArazzo.isArazzoSpecification1Element)(file.parseResult?.result);
|
|
43
|
+
}
|
|
44
|
+
async bundle(file, options) {
|
|
45
|
+
const immutableRefSet = options.bundle.refSet ?? new _ReferenceSet.default();
|
|
46
|
+
const mutableRefSet = new _ReferenceSet.default();
|
|
47
|
+
let refSet = immutableRefSet;
|
|
48
|
+
let reference;
|
|
49
|
+
|
|
50
|
+
// determine the initial reference
|
|
51
|
+
if (!immutableRefSet.has(file.uri)) {
|
|
52
|
+
reference = new _Reference.default({
|
|
53
|
+
uri: file.uri,
|
|
54
|
+
value: file.parseResult
|
|
55
|
+
});
|
|
56
|
+
immutableRefSet.add(reference);
|
|
57
|
+
} else {
|
|
58
|
+
// pre-computed refSet was provided as configuration option
|
|
59
|
+
reference = immutableRefSet.find(ref => ref.uri === file.uri);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Clone refSet due the bundling process being mutable.
|
|
64
|
+
* We don't want to mutate the original refSet and the references.
|
|
65
|
+
*/
|
|
66
|
+
if (options.bundle.immutable) {
|
|
67
|
+
immutableRefSet.refs.map(ref => new _Reference.default({
|
|
68
|
+
...ref,
|
|
69
|
+
value: (0, _apidomDatamodel.cloneDeep)(ref.value)
|
|
70
|
+
})).forEach(ref => mutableRefSet.add(ref));
|
|
71
|
+
reference = mutableRefSet.find(ref => ref.uri === file.uri);
|
|
72
|
+
refSet = mutableRefSet;
|
|
73
|
+
}
|
|
74
|
+
const visitor = new _visitor.default({
|
|
75
|
+
reference: reference,
|
|
76
|
+
options
|
|
77
|
+
});
|
|
78
|
+
await (0, _apidomTraverse.traverseAsync)(refSet.rootRef.value, visitor, {
|
|
79
|
+
mutable: true
|
|
80
|
+
});
|
|
81
|
+
const parseResult = reference.value;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Release all memory if this refSet was not provided as a configuration option.
|
|
85
|
+
* If provided as configuration option, then provider is responsible for cleanup.
|
|
86
|
+
*/
|
|
87
|
+
if (options.bundle.refSet === null) {
|
|
88
|
+
immutableRefSet.clean();
|
|
89
|
+
}
|
|
90
|
+
mutableRefSet.clean();
|
|
91
|
+
return parseResult;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
var _default = exports.default = Arazzo1BundleStrategy;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { cloneDeep } from '@speclynx/apidom-datamodel';
|
|
2
|
+
import { traverseAsync } from '@speclynx/apidom-traverse';
|
|
3
|
+
import { mediaTypes, isArazzoSpecification1Element } from '@speclynx/apidom-ns-arazzo-1';
|
|
4
|
+
import Reference from "../../../Reference.mjs";
|
|
5
|
+
import ReferenceSet from "../../../ReferenceSet.mjs";
|
|
6
|
+
import BundleStrategy from "../BundleStrategy.mjs";
|
|
7
|
+
import Arazzo1BundleVisitor from "./visitor.mjs";
|
|
8
|
+
/**
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
class Arazzo1BundleStrategy extends BundleStrategy {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
super({
|
|
17
|
+
...(options ?? {}),
|
|
18
|
+
name: 'arazzo-1'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
canBundle(file) {
|
|
22
|
+
// assert by media type
|
|
23
|
+
if (file.mediaType !== 'text/plain') {
|
|
24
|
+
return mediaTypes.includes(file.mediaType);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// assert by inspecting ApiDOM
|
|
28
|
+
return isArazzoSpecification1Element(file.parseResult?.result);
|
|
29
|
+
}
|
|
30
|
+
async bundle(file, options) {
|
|
31
|
+
const immutableRefSet = options.bundle.refSet ?? new ReferenceSet();
|
|
32
|
+
const mutableRefSet = new ReferenceSet();
|
|
33
|
+
let refSet = immutableRefSet;
|
|
34
|
+
let reference;
|
|
35
|
+
|
|
36
|
+
// determine the initial reference
|
|
37
|
+
if (!immutableRefSet.has(file.uri)) {
|
|
38
|
+
reference = new Reference({
|
|
39
|
+
uri: file.uri,
|
|
40
|
+
value: file.parseResult
|
|
41
|
+
});
|
|
42
|
+
immutableRefSet.add(reference);
|
|
43
|
+
} else {
|
|
44
|
+
// pre-computed refSet was provided as configuration option
|
|
45
|
+
reference = immutableRefSet.find(ref => ref.uri === file.uri);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Clone refSet due the bundling process being mutable.
|
|
50
|
+
* We don't want to mutate the original refSet and the references.
|
|
51
|
+
*/
|
|
52
|
+
if (options.bundle.immutable) {
|
|
53
|
+
immutableRefSet.refs.map(ref => new Reference({
|
|
54
|
+
...ref,
|
|
55
|
+
value: cloneDeep(ref.value)
|
|
56
|
+
})).forEach(ref => mutableRefSet.add(ref));
|
|
57
|
+
reference = mutableRefSet.find(ref => ref.uri === file.uri);
|
|
58
|
+
refSet = mutableRefSet;
|
|
59
|
+
}
|
|
60
|
+
const visitor = new Arazzo1BundleVisitor({
|
|
61
|
+
reference: reference,
|
|
62
|
+
options
|
|
63
|
+
});
|
|
64
|
+
await traverseAsync(refSet.rootRef.value, visitor, {
|
|
65
|
+
mutable: true
|
|
66
|
+
});
|
|
67
|
+
const parseResult = reference.value;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Release all memory if this refSet was not provided as a configuration option.
|
|
71
|
+
* If provided as configuration option, then provider is responsible for cleanup.
|
|
72
|
+
*/
|
|
73
|
+
if (options.bundle.refSet === null) {
|
|
74
|
+
immutableRefSet.clean();
|
|
75
|
+
}
|
|
76
|
+
mutableRefSet.clean();
|
|
77
|
+
return parseResult;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export { Arazzo1BundleVisitor };
|
|
81
|
+
export default Arazzo1BundleStrategy;
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopRequireWildcard").default;
|
|
4
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
var _ramda = require("ramda");
|
|
10
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
11
|
+
var _apidomCore = require("@speclynx/apidom-core");
|
|
12
|
+
var _apidomTraverse = require("@speclynx/apidom-traverse");
|
|
13
|
+
var _apidomJsonPointer = require("@speclynx/apidom-json-pointer");
|
|
14
|
+
var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
|
|
15
|
+
var _BundleError = _interopRequireDefault(require("../../../errors/BundleError.cjs"));
|
|
16
|
+
var _UnresolvableReferenceError = _interopRequireDefault(require("../../../errors/UnresolvableReferenceError.cjs"));
|
|
17
|
+
var _MaximumBundleDepthError = _interopRequireDefault(require("../../../errors/MaximumBundleDepthError.cjs"));
|
|
18
|
+
var _MaximumResolveDepthError = _interopRequireDefault(require("../../../errors/MaximumResolveDepthError.cjs"));
|
|
19
|
+
var _EvaluationJsonSchemaUriError = _interopRequireDefault(require("../../../errors/EvaluationJsonSchemaUriError.cjs"));
|
|
20
|
+
var url = _interopRequireWildcard(require("../../../util/url.cjs"));
|
|
21
|
+
var _index = _interopRequireDefault(require("../../../parse/index.cjs"));
|
|
22
|
+
var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
|
|
23
|
+
var _File = _interopRequireDefault(require("../../../File.cjs"));
|
|
24
|
+
var _$anchor = require("../../../dereference/strategies/arazzo-1/selectors/$anchor.cjs");
|
|
25
|
+
var _uri = require("../../../dereference/strategies/arazzo-1/selectors/uri.cjs");
|
|
26
|
+
var _util = require("../../../dereference/strategies/arazzo-1/util.cjs");
|
|
27
|
+
var _util2 = require("../../util.cjs");
|
|
28
|
+
/**
|
|
29
|
+
* The Components Object field where embedded JSON Schema resources are hoisted.
|
|
30
|
+
* Sourced from the Components Object fixed fields so it cannot drift from the
|
|
31
|
+
* namespace definition.
|
|
32
|
+
*/
|
|
33
|
+
const cf = (0, _apidomCore.fixedFields)(_apidomNsArazzo.ComponentsElement, {
|
|
34
|
+
indexed: true
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
class Arazzo1BundleVisitor {
|
|
45
|
+
reference;
|
|
46
|
+
options;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The entry parse result. Derived from the shared refSet's root reference.
|
|
50
|
+
*/
|
|
51
|
+
get entryParseResult() {
|
|
52
|
+
return this.reference.refSet.rootRef.value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The entry document element. Embedded external schema resources are placed
|
|
57
|
+
* into its `components.inputs` object. The `canBundle` guard guarantees it is
|
|
58
|
+
* an ArazzoSpecification1Element.
|
|
59
|
+
*/
|
|
60
|
+
get entryResult() {
|
|
61
|
+
return this.entryParseResult.result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Shared across the entry document and every external document visitor.
|
|
66
|
+
* Guarantees each external resource is embedded exactly once, keyed by its
|
|
67
|
+
* resource URI.
|
|
68
|
+
*/
|
|
69
|
+
assignments;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Component names already reserved per Components Object field. Reserved
|
|
73
|
+
* before recursion so collision suffixing (`Pet`, `Pet-2`, ...) accounts for
|
|
74
|
+
* components that are still being bundled.
|
|
75
|
+
*/
|
|
76
|
+
reservedNames;
|
|
77
|
+
constructor({
|
|
78
|
+
reference,
|
|
79
|
+
options,
|
|
80
|
+
assignments = new Map(),
|
|
81
|
+
reservedNames = new Map()
|
|
82
|
+
}) {
|
|
83
|
+
this.reference = reference;
|
|
84
|
+
this.options = options;
|
|
85
|
+
this.assignments = assignments;
|
|
86
|
+
this.reservedNames = reservedNames;
|
|
87
|
+
}
|
|
88
|
+
toBaseURI(uri) {
|
|
89
|
+
return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
|
|
90
|
+
}
|
|
91
|
+
async toReference(uri) {
|
|
92
|
+
// detect maximum depth of resolution
|
|
93
|
+
if (this.reference.depth >= this.options.resolve.maxDepth) {
|
|
94
|
+
throw new _MaximumResolveDepthError.default(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`, {
|
|
95
|
+
maxDepth: this.options.resolve.maxDepth,
|
|
96
|
+
uri: this.reference.uri
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const baseURI = this.toBaseURI(uri);
|
|
100
|
+
const {
|
|
101
|
+
refSet
|
|
102
|
+
} = this.reference;
|
|
103
|
+
|
|
104
|
+
// we've already processed this Reference in past
|
|
105
|
+
if (refSet.has(baseURI)) {
|
|
106
|
+
return refSet.find(ref => ref.uri === baseURI);
|
|
107
|
+
}
|
|
108
|
+
const parseResult = await (0, _index.default)(url.unsanitize(baseURI), {
|
|
109
|
+
...this.options,
|
|
110
|
+
parse: {
|
|
111
|
+
...this.options.parse,
|
|
112
|
+
mediaType: 'text/plain'
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const reference = new _Reference.default({
|
|
116
|
+
uri: baseURI,
|
|
117
|
+
value: parseResult,
|
|
118
|
+
depth: this.reference.depth + 1
|
|
119
|
+
});
|
|
120
|
+
refSet.add(reference);
|
|
121
|
+
return reference;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Handles an error according to the `bundle.continueOnError` option.
|
|
126
|
+
*
|
|
127
|
+
* For new errors: wraps in UnresolvableReferenceError with structured
|
|
128
|
+
* context. For errors already wrapped by a nested visitor: prepends the
|
|
129
|
+
* current hop to the trace.
|
|
130
|
+
*
|
|
131
|
+
* Inner/intermediate visitors always throw to let the trace accumulate. Only
|
|
132
|
+
* the entry document visitor respects continueOnError (callback/swallow/throw).
|
|
133
|
+
*/
|
|
134
|
+
handleError(message, error, referencingElement, refFieldName, refFieldValue, visitorPath) {
|
|
135
|
+
// a collision reported as `error` is a deliberate hard stop with no
|
|
136
|
+
// resolution-failure analog, so pass it through unchanged. A maxDepth
|
|
137
|
+
// breach (MaximumBundleDepthError, which also extends BundleError) is a
|
|
138
|
+
// resolution failure and flows through below — wrapped and subject to
|
|
139
|
+
// continueOnError, like dereferencing.
|
|
140
|
+
if (error instanceof _BundleError.default && !(error instanceof _MaximumBundleDepthError.default)) {
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
const {
|
|
144
|
+
continueOnError
|
|
145
|
+
} = this.options.bundle;
|
|
146
|
+
const isEntryDocument = url.stripHash(this.reference.refSet?.rootRef?.uri ?? '') === this.reference.uri;
|
|
147
|
+
const uri = this.reference.uri;
|
|
148
|
+
const type = referencingElement.element;
|
|
149
|
+
const codeFrame = (0, _apidomCore.toYAML)(referencingElement);
|
|
150
|
+
const location = visitorPath.formatPath();
|
|
151
|
+
const hop = {
|
|
152
|
+
uri,
|
|
153
|
+
type,
|
|
154
|
+
refFieldName,
|
|
155
|
+
refFieldValue,
|
|
156
|
+
location,
|
|
157
|
+
codeFrame
|
|
158
|
+
};
|
|
159
|
+
let unresolvedError;
|
|
160
|
+
if (error instanceof _UnresolvableReferenceError.default) {
|
|
161
|
+
// @ts-ignore
|
|
162
|
+
error.trace = [hop, ...error.trace];
|
|
163
|
+
unresolvedError = error;
|
|
164
|
+
} else {
|
|
165
|
+
unresolvedError = new _UnresolvableReferenceError.default(message, {
|
|
166
|
+
cause: error,
|
|
167
|
+
type,
|
|
168
|
+
uri,
|
|
169
|
+
location,
|
|
170
|
+
codeFrame,
|
|
171
|
+
refFieldName,
|
|
172
|
+
refFieldValue,
|
|
173
|
+
trace: []
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (!isEntryDocument || continueOnError === false) throw unresolvedError;
|
|
177
|
+
if (typeof continueOnError === 'function') continueOnError(unresolvedError);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Lazily creates the Components Object (and the `inputs` field within it) on
|
|
182
|
+
* the entry document, returning the field where a resource is embedded.
|
|
183
|
+
*/
|
|
184
|
+
ensureInputsField() {
|
|
185
|
+
const entryResult = this.entryResult;
|
|
186
|
+
let components = entryResult.components;
|
|
187
|
+
if (!(0, _apidomDatamodel.isObjectElement)(components)) {
|
|
188
|
+
components = new _apidomNsArazzo.ComponentsElement();
|
|
189
|
+
entryResult.components = components;
|
|
190
|
+
}
|
|
191
|
+
let inputs = components.get(cf.inputs.name);
|
|
192
|
+
if (!(0, _apidomDatamodel.isObjectElement)(inputs)) {
|
|
193
|
+
inputs = new _apidomNsArazzo.ComponentsInputsElement();
|
|
194
|
+
components.set(cf.inputs.name, inputs);
|
|
195
|
+
}
|
|
196
|
+
return inputs;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Computes the base component name (before collision suffixing) according to
|
|
201
|
+
* the configured naming strategy.
|
|
202
|
+
*/
|
|
203
|
+
baseName(element, baseURI) {
|
|
204
|
+
// strategy specific options take precedence over the top-level bundle options
|
|
205
|
+
const componentNamesStrategy = this.options.bundle.strategyOpts['arazzo-1']?.componentNamesStrategy ?? this.options.bundle.componentNamesStrategy;
|
|
206
|
+
const fallback = url.getBasename(baseURI) || 'Schema';
|
|
207
|
+
if (typeof componentNamesStrategy === 'function') {
|
|
208
|
+
const resolved = componentNamesStrategy({
|
|
209
|
+
element,
|
|
210
|
+
field: cf.inputs.name,
|
|
211
|
+
jsonPointer: '',
|
|
212
|
+
baseURI
|
|
213
|
+
});
|
|
214
|
+
if (typeof resolved === 'string' && resolved !== '') {
|
|
215
|
+
return resolved;
|
|
216
|
+
}
|
|
217
|
+
return fallback;
|
|
218
|
+
}
|
|
219
|
+
if (componentNamesStrategy === 'title') {
|
|
220
|
+
const title = (0, _apidomDatamodel.isObjectElement)(element) ? (0, _apidomCore.toValue)(element.get('title')) : undefined;
|
|
221
|
+
if (typeof title === 'string' && title.trim() !== '') {
|
|
222
|
+
const name = (0, _util2.sanitizeComponentName)((0, _util2.toPascalCase)(title));
|
|
223
|
+
if (name !== '') return name;
|
|
224
|
+
}
|
|
225
|
+
// fall back to basename when no usable title is present
|
|
226
|
+
}
|
|
227
|
+
return fallback;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Computes a collision-free component name within `components.inputs`. A name
|
|
232
|
+
* is taken if it's already placed in inputs or reserved by a component that is
|
|
233
|
+
* still being bundled (reserved before recursion).
|
|
234
|
+
*/
|
|
235
|
+
uniqueName(candidate) {
|
|
236
|
+
const field = cf.inputs.name;
|
|
237
|
+
const inputs = this.ensureInputsField();
|
|
238
|
+
const reserved = this.reservedNames.get(field) ?? new Set();
|
|
239
|
+
return (0, _util2.uniqueName)(candidate, name => inputs.hasKey(name) || reserved.has(name));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Reports a collision-forced rename according to the configured severity.
|
|
244
|
+
* A rename means two distinct targets resolved to the same name; each keeps
|
|
245
|
+
* its own component (and origin), so report the rename per severity.
|
|
246
|
+
*/
|
|
247
|
+
reportNameCollision(preferredName, componentName) {
|
|
248
|
+
// strategy specific options take precedence over the top-level bundle options
|
|
249
|
+
const onComponentNameCollision = this.options.bundle.strategyOpts['arazzo-1']?.onComponentNameCollision ?? this.options.bundle.onComponentNameCollision;
|
|
250
|
+
if (componentName === preferredName || onComponentNameCollision === 'off') {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const message = `Component "${preferredName}" in components/${cf.inputs.name} is referenced with the same name but different content. Renamed to "${componentName}".`;
|
|
254
|
+
if (onComponentNameCollision === 'error') {
|
|
255
|
+
throw new _BundleError.default(message);
|
|
256
|
+
}
|
|
257
|
+
const annotation = new _apidomDatamodel.AnnotationElement(message);
|
|
258
|
+
annotation.classes.push('warning');
|
|
259
|
+
annotation.code = 'bundle-component-name-collision';
|
|
260
|
+
// append (never prepend) so the result element stays ahead of annotations
|
|
261
|
+
this.entryParseResult.content.push(annotation);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* JSON Schema Objects in Arazzo (used by Input Objects and inline schemas) are
|
|
266
|
+
* a JSON Schema 2020-12 dialect — the only external reference type Arazzo
|
|
267
|
+
* defines. They are bundled per the JSON Schema Compound Document rules: the
|
|
268
|
+
* external schema RESOURCE is embedded verbatim into `components.inputs`
|
|
269
|
+
* (carrying its `$id`), and the referencing `$ref` is left UNCHANGED — it
|
|
270
|
+
* keeps resolving against the embedded resource's `$id`. This mirrors the
|
|
271
|
+
* openapi-3-1 strategy's Schema Object handling.
|
|
272
|
+
*/
|
|
273
|
+
async JSONSchemaElement(path) {
|
|
274
|
+
const referencingElement = path.node;
|
|
275
|
+
|
|
276
|
+
// skip current referencing schema as $ref keyword was not defined
|
|
277
|
+
if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
|
|
281
|
+
try {
|
|
282
|
+
// compute baseURI using rules around $id and $ref keywords. The current
|
|
283
|
+
// document's URI is the retrieval URI — derive it directly (not via
|
|
284
|
+
// toReference) so an internal $ref needs no resolution and never trips the
|
|
285
|
+
// resolve.maxDepth guard.
|
|
286
|
+
const retrievalURI = this.reference.uri;
|
|
287
|
+
const $refBaseURI = (0, _util.resolveSchema$refField)(retrievalURI, referencingElement);
|
|
288
|
+
const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
|
|
289
|
+
const file = new _File.default({
|
|
290
|
+
uri: $refBaseURIStrippedHash
|
|
291
|
+
});
|
|
292
|
+
const isUnknownURI = (0, _ramda.none)(r => r.canRead(file), this.options.resolve.resolvers);
|
|
293
|
+
const isURL = !isUnknownURI;
|
|
294
|
+
const isInternalReference = url.stripHash(this.reference.uri) === $refBaseURIStrippedHash;
|
|
295
|
+
const isExternalReference = !isInternalReference;
|
|
296
|
+
|
|
297
|
+
// internal Schema Object $refs resolve against the in-document $id graph;
|
|
298
|
+
// leaving them untouched keeps that resolution valid. A JSON Schema $ref is
|
|
299
|
+
// never normalized to a bare fragment.
|
|
300
|
+
if (isInternalReference) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// honor the resolve.external switch
|
|
305
|
+
if (!this.options.resolve.external && isExternalReference) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// detect maximum depth of bundling before fetching the external resource
|
|
310
|
+
if (this.reference.depth >= this.options.bundle.maxDepth) {
|
|
311
|
+
throw new _MaximumBundleDepthError.default(`Maximum bundle depth of "${this.options.bundle.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
|
|
312
|
+
maxDepth: this.options.bundle.maxDepth,
|
|
313
|
+
uri: this.reference.uri
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// locate the external schema resource the $ref targets, reusing the
|
|
318
|
+
// dereference strategy's classification. First try to resolve the target
|
|
319
|
+
// as a canonical URI / URL against the current document's $id graph; if
|
|
320
|
+
// that fails, fall back to fetching the external document and resolving
|
|
321
|
+
// the fragment there as a $anchor or JSON Pointer.
|
|
322
|
+
let schemaReference = this.reference;
|
|
323
|
+
try {
|
|
324
|
+
const referenceAsSchema = (0, _util.maybeRefractToJSONSchemaElement)(schemaReference.value.result);
|
|
325
|
+
(0, _uri.evaluate)($refBaseURI, referenceAsSchema);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
if (isURL && error instanceof _EvaluationJsonSchemaUriError.default) {
|
|
328
|
+
if ((0, _$anchor.isAnchor)((0, _$anchor.uriToAnchor)($refBaseURI))) {
|
|
329
|
+
schemaReference = await this.toReference(url.unsanitize($refBaseURI));
|
|
330
|
+
const selector = (0, _$anchor.uriToAnchor)($refBaseURI);
|
|
331
|
+
const referenceAsSchema = (0, _util.maybeRefractToJSONSchemaElement)(schemaReference.value.result);
|
|
332
|
+
(0, _$anchor.evaluate)(selector, referenceAsSchema);
|
|
333
|
+
} else {
|
|
334
|
+
schemaReference = await this.toReference(url.unsanitize($refBaseURI));
|
|
335
|
+
const selector = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
336
|
+
const referenceAsSchema = (0, _util.maybeRefractToJSONSchemaElement)(schemaReference.value.result);
|
|
337
|
+
(0, _apidomJsonPointer.evaluate)(referenceAsSchema, selector);
|
|
338
|
+
}
|
|
339
|
+
} else {
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// a $ref that resolves (by $id/$anchor/pointer) within the current document
|
|
345
|
+
// is internal even when its URI form (e.g. a URN or absolute $id) is not a
|
|
346
|
+
// bare fragment. Leave it untouched — it resolves against the in-document
|
|
347
|
+
// $id graph, and embedding the current document into itself would be wrong.
|
|
348
|
+
if (url.stripHash(schemaReference.uri) === url.stripHash(this.reference.uri)) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// the resource to embed is the WHOLE external document (its root schema),
|
|
353
|
+
// identified by its $id. A $ref into a fragment of the document embeds the
|
|
354
|
+
// entire document resource once; the fragment keeps resolving against the
|
|
355
|
+
// embedded $id.
|
|
356
|
+
const resourceRoot = (0, _util.maybeRefractToJSONSchemaElement)(schemaReference.value.result);
|
|
357
|
+
const resourceBaseURI = (0, _util.resolveSchema$idField)(url.stripHash(schemaReference.uri), resourceRoot) ?? url.stripHash(schemaReference.uri);
|
|
358
|
+
const field = cf.inputs.name;
|
|
359
|
+
const canonicalKey = `${field}\t${resourceBaseURI}`;
|
|
360
|
+
|
|
361
|
+
// resource already embedded (or being embedded) — leave the referencing
|
|
362
|
+
// $ref untouched and stop. This also terminates circular external schema
|
|
363
|
+
// references, since the assignment is reserved BEFORE recursion.
|
|
364
|
+
if (this.assignments.has(canonicalKey)) {
|
|
365
|
+
path.skip();
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// own a copy of the resource and ensure it carries a $id so the unchanged
|
|
370
|
+
// $ref still resolves against it once embedded
|
|
371
|
+
const embeddedElement = (0, _apidomDatamodel.cloneDeep)(resourceRoot);
|
|
372
|
+
if (!(0, _apidomDatamodel.isStringElement)(embeddedElement.$id)) {
|
|
373
|
+
embeddedElement.set('$id', resourceBaseURI);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// the embedded thing is the WHOLE resource (keyed by its $id), so its name
|
|
377
|
+
// is derived from the resource URI
|
|
378
|
+
const preferredName = this.baseName(embeddedElement, resourceBaseURI);
|
|
379
|
+
const componentName = this.uniqueName(preferredName);
|
|
380
|
+
this.reportNameCollision(preferredName, componentName);
|
|
381
|
+
|
|
382
|
+
// reserve the assignment and component name before recursing so circular
|
|
383
|
+
// external schema references terminate
|
|
384
|
+
this.assignments.set(canonicalKey, `#/components/${field}/${(0, _apidomJsonPointer.escape)(componentName)}`);
|
|
385
|
+
if (!this.reservedNames.has(field)) this.reservedNames.set(field, new Set());
|
|
386
|
+
this.reservedNames.get(field).add(componentName);
|
|
387
|
+
|
|
388
|
+
// bundle external references contained within the embedded resource;
|
|
389
|
+
// nested external resources land flat in the same components.inputs
|
|
390
|
+
const visitor = new Arazzo1BundleVisitor({
|
|
391
|
+
reference: schemaReference,
|
|
392
|
+
options: this.options,
|
|
393
|
+
assignments: this.assignments,
|
|
394
|
+
reservedNames: this.reservedNames
|
|
395
|
+
});
|
|
396
|
+
const bundledElement = await (0, _apidomTraverse.traverseAsync)(embeddedElement, visitor, {
|
|
397
|
+
mutable: true
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// annotate the embedded resource with info about its origin
|
|
401
|
+
if ((0, _apidomDatamodel.isElement)(bundledElement)) {
|
|
402
|
+
bundledElement.meta.set('ref-origin', schemaReference.uri);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// place the embedded resource into the entry document's components.inputs
|
|
406
|
+
this.ensureInputsField().set(componentName, bundledElement);
|
|
407
|
+
|
|
408
|
+
// the referencing $ref is left UNCHANGED — it resolves against the
|
|
409
|
+
// embedded resource's $id
|
|
410
|
+
path.skip();
|
|
411
|
+
} catch (error) {
|
|
412
|
+
this.handleError(`Error while bundling JSON Schema Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
|
|
413
|
+
path.skip();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
var _default = exports.default = Arazzo1BundleVisitor;
|