@speclynx/apidom-reference 4.7.1 → 4.9.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 +10 -0
  2. package/dist/apidom-reference.browser.js +148 -36
  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 +12 -3
  8. package/src/dereference/strategies/arazzo-1/visitor.mjs +12 -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 +20 -5
  12. package/src/dereference/strategies/asyncapi-2/visitor.mjs +20 -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 +28 -7
  16. package/src/dereference/strategies/openapi-2/visitor.mjs +28 -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 +20 -5
  20. package/src/dereference/strategies/openapi-3-0/visitor.mjs +20 -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 +25 -7
  24. package/src/dereference/strategies/openapi-3-1/visitor.mjs +25 -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.1",
3
+ "version": "4.9.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.1",
283
- "@speclynx/apidom-datamodel": "4.7.1",
284
- "@speclynx/apidom-error": "4.7.1",
285
- "@speclynx/apidom-json-pointer": "4.7.1",
286
- "@speclynx/apidom-ns-arazzo-1": "4.7.1",
287
- "@speclynx/apidom-ns-asyncapi-2": "4.7.1",
288
- "@speclynx/apidom-ns-json-schema-2020-12": "4.7.1",
289
- "@speclynx/apidom-ns-openapi-2": "4.7.1",
290
- "@speclynx/apidom-ns-openapi-3-0": "4.7.1",
291
- "@speclynx/apidom-ns-openapi-3-1": "4.7.1",
292
- "@speclynx/apidom-ns-overlay-1": "4.7.1",
293
- "@speclynx/apidom-parser-adapter-arazzo-json-1": "4.7.1",
294
- "@speclynx/apidom-parser-adapter-arazzo-yaml-1": "4.7.1",
295
- "@speclynx/apidom-parser-adapter-asyncapi-json-2": "4.7.1",
296
- "@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "4.7.1",
297
- "@speclynx/apidom-parser-adapter-json": "4.7.1",
298
- "@speclynx/apidom-parser-adapter-openapi-json-2": "4.7.1",
299
- "@speclynx/apidom-parser-adapter-openapi-json-3-0": "4.7.1",
300
- "@speclynx/apidom-parser-adapter-openapi-json-3-1": "4.7.1",
301
- "@speclynx/apidom-parser-adapter-openapi-yaml-2": "4.7.1",
302
- "@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "4.7.1",
303
- "@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "4.7.1",
304
- "@speclynx/apidom-parser-adapter-overlay-json-1": "4.7.1",
305
- "@speclynx/apidom-parser-adapter-overlay-yaml-1": "4.7.1",
306
- "@speclynx/apidom-parser-adapter-yaml-1-2": "4.7.1",
307
- "@speclynx/apidom-traverse": "4.7.1",
282
+ "@speclynx/apidom-core": "4.9.0",
283
+ "@speclynx/apidom-datamodel": "4.9.0",
284
+ "@speclynx/apidom-error": "4.9.0",
285
+ "@speclynx/apidom-json-pointer": "4.9.0",
286
+ "@speclynx/apidom-ns-arazzo-1": "4.9.0",
287
+ "@speclynx/apidom-ns-asyncapi-2": "4.9.0",
288
+ "@speclynx/apidom-ns-json-schema-2020-12": "4.9.0",
289
+ "@speclynx/apidom-ns-openapi-2": "4.9.0",
290
+ "@speclynx/apidom-ns-openapi-3-0": "4.9.0",
291
+ "@speclynx/apidom-ns-openapi-3-1": "4.9.0",
292
+ "@speclynx/apidom-ns-overlay-1": "4.9.0",
293
+ "@speclynx/apidom-parser-adapter-arazzo-json-1": "4.9.0",
294
+ "@speclynx/apidom-parser-adapter-arazzo-yaml-1": "4.9.0",
295
+ "@speclynx/apidom-parser-adapter-asyncapi-json-2": "4.9.0",
296
+ "@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "4.9.0",
297
+ "@speclynx/apidom-parser-adapter-json": "4.9.0",
298
+ "@speclynx/apidom-parser-adapter-openapi-json-2": "4.9.0",
299
+ "@speclynx/apidom-parser-adapter-openapi-json-3-0": "4.9.0",
300
+ "@speclynx/apidom-parser-adapter-openapi-json-3-1": "4.9.0",
301
+ "@speclynx/apidom-parser-adapter-openapi-yaml-2": "4.9.0",
302
+ "@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "4.9.0",
303
+ "@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "4.9.0",
304
+ "@speclynx/apidom-parser-adapter-overlay-json-1": "4.9.0",
305
+ "@speclynx/apidom-parser-adapter-overlay-yaml-1": "4.9.0",
306
+ "@speclynx/apidom-parser-adapter-yaml-1-2": "4.9.0",
307
+ "@speclynx/apidom-traverse": "4.9.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": "ee7c6fc79ed7af3550290c8fb76557967433167e"
329
+ "gitHead": "918b3a7102bc7a8991b0fe1b92aeeb0aaeb2b30f"
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();
@@ -443,17 +446,23 @@ class Arazzo1DereferenceVisitor {
443
446
  */
444
447
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
445
448
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
446
- 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
+
447
454
  // append referencing reference to ancestors lineage
448
455
  directAncestors.add(referencingElement);
449
456
  const visitor = new Arazzo1DereferenceVisitor({
450
457
  reference,
451
458
  indirections: [...this.indirections],
452
459
  options: this.options,
460
+ visited: this.visited,
453
461
  ancestors: ancestorsLineage
454
462
  });
455
463
  referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
456
- mutable: true
464
+ mutable: true,
465
+ skipVisited: true
457
466
  });
