@speclynx/apidom-reference 4.7.1 → 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 +6 -0
  2. package/dist/apidom-reference.browser.js +142 -32
  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/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
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
+ # [4.8.0](https://github.com/speclynx/apidom/compare/v4.7.1...v4.8.0) (2026-04-17)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **reference:** fix circular: 'replace' hanging on large specs ([7b94468](https://github.com/speclynx/apidom/commit/7b9446889e89dab869648a5e9ad9042f5faa00cc))
11
+
6
12
  ## [4.7.1](https://github.com/speclynx/apidom/compare/v4.7.0...v4.7.1) (2026-04-16)
7
13
 
8
14
  ### Bug Fixes
@@ -997,12 +997,16 @@ class Arazzo1DereferenceStrategy extends _DereferenceStrategy_ts__WEBPACK_IMPORT
997
997
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
998
998
  refSet = mutableRefSet;
999
999
  }
1000
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
1000
1001
  const visitor = new _visitor_ts__WEBPACK_IMPORTED_MODULE_9__["default"]({
1001
1002
  reference,
1002
1003
  options
1003
1004
  });
1004
1005
  const dereferencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_3__.traverseAsync)(refSet.rootRef.value, visitor, {
1005
- mutable: true
1006
+ mutable: true,
1007
+ ...(shouldDetectCircular && {
1008
+ skipVisited: true
1009
+ })
1006
1010
  });
1007
1011
 
1008
1012
  /**
@@ -1429,6 +1433,7 @@ class Arazzo1DereferenceVisitor {
1429
1433
  indirections;
1430
1434
  reference;
1431
1435
  options;
1436
+ visited;
1432
1437
 
1433
1438
  /**
1434
1439
  * Tracks element ancestors across dive-deep traversal boundaries.
@@ -1440,12 +1445,14 @@ class Arazzo1DereferenceVisitor {
1440
1445
  reference,
1441
1446
  options,
1442
1447
  indirections = [],
1443
- ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_26__.AncestorLineage()
1448
+ ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_26__.AncestorLineage(),
1449
+ visited = new WeakSet()
1444
1450
  }) {
1445
1451
  this.indirections = indirections;
1446
1452
  this.reference = reference;
1447
1453
  this.options = options;
1448
1454
  this.ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_26__.AncestorLineage(...ancestors);
1455
+ this.visited = visited;
1449
1456
  }
1450
1457
  toAncestorLineage(path) {
1451
1458
  const ancestorNodes = path.getAncestorNodes();
@@ -1836,17 +1843,23 @@ class Arazzo1DereferenceVisitor {
1836
1843
  */
1837
1844
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_22__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
1838
1845
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
1839
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_arazzo_1__WEBPACK_IMPORTED_MODULE_11__.isJSONSchemaElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_2__.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
1846
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_arazzo_1__WEBPACK_IMPORTED_MODULE_11__.isJSONSchemaElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_2__.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
1847
+ if (shouldDetectCircular) {
1848
+ this.visited.add(referencedElement);
1849
+ }
1850
+
1840
1851
  // append referencing reference to ancestors lineage
1841
1852
  directAncestors.add(referencingElement);
1842
1853
  const visitor = new Arazzo1DereferenceVisitor({
1843
1854
  reference,
1844
1855
  indirections: [...this.indirections],
1845
1856
  options: this.options,
1857
+ visited: this.visited,
1846
1858
  ancestors: ancestorsLineage
1847
1859
  });
1848
1860
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_8__.traverseAsync)(referencedElement, visitor, {
1849
- mutable: true
1861
+ mutable: true,
1862
+ skipVisited: true
1850
1863
  });
1851
1864
 
1852
1865
  // remove referencing reference from ancestors lineage
@@ -1980,12 +1993,16 @@ class AsyncAPI2DereferenceStrategy extends _DereferenceStrategy_ts__WEBPACK_IMPO
1980
1993
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
1981
1994
  refSet = mutableRefSet;
1982
1995
  }
