@usearazzo/resolver 1.0.1-alpha.2
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/LICENSE +202 -0
- package/NOTICE +20 -0
- package/README.md +99 -0
- package/dist/arazzo-resolver.browser.js +93083 -0
- package/dist/arazzo-resolver.browser.min.js +1 -0
- package/package.json +78 -0
- package/src/bundle/arazzo.cjs +92 -0
- package/src/bundle/arazzo.mjs +83 -0
- package/src/bundle/openapi.cjs +101 -0
- package/src/bundle/openapi.mjs +92 -0
- package/src/dereference/arazzo.cjs +246 -0
- package/src/dereference/arazzo.mjs +236 -0
- package/src/dereference/openapi.cjs +239 -0
- package/src/dereference/openapi.mjs +229 -0
- package/src/errors/BundleError.cjs +17 -0
- package/src/errors/BundleError.mjs +12 -0
- package/src/errors/DereferenceError.cjs +17 -0
- package/src/errors/DereferenceError.mjs +12 -0
- package/src/errors/ResolveError.cjs +17 -0
- package/src/errors/ResolveError.mjs +12 -0
- package/src/index.cjs +129 -0
- package/src/index.mjs +9 -0
- package/src/resolve/arazzo.cjs +208 -0
- package/src/resolve/arazzo.mjs +198 -0
- package/src/resolve/openapi.cjs +234 -0
- package/src/resolve/openapi.mjs +224 -0
- package/types/resolver.d.ts +529 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.defaultOptions = void 0;
|
|
8
|
+
exports.dereference = dereference;
|
|
9
|
+
exports.dereferenceElement = dereferenceElement;
|
|
10
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
11
|
+
var _empty = require("@speclynx/apidom-reference/configuration/empty");
|
|
12
|
+
var _arazzo = _interopRequireDefault(require("@speclynx/apidom-reference/dereference/strategies/arazzo-1"));
|
|
13
|
+
var _openapi = _interopRequireDefault(require("@speclynx/apidom-reference/dereference/strategies/openapi-2"));
|
|
14
|
+
var _openapi2 = _interopRequireDefault(require("@speclynx/apidom-reference/dereference/strategies/openapi-3-0"));
|
|
15
|
+
var _openapi3 = _interopRequireDefault(require("@speclynx/apidom-reference/dereference/strategies/openapi-3-1"));
|
|
16
|
+
var _json = _interopRequireDefault(require("@speclynx/apidom-reference/parse/parsers/json"));
|
|
17
|
+
var _yaml = _interopRequireDefault(require("@speclynx/apidom-reference/parse/parsers/yaml-1-2"));
|
|
18
|
+
var _binary = _interopRequireDefault(require("@speclynx/apidom-reference/parse/parsers/binary"));
|
|
19
|
+
var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
|
|
20
|
+
var _parser = require("@usearazzo/parser");
|
|
21
|
+
var _DereferenceError = _interopRequireDefault(require("../errors/DereferenceError.cjs"));
|
|
22
|
+
/**
|
|
23
|
+
* Options for dereferencing Arazzo Documents.
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Default reference options for dereferencing Arazzo Documents.
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
const defaultOptions = exports.defaultOptions = {
|
|
32
|
+
resolve: {
|
|
33
|
+
resolvers: [..._parser.defaultParseArazzoOptions.resolve.resolvers]
|
|
34
|
+
},
|
|
35
|
+
parse: {
|
|
36
|
+
parsers: [..._parser.defaultParseArazzoOptions.parse.parsers, new _json.default({
|
|
37
|
+
allowEmpty: false
|
|
38
|
+
}), new _yaml.default({
|
|
39
|
+
allowEmpty: false
|
|
40
|
+
}), new _binary.default({
|
|
41
|
+
allowEmpty: false
|
|
42
|
+
})],
|
|
43
|
+
parserOpts: {
|
|
44
|
+
..._parser.defaultParseArazzoOptions.parse.parserOpts
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
dereference: {
|
|
48
|
+
strategies: [new _arazzo.default(), new _openapi.default(), new _openapi2.default(), new _openapi3.default()],
|
|
49
|
+
strategyOpts: {
|
|
50
|
+
sourceDescriptions: false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Dereferences an Arazzo Document from a file system path or HTTP(S) URL.
|
|
57
|
+
*
|
|
58
|
+
* This function resolves all JSON References ($ref) and Reusable Object references
|
|
59
|
+
* ($components.*) in the Arazzo Document.
|
|
60
|
+
*
|
|
61
|
+
* Source descriptions can optionally be dereferenced using strategy options:
|
|
62
|
+
* - `sourceDescriptions`: `true` (all) or `['name1', 'name2']` (specific names only)
|
|
63
|
+
* - `sourceDescriptionsMaxDepth`: Maximum recursion depth for nested Arazzo source descriptions (default: `+Infinity`)
|
|
64
|
+
*
|
|
65
|
+
* Options can be passed globally via `strategyOpts` or strategy-specific via `strategyOpts['arazzo-1']`.
|
|
66
|
+
*
|
|
67
|
+
* @param uri - A file system path or HTTP(S) URL to the Arazzo Document
|
|
68
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
69
|
+
* @returns A promise that resolves to the dereferenced Arazzo Document as ApiDOM element
|
|
70
|
+
* @throws DereferenceError - When dereferencing fails or document is not an Arazzo specification. The original error is available via the `cause` property.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* // Dereference from file
|
|
74
|
+
* const result = await dereferenceArazzo('/path/to/arazzo.json');
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // Dereference from URL
|
|
78
|
+
* const result = await dereferenceArazzo('https://example.com/arazzo.yaml');
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* Dereference with source descriptions
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const result = await dereferenceArazzo('/path/to/arazzo.json', {
|
|
84
|
+
* dereference: { strategyOpts: { sourceDescriptions: true } },
|
|
85
|
+
* });
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* // Dereference with custom options
|
|
90
|
+
* const result = await dereferenceArazzo('/path/to/arazzo.json', customReferenceOptions);
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
93
|
+
async function dereference(uri, options = {}) {
|
|
94
|
+
const mergedOptions = (0, _empty.mergeOptions)(defaultOptions, options);
|
|
95
|
+
|
|
96
|
+
// a relative file system path resolves against the current working directory; the string
|
|
97
|
+
// doubles as the base URI for relative references, where a relative base resolves against
|
|
98
|
+
// the filesystem root instead of the document. A leading slash or backslash (UNC,
|
|
99
|
+
// drive-rooted) is already rooted and passes through untouched. `uri` itself is left as
|
|
100
|
+
// is so error messages below quote what the caller passed in.
|
|
101
|
+
const retrievalURI = !_empty.url.isHttpUrl(uri) && _empty.url.getProtocol(uri) !== 'file' && !/^[\\/]/.test(uri) ? _empty.url.resolve(_empty.url.fromFileSystemPath(_empty.url.cwd()), uri) : uri;
|
|
102
|
+
try {
|
|
103
|
+
const parseResult = await (0, _empty.dereference)(retrievalURI, mergedOptions);
|
|
104
|
+
|
|
105
|
+
// validate that the dereferenced document is an Arazzo specification
|
|
106
|
+
if (!(0, _apidomNsArazzo.isArazzoSpecification1Element)(parseResult.api)) {
|
|
107
|
+
throw new _empty.UnmatchedDereferenceStrategyError(`Could not find a dereference strategy that can dereference "${uri}" as an Arazzo specification`);
|
|
108
|
+
}
|
|
109
|
+
parseResult.meta.set('retrievalURI', retrievalURI);
|
|
110
|
+
return parseResult;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
throw new _DereferenceError.default(`Failed to dereference Arazzo Document at "${uri}"`, {
|
|
113
|
+
cause: error
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Dereferences an ApiDOM element representing an Arazzo Document.
|
|
120
|
+
*
|
|
121
|
+
* This function resolves all JSON References ($ref) and Reusable Object references
|
|
122
|
+
* ($components.*) in the Arazzo Document element.
|
|
123
|
+
*
|
|
124
|
+
* Supported scenarios:
|
|
125
|
+
* - ParseResultElement with retrievalURI metadata: baseURI derived automatically
|
|
126
|
+
* - ParseResultElement without retrievalURI: requires `options.resolve.baseURI`
|
|
127
|
+
* - Child element (e.g., WorkflowElement) with parseResult in strategyOpts:
|
|
128
|
+
* requires `options.dereference.strategyOpts.parseResult` for component resolution,
|
|
129
|
+
* and `options.resolve.baseURI` if parseResult lacks retrievalURI metadata
|
|
130
|
+
*
|
|
131
|
+
* Source descriptions can optionally be dereferenced using strategy options:
|
|
132
|
+
* - `sourceDescriptions`: `true` (all) or `['name1', 'name2']` (specific names only)
|
|
133
|
+
* - `sourceDescriptionsMaxDepth`: Maximum recursion depth for nested Arazzo source descriptions (default: `+Infinity`)
|
|
134
|
+
*
|
|
135
|
+
* Options can be passed globally via `strategyOpts` or strategy-specific via `strategyOpts['arazzo-1']`.
|
|
136
|
+
*
|
|
137
|
+
* @param element - An ApiDOM element (ParseResultElement or child element like WorkflowElement)
|
|
138
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
139
|
+
* @returns A promise that resolves to the dereferenced element
|
|
140
|
+
* @throws DereferenceError - When baseURI is required but not provided, or when dereferencing fails
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* Dereference ParseResultElement with retrievalURI (from file parsing)
|
|
144
|
+
* ```typescript
|
|
145
|
+
* import { parseArazzo } from '@usearazzo/parser';
|
|
146
|
+
*
|
|
147
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
148
|
+
* const dereferenced = await dereferenceArazzoElement(parseResult);
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* Dereference ParseResultElement without retrievalURI (from inline parsing)
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const parseResult = await parseArazzo({ arazzo: '1.0.1', ... });
|
|
155
|
+
* const dereferenced = await dereferenceArazzoElement(parseResult, {
|
|
156
|
+
* resolve: { baseURI: 'https://example.com/arazzo.json' },
|
|
157
|
+
* });
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* Dereference child element (e.g., WorkflowElement)
|
|
162
|
+
* ```typescript
|
|
163
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
164
|
+
* const workflow = parseResult.api.workflows.get(0);
|
|
165
|
+
* const dereferenced = await dereferenceArazzoElement(workflow, {
|
|
166
|
+
* dereference: { strategyOpts: { parseResult } },
|
|
167
|
+
* });
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* Dereference with source descriptions
|
|
172
|
+
* ```typescript
|
|
173
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
174
|
+
* const dereferenced = await dereferenceArazzoElement(parseResult, {
|
|
175
|
+
* dereference: { strategyOpts: { sourceDescriptions: true } },
|
|
176
|
+
* });
|
|
177
|
+
* ```
|
|
178
|
+
* @public
|
|
179
|
+
*/
|
|
180
|
+
async function dereferenceElement(element, options = {}) {
|
|
181
|
+
const mergedOptions = (0, _empty.mergeOptions)(defaultOptions, options);
|
|
182
|
+
const refSet = mergedOptions.dereference?.refSet ?? new _empty.ReferenceSet();
|
|
183
|
+
let baseURI = mergedOptions.resolve?.baseURI;
|
|
184
|
+
let mediaType = 'text/plain';
|
|
185
|
+
if (refSet.size === 0) {
|
|
186
|
+
if ((0, _apidomDatamodel.isParseResultElement)(element)) {
|
|
187
|
+
if ((0, _apidomNsArazzo.isArazzoSpecification1Element)(element.api)) {
|
|
188
|
+
mediaType = _apidomNsArazzo.mediaTypes.latest();
|
|
189
|
+
}
|
|
190
|
+
if (element.hasMetaProperty('retrievalURI')) {
|
|
191
|
+
baseURI = element.meta.get('retrievalURI');
|
|
192
|
+
} else if (!baseURI) {
|
|
193
|
+
throw new _DereferenceError.default('baseURI option is required when dereferencing a ParseResultElement without retrievalURI metadata');
|
|
194
|
+
}
|
|
195
|
+
} else if ((0, _apidomDatamodel.isParseResultElement)(mergedOptions.dereference?.strategyOpts?.parseResult)) {
|
|
196
|
+
// dereferencing child element requires refSet for component resolution
|
|
197
|
+
const {
|
|
198
|
+
parseResult
|
|
199
|
+
} = mergedOptions.dereference.strategyOpts;
|
|
200
|
+
let rootURI;
|
|
201
|
+
if ((0, _apidomNsArazzo.isArazzoSpecification1Element)(parseResult.api)) {
|
|
202
|
+
mediaType = _apidomNsArazzo.mediaTypes.latest();
|
|
203
|
+
}
|
|
204
|
+
if (parseResult.hasMetaProperty('retrievalURI')) {
|
|
205
|
+
rootURI = parseResult.meta.get('retrievalURI');
|
|
206
|
+
} else if (baseURI) {
|
|
207
|
+
rootURI = baseURI;
|
|
208
|
+
} else {
|
|
209
|
+
throw new _DereferenceError.default('baseURI option is required when dereferencing a child element without retrievalURI metadata');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// the fragment wrapper must expose the element as its result, the same way
|
|
213
|
+
// dereferenceApiDOM wraps a bare element: dereference strategies read the
|
|
214
|
+
// traversed root's `result` to index the `$id`s of the fragment
|
|
215
|
+
const elementClone = (0, _apidomDatamodel.cloneShallow)(element);
|
|
216
|
+
elementClone.classes.push('result');
|
|
217
|
+
const elementReference = new _empty.Reference({
|
|
218
|
+
uri: `${rootURI}#fragment`,
|
|
219
|
+
value: new _apidomDatamodel.ParseResultElement([elementClone])
|
|
220
|
+
});
|
|
221
|
+
const rootReference = new _empty.Reference({
|
|
222
|
+
uri: rootURI,
|
|
223
|
+
value: parseResult
|
|
224
|
+
});
|
|
225
|
+
refSet.add(elementReference).add(rootReference);
|
|
226
|
+
baseURI = rootURI;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
return await (0, _empty.dereferenceApiDOM)(element, (0, _empty.mergeOptions)(mergedOptions, {
|
|
231
|
+
resolve: {
|
|
232
|
+
baseURI
|
|
233
|
+
},
|
|
234
|
+
parse: {
|
|
235
|
+
mediaType
|
|
236
|
+
},
|
|
237
|
+
dereference: {
|
|
238
|
+
refSet
|
|
239
|
+
}
|
|
240
|
+
}));
|
|
241
|
+
} catch (error) {
|
|
242
|
+
throw new _DereferenceError.default('Failed to dereference Arazzo Document', {
|
|
243
|
+
cause: error
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { isParseResultElement, ParseResultElement, cloneShallow } from '@speclynx/apidom-datamodel';
|
|
2
|
+
import { url, dereference as dereferenceURI, dereferenceApiDOM as dereferenceApiDOMElement, mergeOptions, ReferenceSet, Reference, UnmatchedDereferenceStrategyError } from '@speclynx/apidom-reference/configuration/empty';
|
|
3
|
+
import Arazzo1DereferenceStrategy from '@speclynx/apidom-reference/dereference/strategies/arazzo-1';
|
|
4
|
+
import OpenAPI2DereferenceStrategy from '@speclynx/apidom-reference/dereference/strategies/openapi-2';
|
|
5
|
+
import OpenAPI30DereferenceStrategy from '@speclynx/apidom-reference/dereference/strategies/openapi-3-0';
|
|
6
|
+
import OpenAPI31DereferenceStrategy from '@speclynx/apidom-reference/dereference/strategies/openapi-3-1';
|
|
7
|
+
import JSONParser from '@speclynx/apidom-reference/parse/parsers/json';
|
|
8
|
+
import YAMLParser from '@speclynx/apidom-reference/parse/parsers/yaml-1-2';
|
|
9
|
+
import BinaryParser from '@speclynx/apidom-reference/parse/parsers/binary';
|
|
10
|
+
import { isArazzoSpecification1Element, mediaTypes } from '@speclynx/apidom-ns-arazzo-1';
|
|
11
|
+
import { defaultParseArazzoOptions as parserDefaultOptions } from '@usearazzo/parser';
|
|
12
|
+
import DereferenceError from "../errors/DereferenceError.mjs";
|
|
13
|
+
/**
|
|
14
|
+
* Options for dereferencing Arazzo Documents.
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Default reference options for dereferencing Arazzo Documents.
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export const defaultOptions = {
|
|
22
|
+
resolve: {
|
|
23
|
+
resolvers: [...parserDefaultOptions.resolve.resolvers]
|
|
24
|
+
},
|
|
25
|
+
parse: {
|
|
26
|
+
parsers: [...parserDefaultOptions.parse.parsers, new JSONParser({
|
|
27
|
+
allowEmpty: false
|
|
28
|
+
}), new YAMLParser({
|
|
29
|
+
allowEmpty: false
|
|
30
|
+
}), new BinaryParser({
|
|
31
|
+
allowEmpty: false
|
|
32
|
+
})],
|
|
33
|
+
parserOpts: {
|
|
34
|
+
...parserDefaultOptions.parse.parserOpts
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
dereference: {
|
|
38
|
+
strategies: [new Arazzo1DereferenceStrategy(), new OpenAPI2DereferenceStrategy(), new OpenAPI30DereferenceStrategy(), new OpenAPI31DereferenceStrategy()],
|
|
39
|
+
strategyOpts: {
|
|
40
|
+
sourceDescriptions: false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Dereferences an Arazzo Document from a file system path or HTTP(S) URL.
|
|
47
|
+
*
|
|
48
|
+
* This function resolves all JSON References ($ref) and Reusable Object references
|
|
49
|
+
* ($components.*) in the Arazzo Document.
|
|
50
|
+
*
|
|
51
|
+
* Source descriptions can optionally be dereferenced using strategy options:
|
|
52
|
+
* - `sourceDescriptions`: `true` (all) or `['name1', 'name2']` (specific names only)
|
|
53
|
+
* - `sourceDescriptionsMaxDepth`: Maximum recursion depth for nested Arazzo source descriptions (default: `+Infinity`)
|
|
54
|
+
*
|
|
55
|
+
* Options can be passed globally via `strategyOpts` or strategy-specific via `strategyOpts['arazzo-1']`.
|
|
56
|
+
*
|
|
57
|
+
* @param uri - A file system path or HTTP(S) URL to the Arazzo Document
|
|
58
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
59
|
+
* @returns A promise that resolves to the dereferenced Arazzo Document as ApiDOM element
|
|
60
|
+
* @throws DereferenceError - When dereferencing fails or document is not an Arazzo specification. The original error is available via the `cause` property.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* // Dereference from file
|
|
64
|
+
* const result = await dereferenceArazzo('/path/to/arazzo.json');
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* // Dereference from URL
|
|
68
|
+
* const result = await dereferenceArazzo('https://example.com/arazzo.yaml');
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* Dereference with source descriptions
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const result = await dereferenceArazzo('/path/to/arazzo.json', {
|
|
74
|
+
* dereference: { strategyOpts: { sourceDescriptions: true } },
|
|
75
|
+
* });
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // Dereference with custom options
|
|
80
|
+
* const result = await dereferenceArazzo('/path/to/arazzo.json', customReferenceOptions);
|
|
81
|
+
* @public
|
|
82
|
+
*/
|
|
83
|
+
export async function dereference(uri, options = {}) {
|
|
84
|
+
const mergedOptions = mergeOptions(defaultOptions, options);
|
|
85
|
+
|
|
86
|
+
// a relative file system path resolves against the current working directory; the string
|
|
87
|
+
// doubles as the base URI for relative references, where a relative base resolves against
|
|
88
|
+
// the filesystem root instead of the document. A leading slash or backslash (UNC,
|
|
89
|
+
// drive-rooted) is already rooted and passes through untouched. `uri` itself is left as
|
|
90
|
+
// is so error messages below quote what the caller passed in.
|
|
91
|
+
const retrievalURI = !url.isHttpUrl(uri) && url.getProtocol(uri) !== 'file' && !/^[\\/]/.test(uri) ? url.resolve(url.fromFileSystemPath(url.cwd()), uri) : uri;
|
|
92
|
+
try {
|
|
93
|
+
const parseResult = await dereferenceURI(retrievalURI, mergedOptions);
|
|
94
|
+
|
|
95
|
+
// validate that the dereferenced document is an Arazzo specification
|
|
96
|
+
if (!isArazzoSpecification1Element(parseResult.api)) {
|
|
97
|
+
throw new UnmatchedDereferenceStrategyError(`Could not find a dereference strategy that can dereference "${uri}" as an Arazzo specification`);
|
|
98
|
+
}
|
|
99
|
+
parseResult.meta.set('retrievalURI', retrievalURI);
|
|
100
|
+
return parseResult;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new DereferenceError(`Failed to dereference Arazzo Document at "${uri}"`, {
|
|
103
|
+
cause: error
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Dereferences an ApiDOM element representing an Arazzo Document.
|
|
110
|
+
*
|
|
111
|
+
* This function resolves all JSON References ($ref) and Reusable Object references
|
|
112
|
+
* ($components.*) in the Arazzo Document element.
|
|
113
|
+
*
|
|
114
|
+
* Supported scenarios:
|
|
115
|
+
* - ParseResultElement with retrievalURI metadata: baseURI derived automatically
|
|
116
|
+
* - ParseResultElement without retrievalURI: requires `options.resolve.baseURI`
|
|
117
|
+
* - Child element (e.g., WorkflowElement) with parseResult in strategyOpts:
|
|
118
|
+
* requires `options.dereference.strategyOpts.parseResult` for component resolution,
|
|
119
|
+
* and `options.resolve.baseURI` if parseResult lacks retrievalURI metadata
|
|
120
|
+
*
|
|
121
|
+
* Source descriptions can optionally be dereferenced using strategy options:
|
|
122
|
+
* - `sourceDescriptions`: `true` (all) or `['name1', 'name2']` (specific names only)
|
|
123
|
+
* - `sourceDescriptionsMaxDepth`: Maximum recursion depth for nested Arazzo source descriptions (default: `+Infinity`)
|
|
124
|
+
*
|
|
125
|
+
* Options can be passed globally via `strategyOpts` or strategy-specific via `strategyOpts['arazzo-1']`.
|
|
126
|
+
*
|
|
127
|
+
* @param element - An ApiDOM element (ParseResultElement or child element like WorkflowElement)
|
|
128
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
129
|
+
* @returns A promise that resolves to the dereferenced element
|
|
130
|
+
* @throws DereferenceError - When baseURI is required but not provided, or when dereferencing fails
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* Dereference ParseResultElement with retrievalURI (from file parsing)
|
|
134
|
+
* ```typescript
|
|
135
|
+
* import { parseArazzo } from '@usearazzo/parser';
|
|
136
|
+
*
|
|
137
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
138
|
+
* const dereferenced = await dereferenceArazzoElement(parseResult);
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* Dereference ParseResultElement without retrievalURI (from inline parsing)
|
|
143
|
+
* ```typescript
|
|
144
|
+
* const parseResult = await parseArazzo({ arazzo: '1.0.1', ... });
|
|
145
|
+
* const dereferenced = await dereferenceArazzoElement(parseResult, {
|
|
146
|
+
* resolve: { baseURI: 'https://example.com/arazzo.json' },
|
|
147
|
+
* });
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* Dereference child element (e.g., WorkflowElement)
|
|
152
|
+
* ```typescript
|
|
153
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
154
|
+
* const workflow = parseResult.api.workflows.get(0);
|
|
155
|
+
* const dereferenced = await dereferenceArazzoElement(workflow, {
|
|
156
|
+
* dereference: { strategyOpts: { parseResult } },
|
|
157
|
+
* });
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* Dereference with source descriptions
|
|
162
|
+
* ```typescript
|
|
163
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
164
|
+
* const dereferenced = await dereferenceArazzoElement(parseResult, {
|
|
165
|
+
* dereference: { strategyOpts: { sourceDescriptions: true } },
|
|
166
|
+
* });
|
|
167
|
+
* ```
|
|
168
|
+
* @public
|
|
169
|
+
*/
|
|
170
|
+
export async function dereferenceElement(element, options = {}) {
|
|
171
|
+
const mergedOptions = mergeOptions(defaultOptions, options);
|
|
172
|
+
const refSet = mergedOptions.dereference?.refSet ?? new ReferenceSet();
|
|
173
|
+
let baseURI = mergedOptions.resolve?.baseURI;
|
|
174
|
+
let mediaType = 'text/plain';
|
|
175
|
+
if (refSet.size === 0) {
|
|
176
|
+
if (isParseResultElement(element)) {
|
|
177
|
+
if (isArazzoSpecification1Element(element.api)) {
|
|
178
|
+
mediaType = mediaTypes.latest();
|
|
179
|
+
}
|
|
180
|
+
if (element.hasMetaProperty('retrievalURI')) {
|
|
181
|
+
baseURI = element.meta.get('retrievalURI');
|
|
182
|
+
} else if (!baseURI) {
|
|
183
|
+
throw new DereferenceError('baseURI option is required when dereferencing a ParseResultElement without retrievalURI metadata');
|
|
184
|
+
}
|
|
185
|
+
} else if (isParseResultElement(mergedOptions.dereference?.strategyOpts?.parseResult)) {
|
|
186
|
+
// dereferencing child element requires refSet for component resolution
|
|
187
|
+
const {
|
|
188
|
+
parseResult
|
|
189
|
+
} = mergedOptions.dereference.strategyOpts;
|
|
190
|
+
let rootURI;
|
|
191
|
+
if (isArazzoSpecification1Element(parseResult.api)) {
|
|
192
|
+
mediaType = mediaTypes.latest();
|
|
193
|
+
}
|
|
194
|
+
if (parseResult.hasMetaProperty('retrievalURI')) {
|
|
195
|
+
rootURI = parseResult.meta.get('retrievalURI');
|
|
196
|
+
} else if (baseURI) {
|
|
197
|
+
rootURI = baseURI;
|
|
198
|
+
} else {
|
|
199
|
+
throw new DereferenceError('baseURI option is required when dereferencing a child element without retrievalURI metadata');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// the fragment wrapper must expose the element as its result, the same way
|
|
203
|
+
// dereferenceApiDOM wraps a bare element: dereference strategies read the
|
|
204
|
+
// traversed root's `result` to index the `$id`s of the fragment
|
|
205
|
+
const elementClone = cloneShallow(element);
|
|
206
|
+
elementClone.classes.push('result');
|
|
207
|
+
const elementReference = new Reference({
|
|
208
|
+
uri: `${rootURI}#fragment`,
|
|
209
|
+
value: new ParseResultElement([elementClone])
|
|
210
|
+
});
|
|
211
|
+
const rootReference = new Reference({
|
|
212
|
+
uri: rootURI,
|
|
213
|
+
value: parseResult
|
|
214
|
+
});
|
|
215
|
+
refSet.add(elementReference).add(rootReference);
|
|
216
|
+
baseURI = rootURI;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
return await dereferenceApiDOMElement(element, mergeOptions(mergedOptions, {
|
|
221
|
+
resolve: {
|
|
222
|
+
baseURI
|
|
223
|
+
},
|
|
224
|
+
parse: {
|
|
225
|
+
mediaType
|
|
226
|
+
},
|
|
227
|
+
dereference: {
|
|
228
|
+
refSet
|
|
229
|
+
}
|
|
230
|
+
}));
|
|
231
|
+
} catch (error) {
|
|
232
|
+
throw new DereferenceError('Failed to dereference Arazzo Document', {
|
|
233
|
+
cause: error
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|