@speclynx/apidom-reference 4.7.0 → 4.8.0

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.
Files changed (29) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/apidom-reference.browser.js +167 -37
  3. package/dist/apidom-reference.browser.min.js +1 -1
  4. package/package.json +28 -28
  5. package/src/dereference/strategies/arazzo-1/index.cjs +5 -1
  6. package/src/dereference/strategies/arazzo-1/index.mjs +5 -1
  7. package/src/dereference/strategies/arazzo-1/visitor.cjs +15 -3
  8. package/src/dereference/strategies/arazzo-1/visitor.mjs +15 -3
  9. package/src/dereference/strategies/asyncapi-2/index.cjs +5 -1
  10. package/src/dereference/strategies/asyncapi-2/index.mjs +5 -1
  11. package/src/dereference/strategies/asyncapi-2/visitor.cjs +23 -5
  12. package/src/dereference/strategies/asyncapi-2/visitor.mjs +23 -5
  13. package/src/dereference/strategies/openapi-2/index.cjs +5 -1
  14. package/src/dereference/strategies/openapi-2/index.mjs +5 -1
  15. package/src/dereference/strategies/openapi-2/visitor.cjs +33 -7
  16. package/src/dereference/strategies/openapi-2/visitor.mjs +33 -7
  17. package/src/dereference/strategies/openapi-3-0/index.cjs +5 -1
  18. package/src/dereference/strategies/openapi-3-0/index.mjs +5 -1
  19. package/src/dereference/strategies/openapi-3-0/visitor.cjs +23 -5
  20. package/src/dereference/strategies/openapi-3-0/visitor.mjs +23 -5
  21. package/src/dereference/strategies/openapi-3-1/index.cjs +5 -1
  22. package/src/dereference/strategies/openapi-3-1/index.mjs +5 -1
  23. package/src/dereference/strategies/openapi-3-1/visitor.cjs +29 -7
  24. package/src/dereference/strategies/openapi-3-1/visitor.mjs +29 -7
  25. package/types/dereference/strategies/arazzo-1/visitor.d.ts +3 -1
  26. package/types/dereference/strategies/asyncapi-2/visitor.d.ts +3 -1
  27. package/types/dereference/strategies/openapi-2/visitor.d.ts +3 -1
  28. package/types/dereference/strategies/openapi-3-0/visitor.d.ts +3 -1
  29. package/types/dereference/strategies/openapi-3-1/visitor.d.ts +3 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@speclynx/apidom-reference",
3
- "version": "4.7.0",
3
+ "version": "4.8.0",
4
4
  "description": "Advanced algorithms for semantic ApiDOM manipulations like dereferencing or resolution.",