1996
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
1983
1997
  const visitor = new _visitor_ts__WEBPACK_IMPORTED_MODULE_7__["default"]({
1984
1998
  reference,
1985
1999
  options
1986
2000
  });
1987
2001
  const dereferencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_1__.traverseAsync)(refSet.rootRef.value, visitor, {
1988
- mutable: true
2002
+ mutable: true,
2003
+ ...(shouldDetectCircular && {
2004
+ skipVisited: true
2005
+ })
1989
2006
  });
1990
2007
 
1991
2008
  /**
@@ -2070,6 +2087,7 @@ class AsyncAPI2DereferenceVisitor {
2070
2087
  reference;
2071
2088
  options;
2072
2089
  refractCache;
2090
+ visited;
2073
2091
 
2074
2092
  /**
2075
2093
  * Tracks element ancestors across dive-deep traversal boundaries.
@@ -2082,13 +2100,15 @@ class AsyncAPI2DereferenceVisitor {
2082
2100
  options,
2083
2101
  indirections = [],
2084
2102
  ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_17__.AncestorLineage(),
2085
- refractCache = new WeakMap()
2103
+ refractCache = new WeakMap(),
2104
+ visited = new WeakSet()
2086
2105
  }) {
2087
2106
  this.indirections = indirections;
2088
2107
  this.reference = reference;
2089
2108
  this.options = options;
2090
2109
  this.ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_17__.AncestorLineage(...ancestors);
2091
2110
  this.refractCache = refractCache;
2111
+ this.visited = visited;
2092
2112
  }
2093
2113
  toAncestorLineage(path) {
2094
2114
  const ancestorNodes = path.getAncestorNodes();
@@ -2337,7 +2357,11 @@ class AsyncAPI2DereferenceVisitor {
2337
2357
  */
2338
2358
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_18__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
2339
2359
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
2340
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_asyncapi_2__WEBPACK_IMPORTED_MODULE_10__.isReferenceElement)(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
2360
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_asyncapi_2__WEBPACK_IMPORTED_MODULE_10__.isReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
2361
+ if (shouldDetectCircular) {
2362
+ this.visited.add(referencedElement);
2363
+ }
2364
+
2341
2365
  // append referencing reference to ancestors lineage
2342
2366
  directAncestors.add(referencingElement);
2343
2367
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -2345,10 +2369,12 @@ class AsyncAPI2DereferenceVisitor {
2345
2369
  indirections: [...this.indirections],
2346
2370
  options: this.options,
2347
2371
  refractCache: this.refractCache,
2372
+ visited: this.visited,
2348
2373
  ancestors: ancestorsLineage
2349
2374
  });
2350
2375
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_7__.traverseAsync)(referencedElement, visitor, {
2351
- mutable: true
2376
+ mutable: true,
2377
+ skipVisited: true
2352
2378
  });
2353
2379
 
2354
2380
  // remove referencing reference from ancestors lineage
@@ -2489,7 +2515,11 @@ class AsyncAPI2DereferenceVisitor {
2489
2515
  */
2490
2516
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_18__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
2491
2517
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
2492
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_asyncapi_2__WEBPACK_IMPORTED_MODULE_10__.isChannelItemElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_1__.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
2518
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_asyncapi_2__WEBPACK_IMPORTED_MODULE_10__.isChannelItemElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_1__.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
2519
+ if (shouldDetectCircular) {
2520
+ this.visited.add(referencedElement);
2521
+ }
2522
+
2493
2523
  // append referencing reference to ancestors lineage
2494
2524
  directAncestors.add(referencingElement);
2495
2525
  const visitor = new AsyncAPI2DereferenceVisitor({
@@ -2497,10 +2527,12 @@ class AsyncAPI2DereferenceVisitor {
2497
2527
  indirections: [...this.indirections],
2498
2528
  options: this.options,
2499
2529
  refractCache: this.refractCache,
2530
+ visited: this.visited,
2500
2531
  ancestors: ancestorsLineage
2501
2532
  });
2502
2533
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_7__.traverseAsync)(referencedElement, visitor, {
2503
- mutable: true
2534
+ mutable: true,
2535
+ skipVisited: true
2504
2536
  });
2505
2537
 
2506
2538
  // remove referencing reference from ancestors lineage
@@ -2621,12 +2653,16 @@ class OpenAPI2DereferenceStrategy extends _DereferenceStrategy_ts__WEBPACK_IMPOR
2621
2653
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
2622
2654
  refSet = mutableRefSet;
2623
2655
  }
