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