@speclynx/apidom-reference 5.2.4 → 5.2.6
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 +19 -0
- package/README.md +37 -14
- package/dist/apidom-reference.browser.js +1234 -596
- package/dist/apidom-reference.browser.min.js +1 -1
- package/package.json +29 -29
- package/src/bundle/strategies/arazzo-1/visitor.cjs +105 -22
- package/src/bundle/strategies/arazzo-1/visitor.mjs +107 -24
- package/src/bundle/strategies/openapi-3-1/visitor.cjs +197 -47
- package/src/bundle/strategies/openapi-3-1/visitor.mjs +200 -50
- package/src/bundle/util.cjs +57 -2
- package/src/bundle/util.mjs +52 -0
- package/src/dereference/strategies/arazzo-1/index.cjs +20 -17
- package/src/dereference/strategies/arazzo-1/index.mjs +14 -11
- package/src/dereference/strategies/arazzo-1/selectors/$anchor.cjs +6 -0
- package/src/dereference/strategies/arazzo-1/selectors/$anchor.mjs +1 -1
- package/src/dereference/strategies/arazzo-1/selectors/uri.cjs +6 -0
- package/src/dereference/strategies/arazzo-1/selectors/uri.mjs +1 -1
- package/src/dereference/strategies/arazzo-1/source-descriptions.cjs +65 -42
- package/src/dereference/strategies/arazzo-1/source-descriptions.mjs +64 -42
- package/src/dereference/strategies/arazzo-1/util.cjs +6 -0
- package/src/dereference/strategies/arazzo-1/util.mjs +1 -1
- package/src/dereference/strategies/arazzo-1/visitor.cjs +97 -35
- package/src/dereference/strategies/arazzo-1/visitor.mjs +101 -39
- package/src/dereference/strategies/openapi-3-1/index.cjs +2 -1
- package/src/dereference/strategies/openapi-3-1/index.mjs +3 -1
- package/src/dereference/strategies/openapi-3-1/selectors/$anchor.cjs +16 -4
- package/src/dereference/strategies/openapi-3-1/selectors/$anchor.mjs +14 -3
- package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.cjs +29 -0
- package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.mjs +22 -0
- package/src/dereference/strategies/openapi-3-1/selectors/uri.cjs +54 -27
- package/src/dereference/strategies/openapi-3-1/selectors/uri.mjs +51 -27
- package/src/dereference/strategies/openapi-3-1/util.cjs +75 -14
- package/src/dereference/strategies/openapi-3-1/util.mjs +68 -14
- package/src/dereference/strategies/openapi-3-1/visitor.cjs +85 -9
- package/src/dereference/strategies/openapi-3-1/visitor.mjs +89 -13
- package/src/util/url.cjs +35 -2
- package/src/util/url.mjs +32 -0
- package/types/apidom-reference.d.ts +12 -0
- package/types/bundle/strategies/arazzo-1/index.d.ts +1 -1
- package/types/bundle/strategies/arazzo-1/visitor.d.ts +51 -6
- package/types/bundle/strategies/openapi-3-1/index.d.ts +1 -1
- package/types/bundle/strategies/openapi-3-1/visitor.d.ts +59 -3
- package/types/bundle/util.d.ts +39 -0
- package/types/dereference/strategies/arazzo-1/selectors/$anchor.d.ts +1 -1
- package/types/dereference/strategies/arazzo-1/selectors/uri.d.ts +1 -1
- package/types/dereference/strategies/arazzo-1/util.d.ts +1 -1
- package/types/dereference/strategies/arazzo-1/visitor.d.ts +17 -1
- package/types/dereference/strategies/openapi-3-1/selectors/$anchor.d.ts +8 -0
- package/types/dereference/strategies/openapi-3-1/selectors/json-pointer.d.ts +9 -0
- package/types/dereference/strategies/openapi-3-1/selectors/uri.d.ts +37 -4
- package/types/dereference/strategies/openapi-3-1/util.d.ts +53 -2
- package/types/dereference/strategies/openapi-3-1/visitor.d.ts +17 -1
- package/types/util/url.d.ts +10 -0
|
@@ -5,6 +5,7 @@ import DereferenceStrategy from "../DereferenceStrategy.mjs";
|
|
|
5
5
|
import Reference from "../../../Reference.mjs";
|
|
6
6
|
import ReferenceSet from "../../../ReferenceSet.mjs";
|
|
7
7
|
import Arazzo1DereferenceVisitor from "./visitor.mjs";
|
|
8
|
+
import { detachedRootSchema$ids } from "../openapi-3-1/util.mjs";
|
|
8
9
|
import { dereferenceSourceDescriptions } from "./source-descriptions.mjs";
|
|
9
10
|
/**
|
|
10
11
|
* @public
|
|
@@ -59,7 +60,8 @@ class Arazzo1DereferenceStrategy extends DereferenceStrategy {
|
|
|
59
60
|
const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
|
|
60
61
|
const visitor = new Arazzo1DereferenceVisitor({
|
|
61
62
|
reference,
|
|
62
|
-
options
|
|
63
|
+
options,
|
|
64
|
+
ancestorSchema$ids: detachedRootSchema$ids(refSet.rootRef.value.result)
|
|
63
65
|
});
|
|
64
66
|
const dereferencedElement = await traverseAsync(refSet.rootRef.value, visitor, {
|
|
65
67
|
mutable: true,
|
|
@@ -68,6 +70,17 @@ class Arazzo1DereferenceStrategy extends DereferenceStrategy {
|
|
|
68
70
|
})
|
|
69
71
|
});
|
|
70
72
|
|
|
73
|
+
/**
|
|
74
|
+
* If immutable option is set, replay refs from the refSet.
|
|
75
|
+
* Replayed before source descriptions are dereferenced, so they can reuse the refs.
|
|
76
|
+
*/
|
|
77
|
+
if (options.dereference.immutable) {
|
|
78
|
+
mutableRefSet.refs.filter(ref => ref.uri.startsWith('immutable://')).map(ref => new Reference({
|
|
79
|
+
...ref,
|
|
80
|
+
uri: ref.uri.replace(/^immutable:\/\//, '')
|
|
81
|
+
})).forEach(ref => immutableRefSet.add(ref));
|
|
82
|
+
}
|
|
83
|
+
|
|
71
84
|
/**
|
|
72
85
|
* Dereference source descriptions if option is enabled.
|
|
73
86
|
*/
|
|
@@ -82,16 +95,6 @@ class Arazzo1DereferenceStrategy extends DereferenceStrategy {
|
|
|
82
95
|
parseResult.push(...sourceDescriptions);
|
|
83
96
|
}
|
|
84
97
|
|
|
85
|
-
/**
|
|
86
|
-
* If immutable option is set, replay refs from the refSet.
|
|
87
|
-
*/
|
|
88
|
-
if (options.dereference.immutable) {
|
|
89
|
-
mutableRefSet.refs.filter(ref => ref.uri.startsWith('immutable://')).map(ref => new Reference({
|
|
90
|
-
...ref,
|
|
91
|
-
uri: ref.uri.replace(/^immutable:\/\//, '')
|
|
92
|
-
})).forEach(ref => immutableRefSet.add(ref));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
98
|
/**
|
|
96
99
|
* Release all memory if this refSet was not provided as a configuration option.
|
|
97
100
|
* If provided as configuration option, then provider is responsible for cleanup.
|
|
@@ -33,6 +33,12 @@ Object.defineProperty(exports, "isAnchor", {
|
|
|
33
33
|
return _$anchor.isAnchor;
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
|
+
Object.defineProperty(exports, "locate", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _$anchor.locate;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
36
42
|
Object.defineProperty(exports, "parse", {
|
|
37
43
|
enumerable: true,
|
|
38
44
|
get: function () {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { isAnchor, uriToAnchor, parse, evaluate, EvaluationJsonSchema$anchorError, InvalidJsonSchema$anchorError, JsonSchema$anchorError } from "../../openapi-3-1/selectors/$anchor.mjs";
|
|
1
|
+
export { isAnchor, uriToAnchor, parse, locate, evaluate, EvaluationJsonSchema$anchorError, InvalidJsonSchema$anchorError, JsonSchema$anchorError } from "../../openapi-3-1/selectors/$anchor.mjs";
|
|
@@ -21,4 +21,10 @@ Object.defineProperty(exports, "evaluate", {
|
|
|
21
21
|
return _uri.evaluate;
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
|
+
Object.defineProperty(exports, "locate", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _uri.locate;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
24
30
|
var _uri = require("../../openapi-3-1/selectors/uri.cjs");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { evaluate, EvaluationJsonSchemaUriError, JsonSchemaUriError } from "../../openapi-3-1/selectors/uri.mjs";
|
|
1
|
+
export { locate, evaluate, EvaluationJsonSchemaUriError, JsonSchemaUriError } from "../../openapi-3-1/selectors/uri.mjs";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
3
4
|
var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopRequireWildcard").default;
|
|
4
5
|
Object.defineProperty(exports, "__esModule", {
|
|
5
6
|
value: true
|
|
@@ -8,9 +9,13 @@ exports.dereferenceSourceDescriptions = dereferenceSourceDescriptions;
|
|
|
8
9
|
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
9
10
|
var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
|
|
10
11
|
var _apidomCore = require("@speclynx/apidom-core");
|
|
12
|
+
var _ramda = require("ramda");
|
|
11
13
|
var url = _interopRequireWildcard(require("../../../util/url.cjs"));
|
|
14
|
+
var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
|
|
15
|
+
var _ReferenceSet = _interopRequireDefault(require("../../../ReferenceSet.cjs"));
|
|
12
16
|
var _util = require("../../../options/util.cjs");
|
|
13
|
-
var _index =
|
|
17
|
+
var _index = _interopRequireDefault(require("../../../parse/index.cjs"));
|
|
18
|
+
var _index2 = require("../../index.cjs");
|
|
14
19
|
var _util2 = require("./util.cjs");
|
|
15
20
|
/**
|
|
16
21
|
* Dereferences a single source description element.
|
|
@@ -63,53 +68,64 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
|
|
|
63
68
|
|
|
64
69
|
// check if source description was already parsed (e.g., during parse phase with sourceDescriptions: true)
|
|
65
70
|
const existingParseResult = sourceDescription.meta.get('parseResult');
|
|
71
|
+
const parentRefSet = ctx.options.dereference.refSet;
|
|
72
|
+
let refSet = null;
|
|
66
73
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
77
|
-
dereference: {
|
|
78
|
-
strategyOpts: {
|
|
79
|
-
// nested documents should dereference all their source descriptions
|
|
80
|
-
// (parent's name filter doesn't apply to nested documents)
|
|
81
|
-
// set at strategy-specific level to override any inherited filters
|
|
82
|
-
[ctx.strategyName]: {
|
|
83
|
-
sourceDescriptions: true,
|
|
84
|
-
sourceDescriptionsDepth: ctx.currentDepth + 1,
|
|
85
|
-
sourceDescriptionsAncestors: ctx.ancestors,
|
|
86
|
-
sourceDescriptionsDereferenced: ctx.dereferenced
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}));
|
|
74
|
+
// source description document comes from, in order of preference:
|
|
75
|
+
// caller-supplied refSet, parse phase, or is fetched and parsed now
|
|
76
|
+
const cachedReference = parentRefSet?.find(ref => ref.uri === retrievalURI);
|
|
77
|
+
let sourceDescriptionParseResult;
|
|
78
|
+
let isFreshlyParsed = false;
|
|
79
|
+
if (cachedReference !== undefined) {
|
|
80
|
+
sourceDescriptionParseResult = cachedReference.value;
|
|
81
|
+
} else if ((0, _apidomDatamodel.isParseResultElement)(existingParseResult)) {
|
|
82
|
+
sourceDescriptionParseResult = existingParseResult;
|
|
91
83
|
} else {
|
|
92
|
-
|
|
93
|
-
sourceDescriptionDereferenced = await (0, _index.default)(retrievalURI, (0, _util.merge)(ctx.options, {
|
|
84
|
+
sourceDescriptionParseResult = await (0, _index.default)(retrievalURI, (0, _util.merge)(ctx.options, {
|
|
94
85
|
parse: {
|
|
95
|
-
mediaType: 'text/plain'
|
|
96
|
-
}
|
|
97
|
-
dereference: {
|
|
98
|
-
strategyOpts: {
|
|
99
|
-
// nested documents should dereference all their source descriptions
|
|
100
|
-
// (parent's name filter doesn't apply to nested documents)
|
|
101
|
-
// set at strategy-specific level to override any inherited filters
|
|
102
|
-
[ctx.strategyName]: {
|
|
103
|
-
sourceDescriptions: true,
|
|
104
|
-
sourceDescriptionsDepth: ctx.currentDepth + 1,
|
|
105
|
-
sourceDescriptionsAncestors: ctx.ancestors,
|
|
106
|
-
sourceDescriptionsDereferenced: ctx.dereferenced
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
86
|
+
mediaType: 'text/plain'
|
|
87
|
+
} // allow parser plugin detection
|
|
110
88
|
}));
|
|
89
|
+
isFreshlyParsed = true;
|
|
111
90
|
}
|
|
112
91
|
|
|
92
|
+
// caller-supplied refSet is rooted at the entry document, but strategies dereference
|
|
93
|
+
// the root of the refSet; re-root its references at the source description
|
|
94
|
+
if (parentRefSet !== null) {
|
|
95
|
+
refSet = new _ReferenceSet.default({
|
|
96
|
+
refs: [new _Reference.default({
|
|
97
|
+
uri: retrievalURI,
|
|
98
|
+
value: sourceDescriptionParseResult
|
|
99
|
+
}), ...parentRefSet.refs.map(ref => new _Reference.default({
|
|
100
|
+
...ref,
|
|
101
|
+
refSet: undefined
|
|
102
|
+
}))]
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const sourceDescriptionDereferenced = await (0, _index2.dereferenceApiDOM)(sourceDescriptionParseResult, (0, _util.merge)((0, _ramda.assocPath)(['dereference', 'refSet'], refSet, ctx.options), {
|
|
106
|
+
parse: {
|
|
107
|
+
mediaType: 'text/plain' // allow dereference strategy detection via ApiDOM inspection
|
|
108
|
+
},
|
|
109
|
+
resolve: {
|
|
110
|
+
baseURI: retrievalURI
|
|
111
|
+
},
|
|
112
|
+
dereference: {
|
|
113
|
+
// freshly parsed document not reported to any refSet can be dereferenced in mutable mode
|
|
114
|
+
immutable: ctx.options.dereference.immutable && !(isFreshlyParsed && parentRefSet === null),
|
|
115
|
+
strategyOpts: {
|
|
116
|
+
// nested documents should dereference all their source descriptions
|
|
117
|
+
// (parent's name filter doesn't apply to nested documents)
|
|
118
|
+
// set at strategy-specific level to override any inherited filters
|
|
119
|
+
[ctx.strategyName]: {
|
|
120
|
+
sourceDescriptions: true,
|
|
121
|
+
sourceDescriptionsDepth: ctx.currentDepth + 1,
|
|
122
|
+
sourceDescriptionsAncestors: ctx.ancestors,
|
|
123
|
+
sourceDescriptionsDereferenced: ctx.dereferenced
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}));
|
|
128
|
+
|
|
113
129
|
// merge dereferenced result into our parse result
|
|
114
130
|
for (const item of sourceDescriptionDereferenced) {
|
|
115
131
|
parseResult.push(item);
|
|
@@ -121,6 +137,13 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
|
|
|
121
137
|
annotation.classes.push('error');
|
|
122
138
|
parseResult.push(annotation);
|
|
123
139
|
return parseResult;
|
|
140
|
+
} finally {
|
|
141
|
+
// report documents reached through the source description to the caller-supplied refSet,
|
|
142
|
+
// even when dereferencing it failed
|
|
143
|
+
if (parentRefSet !== null && refSet !== null) {
|
|
144
|
+
parentRefSet.merge(refSet);
|
|
145
|
+
parentRefSet.circular ||= refSet.circular;
|
|
146
|
+
}
|
|
124
147
|
}
|
|
125
148
|
|
|
126
149
|
// register dereferenced document for later references to it
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { ParseResultElement, AnnotationElement, isArrayElement, isStringElement, isParseResultElement } from '@speclynx/apidom-datamodel';
|
|
2
2
|
import { isArazzoSpecification1Element, isSourceDescriptionElement } from '@speclynx/apidom-ns-arazzo-1';
|
|
3
3
|
import { toValue } from '@speclynx/apidom-core';
|
|
4
|
+
import { assocPath } from 'ramda';
|
|
4
5
|
import * as url from "../../../util/url.mjs";
|
|
6
|
+
import Reference from "../../../Reference.mjs";
|
|
7
|
+
import ReferenceSet from "../../../ReferenceSet.mjs";
|
|
5
8
|
import { merge as mergeOptions } from "../../../options/util.mjs";
|
|
6
|
-
import
|
|
9
|
+
import parse from "../../../parse/index.mjs";
|
|
10
|
+
import { dereferenceApiDOM } from "../../index.mjs";
|
|
7
11
|
import { arazzoDocumentURIs, resolveArazzo$selfField, validateSourceDescriptionAPI } from "./util.mjs";
|
|
8
12
|
/**
|
|
9
13
|
* Dereferences a single source description element.
|
|
@@ -56,53 +60,64 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
|
|
|
56
60
|
|
|
57
61
|
// check if source description was already parsed (e.g., during parse phase with sourceDescriptions: true)
|
|
58
62
|
const existingParseResult = sourceDescription.meta.get('parseResult');
|
|
63
|
+
const parentRefSet = ctx.options.dereference.refSet;
|
|
64
|
+
let refSet = null;
|
|
59
65
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
},
|
|
70
|
-
dereference: {
|
|
71
|
-
strategyOpts: {
|
|
72
|
-
// nested documents should dereference all their source descriptions
|
|
73
|
-
// (parent's name filter doesn't apply to nested documents)
|
|
74
|
-
// set at strategy-specific level to override any inherited filters
|
|
75
|
-
[ctx.strategyName]: {
|
|
76
|
-
sourceDescriptions: true,
|
|
77
|
-
sourceDescriptionsDepth: ctx.currentDepth + 1,
|
|
78
|
-
sourceDescriptionsAncestors: ctx.ancestors,
|
|
79
|
-
sourceDescriptionsDereferenced: ctx.dereferenced
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}));
|
|
66
|
+
// source description document comes from, in order of preference:
|
|
67
|
+
// caller-supplied refSet, parse phase, or is fetched and parsed now
|
|
68
|
+
const cachedReference = parentRefSet?.find(ref => ref.uri === retrievalURI);
|
|
69
|
+
let sourceDescriptionParseResult;
|
|
70
|
+
let isFreshlyParsed = false;
|
|
71
|
+
if (cachedReference !== undefined) {
|
|
72
|
+
sourceDescriptionParseResult = cachedReference.value;
|
|
73
|
+
} else if (isParseResultElement(existingParseResult)) {
|
|
74
|
+
sourceDescriptionParseResult = existingParseResult;
|
|
84
75
|
} else {
|
|
85
|
-
|
|
86
|
-
sourceDescriptionDereferenced = await dereference(retrievalURI, mergeOptions(ctx.options, {
|
|
76
|
+
sourceDescriptionParseResult = await parse(retrievalURI, mergeOptions(ctx.options, {
|
|
87
77
|
parse: {
|
|
88
|
-
mediaType: 'text/plain'
|
|
89
|
-
}
|
|
90
|
-
dereference: {
|
|
91
|
-
strategyOpts: {
|
|
92
|
-
// nested documents should dereference all their source descriptions
|
|
93
|
-
// (parent's name filter doesn't apply to nested documents)
|
|
94
|
-
// set at strategy-specific level to override any inherited filters
|
|
95
|
-
[ctx.strategyName]: {
|
|
96
|
-
sourceDescriptions: true,
|
|
97
|
-
sourceDescriptionsDepth: ctx.currentDepth + 1,
|
|
98
|
-
sourceDescriptionsAncestors: ctx.ancestors,
|
|
99
|
-
sourceDescriptionsDereferenced: ctx.dereferenced
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
78
|
+
mediaType: 'text/plain'
|
|
79
|
+
} // allow parser plugin detection
|
|
103
80
|
}));
|
|
81
|
+
isFreshlyParsed = true;
|
|
104
82
|
}
|
|
105
83
|
|
|
84
|
+
// caller-supplied refSet is rooted at the entry document, but strategies dereference
|
|
85
|
+
// the root of the refSet; re-root its references at the source description
|
|
86
|
+
if (parentRefSet !== null) {
|
|
87
|
+
refSet = new ReferenceSet({
|
|
88
|
+
refs: [new Reference({
|
|
89
|
+
uri: retrievalURI,
|
|
90
|
+
value: sourceDescriptionParseResult
|
|
91
|
+
}), ...parentRefSet.refs.map(ref => new Reference({
|
|
92
|
+
...ref,
|
|
93
|
+
refSet: undefined
|
|
94
|
+
}))]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const sourceDescriptionDereferenced = await dereferenceApiDOM(sourceDescriptionParseResult, mergeOptions(assocPath(['dereference', 'refSet'], refSet, ctx.options), {
|
|
98
|
+
parse: {
|
|
99
|
+
mediaType: 'text/plain' // allow dereference strategy detection via ApiDOM inspection
|
|
100
|
+
},
|
|
101
|
+
resolve: {
|
|
102
|
+
baseURI: retrievalURI
|
|
103
|
+
},
|
|
104
|
+
dereference: {
|
|
105
|
+
// freshly parsed document not reported to any refSet can be dereferenced in mutable mode
|
|
106
|
+
immutable: ctx.options.dereference.immutable && !(isFreshlyParsed && parentRefSet === null),
|
|
107
|
+
strategyOpts: {
|
|
108
|
+
// nested documents should dereference all their source descriptions
|
|
109
|
+
// (parent's name filter doesn't apply to nested documents)
|
|
110
|
+
// set at strategy-specific level to override any inherited filters
|
|
111
|
+
[ctx.strategyName]: {
|
|
112
|
+
sourceDescriptions: true,
|
|
113
|
+
sourceDescriptionsDepth: ctx.currentDepth + 1,
|
|
114
|
+
sourceDescriptionsAncestors: ctx.ancestors,
|
|
115
|
+
sourceDescriptionsDereferenced: ctx.dereferenced
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
120
|
+
|
|
106
121
|
// merge dereferenced result into our parse result
|
|
107
122
|
for (const item of sourceDescriptionDereferenced) {
|
|
108
123
|
parseResult.push(item);
|
|
@@ -114,6 +129,13 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
|
|
|
114
129
|
annotation.classes.push('error');
|
|
115
130
|
parseResult.push(annotation);
|
|
116
131
|
return parseResult;
|
|
132
|
+
} finally {
|
|
133
|
+
// report documents reached through the source description to the caller-supplied refSet,
|
|
134
|
+
// even when dereferencing it failed
|
|
135
|
+
if (parentRefSet !== null && refSet !== null) {
|
|
136
|
+
parentRefSet.merge(refSet);
|
|
137
|
+
parentRefSet.circular ||= refSet.circular;
|
|
138
|
+
}
|
|
117
139
|
}
|
|
118
140
|
|
|
119
141
|
// register dereferenced document for later references to it
|
|
@@ -17,6 +17,12 @@ Object.defineProperty(exports, "resolveSchema$refField", {
|
|
|
17
17
|
return _util.resolveSchema$refField;
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
|
+
Object.defineProperty(exports, "resolveSchemaBaseURI", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function () {
|
|
23
|
+
return _util.resolveSchemaBaseURI;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
20
26
|
exports.validateSourceDescriptionAPI = void 0;
|
|
21
27
|
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
22
28
|
var _apidomCore = require("@speclynx/apidom-core");
|
|
@@ -5,7 +5,7 @@ import { isSwaggerElement } from '@speclynx/apidom-ns-openapi-2';
|
|
|
5
5
|
import { isOpenApi3_0Element } from '@speclynx/apidom-ns-openapi-3-0';
|
|
6
6
|
import { isOpenApi3_1Element } from '@speclynx/apidom-ns-openapi-3-1';
|
|
7
7
|
import * as url from "../../../util/url.mjs";
|
|
8
|
-
export { resolveSchema$refField, resolveSchema$idField } from "../openapi-3-1/util.mjs";
|
|
8
|
+
export { resolveSchema$refField, resolveSchema$idField, resolveSchemaBaseURI } from "../openapi-3-1/util.mjs";
|
|
9
9
|
/**
|
|
10
10
|
* Computes the base URI of an Arazzo document per Arazzo 1.1.0 rules.
|
|
11
11
|
*
|
|
@@ -16,6 +16,7 @@ var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
|
|
|
16
16
|
var _arazzoRuntimeExpression = require("@swaggerexpert/arazzo-runtime-expression");
|
|
17
17
|
var _$anchor = require("./selectors/$anchor.cjs");
|
|
18
18
|
var _uri = require("./selectors/uri.cjs");
|
|
19
|
+
var _jsonPointer = require("../openapi-3-1/selectors/json-pointer.cjs");
|
|
19
20
|
var _util = require("../openapi-3-1/util.cjs");
|
|
20
21
|
var _util2 = require("./util.cjs");
|
|
21
22
|
var _UnresolvableReferenceError = _interopRequireDefault(require("../../../errors/UnresolvableReferenceError.cjs"));
|
|
@@ -45,8 +46,23 @@ class Arazzo1DereferenceVisitor {
|
|
|
45
46
|
*/
|
|
46
47
|
baseURI;
|
|
47
48
|
options;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Index of `$id`-bearing schemas per document, shared across nested visitors
|
|
52
|
+
* of a single dereference run so it never outlives mutations of the documents.
|
|
53
|
+
*/
|
|
54
|
+
schema$idIndex;
|
|
48
55
|
visited;
|
|
49
56
|
|
|
57
|
+
/**
|
|
58
|
+
* `$id`s of the JSON Schema elements enclosing the traversal root within its
|
|
59
|
+
* document (outermost first). Empty when the root is the document itself; a
|
|
60
|
+
* nested visitor traversing a fragment is seeded with the `$id`s of the
|
|
61
|
+
* schemas the fragment sits under, so the `$ref`s and `$id`s within resolve
|
|
62
|
+
* against the same base as they would from the document root.
|
|
63
|
+
*/
|
|
64
|
+
ancestorSchema$ids;
|
|
65
|
+
|
|
50
66
|
/**
|
|
51
67
|
* Tracks element ancestors across dive-deep traversal boundaries.
|
|
52
68
|
* Used for cycle detection: if a referenced element is found in
|
|
@@ -57,15 +73,19 @@ class Arazzo1DereferenceVisitor {
|
|
|
57
73
|
reference,
|
|
58
74
|
options,
|
|
59
75
|
indirections = [],
|
|
76
|
+
schema$idIndex = new WeakMap(),
|
|
60
77
|
ancestors = new _util3.AncestorLineage(),
|
|
61
|
-
visited = new WeakSet()
|
|
78
|
+
visited = new WeakSet(),
|
|
79
|
+
ancestorSchema$ids = []
|
|
62
80
|
}) {
|
|
63
81
|
this.indirections = indirections;
|
|
64
82
|
this.reference = reference;
|
|
65
83
|
this.baseURI = (0, _util2.resolveArazzo$selfField)(reference.uri, reference.value?.result);
|
|
66
84
|
this.options = options;
|
|
85
|
+
this.schema$idIndex = schema$idIndex;
|
|
67
86
|
this.ancestors = new _util3.AncestorLineage(...ancestors);
|
|
68
87
|
this.visited = visited;
|
|
88
|
+
this.ancestorSchema$ids = ancestorSchema$ids;
|
|
69
89
|
}
|
|
70
90
|
toAncestorLineage(path) {
|
|
71
91
|
const ancestorNodes = path.getAncestorNodes();
|
|
@@ -232,34 +252,38 @@ class Arazzo1DereferenceVisitor {
|
|
|
232
252
|
return;
|
|
233
253
|
}
|
|
234
254
|
const runtimeExpression = (0, _apidomCore.toValue)(referencingElement.reference);
|
|
235
|
-
|
|
236
|
-
// parse the runtime expression
|
|
237
|
-
const {
|
|
238
|
-
result,
|
|
239
|
-
tree
|
|
240
|
-
} = (0, _arazzoRuntimeExpression.parse)(runtimeExpression);
|
|
241
|
-
if (!result.success) {
|
|
242
|
-
throw new _apidomError.ApiDOMStructuredError(`Invalid Reusable Object reference format: "${runtimeExpression}"`, {
|
|
243
|
-
runtimeExpression
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// ReusableElement can only reference components
|
|
248
|
-
if (tree.type !== 'ComponentsExpression') {
|
|
249
|
-
throw new _apidomError.ApiDOMStructuredError(`Reusable Object reference "${runtimeExpression}" must be a components expression`, {
|
|
250
|
-
runtimeExpression
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// evaluate runtime expression as JSON Pointer to get the referenced element
|
|
255
|
-
const jsonPointer = (0, _apidomJsonPointer.compile)(['components', tree.componentType, tree.componentName]);
|
|
256
255
|
let referencedElement;
|
|
257
256
|
try {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
});
|
|
257
|
+
// parse the runtime expression
|
|
258
|
+
const {
|
|
259
|
+
result,
|
|
260
|
+
tree
|
|
261
|
+
} = (0, _arazzoRuntimeExpression.parse)(runtimeExpression);
|
|
262
|
+
if (!result.success) {
|
|
263
|
+
throw new _apidomError.ApiDOMStructuredError(`Invalid Reusable Object reference format: "${runtimeExpression}"`, {
|
|
264
|
+
runtimeExpression
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ReusableElement can only reference components
|
|
269
|
+
if (tree.type !== 'ComponentsExpression') {
|
|
270
|
+
throw new _apidomError.ApiDOMStructuredError(`Reusable Object reference "${runtimeExpression}" must be a components expression`, {
|
|
271
|
+
runtimeExpression
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// evaluate runtime expression as JSON Pointer to get the referenced element
|
|
276
|
+
const jsonPointer = (0, _apidomJsonPointer.compile)(['components', tree.componentType, tree.componentName]);
|
|
277
|
+
try {
|
|
278
|
+
referencedElement = (0, _apidomJsonPointer.evaluate)(this.reference.value.result, jsonPointer);
|
|
279
|
+
} catch {
|
|
280
|
+
throw new _apidomError.ApiDOMStructuredError(`Reusable Object reference "${runtimeExpression}" cannot be resolved`, {
|
|
281
|
+
runtimeExpression
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
this.handleError(`Error while dereferencing Reusable Object. Cannot resolve reference "${runtimeExpression}": ${error.message}`, error, referencingElement, 'reference', runtimeExpression, path);
|
|
286
|
+
return;
|
|
263
287
|
}
|
|
264
288
|
|
|
265
289
|
/**
|
|
@@ -307,7 +331,11 @@ class Arazzo1DereferenceVisitor {
|
|
|
307
331
|
let {
|
|
308
332
|
uri: retrievalURI
|
|
309
333
|
} = reference;
|
|
310
|
-
|
|
334
|
+
// base URI in effect at the traversal root: the document base refined by
|
|
335
|
+
// the $ids of the schemas enclosing the root when the root is a fragment
|
|
336
|
+
const rootBaseURI = (0, _util.resolveSchema$ids)(this.baseURI, this.ancestorSchema$ids);
|
|
337
|
+
const schemaBaseURI = (0, _util.resolveSchemaBaseURI)(rootBaseURI, path);
|
|
338
|
+
const $refBaseURI = (0, _util.resolveSchema$refField)(schemaBaseURI, referencingElement);
|
|
311
339
|
const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
|
|
312
340
|
const file = new _File.default({
|
|
313
341
|
uri: $refBaseURIStrippedHash
|
|
@@ -319,13 +347,32 @@ class Arazzo1DereferenceVisitor {
|
|
|
319
347
|
|
|
320
348
|
// determining reference, proper evaluation and selection mechanism
|
|
321
349
|
let referencedElement;
|
|
350
|
+
// $ids of the schemas enclosing the referenced element within its document
|
|
351
|
+
let ancestorSchema$ids;
|
|
322
352
|
try {
|
|
323
353
|
if (isUnknownURI || isURL) {
|
|
324
354
|
// we're dealing with canonical URI or URL with possible fragment
|
|
325
355
|
retrievalURI = this.toBaseURI($refBaseURI);
|
|
326
356
|
const selector = $refBaseURI;
|
|
327
|
-
const
|
|
328
|
-
|
|
357
|
+
const documentElement = reference.value.result;
|
|
358
|
+
const referenceAsSchema = (0, _util2.maybeRefractToJSONSchemaElement)(documentElement);
|
|
359
|
+
// a document that is itself a fragment detached from its source carries
|
|
360
|
+
// the $ids enclosing it only as `ancestorSchema$ids`: its $ids are then
|
|
361
|
+
// indexed against the base that chain leaves in effect, and the chain
|
|
362
|
+
// prefixes the one of the located schema. A nested visitor rooted at a
|
|
363
|
+
// fragment of a whole document indexes that document against its own base.
|
|
364
|
+
const rootNode = path.getAncestorNodes().at(-1) ?? path.node;
|
|
365
|
+
const isDocumentRoot = ((0, _apidomDatamodel.isParseResultElement)(rootNode) ? rootNode.result : rootNode) === documentElement;
|
|
366
|
+
({
|
|
367
|
+
element: referencedElement,
|
|
368
|
+
ancestorSchema$ids
|
|
369
|
+
} = (0, _uri.locate)(selector, referenceAsSchema, {
|
|
370
|
+
baseURI: isDocumentRoot ? rootBaseURI : this.baseURI,
|
|
371
|
+
index: this.schema$idIndex
|
|
372
|
+
}));
|
|
373
|
+
if (isDocumentRoot) {
|
|
374
|
+
ancestorSchema$ids = [...this.ancestorSchema$ids, ...ancestorSchema$ids];
|
|
375
|
+
}
|
|
329
376
|
referencedElement = (0, _util2.maybeRefractToJSONSchemaElement)(referencedElement);
|
|
330
377
|
|
|
331
378
|
// ignore resolving internal Schema Objects
|
|
@@ -357,7 +404,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
357
404
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
358
405
|
const selector = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
359
406
|
const referenceAsSchema = (0, _util2.maybeRefractToJSONSchemaElement)(reference.value.result);
|
|
360
|
-
|
|
407
|
+
({
|
|
408
|
+
element: referencedElement,
|
|
409
|
+
ancestorSchema$ids
|
|
410
|
+
} = (0, _jsonPointer.locate)(referenceAsSchema, selector));
|
|
361
411
|
referencedElement = (0, _util2.maybeRefractToJSONSchemaElement)(referencedElement);
|
|
362
412
|
}
|
|
363
413
|
} catch (error) {
|
|
@@ -384,7 +434,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
384
434
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
385
435
|
const selector = (0, _$anchor.uriToAnchor)($refBaseURI);
|
|
386
436
|
const referenceAsSchema = (0, _util2.maybeRefractToJSONSchemaElement)(reference.value.result);
|
|
387
|
-
|
|
437
|
+
({
|
|
438
|
+
element: referencedElement,
|
|
439
|
+
ancestorSchema$ids
|
|
440
|
+
} = (0, _$anchor.locate)(selector, referenceAsSchema));
|
|
388
441
|
referencedElement = (0, _util2.maybeRefractToJSONSchemaElement)(referencedElement);
|
|
389
442
|
} else {
|
|
390
443
|
// we're assuming here that we're dealing with JSON Pointer here
|
|
@@ -405,7 +458,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
405
458
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
406
459
|
const selector = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
407
460
|
const referenceAsSchema = (0, _util2.maybeRefractToJSONSchemaElement)(reference.value.result);
|
|
408
|
-
|
|
461
|
+
({
|
|
462
|
+
element: referencedElement,
|
|
463
|
+
ancestorSchema$ids
|
|
464
|
+
} = (0, _jsonPointer.locate)(referenceAsSchema, selector));
|
|
409
465
|
referencedElement = (0, _util2.maybeRefractToJSONSchemaElement)(referencedElement);
|
|
410
466
|
}
|
|
411
467
|
} else {
|
|
@@ -462,7 +518,11 @@ class Arazzo1DereferenceVisitor {
|
|
|
462
518
|
*/
|
|
463
519
|
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
464
520
|
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
465
|
-
|
|
521
|
+
// content transplanted into the referencing site is traversed under that
|
|
522
|
+
// site's $id chain; a fragment enclosed by a different chain has its own
|
|
523
|
+
// $refs resolved in place first, by a nested visitor seeded with that chain
|
|
524
|
+
const hasDifferentEnclosingBase = (0, _util.resolveSchema$ids)((0, _util2.resolveArazzo$selfField)(reference.uri, reference.value.result), ancestorSchema$ids) !== (0, _util.resolveSchema$ids)(rootBaseURI, (0, _util.schema$idsOf)(path.getAncestorNodes().reverse()));
|
|
525
|
+
if ((isExternalReference || isNonEntryDocument || hasDifferentEnclosingBase || (0, _apidomNsArazzo.isJSONSchemaElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
466
526
|
if (shouldDetectCircular) {
|
|
467
527
|
this.visited.add(referencedElement);
|
|
468
528
|
}
|
|
@@ -473,8 +533,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
473
533
|
reference,
|
|
474
534
|
indirections: [...this.indirections],
|
|
475
535
|
options: this.options,
|
|
536
|
+
schema$idIndex: this.schema$idIndex,
|
|
476
537
|
visited: this.visited,
|
|
477
|
-
ancestors: ancestorsLineage
|
|
538
|
+
ancestors: ancestorsLineage,
|
|
539
|
+
ancestorSchema$ids
|
|
478
540
|
});
|
|
479
541
|
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
480
542
|
mutable: true,
|