2656
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
2624
2657
  const visitor = new _visitor_ts__WEBPACK_IMPORTED_MODULE_7__["default"]({
2625
2658
  reference: reference,
2626
2659
  options
2627
2660
  });
2628
2661
  const dereferencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_3__.traverseAsync)(refSet.rootRef.value, visitor, {
2629
- mutable: true
2662
+ mutable: true,
2663
+ ...(shouldDetectCircular && {
2664
+ skipVisited: true
2665
+ })
2630
2666
  });
2631
2667
 
2632
2668
  /**
@@ -2715,6 +2751,7 @@ class OpenAPI2DereferenceVisitor {
2715
2751
  reference;
2716
2752
  options;
2717
2753
  refractCache;
2754
+ visited;
2718
2755
 
2719
2756
  /**
2720
2757
  * Tracks element ancestors across dive-deep traversal boundaries.
@@ -2727,13 +2764,15 @@ class OpenAPI2DereferenceVisitor {
2727
2764
  options,
2728
2765
  indirections = [],
2729
2766
  ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_19__.AncestorLineage(),
2730
- refractCache = new WeakMap()
2767
+ refractCache = new WeakMap(),
2768
+ visited = new WeakSet()
2731
2769
  }) {
2732
2770
  this.indirections = indirections;
2733
2771
  this.reference = reference;
2734
2772
  this.options = options;
2735
2773
  this.refractCache = refractCache;
2736
2774
  this.ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_19__.AncestorLineage(...ancestors);
2775
+ this.visited = visited;
2737
2776
  }
2738
2777
  toAncestorLineage(path) {
2739
2778
  const ancestorNodes = path.getAncestorNodes();
@@ -2982,7 +3021,11 @@ class OpenAPI2DereferenceVisitor {
2982
3021
  */
2983
3022
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_20__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
2984
3023
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
2985
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_2__WEBPACK_IMPORTED_MODULE_14__.isReferenceElement)(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
3024
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_2__WEBPACK_IMPORTED_MODULE_14__.isReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
3025
+ if (shouldDetectCircular) {
3026
+ this.visited.add(referencedElement);
3027
+ }
3028
+
2986
3029
  // append referencing reference to ancestors lineage
2987
3030
  directAncestors.add(referencingElement);
2988
3031
  const visitor = new OpenAPI2DereferenceVisitor({
@@ -2990,10 +3033,12 @@ class OpenAPI2DereferenceVisitor {
2990
3033
  indirections: [...this.indirections],
2991
3034
  options: this.options,
2992
3035
  refractCache: this.refractCache,
3036
+ visited: this.visited,
2993
3037
  ancestors: ancestorsLineage
2994
3038
  });
2995
3039
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_7__.traverseAsync)(referencedElement, visitor, {
2996
- mutable: true
3040
+ mutable: true,
3041
+ skipVisited: true
2997
3042
  });
2998
3043
  directAncestors.delete(referencingElement);
2999
3044
  }