5
5
  "keywords": [
6
6
  "apidom",
@@ -279,32 +279,32 @@
279
279
  "license": "Apache-2.0",
280
280
  "dependencies": {
281
281
  "@babel/runtime-corejs3": "^7.28.4",
282
- "@speclynx/apidom-core": "4.7.0",
283
- "@speclynx/apidom-datamodel": "4.7.0",
284
- "@speclynx/apidom-error": "4.7.0",
285
- "@speclynx/apidom-json-pointer": "4.7.0",
286
- "@speclynx/apidom-ns-arazzo-1": "4.7.0",
287
- "@speclynx/apidom-ns-asyncapi-2": "4.7.0",
288
- "@speclynx/apidom-ns-json-schema-2020-12": "4.7.0",
289
- "@speclynx/apidom-ns-openapi-2": "4.7.0",
290
- "@speclynx/apidom-ns-openapi-3-0": "4.7.0",
291
- "@speclynx/apidom-ns-openapi-3-1": "4.7.0",
292
- "@speclynx/apidom-ns-overlay-1": "4.7.0",
293
- "@speclynx/apidom-parser-adapter-arazzo-json-1": "4.7.0",
294
- "@speclynx/apidom-parser-adapter-arazzo-yaml-1": "4.7.0",
295
- "@speclynx/apidom-parser-adapter-asyncapi-json-2": "4.7.0",
296
- "@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "4.7.0",
297
- "@speclynx/apidom-parser-adapter-json": "4.7.0",
298
- "@speclynx/apidom-parser-adapter-openapi-json-2": "4.7.0",
299
- "@speclynx/apidom-parser-adapter-openapi-json-3-0": "4.7.0",
300
- "@speclynx/apidom-parser-adapter-openapi-json-3-1": "4.7.0",
301
- "@speclynx/apidom-parser-adapter-openapi-yaml-2": "4.7.0",
302
- "@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "4.7.0",
303
- "@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "4.7.0",
304
- "@speclynx/apidom-parser-adapter-overlay-json-1": "4.7.0",
305
- "@speclynx/apidom-parser-adapter-overlay-yaml-1": "4.7.0",
306
- "@speclynx/apidom-parser-adapter-yaml-1-2": "4.7.0",
307
- "@speclynx/apidom-traverse": "4.7.0",
282
+ "@speclynx/apidom-core": "4.8.0",
283
+ "@speclynx/apidom-datamodel": "4.8.0",
284
+ "@speclynx/apidom-error": "4.8.0",
285
+ "@speclynx/apidom-json-pointer": "4.8.0",
286
+ "@speclynx/apidom-ns-arazzo-1": "4.8.0",
287
+ "@speclynx/apidom-ns-asyncapi-2": "4.8.0",
288
+ "@speclynx/apidom-ns-json-schema-2020-12": "4.8.0",
289
+ "@speclynx/apidom-ns-openapi-2": "4.8.0",
290
+ "@speclynx/apidom-ns-openapi-3-0": "4.8.0",
291
+ "@speclynx/apidom-ns-openapi-3-1": "4.8.0",
292
+ "@speclynx/apidom-ns-overlay-1": "4.8.0",
293
+ "@speclynx/apidom-parser-adapter-arazzo-json-1": "4.8.0",
294
+ "@speclynx/apidom-parser-adapter-arazzo-yaml-1": "4.8.0",
295
+ "@speclynx/apidom-parser-adapter-asyncapi-json-2": "4.8.0",
296
+ "@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "4.8.0",
297
+ "@speclynx/apidom-parser-adapter-json": "4.8.0",
298
+ "@speclynx/apidom-parser-adapter-openapi-json-2": "4.8.0",
299
+ "@speclynx/apidom-parser-adapter-openapi-json-3-0": "4.8.0",
300
+ "@speclynx/apidom-parser-adapter-openapi-json-3-1": "4.8.0",
301
+ "@speclynx/apidom-parser-adapter-openapi-yaml-2": "4.8.0",
302
+ "@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "4.8.0",
303
+ "@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "4.8.0",
304
+ "@speclynx/apidom-parser-adapter-overlay-json-1": "4.8.0",
305
+ "@speclynx/apidom-parser-adapter-overlay-yaml-1": "4.8.0",
306
+ "@speclynx/apidom-parser-adapter-yaml-1-2": "4.8.0",
307
+ "@speclynx/apidom-traverse": "4.8.0",
308
308
  "@swaggerexpert/arazzo-runtime-expression": "^2.0.3",
309
309
  "axios": "^1.15.0",
310
310
  "picomatch": "^4.0.4",
@@ -326,5 +326,5 @@
326
326
  "README.md",
327
327
  "CHANGELOG.md"
328
328
  ],
329
- "gitHead": "5afe50e432d90185f514e91bb35319aa4e23ba7b"
329
+ "gitHead": "ef77101edfc6567d9b265bc30708bf2c65fd1d15"
330
330
  }
@@ -68,12 +68,16 @@ class Arazzo1DereferenceStrategy extends _DereferenceStrategy.default {
68
68
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
69
69
  refSet = mutableRefSet;
70
70
  }
71
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
71
72
  const visitor = new _visitor.default({
72
73
  reference,
73
74
  options
74
75
  });
75
76
  const dereferencedElement = await (0, _apidomTraverse.traverseAsync)(refSet.rootRef.value, visitor, {
76
- mutable: true
77
+ mutable: true,
78
+ ...(shouldDetectCircular && {
79
+ skipVisited: true
80
+ })
77
81
  });
78
82
 
