@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/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
+ }