458
467
 
459
468
  // remove referencing reference from ancestors lineage
@@ -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();
@@ -436,17 +439,23 @@ class Arazzo1DereferenceVisitor {
436
439
  */
437
440
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
438
441
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
439
- 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
+
440
447
  // append referencing reference to ancestors lineage
441
448
  directAncestors.add(referencingElement);
442
449
  const visitor = new Arazzo1DereferenceVisitor({
443
450
  reference,
444
451
  indirections: [...this.indirections],
445
452
  options: this.options,
453
+ visited: this.visited,
446
454
  ancestors: ancestorsLineage
447
455
  });
448
456
  referencedElement = await traverseAsync(referencedElement, visitor, {
449
- mutable: true
457
+ mutable: true,
458
+ skipVisited: true
450
459
  });
451
460
 
452
461
  // remove referencing reference from ancestors lineage
@@ -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();
@@ -297,7 +300,11 @@ class AsyncAPI2DereferenceVisitor {
297
300
  */
298
301
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
299
302
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
300
- 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
+
301
308
  // append referencing reference to ancestors lineage
302
309
  directAncestors.add(referencingElement);
303
310
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -305,10 +312,12 @@ class AsyncAPI2DereferenceVisitor {
305
312
  indirections: [...this.indirections],
306
313
  options: this.options,
307
314
  refractCache: this.refractCache,
315
+ visited: this.visited,
308
316
  ancestors: ancestorsLineage
309
317
  });
310
318
  referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
311
- mutable: true
319
+ mutable: true,
320
+ skipVisited: true
312
321
  });
313
322
 
314
323
  // remove referencing reference from ancestors lineage
@@ -449,7 +458,11 @@ class AsyncAPI2DereferenceVisitor {
449
458
  */
450
459
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
451
460
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
452
- 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
+
453
466
  // append referencing reference to ancestors lineage
454
467
  directAncestors.add(referencingElement);
455
468
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -457,10 +470,12 @@ class AsyncAPI2DereferenceVisitor {
457
470
  indirections: [...this.indirections],
458
471
  options: this.options,
459
472
  refractCache: this.refractCache,
473
+ visited: this.visited,
460
474
  ancestors: ancestorsLineage
461
475
  });
462
476
  referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
463
- mutable: true
477
+ mutable: true,
478
+ skipVisited: true
464
479
  });
465
480
 
466
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();
@@ -290,7 +293,11 @@ class AsyncAPI2DereferenceVisitor {
290
293
  */
291
294
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
292
295
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
293
- 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
+
294
301
  // append referencing reference to ancestors lineage
295
302
  directAncestors.add(referencingElement);
296
303
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -298,10 +305,12 @@ class AsyncAPI2DereferenceVisitor {
298
305
  indirections: [...this.indirections],
299
306
  options: this.options,
300
307
  refractCache: this.refractCache,
308
+ visited: this.visited,
301
309
  ancestors: ancestorsLineage
302
310
  });
