@speclynx/apidom-reference 5.2.5 → 5.2.6

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,13 @@
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
+ ## [5.2.6](https://github.com/speclynx/apidom/compare/v5.2.5...v5.2.6) (2026-09-19)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **reference:** honour continueOnError for Arazzo Reusable Objects ([#552](https://github.com/speclynx/apidom/issues/552)) ([2bcd675](https://github.com/speclynx/apidom/commit/2bcd675d16fc1bf3fc5079fdee37419c1d84017c)), closes [#550](https://github.com/speclynx/apidom/issues/550)
11
+ - **reference:** isolate refSet of Arazzo source descriptions ([#553](https://github.com/speclynx/apidom/issues/553)) ([ac21c16](https://github.com/speclynx/apidom/commit/ac21c165104e604aa59f027a8d78645c8bd9ca66)), closes [#551](https://github.com/speclynx/apidom/issues/551)
12
+
6
13
  ## [5.2.5](https://github.com/speclynx/apidom/compare/v5.2.4...v5.2.5) (2026-09-17)
7
14
 
8
15
  ### Bug Fixes
package/README.md CHANGED
@@ -2071,6 +2071,10 @@ Strategy-specific options take precedence over global options.
2071
2071
  The `SourceDescriptionElement`'s `'parseResult'` meta points at that same `ParseResultElement`, so the dereferenced
2072
2072
  document is reachable from every source description that references it.
2073
2073
 
2074
+ When `dereference.refSet` is provided, source descriptions reuse the documents it already contains,
2075
+ and every document reached through a source description is added to it.
2076
+ This is also how resolving with `sourceDescriptions` enabled reports the documents behind source descriptions.
2077
+
2074
2078
  ###### Error handling
2075
2079
 
2076
2080
  The source descriptions dereferencing uses annotations instead of throwing errors, allowing dereferencing to continue
@@ -2496,16 +2500,16 @@ await dereference('/home/user/oas.json', {
2496
2500
  // errors is an array of UnresolvableReferenceError instances
2497
2501
  ```
2498
2502
 
2499
- Each `UnresolvableReferenceError` (extends `DereferenceError`) carries structured context:
2503
+ Each `UnresolvableReferenceError` (extends `ResolveError`) carries structured context:
2500
2504
 
2501
2505
  | Property | Type | Description |
2502
2506
  |---|---|---|
2503
2507
  | `message` | `string` | Contextual description (e.g., "Error while dereferencing Reference Object. Cannot resolve $ref ...") |
2504
- | `type` | `string` | Element type that failed (e.g., `"reference"`, `"pathItem"`, `"schema"`, `"link"`, `"example"`) |
2508
+ | `type` | `string` | Element type that failed (e.g., `"reference"`, `"pathItem"`, `"schema"`, `"link"`, `"example"`, `"reusable"`) |
2505
2509
  | `uri` | `string` | Document URI where the error occurred |
2506
2510
  | `location` | `string` | JSON Pointer to the element within the document |
2507
2511
  | `codeFrame` | `string` | YAML snippet of the referencing element |
2508
- | `refFieldName` | `string` | Reference field name (e.g., `"$ref"`, `"operationRef"`, `"externalValue"`) |
2512
+ | `refFieldName` | `string` | Reference field name (e.g., `"$ref"`, `"operationRef"`, `"externalValue"`, `"reference"`) |
2509
2513
  | `refFieldValue` | `string` | Reference field value |
2510
2514
  | `trace` | `Array` | Chain of reference hops from entry document to the failure |
2511
2515
  | `cause` | `Error` | The underlying error |
@@ -5036,6 +5036,17 @@ class Arazzo1DereferenceStrategy extends _DereferenceStrategy_ts__WEBPACK_IMPORT
5036
5036
  })
5037
5037
  });
5038
5038
 
5039
+ /**
5040
+ * If immutable option is set, replay refs from the refSet.
5041
+ * Replayed before source descriptions are dereferenced, so they can reuse the refs.
5042
+ */
5043
+ if (options.dereference.immutable) {
5044
+ mutableRefSet.refs.filter(ref => ref.uri.startsWith('immutable://')).map(ref => new _Reference_ts__WEBPACK_IMPORTED_MODULE_7__["default"]({
5045
+ ...ref,
5046
+ uri: ref.uri.replace(/^immutable:\/\//, '')
5047
+ })).forEach(ref => immutableRefSet.add(ref));
5048
+ }
5049
+
5039
5050
  /**
5040
5051
  * Dereference source descriptions if option is enabled.
5041
5052
  */
@@ -5050,16 +5061,6 @@ class Arazzo1DereferenceStrategy extends _DereferenceStrategy_ts__WEBPACK_IMPORT
5050
5061
  parseResult.push(...sourceDescriptions);
5051
5062
  }
5052
5063
 
5053
- /**
5054
- * If immutable option is set, replay refs from the refSet.
5055
- */
5056
- if (options.dereference.immutable) {
5057
- mutableRefSet.refs.filter(ref => ref.uri.startsWith('immutable://')).map(ref => new _Reference_ts__WEBPACK_IMPORTED_MODULE_7__["default"]({
5058
- ...ref,
5059
- uri: ref.uri.replace(/^immutable:\/\//, '')
5060
- })).forEach(ref => immutableRefSet.add(ref));
5061
- }
5062
-
5063
5064
  /**
5064
5065
  * Release all memory if this refSet was not provided as a configuration option.
5065
5066
  * If provided as configuration option, then provider is responsible for cleanup.
@@ -5096,10 +5097,18 @@ __webpack_require__.r(__webpack_exports__);
5096
5097
  /* harmony import */ var _speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4823);
5097
5098
  /* harmony import */ var _speclynx_apidom_ns_arazzo_1__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(28839);
5098
5099
  /* harmony import */ var _speclynx_apidom_core__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(44673);
5099
- /* harmony import */ var _util_url_ts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(30658);
5100
- /* harmony import */ var _options_util_ts__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(86547);
5101
- /* harmony import */ var _index_ts__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(42037);
5102
- /* harmony import */ var _util_ts__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(91005);
5100
+ /* harmony import */ var ramda__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(164);
5101
+ /* harmony import */ var _util_url_ts__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(30658);
5102
+ /* harmony import */ var _Reference_ts__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(98979);
5103
+ /* harmony import */ var _ReferenceSet_ts__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(24131);
5104
+ /* harmony import */ var _options_util_ts__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(86547);
5105
+ /* harmony import */ var _parse_index_ts__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(35014);
5106
+ /* harmony import */ var _index_ts__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(42037);
5107
+ /* harmony import */ var _util_ts__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(91005);
5108
+
5109
+
5110
+
5111
+
5103
5112
 
5104
5113
 
5105
5114
 
@@ -5133,7 +5142,7 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
5133
5142
  }
5134
5143
 
5135
5144
  // normalize URI for consistent cycle detection and refSet cache key matching
5136
- const retrievalURI = _util_url_ts__WEBPACK_IMPORTED_MODULE_6__.sanitize(_util_url_ts__WEBPACK_IMPORTED_MODULE_6__.stripHash(_util_url_ts__WEBPACK_IMPORTED_MODULE_6__.resolve(ctx.baseURI, sourceDescriptionURI)));
5145
+ const retrievalURI = _util_url_ts__WEBPACK_IMPORTED_MODULE_7__.sanitize(_util_url_ts__WEBPACK_IMPORTED_MODULE_7__.stripHash(_util_url_ts__WEBPACK_IMPORTED_MODULE_7__.resolve(ctx.baseURI, sourceDescriptionURI)));
5137
5146
  parseResult.setMetaProperty('retrievalURI', retrievalURI);
5138
5147
 
5139
5148
  // skip if the document is still being dereferenced higher up the chain (cycle detection)
@@ -5152,58 +5161,69 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
5152
5161
  annotation.classes.push('info');
5153
5162
  parseResult.push(annotation);
5154
5163
  parseResult.meta.set('parseResult', sharedDereferenceResult);
5155
- (0,_util_ts__WEBPACK_IMPORTED_MODULE_9__.validateSourceDescriptionAPI)(parseResult, sourceDescription, sharedDereferenceResult.api, retrievalURI);
5164
+ (0,_util_ts__WEBPACK_IMPORTED_MODULE_13__.validateSourceDescriptionAPI)(parseResult, sourceDescription, sharedDereferenceResult.api, retrievalURI);
5156
5165
  return parseResult;
5157
5166
  }
5158
5167
 
5159
5168
  // check if source description was already parsed (e.g., during parse phase with sourceDescriptions: true)
5160
5169
  const existingParseResult = sourceDescription.meta.get('parseResult');
5170
+ const parentRefSet = ctx.options.dereference.refSet;
5171
+ let refSet = null;
5161
5172
  try {
5162
- let sourceDescriptionDereferenced;
5163
- if ((0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_1__.isParseResultElement)(existingParseResult)) {
5164
- // use existing parsed result - just dereference it (no re-fetch/re-parse)
5165
- sourceDescriptionDereferenced = await (0,_index_ts__WEBPACK_IMPORTED_MODULE_8__.dereferenceApiDOM)(existingParseResult, (0,_options_util_ts__WEBPACK_IMPORTED_MODULE_7__.merge)(ctx.options, {
5166
- parse: {
5167
- mediaType: 'text/plain' // allow dereference strategy detection via ApiDOM inspection
5168
- },
5169
- resolve: {
5170
- baseURI: retrievalURI
5171
- },
5172
- dereference: {
5173
- strategyOpts: {
5174
- // nested documents should dereference all their source descriptions
5175
- // (parent's name filter doesn't apply to nested documents)
5176
- // set at strategy-specific level to override any inherited filters
5177
- [ctx.strategyName]: {
5178
- sourceDescriptions: true,
5179
- sourceDescriptionsDepth: ctx.currentDepth + 1,
5180
- sourceDescriptionsAncestors: ctx.ancestors,
5181
- sourceDescriptionsDereferenced: ctx.dereferenced
5182
- }
5183
- }
5184
- }
5185
- }));
5173
+ // source description document comes from, in order of preference:
5174
+ // caller-supplied refSet, parse phase, or is fetched and parsed now
5175
+ const cachedReference = parentRefSet?.find(ref => ref.uri === retrievalURI);
5176
+ let sourceDescriptionParseResult;
5177
+ let isFreshlyParsed = false;
5178
+ if (cachedReference !== undefined) {
5179
+ sourceDescriptionParseResult = cachedReference.value;
5180
+ } else if ((0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_1__.isParseResultElement)(existingParseResult)) {
5181
+ sourceDescriptionParseResult = existingParseResult;
5186
5182
  } else {
5187
- // no existing parse result - fetch, parse, and dereference
5188
- sourceDescriptionDereferenced = await (0,_index_ts__WEBPACK_IMPORTED_MODULE_8__["default"])(retrievalURI, (0,_options_util_ts__WEBPACK_IMPORTED_MODULE_7__.merge)(ctx.options, {
5183
+ sourceDescriptionParseResult = await (0,_parse_index_ts__WEBPACK_IMPORTED_MODULE_11__["default"])(retrievalURI, (0,_options_util_ts__WEBPACK_IMPORTED_MODULE_10__.merge)(ctx.options, {
5189
5184
  parse: {
5190
- mediaType: 'text/plain' // allow parser plugin detection
5191
- },
5192
- dereference: {
5193
- strategyOpts: {
5194
- // nested documents should dereference all their source descriptions
5195
- // (parent's name filter doesn't apply to nested documents)
5196
- // set at strategy-specific level to override any inherited filters
5197
- [ctx.strategyName]: {
5198
- sourceDescriptions: true,
5199
- sourceDescriptionsDepth: ctx.currentDepth + 1,
5200
- sourceDescriptionsAncestors: ctx.ancestors,
5201
- sourceDescriptionsDereferenced: ctx.dereferenced
5202
- }
5203
- }
5204
- }
5185
+ mediaType: 'text/plain'
5186
+ } // allow parser plugin detection
5205
5187
  }));
5188
+ isFreshlyParsed = true;
5189
+ }
5190
+
5191
+ // caller-supplied refSet is rooted at the entry document, but strategies dereference
5192
+ // the root of the refSet; re-root its references at the source description
5193
+ if (parentRefSet !== null) {
5194
+ refSet = new _ReferenceSet_ts__WEBPACK_IMPORTED_MODULE_9__["default"]({
5195
+ refs: [new _Reference_ts__WEBPACK_IMPORTED_MODULE_8__["default"]({
5196
+ uri: retrievalURI,
5197
+ value: sourceDescriptionParseResult
5198
+ }), ...parentRefSet.refs.map(ref => new _Reference_ts__WEBPACK_IMPORTED_MODULE_8__["default"]({
5199
+ ...ref,
5200
+ refSet: undefined
5201
+ }))]
5202
+ });
5206
5203
  }
5204
+ const sourceDescriptionDereferenced = await (0,_index_ts__WEBPACK_IMPORTED_MODULE_12__.dereferenceApiDOM)(sourceDescriptionParseResult, (0,_options_util_ts__WEBPACK_IMPORTED_MODULE_10__.merge)((0,ramda__WEBPACK_IMPORTED_MODULE_6__["default"])(['dereference', 'refSet'], refSet, ctx.options), {
5205
+ parse: {
5206
+ mediaType: 'text/plain' // allow dereference strategy detection via ApiDOM inspection
5207
+ },
5208
+ resolve: {
5209
+ baseURI: retrievalURI
5210
+ },
5211
+ dereference: {
5212
+ // freshly parsed document not reported to any refSet can be dereferenced in mutable mode
5213
+ immutable: ctx.options.dereference.immutable && !(isFreshlyParsed && parentRefSet === null),
5214
+ strategyOpts: {
5215
+ // nested documents should dereference all their source descriptions
5216
+ // (parent's name filter doesn't apply to nested documents)
5217
+ // set at strategy-specific level to override any inherited filters
5218
+ [ctx.strategyName]: {
5219
+ sourceDescriptions: true,
5220
+ sourceDescriptionsDepth: ctx.currentDepth + 1,
5221
+ sourceDescriptionsAncestors: ctx.ancestors,
5222
+ sourceDescriptionsDereferenced: ctx.dereferenced
5223
+ }
5224
+ }
5225
+ }
5226
+ }));
5207
5227
 
5208
5228
  // merge dereferenced result into our parse result
5209
5229
  for (const item of sourceDescriptionDereferenced) {
@@ -5216,16 +5236,23 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
5216
5236
  annotation.classes.push('error');
5217
5237
  parseResult.push(annotation);
5218
5238
  return parseResult;
5239
+ } finally {
5240
+ // report documents reached through the source description to the caller-supplied refSet,
5241
+ // even when dereferencing it failed
5242
+ if (parentRefSet !== null && refSet !== null) {
5243
+ parentRefSet.merge(refSet);
5244
+ parentRefSet.circular ||= refSet.circular;
5245
+ }
5219
5246
  }
5220
5247
 
5221
5248
  // register dereferenced document for later references to it
5222
5249
  const {
5223
5250
  api: sourceDescriptionAPI
5224
5251
  } = parseResult;
5225
- for (const documentURI of (0,_util_ts__WEBPACK_IMPORTED_MODULE_9__.arazzoDocumentURIs)(retrievalURI, sourceDescriptionAPI)) {
5252
+ for (const documentURI of (0,_util_ts__WEBPACK_IMPORTED_MODULE_13__.arazzoDocumentURIs)(retrievalURI, sourceDescriptionAPI)) {
5226
5253
  ctx.dereferenced.set(documentURI, parseResult);
5227
5254
  }
5228
- ;(0,_util_ts__WEBPACK_IMPORTED_MODULE_9__.validateSourceDescriptionAPI)(parseResult, sourceDescription, sourceDescriptionAPI, retrievalURI);
5255
+ ;(0,_util_ts__WEBPACK_IMPORTED_MODULE_13__.validateSourceDescriptionAPI)(parseResult, sourceDescription, sourceDescriptionAPI, retrievalURI);
5229
5256
  return parseResult;
5230
5257
  }
5231
5258
 
@@ -5278,7 +5305,7 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
5278
5305
  * @public
5279
5306
  */
5280
5307
  async function dereferenceSourceDescriptions(parseResult, parseResultRetrievalURI, options, strategyName = 'arazzo-1') {
5281
- const retrievalURI = _util_url_ts__WEBPACK_IMPORTED_MODULE_6__.sanitize(_util_url_ts__WEBPACK_IMPORTED_MODULE_6__.stripHash(parseResultRetrievalURI));
5308
+ const retrievalURI = _util_url_ts__WEBPACK_IMPORTED_MODULE_7__.sanitize(_util_url_ts__WEBPACK_IMPORTED_MODULE_7__.stripHash(parseResultRetrievalURI));
5282
5309
  const results = [];
5283
5310
 
5284
5311
  // get API from dereferenced parse result
@@ -5309,7 +5336,7 @@ async function dereferenceSourceDescriptions(parseResult, parseResultRetrievalUR
5309
5336
  const currentDepth = sharedOpts.sourceDescriptionsDepth ?? 0;
5310
5337
  const dereferenced = sharedOpts.sourceDescriptionsDereferenced ?? new Map();
5311
5338
  // current file is an ancestor of everything dereferenced below
5312
- const ancestors = [...(sharedOpts.sourceDescriptionsAncestors ?? []), ...(0,_util_ts__WEBPACK_IMPORTED_MODULE_9__.arazzoDocumentURIs)(retrievalURI, api)];
5339
+ const ancestors = [...(sharedOpts.sourceDescriptionsAncestors ?? []), ...(0,_util_ts__WEBPACK_IMPORTED_MODULE_13__.arazzoDocumentURIs)(retrievalURI, api)];
5313
5340
  if (currentDepth >= maxDepth) {
5314
5341
  const annotation = new _speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_2__["default"](`Maximum dereference depth of ${maxDepth} has been exceeded by file "${retrievalURI}"`);
5315
5342
  annotation.classes.push('error');
@@ -5319,7 +5346,7 @@ async function dereferenceSourceDescriptions(parseResult, parseResultRetrievalUR
5319
5346
  }
5320
5347
  const ctx = {
5321
5348
  // base URI for resolving source description URLs honors Arazzo `$self` field
5322
- baseURI: (0,_util_ts__WEBPACK_IMPORTED_MODULE_9__.resolveArazzo$selfField)(retrievalURI, api),
5349
+ baseURI: (0,_util_ts__WEBPACK_IMPORTED_MODULE_13__.resolveArazzo$selfField)(retrievalURI, api),
5323
5350
  options,
5324
5351
  strategyName,
5325
5352
  currentDepth,
@@ -5771,34 +5798,38 @@ class Arazzo1DereferenceVisitor {
5771
5798
  return;
5772
5799
  }
5773
5800
  const runtimeExpression = (0,_speclynx_apidom_core__WEBPACK_IMPORTED_MODULE_6__["default"])(referencingElement.reference);
5774
-
5775
- // parse the runtime expression
5776
- const {
5777
- result,
5778
- tree
5779
- } = (0,_swaggerexpert_arazzo_runtime_expression__WEBPACK_IMPORTED_MODULE_15__.parse)(runtimeExpression);
5780
- if (!result.success) {
5781
- throw new _speclynx_apidom_error__WEBPACK_IMPORTED_MODULE_8__["default"](`Invalid Reusable Object reference format: "${runtimeExpression}"`, {
5782
- runtimeExpression
5783
- });
5784
- }
5785
-
5786
- // ReusableElement can only reference components
5787
- if (tree.type !== 'ComponentsExpression') {
5788
- throw new _speclynx_apidom_error__WEBPACK_IMPORTED_MODULE_8__["default"](`Reusable Object reference "${runtimeExpression}" must be a components expression`, {
5789
- runtimeExpression
5790
- });
5791
- }
5792
-
5793
- // evaluate runtime expression as JSON Pointer to get the referenced element
5794
- const jsonPointer = (0,_speclynx_apidom_json_pointer__WEBPACK_IMPORTED_MODULE_11__.compile)(['components', tree.componentType, tree.componentName]);
5795
5801
  let referencedElement;
5796
5802
  try {
5797
- referencedElement = (0,_speclynx_apidom_json_pointer__WEBPACK_IMPORTED_MODULE_10__.evaluate)(this.reference.value.result, jsonPointer);
5798
- } catch {
5799
- throw new _speclynx_apidom_error__WEBPACK_IMPORTED_MODULE_8__["default"](`Reusable Object reference "${runtimeExpression}" cannot be resolved`, {
5800
- runtimeExpression
5801
- });
5803
+ // parse the runtime expression
5804
+ const {
5805
+ result,
5806
+ tree
5807
+ } = (0,_swaggerexpert_arazzo_runtime_expression__WEBPACK_IMPORTED_MODULE_15__.parse)(runtimeExpression);
5808
+ if (!result.success) {
5809
+ throw new _speclynx_apidom_error__WEBPACK_IMPORTED_MODULE_8__["default"](`Invalid Reusable Object reference format: "${runtimeExpression}"`, {
5810
+ runtimeExpression
5811
+ });
5812
+ }
5813
+
5814
+ // ReusableElement can only reference components
5815
+ if (tree.type !== 'ComponentsExpression') {
5816
+ throw new _speclynx_apidom_error__WEBPACK_IMPORTED_MODULE_8__["default"](`Reusable Object reference "${runtimeExpression}" must be a components expression`, {
5817
+ runtimeExpression
5818
+ });
5819
+ }
5820
+
5821
+ // evaluate runtime expression as JSON Pointer to get the referenced element
5822
+ const jsonPointer = (0,_speclynx_apidom_json_pointer__WEBPACK_IMPORTED_MODULE_11__.compile)(['components', tree.componentType, tree.componentName]);
5823
+ try {
5824
+ referencedElement = (0,_speclynx_apidom_json_pointer__WEBPACK_IMPORTED_MODULE_10__.evaluate)(this.reference.value.result, jsonPointer);
5825
+ } catch {
5826
+ throw new _speclynx_apidom_error__WEBPACK_IMPORTED_MODULE_8__["default"](`Reusable Object reference "${runtimeExpression}" cannot be resolved`, {
5827
+ runtimeExpression
5828
+ });
5829
+ }
5830
+ } catch (error) {
5831
+ this.handleError(`Error while dereferencing Reusable Object. Cannot resolve reference "${runtimeExpression}": ${error.message}`, error, referencingElement, 'reference', runtimeExpression, path);
5832
+ return;
5802
5833
  }
5803
5834
 
5804
5835
  /**