79
83
  /**
@@ -56,12 +56,16 @@ class Arazzo1DereferenceStrategy extends DereferenceStrategy {
56
56
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
57
57
  refSet = mutableRefSet;
58
58
  }
59
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
59
60
  const visitor = new Arazzo1DereferenceVisitor({
60
61
  reference,
61
62
  options
62
63
  });
63
64
  const dereferencedElement = await traverseAsync(refSet.rootRef.value, visitor, {
64
- mutable: true
65
+ mutable: true,
66
+ ...(shouldDetectCircular && {
67
+ skipVisited: true
68
+ })
65
69
  });
66
70
 
67
71
  /**
@@ -36,6 +36,7 @@ class Arazzo1DereferenceVisitor {
36
36
  indirections;
37
37
  reference;
38
38
  options;
39
+ visited;
39
40
 
40
41
  /**
41
42
  * Tracks element ancestors across dive-deep traversal boundaries.
@@ -47,12 +48,14 @@ class Arazzo1DereferenceVisitor {
47
48
  reference,
48
49
  options,
49
50
  indirections = [],
50
- ancestors = new _util3.AncestorLineage()
51
+ ancestors = new _util3.AncestorLineage(),
52
+ visited = new WeakSet()
51
53
  }) {
52
54
  this.indirections = indirections;
53
55
  this.reference = reference;
54
56
  this.options = options;
55
57
  this.ancestors = new _util3.AncestorLineage(...ancestors);
58
+ this.visited = visited;
56
59
  }
57
60
  toAncestorLineage(path) {
58
61
  const ancestorNodes = path.getAncestorNodes();
@@ -266,6 +269,7 @@ class Arazzo1DereferenceVisitor {
266
269
  * Transclude referencing element with merged referenced element.
267
270
  */
268
271
  path.replaceWith(mergedElement);
272
+ path.skip();
269
273
  }
270
274
  async JSONSchemaElement(path) {
271
275
  const referencingElement = path.node;
@@ -426,6 +430,7 @@ class Arazzo1DereferenceVisitor {
426
430
  const replacer = this.options.dereference.strategyOpts['arazzo-1']?.circularReplacer ?? this.options.dereference.circularReplacer;
427
431
  const replacement = replacer(refElement);
428
432
  path.replaceWith(replacement);
433
+ path.skip();
429
434
  return;
430
435
  }
431
436
  }
