@modernrelay/orbit-engine-cosmos 0.13.6 → 0.15.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.
- package/README.md +33 -34
- package/dist/index.d.ts +83 -83
- package/dist/index.js +155 -56
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -67,21 +67,21 @@ var DEFAULT_SPACE_SIZE = 4096;
|
|
|
67
67
|
var DEFAULT_RESTORE_DEADLINE_MS = 1e4;
|
|
68
68
|
var CosmosEngine = class {
|
|
69
69
|
capabilities = {
|
|
70
|
-
// Evidence-backed
|
|
70
|
+
// Evidence-backed flip: the probe measured native onLinkClick/
|
|
71
71
|
// onLinkMouseOver delivering correct link indices with ~4px perpendicular
|
|
72
|
-
// tolerance
|
|
72
|
+
// tolerance.
|
|
73
73
|
linkPicking: true,
|
|
74
74
|
rangeUpdates: [],
|
|
75
75
|
trackedPositions: true,
|
|
76
76
|
simulation: true,
|
|
77
|
-
//
|
|
77
|
+
// cosmos 3.3.0 exposes the `linkDefaultArrows` config key
|
|
78
78
|
// (config.d.ts) — instanced arrowheads toggle atomically via setConfigPartial.
|
|
79
79
|
edgeArrows: true,
|
|
80
|
-
//
|
|
80
|
+
// cosmos 3.3.0 exposes setImageData(ImageData[]) +
|
|
81
81
|
// setPointImageIndices(Float32Array) (index.d.ts) — per-point sprites via
|
|
82
82
|
// an adapter-maintained slot→ImageData atlas.
|
|
83
83
|
pointImages: true,
|
|
84
|
-
//
|
|
84
|
+
// stage 4: cosmos 3.3.0 ships a real GPU cluster force
|
|
85
85
|
// `setPointClusters((number|undefined)[])`, `setClusterPositions(
|
|
86
86
|
// (number|undefined)[])`, `setPointClusterStrength(Float32Array)`
|
|
87
87
|
// (index.d.ts) plus the `simulationCluster` coefficient (config.d.ts,
|
|
@@ -89,14 +89,14 @@ var CosmosEngine = class {
|
|
|
89
89
|
// `undefined` is cosmos' documented "not in any cluster" value, which the
|
|
90
90
|
// adapter maps from the contract's NaN.
|
|
91
91
|
clusterForce: true,
|
|
92
|
-
//
|
|
92
|
+
// stop-at-rest: cosmos 3.4.0 ships on-demand rendering — the M0
|
|
93
93
|
// quiescence row measured 0 idle frames AND 0 rAF registrations per
|
|
94
|
-
// 750ms at rest
|
|
95
|
-
// The adapter's own activity clock is gated to match
|
|
94
|
+
// 750ms at rest.
|
|
95
|
+
// The adapter's own activity clock is gated to match.
|
|
96
96
|
idleFrames: "stops",
|
|
97
97
|
// onFrame is an activity clock, not an exact post-draw hook — cosmos
|
|
98
98
|
// still exposes no public frame hook (probe `post-draw-frames`);
|
|
99
|
-
// overlays may lag one sample
|
|
99
|
+
// overlays may lag one sample.
|
|
100
100
|
postDrawFrames: false
|
|
101
101
|
};
|
|
102
102
|
options;
|
|
@@ -118,7 +118,7 @@ var CosmosEngine = class {
|
|
|
118
118
|
/** Sticky override from EngineConfigUpdate.seedRadius. */
|
|
119
119
|
seedRadiusOverride;
|
|
120
120
|
/** Point count of the last structure-bearing commit — the roster length a
|
|
121
|
-
* `cluster: null` clear must write an all-unclustered array for
|
|
121
|
+
* `cluster: null` clear must write an all-unclustered array for. */
|
|
122
122
|
lastPointCount = 0;
|
|
123
123
|
/**
|
|
124
124
|
* cosmos fires `onClick` (index undefined) AND `onBackgroundClick` for the
|
|
@@ -129,7 +129,7 @@ var CosmosEngine = class {
|
|
|
129
129
|
dragIndex = null;
|
|
130
130
|
/** Last hover reported by cosmos — fallback dragged-point source. */
|
|
131
131
|
lastHoverIndex = null;
|
|
132
|
-
/** Live native link hover
|
|
132
|
+
/** Live native link hover gates the unified onClick's
|
|
133
133
|
* background interpretation — a hovered link owns the click). */
|
|
134
134
|
hoveredLinkIndex = null;
|
|
135
135
|
// --- adapter-owned GATED overlay/activity clock (see module header) ---
|
|
@@ -143,7 +143,7 @@ var CosmosEngine = class {
|
|
|
143
143
|
pendingTicks = 0;
|
|
144
144
|
/** One-shot guard for the repulsionTheta deprecation diagnostic. */
|
|
145
145
|
repulsionThetaWarned = false;
|
|
146
|
-
// --- WebGL context-loss recovery state
|
|
146
|
+
// --- WebGL context-loss recovery state ---
|
|
147
147
|
/** cosmos' canvas (queried post-mount) carrying the webglcontext* listeners. */
|
|
148
148
|
canvas = null;
|
|
149
149
|
contextLost = false;
|
|
@@ -152,7 +152,7 @@ var CosmosEngine = class {
|
|
|
152
152
|
deadline = null;
|
|
153
153
|
/** Graph constructor cached at mount so recovery never re-imports cosmos. */
|
|
154
154
|
cosmosCtor = null;
|
|
155
|
-
// ---
|
|
155
|
+
// --- image atlas (capability pointImages) ---
|
|
156
156
|
/**
|
|
157
157
|
* slot → ImageData mirror of the cosmos image atlas. ImageData is CPU-side,
|
|
158
158
|
* so the mirror survives context loss — any post-restore atlas commit
|
|
@@ -262,7 +262,7 @@ var CosmosEngine = class {
|
|
|
262
262
|
* zoom — `setViewport(p)` then `getViewport()` returns p, modulo cosmos'
|
|
263
263
|
* d3 scaleExtent clamp. A missing x or y is filled from the current
|
|
264
264
|
* viewport; zoom-only calls keep the `setZoomLevel` path (d3's scaleTo
|
|
265
|
-
* preserves the current center). Instant unless `durationMs` is given
|
|
265
|
+
* preserves the current center). Instant unless `durationMs` is given
|
|
266
266
|
* cosmos' own default duration is 250 ms, which would animate context-
|
|
267
267
|
* recovery replays.
|
|
268
268
|
*/
|
|
@@ -323,7 +323,7 @@ var CosmosEngine = class {
|
|
|
323
323
|
});
|
|
324
324
|
this.requestTicks(2);
|
|
325
325
|
}
|
|
326
|
-
// --- spatial queries & pinning
|
|
326
|
+
// --- spatial queries & pinning ---
|
|
327
327
|
pointsInPolygon(screenPolygon) {
|
|
328
328
|
const graph = this.activeGraph;
|
|
329
329
|
if (!graph) return [];
|
|
@@ -361,7 +361,7 @@ var CosmosEngine = class {
|
|
|
361
361
|
return graph ? Float32Array.from(graph.getPointPositions()) : null;
|
|
362
362
|
}
|
|
363
363
|
/**
|
|
364
|
-
* Captures the cosmos canvas via the
|
|
364
|
+
* Captures the cosmos canvas via the same-tick method (see module
|
|
365
365
|
* header): one rAF is scheduled and, inside that tick, the WebGL canvas is
|
|
366
366
|
* drawn synchronously onto an offscreen 2D canvas (cosmos renders with
|
|
367
367
|
* preserveDrawingBuffer:false, so the buffer is only readable same-tick).
|
|
@@ -428,13 +428,13 @@ var CosmosEngine = class {
|
|
|
428
428
|
// --- gated overlay/activity clock (see module header) ---
|
|
429
429
|
/**
|
|
430
430
|
* One rAF tick of the gated activity clock. Ordering is load-bearing:
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
431
|
+
* 1. the tick budget is consumed BEFORE the host callback runs, so work
|
|
432
|
+
* scheduled from inside onFrame re-arms a fresh tick instead of being
|
|
433
|
+
* coalesced away;
|
|
434
|
+
* 2. the reschedule/stop decision is made BEFORE the host callback, so a
|
|
435
|
+
* throwing host can never kill a should-keep-running clock (and a
|
|
436
|
+
* stopping clock stays stopped even if the callback wakes it — the
|
|
437
|
+
* wake starts a new loop via requestTicks/wake, not this handle).
|
|
438
438
|
* Skips the callback (but keeps its schedule) while the document is hidden.
|
|
439
439
|
*/
|
|
440
440
|
frameTick = (timeMs) => {
|
|
@@ -488,14 +488,14 @@ var CosmosEngine = class {
|
|
|
488
488
|
this.runReasons.clear();
|
|
489
489
|
this.pendingTicks = 0;
|
|
490
490
|
}
|
|
491
|
-
// --- WebGL context-loss recovery
|
|
491
|
+
// --- WebGL context-loss recovery ---
|
|
492
492
|
/** The graph, unless it is unusable (context lost / terminally failed). */
|
|
493
493
|
get activeGraph() {
|
|
494
494
|
return this.contextLost || this.failed ? null : this.graph;
|
|
495
495
|
}
|
|
496
496
|
/**
|
|
497
497
|
* cosmos owns its canvas; we can only wire webglcontext* listeners after
|
|
498
|
-
* init by querying it. A missing canvas downgrades to an info diagnostic
|
|
498
|
+
* init by querying it. A missing canvas downgrades to an info diagnostic
|
|
499
499
|
* the engine keeps working, just without context-loss recovery.
|
|
500
500
|
*/
|
|
501
501
|
attachContextListeners() {
|
|
@@ -617,31 +617,38 @@ var CosmosEngine = class {
|
|
|
617
617
|
* later explicit directive (including `false`) replaces it.
|
|
618
618
|
*/
|
|
619
619
|
queueCommit(update) {
|
|
620
|
+
const ownedUpdate = cloneCommitForQueue(update);
|
|
620
621
|
const previous = this.pendingCommit;
|
|
621
622
|
if (previous === null) {
|
|
622
|
-
this.pendingCommit =
|
|
623
|
+
this.pendingCommit = ownedUpdate;
|
|
623
624
|
return;
|
|
624
625
|
}
|
|
625
|
-
const merged = { revision:
|
|
626
|
-
const structure =
|
|
626
|
+
const merged = { revision: ownedUpdate.revision };
|
|
627
|
+
const structure = ownedUpdate.structure ?? previous.structure;
|
|
627
628
|
if (structure !== void 0) merged.structure = structure;
|
|
628
|
-
if (previous.buffers !== void 0 ||
|
|
629
|
-
merged.buffers = { ...previous.buffers, ...
|
|
629
|
+
if (previous.buffers !== void 0 || ownedUpdate.buffers !== void 0) {
|
|
630
|
+
merged.buffers = { ...previous.buffers, ...ownedUpdate.buffers };
|
|
630
631
|
}
|
|
631
|
-
if (previous.
|
|
632
|
-
|
|
633
|
-
|
|
632
|
+
if (previous.bufferPatches !== void 0 || ownedUpdate.bufferPatches !== void 0) {
|
|
633
|
+
merged.bufferPatches = {
|
|
634
|
+
...previous.bufferPatches,
|
|
635
|
+
...ownedUpdate.bufferPatches
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
if (previous.config !== void 0 || ownedUpdate.config !== void 0) {
|
|
639
|
+
const config = { ...previous.config, ...ownedUpdate.config };
|
|
640
|
+
if (previous.config?.simulation !== void 0 || ownedUpdate.config?.simulation !== void 0) {
|
|
634
641
|
config.simulation = {
|
|
635
642
|
...previous.config?.simulation,
|
|
636
|
-
...
|
|
643
|
+
...ownedUpdate.config?.simulation
|
|
637
644
|
};
|
|
638
645
|
}
|
|
639
646
|
merged.config = config;
|
|
640
647
|
}
|
|
641
|
-
if (previous.resources !== void 0 ||
|
|
642
|
-
merged.resources = mergeResources(previous.resources,
|
|
648
|
+
if (previous.resources !== void 0 || ownedUpdate.resources !== void 0) {
|
|
649
|
+
merged.resources = mergeResources(previous.resources, ownedUpdate.resources);
|
|
643
650
|
}
|
|
644
|
-
const restart =
|
|
651
|
+
const restart = ownedUpdate.restart !== void 0 && ownedUpdate.restart !== false ? ownedUpdate.restart : previous.restart;
|
|
645
652
|
if (restart !== void 0) merged.restart = restart;
|
|
646
653
|
this.pendingCommit = merged;
|
|
647
654
|
}
|
|
@@ -669,10 +676,10 @@ var CosmosEngine = class {
|
|
|
669
676
|
// cosmos 3.4 defaults this to true. Pinned OFF for the 3.4 upgrade:
|
|
670
677
|
// occlusion culling changes which overlapping pixels draw, and the M5
|
|
671
678
|
// pixel-diff thresholds (CHANGED_MIN/RESTORED_MAX) were tuned against
|
|
672
|
-
// un-culled rendering.
|
|
673
|
-
//
|
|
679
|
+
// un-culled rendering. Keep one variable at a time; enabling culling
|
|
680
|
+
// requires its own threshold re-validation.
|
|
674
681
|
pointOcclusionCulling: false,
|
|
675
|
-
// Native point dragging; the core owns pin semantics on top
|
|
682
|
+
// Native point dragging; the core owns pin semantics on top.
|
|
676
683
|
enableDrag: this.options.enableDrag ?? true,
|
|
677
684
|
onPointClick: (index, _position, event) => {
|
|
678
685
|
this.events?.onPointClick?.(index, clickModifiers(event));
|
|
@@ -733,7 +740,7 @@ var CosmosEngine = class {
|
|
|
733
740
|
this.requestTicks(2);
|
|
734
741
|
this.emitViewportChange();
|
|
735
742
|
},
|
|
736
|
-
// --- gated-clock run reasons
|
|
743
|
+
// --- gated-clock run reasons ---
|
|
737
744
|
onSimulationStart: () => {
|
|
738
745
|
this.wake("sim");
|
|
739
746
|
},
|
|
@@ -833,8 +840,8 @@ var CosmosEngine = class {
|
|
|
833
840
|
if (viewport) this.events?.onViewportChange?.(viewport);
|
|
834
841
|
}
|
|
835
842
|
/**
|
|
836
|
-
* One visibly atomic update
|
|
837
|
-
* exactly one render
|
|
843
|
+
* One visibly atomic update: all channels and config are staged, then
|
|
844
|
+
* exactly one render draws them; restart reheats after the render.
|
|
838
845
|
*/
|
|
839
846
|
applyCommit(graph, update) {
|
|
840
847
|
const { config, structure, buffers, resources, restart } = update;
|
|
@@ -909,17 +916,17 @@ var CosmosEngine = class {
|
|
|
909
916
|
this.requestTicks(2);
|
|
910
917
|
}
|
|
911
918
|
/**
|
|
912
|
-
* Applies the
|
|
919
|
+
* Applies the stage-4 cluster force (capability `clusterForce`).
|
|
913
920
|
*
|
|
914
921
|
* Contract mapping, verified against the 3.3.0 dist (`index.d.ts`):
|
|
915
|
-
*
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
*
|
|
921
|
-
*
|
|
922
|
-
*
|
|
922
|
+
* - `pointClusters` (Float32Array, NaN = unclustered) →
|
|
923
|
+
* `setPointClusters((number | undefined)[])`, where cosmos' documented
|
|
924
|
+
* "does not belong to any cluster" value is `undefined`;
|
|
925
|
+
* - `centers` (Float32Array, `[x0,y0,x1,y1,…]`) →
|
|
926
|
+
* `setClusterPositions((number | undefined)[])`; a non-finite entry means
|
|
927
|
+
* "no position" and cosmos falls back to that cluster's centermass;
|
|
928
|
+
* - `null` clears: an all-`undefined` array of the CURRENT roster length
|
|
929
|
+
* (length must track the roster) plus empty cluster positions.
|
|
923
930
|
* `strength` is the scene-wide `simulationCluster` config coefficient
|
|
924
931
|
* applied in the config block, not the per-point
|
|
925
932
|
* `setPointClusterStrength` buffer (Orbit's strength is scene-wide).
|
|
@@ -948,12 +955,12 @@ var CosmosEngine = class {
|
|
|
948
955
|
}
|
|
949
956
|
}
|
|
950
957
|
/**
|
|
951
|
-
* Applies the
|
|
958
|
+
* Applies the image-atlas channel: upserts convert ImageBitmap →
|
|
952
959
|
* ImageData through an offscreen 2D canvas into the slot mirror, removals
|
|
953
960
|
* blank their slot, and any atlas change re-uploads the FULL ImageData
|
|
954
961
|
* array (cosmos' setImageData is whole-array only, matching
|
|
955
962
|
* `rangeUpdates: []`). Environments without a usable 2D context (jsdom)
|
|
956
|
-
* no-op the channel and report `engine:image-channel-unavailable` once
|
|
963
|
+
* no-op the channel and report `engine:image-channel-unavailable` once
|
|
957
964
|
* never throw.
|
|
958
965
|
*/
|
|
959
966
|
applyResources(graph, resources) {
|
|
@@ -999,7 +1006,7 @@ var CosmosEngine = class {
|
|
|
999
1006
|
/**
|
|
1000
1007
|
* Reads an ImageData of the given size off an offscreen 2D canvas, drawing
|
|
1001
1008
|
* `bitmap` onto it first when provided (ImageBitmap → ImageData transcode;
|
|
1002
|
-
* without a bitmap: a transparent blank). Returns null — never throws
|
|
1009
|
+
* without a bitmap: a transparent blank). Returns null — never throws
|
|
1003
1010
|
* where no 2D context exists (jsdom without the canvas package).
|
|
1004
1011
|
*/
|
|
1005
1012
|
canvasImageData(width, height, bitmap) {
|
|
@@ -1028,7 +1035,7 @@ var CosmosEngine = class {
|
|
|
1028
1035
|
});
|
|
1029
1036
|
}
|
|
1030
1037
|
/**
|
|
1031
|
-
* Replaces NaN pairs (= "no known position"
|
|
1038
|
+
* Replaces NaN pairs (= "no known position") with random points on a
|
|
1032
1039
|
* ring of radius seedRadius around the space center. cosmos treats NaN
|
|
1033
1040
|
* positions as *absent* points, so they must never reach setPointPositions.
|
|
1034
1041
|
* Known positions pass through verbatim (same array when nothing to seed).
|
|
@@ -1055,6 +1062,98 @@ var CosmosEngine = class {
|
|
|
1055
1062
|
return out;
|
|
1056
1063
|
}
|
|
1057
1064
|
};
|
|
1065
|
+
function cloneCommitForQueue(update) {
|
|
1066
|
+
const owned = { revision: update.revision };
|
|
1067
|
+
if (update.structure !== void 0) {
|
|
1068
|
+
owned.structure = {
|
|
1069
|
+
pointCount: update.structure.pointCount,
|
|
1070
|
+
positions: update.structure.positions.slice(),
|
|
1071
|
+
links: update.structure.links.slice()
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
if (update.buffers !== void 0) {
|
|
1075
|
+
const buffers = {};
|
|
1076
|
+
if (update.buffers.pointColor !== void 0) {
|
|
1077
|
+
buffers.pointColor = update.buffers.pointColor.slice();
|
|
1078
|
+
}
|
|
1079
|
+
if (update.buffers.pointSize !== void 0) {
|
|
1080
|
+
buffers.pointSize = update.buffers.pointSize.slice();
|
|
1081
|
+
}
|
|
1082
|
+
if (update.buffers.linkColor !== void 0) {
|
|
1083
|
+
buffers.linkColor = update.buffers.linkColor.slice();
|
|
1084
|
+
}
|
|
1085
|
+
if (update.buffers.linkWidth !== void 0) {
|
|
1086
|
+
buffers.linkWidth = update.buffers.linkWidth.slice();
|
|
1087
|
+
}
|
|
1088
|
+
owned.buffers = buffers;
|
|
1089
|
+
}
|
|
1090
|
+
if (update.bufferPatches !== void 0) {
|
|
1091
|
+
const patches = {};
|
|
1092
|
+
if (update.bufferPatches.pointColor !== void 0) {
|
|
1093
|
+
patches.pointColor = update.bufferPatches.pointColor.map(({ start, data }) => ({
|
|
1094
|
+
start,
|
|
1095
|
+
data: data.slice()
|
|
1096
|
+
}));
|
|
1097
|
+
}
|
|
1098
|
+
if (update.bufferPatches.pointSize !== void 0) {
|
|
1099
|
+
patches.pointSize = update.bufferPatches.pointSize.map(({ start, data }) => ({
|
|
1100
|
+
start,
|
|
1101
|
+
data: data.slice()
|
|
1102
|
+
}));
|
|
1103
|
+
}
|
|
1104
|
+
if (update.bufferPatches.linkColor !== void 0) {
|
|
1105
|
+
patches.linkColor = update.bufferPatches.linkColor.map(({ start, data }) => ({
|
|
1106
|
+
start,
|
|
1107
|
+
data: data.slice()
|
|
1108
|
+
}));
|
|
1109
|
+
}
|
|
1110
|
+
if (update.bufferPatches.linkWidth !== void 0) {
|
|
1111
|
+
patches.linkWidth = update.bufferPatches.linkWidth.map(({ start, data }) => ({
|
|
1112
|
+
start,
|
|
1113
|
+
data: data.slice()
|
|
1114
|
+
}));
|
|
1115
|
+
}
|
|
1116
|
+
owned.bufferPatches = patches;
|
|
1117
|
+
}
|
|
1118
|
+
if (update.config !== void 0) {
|
|
1119
|
+
const config = { ...update.config };
|
|
1120
|
+
if (update.config.simulation !== void 0) {
|
|
1121
|
+
config.simulation = { ...update.config.simulation };
|
|
1122
|
+
}
|
|
1123
|
+
if (update.config.cluster !== void 0) {
|
|
1124
|
+
config.cluster = update.config.cluster === null ? null : {
|
|
1125
|
+
...update.config.cluster,
|
|
1126
|
+
pointClusters: update.config.cluster.pointClusters.slice(),
|
|
1127
|
+
...update.config.cluster.centers === void 0 ? {} : { centers: update.config.cluster.centers.slice() }
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
owned.config = config;
|
|
1131
|
+
}
|
|
1132
|
+
if (update.resources !== void 0) {
|
|
1133
|
+
const resources = {};
|
|
1134
|
+
if (update.resources.imageAtlas !== void 0) {
|
|
1135
|
+
const imageAtlas = {};
|
|
1136
|
+
if (update.resources.imageAtlas.upserts !== void 0) {
|
|
1137
|
+
imageAtlas.upserts = update.resources.imageAtlas.upserts.map(({ slot, bitmap }) => ({
|
|
1138
|
+
slot,
|
|
1139
|
+
bitmap
|
|
1140
|
+
}));
|
|
1141
|
+
}
|
|
1142
|
+
if (update.resources.imageAtlas.removeSlots !== void 0) {
|
|
1143
|
+
imageAtlas.removeSlots = Array.from(update.resources.imageAtlas.removeSlots);
|
|
1144
|
+
}
|
|
1145
|
+
resources.imageAtlas = imageAtlas;
|
|
1146
|
+
}
|
|
1147
|
+
if (update.resources.pointImageIndex !== void 0) {
|
|
1148
|
+
resources.pointImageIndex = update.resources.pointImageIndex.slice();
|
|
1149
|
+
}
|
|
1150
|
+
owned.resources = resources;
|
|
1151
|
+
}
|
|
1152
|
+
if (update.restart !== void 0) {
|
|
1153
|
+
owned.restart = update.restart === false ? false : { alpha: update.restart.alpha };
|
|
1154
|
+
}
|
|
1155
|
+
return owned;
|
|
1156
|
+
}
|
|
1058
1157
|
function mergeResources(previous, update) {
|
|
1059
1158
|
const upsertBySlot = /* @__PURE__ */ new Map();
|
|
1060
1159
|
const removes = /* @__PURE__ */ new Set();
|