@@ -3118,7 +3163,11 @@ class OpenAPI2DereferenceVisitor {
3118
3163
  */
3119
3164
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_20__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
3120
3165
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
3121
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_2__WEBPACK_IMPORTED_MODULE_14__.isPathItemElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_1__.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
3166
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_2__WEBPACK_IMPORTED_MODULE_14__.isPathItemElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_1__.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
3167
+ if (shouldDetectCircular) {
3168
+ this.visited.add(referencedElement);
3169
+ }
3170
+
3122
3171
  // append referencing reference to ancestors lineage
3123
3172
  directAncestors.add(referencingElement);
3124
3173
  const visitor = new OpenAPI2DereferenceVisitor({
@@ -3126,10 +3175,12 @@ class OpenAPI2DereferenceVisitor {
3126
3175
  indirections: [...this.indirections],
3127
3176
  options: this.options,
3128
3177
  refractCache: this.refractCache,
3178
+ visited: this.visited,
3129
3179
  ancestors: ancestorsLineage
3130
3180
  });
3131
3181
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_7__.traverseAsync)(referencedElement, visitor, {
3132
- mutable: true
3182
+ mutable: true,
3183
+ skipVisited: true
3133
3184
  });
3134
3185
 
3135
3186
  // remove referencing reference from ancestors lineage
@@ -3273,7 +3324,11 @@ class OpenAPI2DereferenceVisitor {
3273
3324
  */
3274
3325
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_20__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
3275
3326
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
3276
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_2__WEBPACK_IMPORTED_MODULE_11__.isJSONReferenceElement)(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
3327
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_2__WEBPACK_IMPORTED_MODULE_11__.isJSONReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
3328
+ if (shouldDetectCircular) {
3329
+ this.visited.add(referencedElement);
3330
+ }
3331
+
3277
3332
  // append referencing reference to ancestors lineage
3278
3333
  directAncestors.add(referencingElement);
3279
3334
  const visitor = new OpenAPI2DereferenceVisitor({
@@ -3281,10 +3336,12 @@ class OpenAPI2DereferenceVisitor {
3281
3336
  indirections: [...this.indirections],
3282
3337
  options: this.options,
3283
3338
  refractCache: this.refractCache,
3339
+ visited: this.visited,
3284
3340
  ancestors: ancestorsLineage
3285
3341
  });
3286
3342
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_7__.traverseAsync)(referencedElement, visitor, {
3287
- mutable: true
3343
+ mutable: true,
3344
+ skipVisited: true
3288
3345
  });
3289
3346
 
3290
3347
  // remove referencing reference from ancestors lineage
@@ -3398,12 +3455,16 @@ class OpenAPI3_0DereferenceStrategy extends _DereferenceStrategy_ts__WEBPACK_IMP
3398
3455
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
3399
3456
  refSet = mutableRefSet;
3400
3457
  }
3458
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
3401
3459
  const visitor = new _visitor_ts__WEBPACK_IMPORTED_MODULE_7__["default"]({
3402
3460
  reference: reference,
3403
3461
  options
3404
3462
  });
3405
3463
  const dereferencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_1__.traverseAsync)(refSet.rootRef.value, visitor, {
3406
- mutable: true
3464
+ mutable: true,
3465
+ ...(shouldDetectCircular && {
3466
+ skipVisited: true
3467
+ })
3407
3468
  });
3408
3469
 
3409
3470
  /**
@@ -3489,6 +3550,7 @@ class OpenAPI3_0DereferenceVisitor {
3489
3550
  reference;
3490
3551
  options;
3491
3552
  refractCache;
3553
+ visited;
3492
3554
 
3493
3555
  /**
3494
3556
  * Tracks element ancestors across dive-deep traversal boundaries.
@@ -3501,13 +3563,15 @@ class OpenAPI3_0DereferenceVisitor {
3501
3563
  options,
3502
3564
  indirections = [],
3503
3565
  ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_20__.AncestorLineage(),
3504
- refractCache = new WeakMap()
3566
+ refractCache = new WeakMap(),
3567
+ visited = new WeakSet()
3505
3568
  }) {
3506
3569
  this.indirections = indirections;
3507
3570
  this.reference = reference;
3508
3571
  this.options = options;
3509
3572
  this.ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_20__.AncestorLineage(...ancestors);
3510
3573
  this.refractCache = refractCache;
3574
+ this.visited = visited;
3511
3575
  }
3512
3576
  toAncestorLineage(path) {
3513
3577
  const ancestorNodes = path.getAncestorNodes();
@@ -3756,7 +3820,11 @@ class OpenAPI3_0DereferenceVisitor {
3756
3820
  */