@@ -441,17 +446,23 @@ class Arazzo1DereferenceVisitor {
441
446
  */
442
447
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
443
448
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
444
- if ((isExternalReference || isNonEntryDocument || (0, _apidomNsArazzo.isJSONSchemaElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
449
+ if ((isExternalReference || isNonEntryDocument || (0, _apidomNsArazzo.isJSONSchemaElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
450
+ if (shouldDetectCircular) {
451
+ this.visited.add(referencedElement);
452
+ }
453
+
445
454
  // append referencing reference to ancestors lineage
446
455
  directAncestors.add(referencingElement);
447
456
  const visitor = new Arazzo1DereferenceVisitor({
448
457
  reference,
449
458
  indirections: [...this.indirections],
450
459
  options: this.options,
460
+ visited: this.visited,
451
461
  ancestors: ancestorsLineage
452
462
  });
453
463
  referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
454
- mutable: true
464
+ mutable: true,
465
+ skipVisited: true
455
466
  });
456
467
 
457
468
  // remove referencing reference from ancestors lineage
@@ -469,6 +480,7 @@ class Arazzo1DereferenceVisitor {
469
480
  booleanJsonSchemaElement.meta.set('ref-origin', reference.uri);
470
481
  booleanJsonSchemaElement.meta.set('ref-type', referencingElement.element);
471
482
  path.replaceWith(booleanJsonSchemaElement);
483
+ path.skip();
472
484
  return;
473
485
  }
474
486
 
@@ -29,6 +29,7 @@ class Arazzo1DereferenceVisitor {
29
29
  indirections;
30
30
  reference;
31
31
  options;
32
+ visited;
32
33
 
33
34
  /**
34
35
  * Tracks element ancestors across dive-deep traversal boundaries.
@@ -40,12 +41,14 @@ class Arazzo1DereferenceVisitor {
40
41
  reference,
41
42
  options,
42
43
  indirections = [],
43
- ancestors = new AncestorLineage()
44
+ ancestors = new AncestorLineage(),
45
+ visited = new WeakSet()
44
46
  }) {
45
47
  this.indirections = indirections;
46
48
  this.reference = reference;
47
49
  this.options = options;
48
50
  this.ancestors = new AncestorLineage(...ancestors);
51
+ this.visited = visited;
49
52
  }
50
53
  toAncestorLineage(path) {
51
54
  const ancestorNodes = path.getAncestorNodes();
@@ -259,6 +262,7 @@ class Arazzo1DereferenceVisitor {
259
262
  * Transclude referencing element with merged referenced element.
260
263
  */
261
264
  path.replaceWith(mergedElement);
265
+ path.skip();
262
266
  }
263
267
  async JSONSchemaElement(path) {
264
268
  const referencingElement = path.node;
@@ -419,6 +423,7 @@ class Arazzo1DereferenceVisitor {
419
423
  const replacer = this.options.dereference.strategyOpts['arazzo-1']?.circularReplacer ?? this.options.dereference.circularReplacer;
420
424
  const replacement = replacer(refElement);
421
425
  path.replaceWith(replacement);
426
+ path.skip();
422
427
  return;
423
428
  }
424
429
  }
@@ -434,17 +439,23 @@ class Arazzo1DereferenceVisitor {
434
439
  */
435
440
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
436
441
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
437
- if ((isExternalReference || isNonEntryDocument || isJSONSchemaElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
442
+ if ((isExternalReference || isNonEntryDocument || isJSONSchemaElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
443
+ if (shouldDetectCircular) {
444
+ this.visited.add(referencedElement);
445
+ }
446
+
438
447
  // append referencing reference to ancestors lineage
439
448
  directAncestors.add(referencingElement);
440
449
  const visitor = new Arazzo1DereferenceVisitor({
441
450
  reference,
442
451
  indirections: [...this.indirections],
443
452
  options: this.options,
453
+ visited: this.visited,
444
454
  ancestors: ancestorsLineage
445
455
  });
446
456
  referencedElement = await traverseAsync(referencedElement, visitor, {
447
- mutable: true
457
+ mutable: true,
458
+ skipVisited: true
448
459
  });
449
460
 
450
461
  // remove referencing reference from ancestors lineage
@@ -462,6 +473,7 @@ class Arazzo1DereferenceVisitor {
462
473
  booleanJsonSchemaElement.meta.set('ref-origin', reference.uri);
463
474
  booleanJsonSchemaElement.meta.set('ref-type', referencingElement.element);
464
475
  path.replaceWith(booleanJsonSchemaElement);
476
+ path.skip();
465
477
  return;
466
478
  }
467
479
 
@@ -62,12 +62,16 @@ class AsyncAPI2DereferenceStrategy extends _DereferenceStrategy.default {
62
62
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
63
63
  refSet = mutableRefSet;
64
64
  }
65
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
65
66
  const visitor = new _visitor.default({
66
67
  reference,
67
68
  options
68
69
  });
69
70
  const dereferencedElement = await (0, _apidomTraverse.traverseAsync)(refSet.rootRef.value, visitor, {
70
- mutable: true
71
+ mutable: true,
72
+ ...(shouldDetectCircular && {
73
+ skipVisited: true
74
+ })
71
75
  });
72
76
 
73
77
  /**
@@ -55,12 +55,16 @@ class AsyncAPI2DereferenceStrategy extends DereferenceStrategy {
55
55
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
56
56
  refSet = mutableRefSet;
57
57
  }
58
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
58
59
  const visitor = new AsyncAPI2DereferenceVisitor({
59
60
  reference,
60
61
  options
61
62
  });
62
63
  const dereferencedElement = await traverseAsync(refSet.rootRef.value, visitor, {
63
- mutable: true
64
+ mutable: true,
65
+ ...(shouldDetectCircular && {
66
+ skipVisited: true
67
+ })
64
68
  });
65
69
 
66
70
  /**
@@ -30,6 +30,7 @@ class AsyncAPI2DereferenceVisitor {
30
30
  reference;
31
31
  options;
32
32
  refractCache;
33
+ visited;
33
34
 
34
35
  /**
35
36
  * Tracks element ancestors across dive-deep traversal boundaries.
@@ -42,13 +43,15 @@ class AsyncAPI2DereferenceVisitor {
42
43
  options,
43
44
  indirections = [],
44
45
  ancestors = new _util.AncestorLineage(),
45
- refractCache = new WeakMap()
46
+ refractCache = new WeakMap(),
47
+ visited = new WeakSet()
46
48
  }) {
47
49
  this.indirections = indirections;
48
50
  this.reference = reference;
49
51
  this.options = options;
50
52
  this.ancestors = new _util.AncestorLineage(...ancestors);
51
53
  this.refractCache = refractCache;
54
+ this.visited = visited;
52
55
  }
53
56
  toAncestorLineage(path) {
54
57
  const ancestorNodes = path.getAncestorNodes();
@@ -281,6 +284,7 @@ class AsyncAPI2DereferenceVisitor {
281
284
  const replacer = this.options.dereference.strategyOpts['asyncapi-2']?.circularReplacer ?? this.options.dereference.circularReplacer;
282
285
  const replacement = replacer(refElement);
283
286
  path.replaceWith(replacement);
287
+ path.skip();
284
288
  return;
285
289
  }
286
290
  }
@@ -296,7 +300,11 @@ class AsyncAPI2DereferenceVisitor {
296
300
  */
297
301
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
298
302
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
299
- if ((isExternalReference || isNonEntryDocument || (0, _apidomNsAsyncapi.isReferenceElement)(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
303
+ if ((isExternalReference || isNonEntryDocument || (0, _apidomNsAsyncapi.isReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
304
+ if (shouldDetectCircular) {
305
+ this.visited.add(referencedElement);
306
+ }
307
+
300
308
  // append referencing reference to ancestors lineage
301
309
  directAncestors.add(referencingElement);
302
310
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -304,10 +312,12 @@ class AsyncAPI2DereferenceVisitor {
304
312
  indirections: [...this.indirections],
305
313
  options: this.options,
306
314
  refractCache: this.refractCache,
315
+ visited: this.visited,
307
316
  ancestors: ancestorsLineage
308
317
  });
309
318
  referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
310
- mutable: true
319
+ mutable: true,
320
+ skipVisited: true
311
321
  });
312
322
 
313
323
  // remove referencing reference from ancestors lineage
@@ -325,6 +335,7 @@ class AsyncAPI2DereferenceVisitor {
325
335
  booleanJsonSchemaElement.meta.set('ref-origin', reference.uri);
326
336
  booleanJsonSchemaElement.meta.set('ref-type', referencingElement.element);
327
337
  path.replaceWith(booleanJsonSchemaElement);
338
+ path.skip();
328
339
  return;
329
340
  }
330
341
 
@@ -344,6 +355,7 @@ class AsyncAPI2DereferenceVisitor {
344
355
  * Transclude referencing element with merged referenced element.
345
356
  */
346
357
  path.replaceWith(mergedElement);
358
+ path.skip();
347
359
  } catch (error) {
348
360
  const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
349
361
  this.handleError(`Error while dereferencing Reference Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
@@ -446,7 +458,11 @@ class AsyncAPI2DereferenceVisitor {
446
458
  */
447
459
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
448
460
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
449
- if ((isExternalReference || isNonEntryDocument || (0, _apidomNsAsyncapi.isChannelItemElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
461
+ if ((isExternalReference || isNonEntryDocument || (0, _apidomNsAsyncapi.isChannelItemElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
462
+ if (shouldDetectCircular) {
463
+ this.visited.add(referencedElement);
464
+ }
465
+
450
466
  // append referencing reference to ancestors lineage
451
467
  directAncestors.add(referencingElement);
452
468
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -454,10 +470,12 @@ class AsyncAPI2DereferenceVisitor {
454
470
  indirections: [...this.indirections],
455
471
  options: this.options,
456
472
  refractCache: this.refractCache,
473
+ visited: this.visited,
457
474
  ancestors: ancestorsLineage
458
475
  });
459
476
  referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
460
- mutable: true
477
+ mutable: true,
478
+ skipVisited: true
461
479
  });
462
480
 
463
481
  // remove referencing reference from ancestors lineage
@@ -23,6 +23,7 @@ class AsyncAPI2DereferenceVisitor {
23
23
  reference;
24
24
  options;
25
25
  refractCache;
26
+ visited;
26
27
 
27
28
  /**
28
29
  * Tracks element ancestors across dive-deep traversal boundaries.
@@ -35,13 +36,15 @@ class AsyncAPI2DereferenceVisitor {
35
36
  options,
36
37
  indirections = [],
37
38
  ancestors = new AncestorLineage(),
38
- refractCache = new WeakMap()
39
+ refractCache = new WeakMap(),
40
+ visited = new WeakSet()
39
41
  }) {
40
42
  this.indirections = indirections;
41
43
  this.reference = reference;
42
44
  this.options = options;
43
45
  this.ancestors = new AncestorLineage(...ancestors);
44
46
  this.refractCache = refractCache;
47
+ this.visited = visited;
45
48
  }
46
49
  toAncestorLineage(path) {
47
50
  const ancestorNodes = path.getAncestorNodes();
@@ -274,6 +277,7 @@ class AsyncAPI2DereferenceVisitor {
274
277
  const replacer = this.options.dereference.strategyOpts['asyncapi-2']?.circularReplacer ?? this.options.dereference.circularReplacer;
275
278
  const replacement = replacer(refElement);
276
279
  path.replaceWith(replacement);
280
+ path.skip();
277
281
  return;
278
282
  }
279
283
  }
@@ -289,7 +293,11 @@ class AsyncAPI2DereferenceVisitor {
289
293
  */
290
294
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
291
295
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
292
- if ((isExternalReference || isNonEntryDocument || isReferenceElement(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
296
+ if ((isExternalReference || isNonEntryDocument || isReferenceElement(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
297
+ if (shouldDetectCircular) {
298
+ this.visited.add(referencedElement);
299
+ }
300
+
293
301
  // append referencing reference to ancestors lineage
294
302
  directAncestors.add(referencingElement);
295
303
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -297,10 +305,12 @@ class AsyncAPI2DereferenceVisitor {
297
305
  indirections: [...this.indirections],
298
306
  options: this.options,
299
307
  refractCache: this.refractCache,
308
+ visited: this.visited,
300
309
  ancestors: ancestorsLineage
301
310
  });
302
311
  referencedElement = await traverseAsync(referencedElement, visitor, {
303
- mutable: true
312
+ mutable: true,
313
+ skipVisited: true
304
314
  });
305
315
 
306
316
  // remove referencing reference from ancestors lineage
@@ -318,6 +328,7 @@ class AsyncAPI2DereferenceVisitor {
318
328
  booleanJsonSchemaElement.meta.set('ref-origin', reference.uri);
319
329
  booleanJsonSchemaElement.meta.set('ref-type', referencingElement.element);
320
330
  path.replaceWith(booleanJsonSchemaElement);
331
+ path.skip();
321
332
  return;
322
333
  }
323
334
 
@@ -337,6 +348,7 @@ class AsyncAPI2DereferenceVisitor {
337
348
  * Transclude referencing element with merged referenced element.
338
349
  */
339
350
  path.replaceWith(mergedElement);
351
+ path.skip();
340
352
  } catch (error) {
341
353
  const $ref = toValue(referencingElement.$ref);
342
354
  this.handleError(`Error while dereferencing Reference Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
@@ -439,7 +451,11 @@ class AsyncAPI2DereferenceVisitor {
439
451
  */
440
452
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
441
453
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
442
- if ((isExternalReference || isNonEntryDocument || isChannelItemElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
454
+ if ((isExternalReference || isNonEntryDocument || isChannelItemElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
455
+ if (shouldDetectCircular) {
456
+ this.visited.add(referencedElement);
457
+ }
458
+
443
459
  // append referencing reference to ancestors lineage
444
460
  directAncestors.add(referencingElement);
445
461
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -447,10 +463,12 @@ class AsyncAPI2DereferenceVisitor {
447
463
  indirections: [...this.indirections],
448
464
  options: this.options,
449
465
  refractCache: this.refractCache,
466
+ visited: this.visited,
450
467
  ancestors: ancestorsLineage
451
468
  });
452
469
  referencedElement = await traverseAsync(referencedElement, visitor, {
453
- mutable: true
470
+ mutable: true,
471
+ skipVisited: true
454
472
  });
455
473
 
456
474
  // remove referencing reference from ancestors lineage
@@ -62,12 +62,16 @@ class OpenAPI2DereferenceStrategy extends _DereferenceStrategy.default {
62
62
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
63
63
  refSet = mutableRefSet;
64
64
  }
65
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
65
66
  const visitor = new _visitor.default({
66
67
  reference: reference,
67
68
  options
68
69
  });
69
70
  const dereferencedElement = await (0, _apidomTraverse.traverseAsync)(refSet.rootRef.value, visitor, {
70
- mutable: true
71
+ mutable: true,
72
+ ...(shouldDetectCircular && {
73
+ skipVisited: true
74
+ })
71
75
  });
72
76
 
73
77
  /**
@@ -55,12 +55,16 @@ class OpenAPI2DereferenceStrategy extends DereferenceStrategy {
55
55
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
56
56
  refSet = mutableRefSet;
57
57
  }
58
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
58
59
  const visitor = new OpenAPI2DereferenceVisitor({
59
60
  reference: reference,
60
61
  options
61
62
  });
62
63
  const dereferencedElement = await traverseAsync(refSet.rootRef.value, visitor, {
63
- mutable: true
64
+ mutable: true,
65
+ ...(shouldDetectCircular && {
66
+ skipVisited: true
67
+ })
64
68
  });
65
69
 
66
70
  /**