@rivet-health/design-system 40.7.0 → 40.7.1

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.
@@ -2064,12 +2064,19 @@ var RivSelect;
2064
2064
  return this.byId.has(option.id);
2065
2065
  }
2066
2066
  add(option) {
2067
- if (this.byId.has(option.id)) {
2068
- // Preserve insertion order while swapping to the newer object
2069
- // reference. Rebuilding the full Set matches the previous
2070
- // implementation's semantics; the cost of that rebuild only hits
2071
- // the rare "re-add existing" path — the common O(N²) case (fresh
2072
- // adds during construction) no longer pays it.
2067
+ const existing = this.byId.get(option.id);
2068
+ if (existing === option) {
2069
+ // Same object reference — nothing to update. Ancestor re-adds
2070
+ // during a selection snapshot hit this path repeatedly, so keeping
2071
+ // it O(1) is what prevents the tree-select N² blow-up seen in
2072
+ // RVT-25470.
2073
+ return this;
2074
+ }
2075
+ if (existing !== undefined) {
2076
+ // Different reference, same id — swap the stored ref while
2077
+ // preserving Set insertion order. The rebuild is O(N), but this
2078
+ // path is rare in practice (the hot ancestor-snapshot loop takes
2079
+ // the identity fast-path above).
2073
2080
  const items = [...this].map(o => (o.id === option.id ? option : o));
2074
2081
  this.byId.set(option.id, option);
2075
2082
  super.clear();
@@ -2410,17 +2417,22 @@ var RivSelect;
2410
2417
  const snapshot = new OptionSet(state.selection.selected);
2411
2418
  const topLevelOptions = getTopLevelOptions(state.sourceOptionGroups);
2412
2419
  const [parents] = getRelationshipMaps(topLevelOptions);
2413
- // For each selected option, add all its ancestors to the snapshot
2420
+ // Sibling leaves share ancestors, so once we've walked up through a
2421
+ // given parent id we can stop — anything above it has already been
2422
+ // added by a previous iteration. Without this short-circuit the loop
2423
+ // re-runs findOption (which is a full tree walk) once per shared
2424
+ // ancestor edge, which was the #2 hotspot behind OptionSet.add in
2425
+ // RVT-25470's profile.
2426
+ const visited = new Set();
2414
2427
  for (const selectedOption of state.selection.selected) {
2415
- let currentId = selectedOption.id;
2416
- let parentId = parents.get(currentId);
2417
- while (parentId !== undefined) {
2428
+ let parentId = parents.get(selectedOption.id);
2429
+ while (parentId !== undefined && !visited.has(parentId)) {
2430
+ visited.add(parentId);
2418
2431
  const parentOption = findOption(topLevelOptions, parentId);
2419
2432
  if (parentOption) {
2420
2433
  snapshot.add(parentOption);
2421
2434
  }
2422
- currentId = parentId;
2423
- parentId = parents.get(currentId);
2435
+ parentId = parents.get(parentId);
2424
2436
  }
2425
2437
  }
2426
2438
  return snapshot;