@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
package/src/index.mjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { defaultOptions as defaultDereferenceArazzoOptions, dereference as dereferenceArazzo, dereferenceElement as dereferenceArazzoElement } from "./dereference/arazzo.mjs";
|
|
2
|
+
export { defaultOptions as defaultDereferenceOpenAPIOptions, dereference as dereferenceOpenAPI, dereferenceElement as dereferenceOpenAPIElement } from "./dereference/openapi.mjs";
|
|
3
|
+
export { defaultOptions as defaultResolveArazzoOptions, resolve as resolveArazzo, resolveElement as resolveArazzoElement } from "./resolve/arazzo.mjs";
|
|
4
|
+
export { defaultOptions as defaultResolveOpenAPIOptions, resolve as resolveOpenAPI, resolveElement as resolveOpenAPIElement } from "./resolve/openapi.mjs";
|
|
5
|
+
export { defaultOptions as defaultBundleArazzoOptions, bundle as bundleArazzo } from "./bundle/arazzo.mjs";
|
|
6
|
+
export { defaultOptions as defaultBundleOpenAPIOptions, bundle as bundleOpenAPI } from "./bundle/openapi.mjs";
|
|
7
|
+
export { default as DereferenceError } from "./errors/DereferenceError.mjs";
|
|
8
|
+
export { default as ResolveError } from "./errors/ResolveError.mjs";
|
|
9
|
+
export { default as BundleError } from "./errors/BundleError.mjs";
|
|
@@ -0,0 +1,208 @@
|
|
|
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.resolve = resolve;
|
|
9
|
+
exports.resolveElement = resolveElement;
|
|
10
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
11
|
+
var _empty = require("@speclynx/apidom-reference/configuration/empty");
|
|
12
|
+
var _arazzo = _interopRequireDefault(require("@speclynx/apidom-reference/resolve/strategies/arazzo-1"));
|
|
13
|
+
var _openapi = _interopRequireDefault(require("@speclynx/apidom-reference/resolve/strategies/openapi-2"));
|
|
14
|
+
var _openapi2 = _interopRequireDefault(require("@speclynx/apidom-reference/resolve/strategies/openapi-3-0"));
|
|
15
|
+
var _openapi3 = _interopRequireDefault(require("@speclynx/apidom-reference/resolve/strategies/openapi-3-1"));
|
|
16
|
+
var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
|
|
17
|
+
var _arazzo2 = require("../dereference/arazzo.cjs");
|
|
18
|
+
var _ResolveError = _interopRequireDefault(require("../errors/ResolveError.cjs"));
|
|
19
|
+
/**
|
|
20
|
+
* Options for resolving Arazzo Documents.
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Default reference options for resolving Arazzo Documents.
|
|
26
|
+
*
|
|
27
|
+
* The resolve strategies delegate to the dereference strategies of the same name,
|
|
28
|
+
* so the dereference defaults are carried over and only the resolve strategies are added.
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
const defaultOptions = exports.defaultOptions = {
|
|
32
|
+
resolve: {
|
|
33
|
+
resolvers: [..._arazzo2.defaultOptions.resolve.resolvers],
|
|
34
|
+
strategies: [new _arazzo.default(), new _openapi.default(), new _openapi2.default(), new _openapi3.default()]
|
|
35
|
+
},
|
|
36
|
+
parse: {
|
|
37
|
+
parsers: [..._arazzo2.defaultOptions.parse.parsers],
|
|
38
|
+
parserOpts: {
|
|
39
|
+
..._arazzo2.defaultOptions.parse.parserOpts
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
dereference: {
|
|
43
|
+
strategies: [..._arazzo2.defaultOptions.dereference.strategies],
|
|
44
|
+
strategyOpts: {
|
|
45
|
+
..._arazzo2.defaultOptions.dereference.strategyOpts
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolves an Arazzo Document from a file system path or HTTP(S) URL.
|
|
52
|
+
*
|
|
53
|
+
* This function collects the Arazzo Document and every external document its
|
|
54
|
+
* JSON References ($ref) reach into a ReferenceSet. Nothing is dereferenced;
|
|
55
|
+
* the root reference of the set holds the parsed entry document.
|
|
56
|
+
*
|
|
57
|
+
* The `dereference.refSet` option is ignored: resolving always builds a fresh ReferenceSet.
|
|
58
|
+
*
|
|
59
|
+
* @param uri - A file system path or HTTP(S) URL to the Arazzo Document
|
|
60
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
61
|
+
* @returns A promise that resolves to the ReferenceSet of the Arazzo Document and its external references
|
|
62
|
+
* @throws ResolveError - When resolving fails or document is not an Arazzo specification. The original error is available via the `cause` property.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Resolve from file
|
|
66
|
+
* const refSet = await resolveArazzo('/path/to/arazzo.json');
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // Resolve from URL
|
|
70
|
+
* const refSet = await resolveArazzo('https://example.com/arazzo.yaml');
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* // Resolve with custom options
|
|
74
|
+
* const refSet = await resolveArazzo('/path/to/arazzo.json', customReferenceOptions);
|
|
75
|
+
* @public
|
|
76
|
+
*/
|
|
77
|
+
async function resolve(uri, options = {}) {
|
|
78
|
+
const mergedOptions = (0, _empty.mergeOptions)(defaultOptions, options);
|
|
79
|
+
|
|
80
|
+
// a relative file system path resolves against the current working directory; the string
|
|
81
|
+
// doubles as the base URI for relative references, where a relative base resolves against
|
|
82
|
+
// the filesystem root instead of the document. A leading slash or backslash (UNC,
|
|
83
|
+
// drive-rooted) is already rooted and passes through untouched. `uri` itself is left as
|
|
84
|
+
// is so error messages below quote what the caller passed in.
|
|
85
|
+
const retrievalURI = !_empty.url.isHttpUrl(uri) && _empty.url.getProtocol(uri) !== 'file' && !/^[\\/]/.test(uri) ? _empty.url.resolve(_empty.url.fromFileSystemPath(_empty.url.cwd()), uri) : uri;
|
|
86
|
+
try {
|
|
87
|
+
const refSet = await (0, _empty.resolve)(retrievalURI, withoutSeededRefSet(mergedOptions));
|
|
88
|
+
const parseResult = refSet.rootRef?.value;
|
|
89
|
+
|
|
90
|
+
// validate that the resolved document is an Arazzo specification
|
|
91
|
+
if (!(0, _apidomDatamodel.isParseResultElement)(parseResult) || !(0, _apidomNsArazzo.isArazzoSpecification1Element)(parseResult.api)) {
|
|
92
|
+
throw new _empty.UnmatchedResolveStrategyError(`Could not find a resolve strategy that can resolve "${uri}" as an Arazzo specification`);
|
|
93
|
+
}
|
|
94
|
+
parseResult.meta.set('retrievalURI', retrievalURI);
|
|
95
|
+
return refSet;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
throw new _ResolveError.default(`Failed to resolve Arazzo Document at "${uri}"`, {
|
|
98
|
+
cause: error
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Resolves an ApiDOM element representing an Arazzo Document.
|
|
105
|
+
*
|
|
106
|
+
* This function collects the element and every external document its
|
|
107
|
+
* JSON References ($ref) reach into a ReferenceSet. Nothing is dereferenced
|
|
108
|
+
* and the element itself is left untouched.
|
|
109
|
+
*
|
|
110
|
+
* Supported scenarios:
|
|
111
|
+
* - ParseResultElement with retrievalURI metadata: baseURI derived automatically
|
|
112
|
+
* - ParseResultElement without retrievalURI: requires `options.resolve.baseURI`
|
|
113
|
+
* - Child element (e.g., WorkflowElement) with parseResult in strategyOpts:
|
|
114
|
+
* requires `options.dereference.strategyOpts.parseResult`,
|
|
115
|
+
* and `options.resolve.baseURI` if parseResult lacks retrievalURI metadata
|
|
116
|
+
* - Child element without parseResult: requires `options.resolve.baseURI` and
|
|
117
|
+
* `options.parse.mediaType`, since the element alone cannot identify the document kind
|
|
118
|
+
*
|
|
119
|
+
* For a child element the root reference of the returned set is keyed by the root document
|
|
120
|
+
* URI but holds a ParseResultElement wrapping a copy of the child, not the root document.
|
|
121
|
+
* The set describes the references reachable from the child and is not a whole-document set.
|
|
122
|
+
*
|
|
123
|
+
* The `dereference.refSet` option is ignored: resolving always builds a fresh ReferenceSet.
|
|
124
|
+
*
|
|
125
|
+
* @param element - An ApiDOM element (ParseResultElement or child element like WorkflowElement)
|
|
126
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
127
|
+
* @returns A promise that resolves to the ReferenceSet of the element and its external references
|
|
128
|
+
* @throws ResolveError - When baseURI is required but not provided, when the document is not an Arazzo specification, or when resolving fails
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* Resolve ParseResultElement with retrievalURI (from file parsing)
|
|
132
|
+
* ```typescript
|
|
133
|
+
* import { parseArazzo } from '@usearazzo/parser';
|
|
134
|
+
*
|
|
135
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
136
|
+
* const refSet = await resolveArazzoElement(parseResult);
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* Resolve ParseResultElement without retrievalURI (from inline parsing)
|
|
141
|
+
* ```typescript
|
|
142
|
+
* const parseResult = await parseArazzo({ arazzo: '1.0.1', ... });
|
|
143
|
+
* const refSet = await resolveArazzoElement(parseResult, {
|
|
144
|
+
* resolve: { baseURI: 'https://example.com/arazzo.json' },
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* Resolve child element (e.g., WorkflowElement)
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
152
|
+
* const workflow = parseResult.api.workflows.get(0);
|
|
153
|
+
* const refSet = await resolveArazzoElement(workflow, {
|
|
154
|
+
* dereference: { strategyOpts: { parseResult } },
|
|
155
|
+
* });
|
|
156
|
+
* ```
|
|
157
|
+
* @public
|
|
158
|
+
*/
|
|
159
|
+
async function resolveElement(element, options = {}) {
|
|
160
|
+
const mergedOptions = (0, _empty.mergeOptions)(defaultOptions, options);
|
|
161
|
+
const subject = (0, _apidomDatamodel.isParseResultElement)(element) ? 'a ParseResultElement' : 'a child element';
|
|
162
|
+
// a child element resolves against the URI of its root document
|
|
163
|
+
const root = (0, _apidomDatamodel.isParseResultElement)(element) ? element : mergedOptions.dereference?.strategyOpts?.parseResult;
|
|
164
|
+
let baseURI = mergedOptions.resolve?.baseURI;
|
|
165
|
+
let mediaType = mergedOptions.parse?.mediaType ?? 'text/plain';
|
|
166
|
+
if ((0, _apidomDatamodel.isParseResultElement)(root)) {
|
|
167
|
+
if (!(0, _apidomNsArazzo.isArazzoSpecification1Element)(root.api)) {
|
|
168
|
+
throw new _ResolveError.default('Failed to resolve Arazzo Document', {
|
|
169
|
+
cause: new _empty.UnmatchedResolveStrategyError(`Could not find a resolve strategy that can resolve ${subject} as an Arazzo specification`)
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
mediaType = mergedOptions.parse?.mediaType ?? _apidomNsArazzo.mediaTypes.latest();
|
|
173
|
+
if (root.hasMetaProperty('retrievalURI')) {
|
|
174
|
+
baseURI = root.meta.get('retrievalURI');
|
|
175
|
+
} else if (!baseURI) {
|
|
176
|
+
throw new _ResolveError.default(`baseURI option is required when resolving ${subject} without retrievalURI metadata`);
|
|
177
|
+
}
|
|
178
|
+
} else if (!baseURI) {
|
|
179
|
+
throw new _ResolveError.default('baseURI option is required when resolving a child element without parseResult in strategyOpts');
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
return await (0, _empty.resolveApiDOM)(element, withoutSeededRefSet((0, _empty.mergeOptions)(mergedOptions, {
|
|
183
|
+
resolve: {
|
|
184
|
+
baseURI
|
|
185
|
+
},
|
|
186
|
+
parse: {
|
|
187
|
+
mediaType
|
|
188
|
+
}
|
|
189
|
+
})));
|
|
190
|
+
} catch (error) {
|
|
191
|
+
throw new _ResolveError.default('Failed to resolve Arazzo Document', {
|
|
192
|
+
cause: error
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Drops a seeded `dereference.refSet`. The resolve strategies always build their own
|
|
199
|
+
* ReferenceSet and deep-merge it over the option, which strips the prototype of a
|
|
200
|
+
* caller-supplied instance and crashes the resolution.
|
|
201
|
+
*/
|
|
202
|
+
function withoutSeededRefSet(options) {
|
|
203
|
+
return (0, _empty.mergeOptions)(options, {
|
|
204
|
+
dereference: {
|
|
205
|
+
refSet: null
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { isParseResultElement } from '@speclynx/apidom-datamodel';
|
|
2
|
+
import { url, resolve as resolveURI, resolveApiDOM as resolveApiDOMElement, mergeOptions, UnmatchedResolveStrategyError } from '@speclynx/apidom-reference/configuration/empty';
|
|
3
|
+
import Arazzo1ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/arazzo-1';
|
|
4
|
+
import OpenAPI2ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/openapi-2';
|
|
5
|
+
import OpenAPI30ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/openapi-3-0';
|
|
6
|
+
import OpenAPI31ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/openapi-3-1';
|
|
7
|
+
import { isArazzoSpecification1Element, mediaTypes } from '@speclynx/apidom-ns-arazzo-1';
|
|
8
|
+
import { defaultOptions as dereferenceDefaultOptions } from "../dereference/arazzo.mjs";
|
|
9
|
+
import ResolveError from "../errors/ResolveError.mjs";
|
|
10
|
+
/**
|
|
11
|
+
* Options for resolving Arazzo Documents.
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Default reference options for resolving Arazzo Documents.
|
|
16
|
+
*
|
|
17
|
+
* The resolve strategies delegate to the dereference strategies of the same name,
|
|
18
|
+
* so the dereference defaults are carried over and only the resolve strategies are added.
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export const defaultOptions = {
|
|
22
|
+
resolve: {
|
|
23
|
+
resolvers: [...dereferenceDefaultOptions.resolve.resolvers],
|
|
24
|
+
strategies: [new Arazzo1ResolveStrategy(), new OpenAPI2ResolveStrategy(), new OpenAPI30ResolveStrategy(), new OpenAPI31ResolveStrategy()]
|
|
25
|
+
},
|
|
26
|
+
parse: {
|
|
27
|
+
parsers: [...dereferenceDefaultOptions.parse.parsers],
|
|
28
|
+
parserOpts: {
|
|
29
|
+
...dereferenceDefaultOptions.parse.parserOpts
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
dereference: {
|
|
33
|
+
strategies: [...dereferenceDefaultOptions.dereference.strategies],
|
|
34
|
+
strategyOpts: {
|
|
35
|
+
...dereferenceDefaultOptions.dereference.strategyOpts
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolves an Arazzo Document from a file system path or HTTP(S) URL.
|
|
42
|
+
*
|
|
43
|
+
* This function collects the Arazzo Document and every external document its
|
|
44
|
+
* JSON References ($ref) reach into a ReferenceSet. Nothing is dereferenced;
|
|
45
|
+
* the root reference of the set holds the parsed entry document.
|
|
46
|
+
*
|
|
47
|
+
* The `dereference.refSet` option is ignored: resolving always builds a fresh ReferenceSet.
|
|
48
|
+
*
|
|
49
|
+
* @param uri - A file system path or HTTP(S) URL to the Arazzo Document
|
|
50
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
51
|
+
* @returns A promise that resolves to the ReferenceSet of the Arazzo Document and its external references
|
|
52
|
+
* @throws ResolveError - When resolving fails or document is not an Arazzo specification. The original error is available via the `cause` property.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Resolve from file
|
|
56
|
+
* const refSet = await resolveArazzo('/path/to/arazzo.json');
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Resolve from URL
|
|
60
|
+
* const refSet = await resolveArazzo('https://example.com/arazzo.yaml');
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* // Resolve with custom options
|
|
64
|
+
* const refSet = await resolveArazzo('/path/to/arazzo.json', customReferenceOptions);
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
export async function resolve(uri, options = {}) {
|
|
68
|
+
const mergedOptions = mergeOptions(defaultOptions, options);
|
|
69
|
+
|
|
70
|
+
// a relative file system path resolves against the current working directory; the string
|
|
71
|
+
// doubles as the base URI for relative references, where a relative base resolves against
|
|
72
|
+
// the filesystem root instead of the document. A leading slash or backslash (UNC,
|
|
73
|
+
// drive-rooted) is already rooted and passes through untouched. `uri` itself is left as
|
|
74
|
+
// is so error messages below quote what the caller passed in.
|
|
75
|
+
const retrievalURI = !url.isHttpUrl(uri) && url.getProtocol(uri) !== 'file' && !/^[\\/]/.test(uri) ? url.resolve(url.fromFileSystemPath(url.cwd()), uri) : uri;
|
|
76
|
+
try {
|
|
77
|
+
const refSet = await resolveURI(retrievalURI, withoutSeededRefSet(mergedOptions));
|
|
78
|
+
const parseResult = refSet.rootRef?.value;
|
|
79
|
+
|
|
80
|
+
// validate that the resolved document is an Arazzo specification
|
|
81
|
+
if (!isParseResultElement(parseResult) || !isArazzoSpecification1Element(parseResult.api)) {
|
|
82
|
+
throw new UnmatchedResolveStrategyError(`Could not find a resolve strategy that can resolve "${uri}" as an Arazzo specification`);
|
|
83
|
+
}
|
|
84
|
+
parseResult.meta.set('retrievalURI', retrievalURI);
|
|
85
|
+
return refSet;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
throw new ResolveError(`Failed to resolve Arazzo Document at "${uri}"`, {
|
|
88
|
+
cause: error
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Resolves an ApiDOM element representing an Arazzo Document.
|
|
95
|
+
*
|
|
96
|
+
* This function collects the element and every external document its
|
|
97
|
+
* JSON References ($ref) reach into a ReferenceSet. Nothing is dereferenced
|
|
98
|
+
* and the element itself is left untouched.
|
|
99
|
+
*
|
|
100
|
+
* Supported scenarios:
|
|
101
|
+
* - ParseResultElement with retrievalURI metadata: baseURI derived automatically
|
|
102
|
+
* - ParseResultElement without retrievalURI: requires `options.resolve.baseURI`
|
|
103
|
+
* - Child element (e.g., WorkflowElement) with parseResult in strategyOpts:
|
|
104
|
+
* requires `options.dereference.strategyOpts.parseResult`,
|
|
105
|
+
* and `options.resolve.baseURI` if parseResult lacks retrievalURI metadata
|
|
106
|
+
* - Child element without parseResult: requires `options.resolve.baseURI` and
|
|
107
|
+
* `options.parse.mediaType`, since the element alone cannot identify the document kind
|
|
108
|
+
*
|
|
109
|
+
* For a child element the root reference of the returned set is keyed by the root document
|
|
110
|
+
* URI but holds a ParseResultElement wrapping a copy of the child, not the root document.
|
|
111
|
+
* The set describes the references reachable from the child and is not a whole-document set.
|
|
112
|
+
*
|
|
113
|
+
* The `dereference.refSet` option is ignored: resolving always builds a fresh ReferenceSet.
|
|
114
|
+
*
|
|
115
|
+
* @param element - An ApiDOM element (ParseResultElement or child element like WorkflowElement)
|
|
116
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
117
|
+
* @returns A promise that resolves to the ReferenceSet of the element and its external references
|
|
118
|
+
* @throws ResolveError - When baseURI is required but not provided, when the document is not an Arazzo specification, or when resolving fails
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* Resolve ParseResultElement with retrievalURI (from file parsing)
|
|
122
|
+
* ```typescript
|
|
123
|
+
* import { parseArazzo } from '@usearazzo/parser';
|
|
124
|
+
*
|
|
125
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
126
|
+
* const refSet = await resolveArazzoElement(parseResult);
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* Resolve ParseResultElement without retrievalURI (from inline parsing)
|
|
131
|
+
* ```typescript
|
|
132
|
+
* const parseResult = await parseArazzo({ arazzo: '1.0.1', ... });
|
|
133
|
+
* const refSet = await resolveArazzoElement(parseResult, {
|
|
134
|
+
* resolve: { baseURI: 'https://example.com/arazzo.json' },
|
|
135
|
+
* });
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* Resolve child element (e.g., WorkflowElement)
|
|
140
|
+
* ```typescript
|
|
141
|
+
* const parseResult = await parseArazzo('/path/to/arazzo.json');
|
|
142
|
+
* const workflow = parseResult.api.workflows.get(0);
|
|
143
|
+
* const refSet = await resolveArazzoElement(workflow, {
|
|
144
|
+
* dereference: { strategyOpts: { parseResult } },
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
* @public
|
|
148
|
+
*/
|
|
149
|
+
export async function resolveElement(element, options = {}) {
|
|
150
|
+
const mergedOptions = mergeOptions(defaultOptions, options);
|
|
151
|
+
const subject = isParseResultElement(element) ? 'a ParseResultElement' : 'a child element';
|
|
152
|
+
// a child element resolves against the URI of its root document
|
|
153
|
+
const root = isParseResultElement(element) ? element : mergedOptions.dereference?.strategyOpts?.parseResult;
|
|
154
|
+
let baseURI = mergedOptions.resolve?.baseURI;
|
|
155
|
+
let mediaType = mergedOptions.parse?.mediaType ?? 'text/plain';
|
|
156
|
+
if (isParseResultElement(root)) {
|
|
157
|
+
if (!isArazzoSpecification1Element(root.api)) {
|
|
158
|
+
throw new ResolveError('Failed to resolve Arazzo Document', {
|
|
159
|
+
cause: new UnmatchedResolveStrategyError(`Could not find a resolve strategy that can resolve ${subject} as an Arazzo specification`)
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
mediaType = mergedOptions.parse?.mediaType ?? mediaTypes.latest();
|
|
163
|
+
if (root.hasMetaProperty('retrievalURI')) {
|
|
164
|
+
baseURI = root.meta.get('retrievalURI');
|
|
165
|
+
} else if (!baseURI) {
|
|
166
|
+
throw new ResolveError(`baseURI option is required when resolving ${subject} without retrievalURI metadata`);
|
|
167
|
+
}
|
|
168
|
+
} else if (!baseURI) {
|
|
169
|
+
throw new ResolveError('baseURI option is required when resolving a child element without parseResult in strategyOpts');
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
return await resolveApiDOMElement(element, withoutSeededRefSet(mergeOptions(mergedOptions, {
|
|
173
|
+
resolve: {
|
|
174
|
+
baseURI
|
|
175
|
+
},
|
|
176
|
+
parse: {
|
|
177
|
+
mediaType
|
|
178
|
+
}
|
|
179
|
+
})));
|
|
180
|
+
} catch (error) {
|
|
181
|
+
throw new ResolveError('Failed to resolve Arazzo Document', {
|
|
182
|
+
cause: error
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Drops a seeded `dereference.refSet`. The resolve strategies always build their own
|
|
189
|
+
* ReferenceSet and deep-merge it over the option, which strips the prototype of a
|
|
190
|
+
* caller-supplied instance and crashes the resolution.
|
|
191
|
+
*/
|
|
192
|
+
function withoutSeededRefSet(options) {
|
|
193
|
+
return mergeOptions(options, {
|
|
194
|
+
dereference: {
|
|
195
|
+
refSet: null
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
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.resolve = resolve;
|
|
9
|
+
exports.resolveElement = resolveElement;
|
|
10
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
11
|
+
var _empty = require("@speclynx/apidom-reference/configuration/empty");
|
|
12
|
+
var _openapi = _interopRequireDefault(require("@speclynx/apidom-reference/resolve/strategies/openapi-2"));
|
|
13
|
+
var _openapi2 = _interopRequireDefault(require("@speclynx/apidom-reference/resolve/strategies/openapi-3-0"));
|
|
14
|
+
var _openapi3 = _interopRequireDefault(require("@speclynx/apidom-reference/resolve/strategies/openapi-3-1"));
|
|
15
|
+
var _apidomNsOpenapi = require("@speclynx/apidom-ns-openapi-2");
|
|
16
|
+
var _apidomNsOpenapi2 = require("@speclynx/apidom-ns-openapi-3-0");
|
|
17
|
+
var _apidomNsOpenapi3 = require("@speclynx/apidom-ns-openapi-3-1");
|
|
18
|
+
var _openapi4 = require("../dereference/openapi.cjs");
|
|
19
|
+
var _ResolveError = _interopRequireDefault(require("../errors/ResolveError.cjs"));
|
|
20
|
+
/**
|
|
21
|
+
* Options for resolving OpenAPI Documents.
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default reference options for resolving OpenAPI Documents.
|
|
27
|
+
*
|
|
28
|
+
* The resolve strategies delegate to the dereference strategies of the same name,
|
|
29
|
+
* so the dereference defaults are carried over and only the resolve strategies are added.
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
const defaultOptions = exports.defaultOptions = {
|
|
33
|
+
resolve: {
|
|
34
|
+
resolvers: [..._openapi4.defaultOptions.resolve.resolvers],
|
|
35
|
+
strategies: [new _openapi.default(), new _openapi2.default(), new _openapi3.default()]
|
|
36
|
+
},
|
|
37
|
+
parse: {
|
|
38
|
+
parsers: [..._openapi4.defaultOptions.parse.parsers],
|
|
39
|
+
parserOpts: {
|
|
40
|
+
..._openapi4.defaultOptions.parse.parserOpts
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
dereference: {
|
|
44
|
+
strategies: [..._openapi4.defaultOptions.dereference.strategies],
|
|
45
|
+
strategyOpts: {
|
|
46
|
+
..._openapi4.defaultOptions.dereference.strategyOpts
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolves an OpenAPI Document from a file system path or HTTP(S) URL.
|
|
53
|
+
*
|
|
54
|
+
* This function collects the OpenAPI Document and every external document its
|
|
55
|
+
* JSON References ($ref) reach into a ReferenceSet. Nothing is dereferenced;
|
|
56
|
+
* the root reference of the set holds the parsed entry document.
|
|
57
|
+
*
|
|
58
|
+
* Supports OpenAPI 2.0 (Swagger), OpenAPI 3.0.x, and OpenAPI 3.1.x.
|
|
59
|
+
*
|
|
60
|
+
* The `dereference.refSet` option is ignored: resolving always builds a fresh ReferenceSet.
|
|
61
|
+
*
|
|
62
|
+
* @param uri - A file system path or HTTP(S) URL to the OpenAPI Document
|
|
63
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
64
|
+
* @returns A promise that resolves to the ReferenceSet of the OpenAPI Document and its external references
|
|
65
|
+
* @throws ResolveError - When resolving fails or document is not an OpenAPI specification. The original error is available via the `cause` property.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // Resolve from file
|
|
69
|
+
* const refSet = await resolveOpenAPI('/path/to/openapi.json');
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* // Resolve from URL
|
|
73
|
+
* const refSet = await resolveOpenAPI('https://example.com/openapi.yaml');
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* // Resolve with custom options
|
|
77
|
+
* const refSet = await resolveOpenAPI('/path/to/openapi.json', customReferenceOptions);
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
async function resolve(uri, options = {}) {
|
|
81
|
+
const mergedOptions = (0, _empty.mergeOptions)(defaultOptions, options);
|
|
82
|
+
|
|
83
|
+
// a relative file system path resolves against the current working directory; the string
|
|
84
|
+
// doubles as the base URI for relative references, where a relative base resolves against
|
|
85
|
+
// the filesystem root instead of the document. A leading slash or backslash (UNC,
|
|
86
|
+
// drive-rooted) is already rooted and passes through untouched. `uri` itself is left as
|
|
87
|
+
// is so error messages below quote what the caller passed in.
|
|
88
|
+
const retrievalURI = !_empty.url.isHttpUrl(uri) && _empty.url.getProtocol(uri) !== 'file' && !/^[\\/]/.test(uri) ? _empty.url.resolve(_empty.url.fromFileSystemPath(_empty.url.cwd()), uri) : uri;
|
|
89
|
+
try {
|
|
90
|
+
const refSet = await (0, _empty.resolve)(retrievalURI, withoutSeededRefSet(mergedOptions));
|
|
91
|
+
const parseResult = refSet.rootRef?.value;
|
|
92
|
+
|
|
93
|
+
// validate that the resolved document is an OpenAPI specification
|
|
94
|
+
if (!(0, _apidomDatamodel.isParseResultElement)(parseResult) || !isOpenApiElement(parseResult.api)) {
|
|
95
|
+
throw new _empty.UnmatchedResolveStrategyError(`Could not find a resolve strategy that can resolve "${uri}" as an OpenAPI specification`);
|
|
96
|
+
}
|
|
97
|
+
parseResult.meta.set('retrievalURI', retrievalURI);
|
|
98
|
+
return refSet;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw new _ResolveError.default(`Failed to resolve OpenAPI Document at "${uri}"`, {
|
|
101
|
+
cause: error
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Resolves an ApiDOM element representing an OpenAPI Document.
|
|
108
|
+
*
|
|
109
|
+
* This function collects the element and every external document its
|
|
110
|
+
* JSON References ($ref) reach into a ReferenceSet. Nothing is dereferenced
|
|
111
|
+
* and the element itself is left untouched.
|
|
112
|
+
*
|
|
113
|
+
* Supported scenarios:
|
|
114
|
+
* - ParseResultElement with retrievalURI metadata: baseURI derived automatically
|
|
115
|
+
* - ParseResultElement without retrievalURI: requires `options.resolve.baseURI`
|
|
116
|
+
* - Child element (e.g., PathItemElement) with parseResult in strategyOpts:
|
|
117
|
+
* requires `options.dereference.strategyOpts.parseResult`,
|
|
118
|
+
* and `options.resolve.baseURI` if parseResult lacks retrievalURI metadata
|
|
119
|
+
* - Child element without parseResult: requires `options.resolve.baseURI` and
|
|
120
|
+
* `options.parse.mediaType`, since the element alone cannot identify the document kind
|
|
121
|
+
*
|
|
122
|
+
* For a child element the root reference of the returned set is keyed by the root document
|
|
123
|
+
* URI but holds a ParseResultElement wrapping a copy of the child, not the root document.
|
|
124
|
+
* The set describes the references reachable from the child and is not a whole-document set.
|
|
125
|
+
*
|
|
126
|
+
* The `dereference.refSet` option is ignored: resolving always builds a fresh ReferenceSet.
|
|
127
|
+
*
|
|
128
|
+
* @param element - An ApiDOM element (ParseResultElement or child element like PathItemElement)
|
|
129
|
+
* @param options - Reference options (uses defaultOptions when not provided)
|
|
130
|
+
* @returns A promise that resolves to the ReferenceSet of the element and its external references
|
|
131
|
+
* @throws ResolveError - When baseURI is required but not provided, when the document is not an OpenAPI specification, or when resolving fails
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* Resolve ParseResultElement with retrievalURI (from file parsing)
|
|
135
|
+
* ```typescript
|
|
136
|
+
* import { parseOpenAPI } from '@usearazzo/parser';
|
|
137
|
+
*
|
|
138
|
+
* const parseResult = await parseOpenAPI('/path/to/openapi.json');
|
|
139
|
+
* const refSet = await resolveOpenAPIElement(parseResult);
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* Resolve ParseResultElement without retrievalURI (from inline parsing)
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const parseResult = await parseOpenAPI({ openapi: '3.1.0', ... });
|
|
146
|
+
* const refSet = await resolveOpenAPIElement(parseResult, {
|
|
147
|
+
* resolve: { baseURI: 'https://example.com/openapi.json' },
|
|
148
|
+
* });
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* Resolve child element (e.g., PathItemElement)
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const parseResult = await parseOpenAPI('/path/to/openapi.json');
|
|
155
|
+
* const pathItem = parseResult.api.paths.get('/users');
|
|
156
|
+
* const refSet = await resolveOpenAPIElement(pathItem, {
|
|
157
|
+
* dereference: { strategyOpts: { parseResult } },
|
|
158
|
+
* });
|
|
159
|
+
* ```
|
|
160
|
+
* @public
|
|
161
|
+
*/
|
|
162
|
+
async function resolveElement(element, options = {}) {
|
|
163
|
+
const mergedOptions = (0, _empty.mergeOptions)(defaultOptions, options);
|
|
164
|
+
const subject = (0, _apidomDatamodel.isParseResultElement)(element) ? 'a ParseResultElement' : 'a child element';
|
|
165
|
+
// a child element resolves against the URI of its root document
|
|
166
|
+
const root = (0, _apidomDatamodel.isParseResultElement)(element) ? element : mergedOptions.dereference?.strategyOpts?.parseResult;
|
|
167
|
+
let baseURI = mergedOptions.resolve?.baseURI;
|
|
168
|
+
let mediaType = mergedOptions.parse?.mediaType ?? 'text/plain';
|
|
169
|
+
if ((0, _apidomDatamodel.isParseResultElement)(root)) {
|
|
170
|
+
if (!isOpenApiElement(root.api)) {
|
|
171
|
+
throw new _ResolveError.default('Failed to resolve OpenAPI Document', {
|
|
172
|
+
cause: new _empty.UnmatchedResolveStrategyError(`Could not find a resolve strategy that can resolve ${subject} as an OpenAPI specification`)
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
mediaType = mergedOptions.parse?.mediaType ?? inferOpenApiMediaType(root.api);
|
|
176
|
+
if (root.hasMetaProperty('retrievalURI')) {
|
|
177
|
+
baseURI = root.meta.get('retrievalURI');
|
|
178
|
+
} else if (!baseURI) {
|
|
179
|
+
throw new _ResolveError.default(`baseURI option is required when resolving ${subject} without retrievalURI metadata`);
|
|
180
|
+
}
|
|
181
|
+
} else if (!baseURI) {
|
|
182
|
+
throw new _ResolveError.default('baseURI option is required when resolving a child element without parseResult in strategyOpts');
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
return await (0, _empty.resolveApiDOM)(element, withoutSeededRefSet((0, _empty.mergeOptions)(mergedOptions, {
|
|
186
|
+
resolve: {
|
|
187
|
+
baseURI
|
|
188
|
+
},
|
|
189
|
+
parse: {
|
|
190
|
+
mediaType
|
|
191
|
+
}
|
|
192
|
+
})));
|
|
193
|
+
} catch (error) {
|
|
194
|
+
throw new _ResolveError.default('Failed to resolve OpenAPI Document', {
|
|
195
|
+
cause: error
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Drops a seeded `dereference.refSet`. The resolve strategies always build their own
|
|
202
|
+
* ReferenceSet and deep-merge it over the option, which strips the prototype of a
|
|
203
|
+
* caller-supplied instance and crashes the resolution.
|
|
204
|
+
*/
|
|
205
|
+
function withoutSeededRefSet(options) {
|
|
206
|
+
return (0, _empty.mergeOptions)(options, {
|
|
207
|
+
dereference: {
|
|
208
|
+
refSet: null
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Checks if the element is a valid OpenAPI specification element.
|
|
215
|
+
*/
|
|
216
|
+
function isOpenApiElement(element) {
|
|
217
|
+
return (0, _apidomNsOpenapi.isSwaggerElement)(element) || (0, _apidomNsOpenapi2.isOpenApi3_0Element)(element) || (0, _apidomNsOpenapi3.isOpenApi3_1Element)(element);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Gets the appropriate mediaType for an OpenAPI element.
|
|
222
|
+
*/
|
|
223
|
+
function inferOpenApiMediaType(element) {
|
|
224
|
+
if ((0, _apidomNsOpenapi.isSwaggerElement)(element)) {
|
|
225
|
+
return _apidomNsOpenapi.mediaTypes.latest();
|
|
226
|
+
}
|
|
227
|
+
if ((0, _apidomNsOpenapi2.isOpenApi3_0Element)(element)) {
|
|
228
|
+
return _apidomNsOpenapi2.mediaTypes.latest();
|
|
229
|
+
}
|
|
230
|
+
if ((0, _apidomNsOpenapi3.isOpenApi3_1Element)(element)) {
|
|
231
|
+
return _apidomNsOpenapi3.mediaTypes.latest();
|
|
232
|
+
}
|
|
233
|
+
return 'text/plain';
|
|
234
|
+
}
|