@jentic/arazzo-parser 1.0.0-alpha.5 → 1.0.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.0.0-alpha.7](https://github.com/jentic/jentic-arazzo-tools/compare/v1.0.0-alpha.6...v1.0.0-alpha.7) (2026-02-04)
7
+
8
+ ### Features
9
+
10
+ - **parser:** add support for parsing OpenAPI Documents ([#35](https://github.com/jentic/jentic-arazzo-tools/issues/35)) ([4c2615e](https://github.com/jentic/jentic-arazzo-tools/commit/4c2615e07c3b74ea7fe74b91b977c8c7123a2188))
11
+
12
+ # [1.0.0-alpha.6](https://github.com/jentic/jentic-arazzo-tools/compare/v1.0.0-alpha.5...v1.0.0-alpha.6) (2026-02-04)
13
+
14
+ ### Features
15
+
16
+ - **parser:** add support for parsing entire Arazzo Description ([#34](https://github.com/jentic/jentic-arazzo-tools/issues/34)) ([44b2bda](https://github.com/jentic/jentic-arazzo-tools/commit/44b2bda1c7449e1db8145af1dea457f2e09a465b))
17
+
6
18
  # [1.0.0-alpha.5](https://github.com/jentic/jentic-arazzo-tools/compare/v1.0.0-alpha.4...v1.0.0-alpha.5) (2026-01-31)
7
19
 
8
20
  **Note:** Version bump only for package @jentic/arazzo-parser
package/README.md CHANGED
@@ -13,17 +13,21 @@ npm install @jentic/arazzo-parser
13
13
 
14
14
  ## Usage
15
15
 
16
- `@jentic/arazzo-parser` provides a unified `parse` function that accepts multiple input types:
16
+ `@jentic/arazzo-parser` provides a `parseArazzo` function for parsing Arazzo documents.
17
17
 
18
- 1. **Plain JavaScript object** - directly refracts into ApiDOM
19
- 2. **String content** - parses inline JSON or YAML Arazzo Documents
18
+ ## Parsing Arazzo Documents
19
+
20
+ The `parseArazzo` function accepts multiple input types:
21
+
22
+ 1. **Plain JavaScript object** - converts to JSON and parses (source maps supported with `strict: false`)
23
+ 2. **String content** - detects Arazzo content and parses inline JSON or YAML
20
24
  3. **File system path** - resolves and parses local Arazzo Documents
21
25
  4. **HTTP(S) URL** - fetches and parses remote Arazzo Documents
22
26
 
23
- ### Parsing from object
27
+ ### From object
24
28
 
25
29
  ```js
26
- import { parse } from '@jentic/arazzo-parser';
30
+ import { parseArazzo } from '@jentic/arazzo-parser';
27
31
 
28
32
  const arazzoDocument = {
29
33
  arazzo: '1.0.1',
@@ -41,20 +45,20 @@ const arazzoDocument = {
41
45
  workflows: [],
42
46
  };
43
47
 
44
- const parseResult = await parse(arazzoDocument);
48
+ const parseResult = await parseArazzo(arazzoDocument);
45
49
  // parseResult is ParseResultElement containing ArazzoSpecification1Element
46
50
  ```
47
51
 
48
- ### Parsing from string
52
+ ### From string
49
53
 
50
54
  ```js
51
- import { parse } from '@jentic/arazzo-parser';
55
+ import { parseArazzo } from '@jentic/arazzo-parser';
52
56
 
53
57
  // JSON string
54
- const parseResult = await parse('{"arazzo": "1.0.1", "info": {...}}');
58
+ const parseResult = await parseArazzo('{"arazzo": "1.0.1", "info": {...}}');
55
59
 
56
60
  // YAML string
57
- const parseResult = await parse(`
61
+ const parseResult = await parseArazzo(`
58
62
  arazzo: '1.0.1'
59
63
  info:
60
64
  title: My API Workflow
@@ -62,30 +66,30 @@ info:
62
66
  `);
63
67
  ```
64
68
 
65
- ### Parsing from file
69
+ ### From file
66
70
 
67
71
  ```js
68
- import { parse } from '@jentic/arazzo-parser';
72
+ import { parseArazzo } from '@jentic/arazzo-parser';
69
73
 
70
- const parseResult = await parse('/path/to/arazzo.json');
74
+ const parseResult = await parseArazzo('/path/to/arazzo.json');
71
75
  ```
72
76
 
73
- ### Parsing from URL
77
+ ### From URL
74
78
 
75
79
  ```js
76
- import { parse } from '@jentic/arazzo-parser';
80
+ import { parseArazzo } from '@jentic/arazzo-parser';
77
81
 
78
- const parseResult = await parse('https://example.com/arazzo.yaml');
82
+ const parseResult = await parseArazzo('https://example.com/arazzo.yaml');
79
83
  ```
80
84
 
81
85
  ## Parse options
82
86
 
83
- The `parse` function accepts an optional second argument with reference options compatible with [SpecLynx ApiDOM Reference Options](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/src/options/index.ts):
87
+ The `parseArazzo` function accepts an optional second argument with reference options compatible with [SpecLynx ApiDOM Reference Options](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/src/options/index.ts):
84
88
 
85
89
  ```js
86
- import { parse } from '@jentic/arazzo-parser';
90
+ import { parseArazzo } from '@jentic/arazzo-parser';
87
91
 
88
- const parseResult = await parse(source, {
92
+ const parseResult = await parseArazzo(source, {
89
93
  parse: {
90
94
  parserOpts: {
91
95
  strict: true, // Use strict parsing mode (default: true)
@@ -105,11 +109,16 @@ import { defaultOptions } from '@jentic/arazzo-parser';
105
109
  console.log(defaultOptions);
106
110
  // {
107
111
  // parse: {
108
- // parsers: [...],
109
- // parserOpts: {},
112
+ // parsers: [
113
+ // ArazzoJSON1Parser, ArazzoYAML1Parser,
114
+ // OpenApiJSON2Parser, OpenApiYAML2Parser,
115
+ // OpenApiJSON3_0Parser, OpenApiYAML3_0Parser,
116
+ // OpenApiJSON3_1Parser, OpenApiYAML3_1Parser,
117
+ // ],
118
+ // parserOpts: { sourceMap: false, strict: true, sourceDescriptions: false },
110
119
  // },
111
120
  // resolve: {
112
- // resolvers: [...],
121
+ // resolvers: [MemoryResolver, FileResolver, HTTPResolverAxios],
113
122
  // resolverOpts: {},
114
123
  // },
115
124
  // }
@@ -120,10 +129,10 @@ console.log(defaultOptions);
120
129
  When parsing fails, a `ParseError` is thrown. The original error is available via the `cause` property:
121
130
 
122
131
  ```js
123
- import { parse } from '@jentic/arazzo-parser';
132
+ import { parseArazzo } from '@jentic/arazzo-parser';
124
133
 
125
134
  try {
126
- await parse('invalid content');
135
+ await parseArazzo('invalid content');
127
136
  } catch (error) {
128
137
  console.error(error.message); // 'Failed to parse Arazzo Document'
129
138
  console.error(error.cause); // Original error from underlying parser
@@ -132,12 +141,12 @@ try {
132
141
 
133
142
  ## Working with the result
134
143
 
135
- The `parse` function returns a [ParseResultElement](https://github.com/speclynx/apidom/blob/main/packages/apidom-datamodel/README.md#parseresultelement) representing the result of the parsing operation.
144
+ The `parseArazzo` function returns a [ParseResultElement](https://github.com/speclynx/apidom/blob/main/packages/apidom-datamodel/README.md#parseresultelement) representing the result of the parsing operation.
136
145
 
137
146
  ```js
138
- import { parse } from '@jentic/arazzo-parser';
147
+ import { parseArazzo } from '@jentic/arazzo-parser';
139
148
 
140
- const parseResult = await parse(source);
149
+ const parseResult = await parseArazzo(source);
141
150
 
142
151
  // Access the main Arazzo specification element
143
152
  const arazzoSpec = parseResult.api;
@@ -154,10 +163,10 @@ const isEmpty = parseResult.isEmpty;
154
163
  When parsing from a file system path or HTTP(S) URL, the `retrievalURI` metadata is set on the parse result:
155
164
 
156
165
  ```js
157
- import { parse } from '@jentic/arazzo-parser';
166
+ import { parseArazzo } from '@jentic/arazzo-parser';
158
167
  import { toValue } from '@speclynx/apidom-core';
159
168
 
160
- const parseResult = await parse('/path/to/arazzo.json');
169
+ const parseResult = await parseArazzo('/path/to/arazzo.json');
161
170
 
162
171
  // Get the URI from which the document was retrieved
163
172
  const uri = toValue(parseResult.meta.get('retrievalURI'));
@@ -170,15 +179,17 @@ Note: `retrievalURI` is not set when parsing from inline content (string) or pla
170
179
 
171
180
  Source maps allow you to track the original position (line, column) of each element in the parsed document. This is useful for error reporting, IDE integrations, linting, and any tooling that needs to show precise locations in the original source.
172
181
 
173
- To enable source maps, set `sourceMap: true` in the parser options:
182
+
183
+ To enable source maps, set `sourceMap: true` and `strict: false` in the parser options:
174
184
 
175
185
  ```js
176
- import { parse } from '@jentic/arazzo-parser';
186
+ import { parseArazzo } from '@jentic/arazzo-parser';
177
187
 
178
- const parseResult = await parse('/path/to/arazzo.yaml', {
188
+ const parseResult = await parseArazzo('/path/to/arazzo.yaml', {
179
189
  parse: {
180
190
  parserOpts: {
181
191
  sourceMap: true,
192
+ strict: false,
182
193
  },
183
194
  },
184
195
  });
@@ -187,10 +198,10 @@ const parseResult = await parse('/path/to/arazzo.yaml', {
187
198
  When source maps are enabled, each element in the parsed result contains positional properties stored directly on the element. Position values use UTF-16 code units for compatibility with Language Server Protocol (LSP) and JavaScript string indexing:
188
199
 
189
200
  ```js
190
- import { parse } from '@jentic/arazzo-parser';
201
+ import { parseArazzo } from '@jentic/arazzo-parser';
191
202
 
192
- const parseResult = await parse('/path/to/arazzo.yaml', {
193
- parse: { parserOpts: { sourceMap: true } },
203
+ const parseResult = await parseArazzo('/path/to/arazzo.yaml', {
204
+ parse: { parserOpts: { sourceMap: true, strict: false } },
194
205
  });
195
206
 
196
207
  const arazzoSpec = parseResult.api;
@@ -210,15 +221,246 @@ console.log(`Workflow starts at line ${workflow.startLine}, column ${workflow.st
210
221
 
211
222
  For more details about source maps, see the [SpecLynx ApiDOM Data Model documentation](https://github.com/speclynx/apidom/tree/main/packages/apidom-datamodel#source-maps).
212
223
 
213
- **Note:** Source maps are only available when parsing from string content, file paths, or URLs. When parsing from a plain JavaScript object, source maps cannot be generated and attempting to enable them will throw a `ParseError`:
224
+ **Note:** Source maps require `strict: false` to be set. When parsing from objects, they are converted to pretty-printed JSON strings internally (2-space indentation), so source map positions refer to this generated JSON representation, not the original object structure:
225
+
226
+ ```js
227
+ // Source maps with objects (requires strict: false)
228
+ // Positions will reference the internally generated JSON string
229
+ await parseArazzo({ arazzo: '1.0.1', ... }, {
230
+ parse: { parserOpts: { sourceMap: true, strict: false } },
231
+ });
232
+ ```
233
+
234
+ ## Parsing source descriptions
235
+
236
+ Arazzo documents can reference external API specifications (OpenAPI, Arazzo) through [Source Descriptions](https://spec.openapis.org/arazzo/latest.html#source-description-object). The parser can automatically fetch and parse these referenced documents.
237
+
238
+ **Note:** Source descriptions parsing is disabled by default for performance reasons. Enable it explicitly when you need to resolve and parse referenced API specifications.
239
+
240
+ ### Enabling source descriptions parsing
241
+
242
+ To parse source descriptions, enable the `sourceDescriptions` option in `parserOpts`:
243
+
244
+ ```js
245
+ import { parseArazzo } from '@jentic/arazzo-parser';
246
+
247
+ const parseResult = await parseArazzo('/path/to/arazzo.json', {
248
+ parse: {
249
+ parserOpts: {
250
+ sourceDescriptions: true,
251
+ },
252
+ },
253
+ });
254
+ ```
255
+
256
+ Alternatively, you can configure it per parser for more granular control:
214
257
 
215
258
  ```js
216
- // This will throw ParseError
217
- await parse({ arazzo: '1.0.1', ... }, {
218
- parse: { parserOpts: { sourceMap: true } },
259
+ const parseResult = await parseArazzo('/path/to/arazzo.json', {
260
+ parse: {
261
+ parserOpts: {
262
+ 'arazzo-json-1': { sourceDescriptions: true },
263
+ 'arazzo-yaml-1': { sourceDescriptions: true },
264
+ },
265
+ },
219
266
  });
220
267
  ```
221
268
 
269
+ ### Selective parsing
270
+
271
+ You can selectively parse only specific source descriptions by providing an array of names:
272
+
273
+ ```js
274
+ const parseResult = await parseArazzo('/path/to/arazzo.json', {
275
+ parse: {
276
+ parserOpts: {
277
+ sourceDescriptions: ['petStoreApi', 'paymentApi'],
278
+ },
279
+ },
280
+ });
281
+ ```
282
+
283
+ ### Result structure
284
+
285
+ When source descriptions are parsed, each parsed document that is a *direct* source description of the main Arazzo document is added to the main `ParseResultElement` as an additional top-level element. The first element is always the main Arazzo document, and subsequent top-level elements are these directly parsed source descriptions. When recursive parsing discovers further source descriptions from within an already parsed source description, those recursively parsed documents are attached as nested `ParseResultElement` instances beneath the source-description element that referenced them (they are not duplicated at the top level). Consumers that need to see all documents should traverse both the top-level elements and any nested `ParseResultElement`s reachable from source-description elements.
286
+
287
+ Source descriptions are parsed into their appropriate SpecLynx ApiDOM namespace data models based on document type:
288
+
289
+ - [Arazzo 1.x](https://spec.openapis.org/arazzo/latest.html) → [@speclynx/apidom-ns-arazzo-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-ns-arazzo-1)
290
+ - [OpenAPI 2.0 (Swagger)](https://spec.openapis.org/oas/v2.0) → [@speclynx/apidom-ns-openapi-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-ns-openapi-2)
291
+ - [OpenAPI 3.0.x](https://spec.openapis.org/oas/v3.0.4) → [@speclynx/apidom-ns-openapi-3-0](https://github.com/speclynx/apidom/tree/main/packages/apidom-ns-openapi-3-0)
292
+ - [OpenAPI 3.1.x](https://spec.openapis.org/oas/v3.1.1) → [@speclynx/apidom-ns-openapi-3-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-ns-openapi-3-1)
293
+
294
+ ```mermaid
295
+ graph TD
296
+ PR["ParseResultElement"] --> API[".api: ArazzoSpecification1Element"]
297
+ PR --> SD1
298
+ PR --> SD2
299
+
300
+ subgraph source-descriptions [source descriptions]
301
+ SD1["ParseResultElement<br/>(petStoreApi)"] --> SD1_API[".api: OpenApi3_1Element"]
302
+ SD2["ParseResultElement<br/>(legacyApi)"] --> SD2_ERR[".errors"]
303
+ SD2 --> SD2_WARN[".warnings"]
304
+ end
305
+
306
+ style PR fill:#e7f1ff,stroke:#0366d6,stroke-width:2px
307
+ style SD1 fill:#f6f8fa,stroke:#586069
308
+ style SD2 fill:#f6f8fa,stroke:#586069
309
+ style API fill:#d4edda,stroke:#28a745
310
+ style SD1_API fill:#d4edda,stroke:#28a745
311
+ style SD2_ERR fill:#f8d7da,stroke:#dc3545
312
+ style SD2_WARN fill:#fff3cd,stroke:#ffc107
313
+ ```
314
+
315
+ ```js
316
+ import { parseArazzo } from '@jentic/arazzo-parser';
317
+
318
+ const parseResult = await parseArazzo('/path/to/arazzo.json', {
319
+ parse: {
320
+ parserOpts: {
321
+ sourceDescriptions: true,
322
+ },
323
+ },
324
+ });
325
+
326
+ // Main Arazzo document
327
+ const arazzoSpec = parseResult.api;
328
+
329
+ // Number of elements (1 main + N source descriptions)
330
+ console.log(parseResult.length);
331
+
332
+ // Access parsed source descriptions (filter by 'source-description' class)
333
+ for (let i = 0; i < parseResult.length; i++) {
334
+ const element = parseResult.get(i);
335
+
336
+ if (element.classes.includes('source-description')) {
337
+ // Source description metadata
338
+ const name = element.meta.get('name')?.toValue();
339
+ const type = element.meta.get('type')?.toValue();
340
+
341
+ console.log(`Source description "${name}" (${type})`);
342
+
343
+ // The parsed API document
344
+ const api = element.api;
345
+ }
346
+ }
347
+ ```
348
+
349
+ ### Recursive parsing
350
+
351
+ When a source description is of type `arazzo`, the parser recursively parses that document's source descriptions as well. This allows you to parse entire dependency trees of Arazzo documents.
352
+
353
+ ### Limiting recursion depth
354
+
355
+ To prevent excessive recursion or handle deeply nested documents, use the `sourceDescriptionsMaxDepth` option:
356
+
357
+ ```js
358
+ const parseResult = await parseArazzo('/path/to/arazzo.json', {
359
+ parse: {
360
+ parserOpts: {
361
+ sourceDescriptions: true,
362
+ sourceDescriptionsMaxDepth: 2, // Only parse 2 levels deep
363
+ },
364
+ },
365
+ });
366
+ ```
367
+
368
+ The default value is `+Infinity` (no limit). Setting it to `0` will create error annotations instead of parsing any source descriptions.
369
+
370
+ ### Cycle detection
371
+
372
+ The parser automatically detects circular references between Arazzo documents. When a cycle is detected, a warning annotation is added instead of causing infinite recursion:
373
+
374
+ ```js
375
+ // arazzo-a.json references arazzo-b.json
376
+ // arazzo-b.json references arazzo-a.json (cycle!)
377
+
378
+ const parseResult = await parseArazzo('/path/to/arazzo-a.json', {
379
+ parse: {
380
+ parserOpts: {
381
+ sourceDescriptions: true,
382
+ },
383
+ },
384
+ });
385
+
386
+ // The cycle is handled gracefully - check for warning annotations
387
+ ```
388
+
389
+ ### Error and warning handling
390
+
391
+ When issues occur during source description parsing, the parser does not throw errors. Instead, it adds annotation elements to the source description's parse result:
392
+
393
+ - **`error`** class - Parsing failed (e.g., file not found, invalid document, max depth exceeded)
394
+ - **`warning`** class - Non-fatal issues (e.g., cycle detected, type mismatch between declared and actual)
395
+
396
+ This allows partial parsing to succeed even if some source descriptions have issues:
397
+
398
+ ```js
399
+ const parseResult = await parseArazzo('/path/to/arazzo.json', {
400
+ parse: {
401
+ parserOpts: {
402
+ sourceDescriptions: true,
403
+ },
404
+ },
405
+ });
406
+
407
+ // Check each source description for errors and warnings
408
+ for (let i = 0; i < parseResult.length; i++) {
409
+ const element = parseResult.get(i);
410
+
411
+ if (element.classes.includes('source-description')) {
412
+ const name = element.meta.get('name')?.toValue();
413
+
414
+ // Use built-in accessors for errors and warnings
415
+ element.errors.forEach((error) => {
416
+ console.error(`Error in "${name}": ${error.toValue()}`);
417
+ });
418
+
419
+ element.warnings.forEach((warning) => {
420
+ console.warn(`Warning in "${name}": ${warning.toValue()}`);
421
+ });
422
+ }
423
+ }
424
+ ```
425
+
426
+ ## Parsing OpenAPI Documents
427
+
428
+ The `parseOpenAPI` function provides complete control for parsing OpenAPI documents manually.
429
+ This is useful when you need to parse source descriptions independently or implement custom source description resolution logic.
430
+
431
+ The function accepts the same input types as `parseArazzo`:
432
+
433
+ 1. **Plain JavaScript object** - converts to JSON and parses
434
+ 2. **String content** - detects OpenAPI content and parses inline JSON or YAML
435
+ 3. **File system path** - resolves and parses local OpenAPI Documents
436
+ 4. **HTTP(S) URL** - fetches and parses remote OpenAPI Documents
437
+
438
+ Documents are parsed into their appropriate SpecLynx ApiDOM namespace data models:
439
+
440
+ - [OpenAPI 2.0 (Swagger)](https://spec.openapis.org/oas/v2.0) → [@speclynx/apidom-ns-openapi-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-ns-openapi-2)
441
+ - [OpenAPI 3.0.x](https://spec.openapis.org/oas/v3.0.4) → [@speclynx/apidom-ns-openapi-3-0](https://github.com/speclynx/apidom/tree/main/packages/apidom-ns-openapi-3-0)
442
+ - [OpenAPI 3.1.x](https://spec.openapis.org/oas/v3.1.1) → [@speclynx/apidom-ns-openapi-3-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-ns-openapi-3-1)
443
+
444
+ ```js
445
+ import { parseOpenAPI } from '@jentic/arazzo-parser';
446
+
447
+ // From object
448
+ const parseResult = await parseOpenAPI({
449
+ openapi: '3.1.0',
450
+ info: { title: 'My API', version: '1.0.0' },
451
+ paths: {},
452
+ });
453
+
454
+ // From string
455
+ const parseResult = await parseOpenAPI('{"openapi": "3.1.0", ...}');
456
+
457
+ // From file
458
+ const parseResult = await parseOpenAPI('/path/to/openapi.json');
459
+
460
+ // From URL
461
+ const parseResult = await parseOpenAPI('https://example.com/openapi.yaml');
462
+ ```
463
+
222
464
  ## SpecLynx ApiDOM tooling
223
465
 
224
466
  Since `@jentic/arazzo-parser` produces a SpecLynx ApiDOM data model, you have access to the full suite of ApiDOM tools for manipulating, traversing, and transforming the parsed document.
@@ -228,11 +470,11 @@ Since `@jentic/arazzo-parser` produces a SpecLynx ApiDOM data model, you have ac
228
470
  The [@speclynx/apidom-core](https://github.com/speclynx/apidom/tree/main/packages/apidom-core) package provides essential utilities for working with ApiDOM elements. Here are just a few examples:
229
471
 
230
472
  ```js
231
- import { parse } from '@jentic/arazzo-parser';
473
+ import { parseArazzo } from '@jentic/arazzo-parser';
232
474
  import { cloneDeep, cloneShallow } from '@speclynx/apidom-datamodel';
233
475
  import { toValue, toJSON, toYAML, sexprs } from '@speclynx/apidom-core';
234
476
 
235
- const parseResult = await parse(source);
477
+ const parseResult = await parseArazzo(source);
236
478
  const arazzoSpec = parseResult.api;
237
479
 
238
480
  // Convert to plain JavaScript object
@@ -257,10 +499,10 @@ const sexpr = sexprs(arazzoSpec);
257
499
  The [@speclynx/apidom-traverse](https://github.com/speclynx/apidom/tree/main/packages/apidom-traverse) package provides powerful traversal capabilities. Here is a basic example:
258
500
 
259
501
  ```js
260
- import { parse } from '@jentic/arazzo-parser';
502
+ import { parseArazzo } from '@jentic/arazzo-parser';
261
503
  import { traverse } from '@speclynx/apidom-traverse';
262
504
 
263
- const parseResult = await parse(source);
505
+ const parseResult = await parseArazzo(source);
264
506
 
265
507
  // Traverse and collect steps using semantic visitor hook
266
508
  const steps = [];