3757
3821
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_17__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
3758
3822
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
3759
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_0__WEBPACK_IMPORTED_MODULE_12__.isReferenceElement)(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
3823
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_0__WEBPACK_IMPORTED_MODULE_12__.isReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
3824
+ if (shouldDetectCircular) {
3825
+ this.visited.add(referencedElement);
3826
+ }
3827
+
3760
3828
  // append referencing reference to ancestors lineage
3761
3829
  directAncestors.add(referencingElement);
3762
3830
  const visitor = new OpenAPI3_0DereferenceVisitor({
@@ -3764,10 +3832,12 @@ class OpenAPI3_0DereferenceVisitor {
3764
3832
  indirections: [...this.indirections],
3765
3833
  options: this.options,
3766
3834
  refractCache: this.refractCache,
3835
+ visited: this.visited,
3767
3836
  ancestors: ancestorsLineage
3768
3837
  });
3769
3838
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_8__.traverseAsync)(referencedElement, visitor, {
3770
- mutable: true
3839
+ mutable: true,
3840
+ skipVisited: true
3771
3841
  });
3772
3842
 
3773
3843
  // remove referencing reference from ancestors lineage
@@ -3894,7 +3964,11 @@ class OpenAPI3_0DereferenceVisitor {
3894
3964
  */
3895
3965
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_17__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
3896
3966
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
3897
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_0__WEBPACK_IMPORTED_MODULE_12__.isPathItemElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_2__.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
3967
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_0__WEBPACK_IMPORTED_MODULE_12__.isPathItemElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_2__.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
3968
+ if (shouldDetectCircular) {
3969
+ this.visited.add(referencedElement);
3970
+ }
3971
+
3898
3972
  // append referencing reference to ancestors lineage
3899
3973
  directAncestors.add(referencingElement);
3900
3974
  const visitor = new OpenAPI3_0DereferenceVisitor({
@@ -3902,10 +3976,12 @@ class OpenAPI3_0DereferenceVisitor {
3902
3976
  indirections: [...this.indirections],
3903
3977
  options: this.options,
3904
3978
  refractCache: this.refractCache,
3979
+ visited: this.visited,
3905
3980
  ancestors: ancestorsLineage
3906
3981
  });
3907
3982
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_8__.traverseAsync)(referencedElement, visitor, {
3908
- mutable: true
3983
+ mutable: true,
3984
+ skipVisited: true
3909
3985
  });
3910
3986
 
3911
3987
  // remove referencing reference from ancestors lineage
@@ -4166,12 +4242,16 @@ class OpenAPI3_1DereferenceStrategy extends _DereferenceStrategy_ts__WEBPACK_IMP
4166
4242
  reference = mutableRefSet.find(ref => ref.uri === file.uri);
4167
4243
  refSet = mutableRefSet;
4168
4244
  }
4245
+ const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
4169
4246
  const visitor = new _visitor_ts__WEBPACK_IMPORTED_MODULE_8__["default"]({
4170
4247
  reference: reference,
4171
4248
  options
4172
4249
  });
4173
4250
  const dereferencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_2__.traverseAsync)(refSet.rootRef.value, visitor, {
4174
- mutable: true
4251
+ mutable: true,
4252
+ ...(shouldDetectCircular && {
4253
+ skipVisited: true
4254
+ })
4175
4255
  });
4176
4256
 