303
311
  referencedElement = await traverseAsync(referencedElement, visitor, {
304
- mutable: true
312
+ mutable: true,
313
+ skipVisited: true
305
314
  });
306
315
 
307
316
  // remove referencing reference from ancestors lineage
@@ -442,7 +451,11 @@ class AsyncAPI2DereferenceVisitor {
442
451
  */
443
452
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
444
453
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
445
- 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
+
446
459
  // append referencing reference to ancestors lineage
447
460
  directAncestors.add(referencingElement);
448
461
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -450,10 +463,12 @@ class AsyncAPI2DereferenceVisitor {
450
463
  indirections: [...this.indirections],
451
464
  options: this.options,
452
465
  refractCache: this.refractCache,
466
+ visited: this.visited,
453
467
  ancestors: ancestorsLineage
454
468
  });
455
469
  referencedElement = await traverseAsync(referencedElement, visitor, {
456
- mutable: true
470
+ mutable: true,
471
+ skipVisited: true
457
472
  });
458
473
 
459
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
  /**
@@ -30,6 +30,7 @@ class OpenAPI2DereferenceVisitor {
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 OpenAPI2DereferenceVisitor {
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.refractCache = refractCache;
51
53
  this.ancestors = new _util.AncestorLineage(...ancestors);
54
+ this.visited = visited;
52
55
  }
53
56
  toAncestorLineage(path) {
54
57
  const ancestorNodes = path.getAncestorNodes();
@@ -297,7 +300,11 @@ class OpenAPI2DereferenceVisitor {
297
300
  */
298
301
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
299
302
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
300
- if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isReferenceElement)(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
303
+ if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
304
+ if (shouldDetectCircular) {
305
+ this.visited.add(referencedElement);
306
+ }
307
+
301
308
  // append referencing reference to ancestors lineage
302
309
  directAncestors.add(referencingElement);
303
310
  const visitor = new OpenAPI2DereferenceVisitor({
@@ -305,10 +312,12 @@ class OpenAPI2DereferenceVisitor {
305
312
  indirections: [...this.indirections],
306
313
  options: this.options,
307
314
  refractCache: this.refractCache,
315
+ visited: this.visited,
308
316
  ancestors: ancestorsLineage
309
317
  });
310
318
  referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
311
- mutable: true
319
+ mutable: true,
320
+ skipVisited: true
312
321
  });
313
322
  directAncestors.delete(referencingElement);
314
323
  }
@@ -433,7 +442,11 @@ class OpenAPI2DereferenceVisitor {
433
442
  */
434
443
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
435
444
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
436
- if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isPathItemElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
445
+ if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isPathItemElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
446
+ if (shouldDetectCircular) {
447
+ this.visited.add(referencedElement);
448
+ }
449
+
437
450
  // append referencing reference to ancestors lineage
438
451
  directAncestors.add(referencingElement);
439
452
  const visitor = new OpenAPI2DereferenceVisitor({
@@ -441,10 +454,12 @@ class OpenAPI2DereferenceVisitor {
441
454
  indirections: [...this.indirections],
442
455
  options: this.options,
443
456
  refractCache: this.refractCache,
457
+ visited: this.visited,
444
458
  ancestors: ancestorsLineage
445
459
  });
446
460
  referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
447
- mutable: true
461
+ mutable: true,
462
+ skipVisited: true
448
463
  });
449
464
 
450
465
  // remove referencing reference from ancestors lineage