4177
4257
  /**
@@ -4497,6 +4577,7 @@ class OpenAPI3_1DereferenceVisitor {
4497
4577
  reference;
4498
4578
  options;
4499
4579
  refractCache;
4580
+ visited;
4500
4581
 
4501
4582
  /**
4502
4583
  * Tracks element ancestors across dive-deep traversal boundaries.
@@ -4509,13 +4590,15 @@ class OpenAPI3_1DereferenceVisitor {
4509
4590
  options,
4510
4591
  indirections = [],
4511
4592
  refractCache = new WeakMap(),
4512
- ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_28__.AncestorLineage()
4593
+ ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_28__.AncestorLineage(),
4594
+ visited = new WeakSet()
4513
4595
  }) {
4514
4596
  this.indirections = indirections;
4515
4597
  this.reference = reference;
4516
4598
  this.options = options;
4517
4599
  this.refractCache = refractCache;
4518
4600
  this.ancestors = new _util_ts__WEBPACK_IMPORTED_MODULE_28__.AncestorLineage(...ancestors);
4601
+ this.visited = visited;
4519
4602
  }
4520
4603
  toAncestorLineage(path) {
4521
4604
  const ancestorNodes = path.getAncestorNodes();
@@ -4764,17 +4847,22 @@ class OpenAPI3_1DereferenceVisitor {
4764
4847
  */
4765
4848
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_23__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
4766
4849
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
4767
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_1__WEBPACK_IMPORTED_MODULE_16__.isReferenceElement)(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
4850
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_1__WEBPACK_IMPORTED_MODULE_16__.isReferenceElement)(referencedElement) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
4851
+ if (shouldDetectCircular) {
4852
+ this.visited.add(referencedElement);
4853
+ }
4768
4854
  directAncestors.add(referencingElement);
4769
4855
  const visitor = new OpenAPI3_1DereferenceVisitor({
4770
4856
  reference,
4771
4857
  indirections: [...this.indirections],
4772
4858
  options: this.options,
4773
4859
  refractCache: this.refractCache,
4860
+ visited: this.visited,
4774
4861
  ancestors: ancestorsLineage
4775
4862
  });
4776
4863
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_10__.traverseAsync)(referencedElement, visitor, {
4777
- mutable: true
4864
+ mutable: true,
4865
+ skipVisited: true
4778
4866
  });
4779
4867
  directAncestors.delete(referencingElement);
4780
4868
  }
@@ -4917,17 +5005,22 @@ class OpenAPI3_1DereferenceVisitor {
4917
5005
  */
4918
5006
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_23__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
4919
5007
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
4920
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_1__WEBPACK_IMPORTED_MODULE_16__.isPathItemElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_3__.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
5008
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_1__WEBPACK_IMPORTED_MODULE_16__.isPathItemElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_3__.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
5009
+ if (shouldDetectCircular) {
5010
+ this.visited.add(referencedElement);
5011
+ }
4921
5012
  directAncestors.add(referencingElement);
4922
5013
  const visitor = new OpenAPI3_1DereferenceVisitor({
4923
5014
  reference,
4924
5015
  indirections: [...this.indirections],
4925
5016
  options: this.options,
4926
5017
  refractCache: this.refractCache,
5018
+ visited: this.visited,
4927
5019
  ancestors: ancestorsLineage
4928
5020
  });
4929
5021
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_10__.traverseAsync)(referencedElement, visitor, {
4930
- mutable: true
5022
+ mutable: true,
5023
+ skipVisited: true
4931
5024
  });
4932
5025
  directAncestors.delete(referencingElement);
4933
5026
  }