@@ -588,7 +603,11 @@ class OpenAPI2DereferenceVisitor {
588
603
  */
589
604
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
590
605
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
591
- if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isJSONReferenceElement)(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
606
+ if ((isExternalReference || isNonEntryDocument || (0, _apidomNsOpenapi.isJSONReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
607
+ if (shouldDetectCircular) {
608
+ this.visited.add(referencedElement);
609
+ }
610
+
592
611
  // append referencing reference to ancestors lineage
593
612
  directAncestors.add(referencingElement);
594
613
  const visitor = new OpenAPI2DereferenceVisitor({
@@ -596,10 +615,12 @@ class OpenAPI2DereferenceVisitor {
596
615
  indirections: [...this.indirections],
597
616
  options: this.options,
598
617
  refractCache: this.refractCache,
618
+ visited: this.visited,
599
619
  ancestors: ancestorsLineage
600
620
  });
601
621
  referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
602
- mutable: true
622
+ mutable: true,
623
+ skipVisited: true
603
624
  });
604
625
 
605
626
  // remove referencing reference from ancestors lineage
@@ -23,6 +23,7 @@ class OpenAPI2DereferenceVisitor {
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 OpenAPI2DereferenceVisitor {
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.refractCache = refractCache;
44
46
  this.ancestors = new AncestorLineage(...ancestors);
47
+ this.visited = visited;
45
48
  }
46
49
  toAncestorLineage(path) {
47
50
  const ancestorNodes = path.getAncestorNodes();
@@ -290,7 +293,11 @@ class OpenAPI2DereferenceVisitor {
290
293
  */
291
294
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
292
295
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
293
- 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
+
294
301
  // append referencing reference to ancestors lineage
295
302
  directAncestors.add(referencingElement);
296
303
  const visitor = new OpenAPI2DereferenceVisitor({
@@ -298,10 +305,12 @@ class OpenAPI2DereferenceVisitor {
298
305
  indirections: [...this.indirections],
299
306
  options: this.options,
300
307
  refractCache: this.refractCache,
308
+ visited: this.visited,
301
309
  ancestors: ancestorsLineage
302
310
  });
303
311
  referencedElement = await traverseAsync(referencedElement, visitor, {
304
- mutable: true
312
+ mutable: true,
313
+ skipVisited: true
305
314
  });
306
315
  directAncestors.delete(referencingElement);
307
316
  }
@@ -426,7 +435,11 @@ class OpenAPI2DereferenceVisitor {
426
435
  */
427
436
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
428
437
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
429
- if ((isExternalReference || isNonEntryDocument || isPathItemElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
438
+ if ((isExternalReference || isNonEntryDocument || isPathItemElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
439
+ if (shouldDetectCircular) {
440
+ this.visited.add(referencedElement);
441
+ }
442
+
430
443
  // append referencing reference to ancestors lineage
431
444
  directAncestors.add(referencingElement);
432
445
  const visitor = new OpenAPI2DereferenceVisitor({
@@ -434,10 +447,12 @@ class OpenAPI2DereferenceVisitor {
434
447
  indirections: [...this.indirections],
435
448
  options: this.options,
436
449
  refractCache: this.refractCache,
450
+ visited: this.visited,
437
451
  ancestors: ancestorsLineage
438
452
  });
439
453
  referencedElement = await traverseAsync(referencedElement, visitor, {
440
- mutable: true
454
+ mutable: true,
455
+ skipVisited: true
441
456
  });
442
457
 
443
458
  // remove referencing reference from ancestors lineage
@@ -581,7 +596,11 @@ class OpenAPI2DereferenceVisitor {
581
596
  */
582
597
  const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
583
598
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
584
- if ((isExternalReference || isNonEntryDocument || isJSONReferenceElement(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
599
+ if ((isExternalReference || isNonEntryDocument || isJSONReferenceElement(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
600
+ if (shouldDetectCircular) {
601
+ this.visited.add(referencedElement);
602
+ }
603
+
585
604
  // append referencing reference to ancestors lineage
586
605
  directAncestors.add(referencingElement);
587
606
  const visitor = new OpenAPI2DereferenceVisitor({
@@ -589,10 +608,12 @@ class OpenAPI2DereferenceVisitor {
589
608
  indirections: [...this.indirections],
590
609
  options: this.options,
591
610
  refractCache: this.refractCache,
611
+ visited: this.visited,
592
612
  ancestors: ancestorsLineage
593
613
  });
594
614
  referencedElement = await traverseAsync(referencedElement, visitor, {
595
- mutable: true
615
+ mutable: true,
616
+ skipVisited: true
596
617
  });
597
618
 
598
619
  // remove referencing reference from ancestors lineage