@@ -5274,17 +5367,22 @@ class OpenAPI3_1DereferenceVisitor {
5274
5367
  */
5275
5368
  const isNonEntryDocument = _util_url_ts__WEBPACK_IMPORTED_MODULE_23__.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
5276
5369
  const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
5277
- if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_1__WEBPACK_IMPORTED_MODULE_16__.isSchemaElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_3__.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
5370
+ if ((isExternalReference || isNonEntryDocument || (0,_speclynx_apidom_ns_openapi_3_1__WEBPACK_IMPORTED_MODULE_16__.isSchemaElement)(referencedElement) && (0,_speclynx_apidom_datamodel__WEBPACK_IMPORTED_MODULE_3__.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
5371
+ if (shouldDetectCircular) {
5372
+ this.visited.add(referencedElement);
5373
+ }
5278
5374
  directAncestors.add(referencingElement);
5279
5375
  const visitor = new OpenAPI3_1DereferenceVisitor({
5280
5376
  reference,
5281
5377
  indirections: [...this.indirections],
5282
5378
  options: this.options,
5283
5379
  refractCache: this.refractCache,
5380
+ visited: this.visited,
5284
5381
  ancestors: ancestorsLineage
5285
5382
  });
5286
5383
  referencedElement = await (0,_speclynx_apidom_traverse__WEBPACK_IMPORTED_MODULE_10__.traverseAsync)(referencedElement, visitor, {
5287
- mutable: true
5384
+ mutable: true,
5385
+ skipVisited: true
5288
5386
  });
5289
5387
  directAncestors.delete(referencingElement);
5290
5388
  }
@@ -99240,10 +99338,12 @@ function* traverseGenerator(root, visitor, options) {
99240
99338
  nodePredicate,
99241
99339
  nodeCloneFn,
99242
99340
  detectCycles,
99341
+ skipVisited,
99243
99342
  mutable,
99244
99343
  mutationFn
99245
99344
  } = options;
99246
99345
  const keyMapIsFunction = typeof keyMap === 'function';
99346
+ const visitedNodes = skipVisited ? new WeakSet() : null;
99247
99347
  let stack;
99248
99348
  let inArray = Array.isArray(root);
99249
99349
  let keys = [root];
@@ -99329,6 +99429,14 @@ function* traverseGenerator(root, visitor, options) {
99329
99429
  continue;
99330
99430
  }
99331
99431
 
99432
+ // Skip already-visited nodes (handles DAG structures from cloneShallow)
99433
+ if (skipVisited && !isLeaving) {
99434
+ if (visitedNodes.has(node)) {
99435
+ continue;
99436
+ }
99437
+ visitedNodes.add(node);
99438
+ }
99439
+
99332
99440
  // Always create Path for the current node (needed for parentPath chain)
99333
99441
  currentPath = new _Path_mjs__WEBPACK_IMPORTED_MODULE_2__.Path(node, parent, parentPath, key, inArray);
99334
99442
  const visitFn = (0,_visitors_mjs__WEBPACK_IMPORTED_MODULE_3__.getVisitFn)(visitor, nodeTypeGetter(node), isLeaving);
@@ -99472,6 +99580,7 @@ const traverse = (root, visitor, options = {}) => {
99472
99580
  nodePredicate: options.nodePredicate ?? _visitors_mjs__WEBPACK_IMPORTED_MODULE_3__.isNode,
99473
99581
  nodeCloneFn: options.nodeCloneFn ?? _visitors_mjs__WEBPACK_IMPORTED_MODULE_3__.cloneNode,
99474
99582
  detectCycles: options.detectCycles ?? true,
99583
+ skipVisited: options.skipVisited ?? false,
99475
99584
  mutable: options.mutable ?? false,
99476
99585
  mutationFn: options.mutationFn ?? _visitors_mjs__WEBPACK_IMPORTED_MODULE_3__.mutateNode
99477
99586
  };
@@ -99503,6 +99612,7 @@ const traverseAsync = async (root, visitor, options = {}) => {
99503
99612
  nodePredicate: options.nodePredicate ?? _visitors_mjs__WEBPACK_IMPORTED_MODULE_3__.isNode,
99504
99613
  nodeCloneFn: options.nodeCloneFn ?? _visitors_mjs__WEBPACK_IMPORTED_MODULE_3__.cloneNode,
99505
99614
  detectCycles: options.detectCycles ?? true,
99615
+ skipVisited: options.skipVisited ?? false,
99506
99616
  mutable: options.mutable ?? false,
99507
99617
  mutationFn: options.mutationFn ?? _visitors_mjs__WEBPACK_IMPORTED_MODULE_3__.mutateNode
99508
99618
  };