@oimdb/core 3.10.0 → 3.11.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.
package/dist/index.cjs CHANGED
@@ -44,6 +44,7 @@ __export(index_exports, {
44
44
  OIMComputeRuntime: () => OIMComputeRuntime,
45
45
  OIMComputed: () => OIMComputed,
46
46
  OIMDBSettings: () => OIMDBSettings,
47
+ OIMDerivedCollection: () => OIMDerivedCollection,
47
48
  OIMDerivedCollectionGlobalIndexArrayBased: () => OIMDerivedCollectionGlobalIndexArrayBased,
48
49
  OIMDerivedCollectionGlobalIndexSetBased: () => OIMDerivedCollectionGlobalIndexSetBased,
49
50
  OIMDerivedCollectionIndexArrayBased: () => OIMDerivedCollectionIndexArrayBased,
@@ -139,7 +140,11 @@ __export(index_exports, {
139
140
  createOIMIndexSlotMap: () => createOIMIndexSlotMap,
140
141
  createOIMOrderedListCommandStreamDiffDriven: () => createOIMOrderedListCommandStreamDiffDriven,
141
142
  createOIMOrderedListMappedCommandStream: () => createOIMOrderedListMappedCommandStream,
142
- diffOrderedListByPk: () => diffOrderedListByPk
143
+ diffOrderedListByPk: () => diffOrderedListByPk,
144
+ getOIMComputeRuntime: () => getOIMComputeRuntime,
145
+ on: () => on,
146
+ peekOIMComputeRuntime: () => peekOIMComputeRuntime,
147
+ setOIMComputeRuntime: () => setOIMComputeRuntime
143
148
  });
144
149
  module.exports = __toCommonJS(index_exports);
145
150
 
@@ -370,6 +375,25 @@ var OIMEventQueueScheduler = class {
370
375
  flush() {
371
376
  this.emitter.emit(0 /* FLUSH */, void 0);
372
377
  }
378
+ /**
379
+ * Detach a Node timer handle from the event loop, returning it unchanged.
380
+ *
381
+ * A pending flush must never be a reason for a process to stay alive: if
382
+ * nothing else is running there is no UI left to update, and the flush is
383
+ * moot. Without this a scheduled-but-not-yet-run flush keeps Node awake —
384
+ * which is why a test suite that leaves a queue scheduled reports
385
+ * "a worker process has failed to exit gracefully".
386
+ *
387
+ * In the browser `setTimeout` returns a number, which has no `unref`, so the
388
+ * call is guarded and the handle passes straight through.
389
+ */
390
+ unref(handle) {
391
+ const maybeUnrefable = handle;
392
+ if (typeof maybeUnrefable?.unref === "function") {
393
+ maybeUnrefable.unref();
394
+ }
395
+ return handle;
396
+ }
373
397
  };
374
398
 
375
399
  // src/core/OIMKeyedBucketSetBased.ts
@@ -709,14 +733,17 @@ var OIMCarrierKeyedEmitter = class {
709
733
  if (this.dirtyCarriers.length === 0) return;
710
734
  const flushing = this.dirtyCarriers;
711
735
  this.dirtyCarriers = [];
736
+ const multiCarrierHandlers = this.multiCarrierHandlers;
737
+ const delivered = multiCarrierHandlers !== void 0 && multiCarrierHandlers.size > 0 ? this.deliveredPool ?? (this.deliveredPool = /* @__PURE__ */ new Set()) : void 0;
712
738
  let i = 0;
713
739
  try {
714
740
  for (; i < flushing.length; i++) {
715
741
  flushing[i].dirty = false;
716
- this.notify(flushing[i]);
742
+ this.notify(flushing[i], delivered);
717
743
  }
718
744
  } finally {
719
745
  for (; i < flushing.length; i++) flushing[i].dirty = false;
746
+ delivered?.clear();
720
747
  }
721
748
  };
722
749
  this.queue = queue;
@@ -734,7 +761,15 @@ var OIMCarrierKeyedEmitter = class {
734
761
  this.addHandler(carrier, handler);
735
762
  carriers.push(carrier);
736
763
  }
764
+ const spansMultipleCarriers = carriers.length > 1;
765
+ if (spansMultipleCarriers) this.retainMultiCarrierHandler(handler);
766
+ let unsubscribed = false;
737
767
  return () => {
768
+ if (unsubscribed) return;
769
+ unsubscribed = true;
770
+ if (spansMultipleCarriers) {
771
+ this.releaseMultiCarrierHandler(handler);
772
+ }
738
773
  for (let i = 0; i < carriers.length; i++) {
739
774
  this.removeHandler(carriers[i], handler);
740
775
  }
@@ -745,10 +780,25 @@ var OIMCarrierKeyedEmitter = class {
745
780
  if (carrier) this.removeHandler(carrier, handler);
746
781
  }
747
782
  unsubscribeFromKeys(keys, handler) {
783
+ if (keys.length > 1) this.releaseMultiCarrierHandler(handler);
748
784
  for (let i = 0; i < keys.length; i++) {
749
785
  this.unsubscribeFromKey(keys[i], handler);
750
786
  }
751
787
  }
788
+ /** Take a reference for a handler whose subscription spans several carriers. */
789
+ retainMultiCarrierHandler(handler) {
790
+ const handlers = this.multiCarrierHandlers ?? (this.multiCarrierHandlers = /* @__PURE__ */ new Map());
791
+ handlers.set(handler, (handlers.get(handler) ?? 0) + 1);
792
+ }
793
+ /** Drop one reference; the handler leaves the set when none are left. */
794
+ releaseMultiCarrierHandler(handler) {
795
+ const handlers = this.multiCarrierHandlers;
796
+ if (handlers === void 0) return;
797
+ const count = handlers.get(handler);
798
+ if (count === void 0) return;
799
+ if (count > 1) handlers.set(handler, count - 1);
800
+ else handlers.delete(handler);
801
+ }
752
802
  addHandler(carrier, handler) {
753
803
  let subscribers = carrier.subscribers;
754
804
  if (!subscribers) {
@@ -837,6 +887,8 @@ var OIMCarrierKeyedEmitter = class {
837
887
  }
838
888
  this.dirtyCarriers = [];
839
889
  this.isFlushEnqueued = false;
890
+ this.multiCarrierHandlers?.clear();
891
+ this.deliveredPool?.clear();
840
892
  }
841
893
  assertNotInFlush() {
842
894
  if (this.queue.isInFlush) {
@@ -850,21 +902,43 @@ var OIMCarrierKeyedEmitter = class {
850
902
  this.isFlushEnqueued = true;
851
903
  this.queue.enqueue(this.onFlush);
852
904
  }
853
- notify(carrier) {
905
+ notify(carrier, delivered) {
854
906
  const subscribers = carrier.subscribers;
855
907
  if (!subscribers || subscribers.size === 0) return;
908
+ const multiCarrierHandlers = delivered ? this.multiCarrierHandlers : void 0;
856
909
  if (subscribers.size === 1) {
857
910
  const only = subscribers.values().next().value;
858
- if (only) only();
911
+ if (!only) return;
912
+ if (this.isAlreadyDelivered(only, delivered, multiCarrierHandlers)) {
913
+ return;
914
+ }
915
+ only();
859
916
  return;
860
917
  }
861
918
  const snapshot = [];
862
919
  subscribers.forEach((h) => snapshot.push(h));
863
920
  for (let i = 0; i < snapshot.length; i++) {
864
921
  const handler = snapshot[i];
865
- if (subscribers.has(handler)) handler();
922
+ if (!subscribers.has(handler)) continue;
923
+ if (this.isAlreadyDelivered(handler, delivered, multiCarrierHandlers)) {
924
+ continue;
925
+ }
926
+ handler();
866
927
  }
867
928
  }
929
+ /**
930
+ * Whether this handler already fired in the current flush and must be
931
+ * skipped. Only multi-carrier handlers are tracked; everything else always
932
+ * delivers. Membership is recorded via the size delta of a single `add` —
933
+ * one hash probe instead of `has` + `add`.
934
+ */
935
+ isAlreadyDelivered(handler, delivered, multiCarrierHandlers) {
936
+ if (delivered === void 0) return false;
937
+ if (multiCarrierHandlers?.has(handler) !== true) return false;
938
+ const size = delivered.size;
939
+ delivered.add(handler);
940
+ return delivered.size === size;
941
+ }
868
942
  };
869
943
 
870
944
  // src/core/OIMKeyedCarrierProvider.ts
@@ -1484,8 +1558,11 @@ var OIMCollection = class {
1484
1558
  this.emitter.emit(0 /* UPDATE */, { pks });
1485
1559
  }
1486
1560
  clear() {
1561
+ const pks = this.store.getAllPks();
1487
1562
  this.store.clear();
1488
- this.emitter.emit(0 /* UPDATE */, { pks: [] });
1563
+ if (pks.length > 0) {
1564
+ this.emitter.emit(0 /* UPDATE */, { pks });
1565
+ }
1489
1566
  }
1490
1567
  countAll() {
1491
1568
  return this.store.countAll();
@@ -1527,14 +1604,12 @@ var OIMReactiveCollection = class extends OIMCollection {
1527
1604
  // Only ever holds `slot.pk` (one canonical reference per logical key), so a
1528
1605
  // native `Set` dedups correctly by reference even for composite pks.
1529
1606
  this.pendingAnyUpdatePks = /* @__PURE__ */ new Set();
1530
- this.isAnyUpdateClearPending = false;
1531
1607
  this.isAnyUpdateScheduled = false;
1532
1608
  this.runAnyUpdate = () => {
1533
1609
  if (!this.isAnyUpdateScheduled) return;
1534
1610
  this.isAnyUpdateScheduled = false;
1535
- const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks.values());
1611
+ const pks = Array.from(this.pendingAnyUpdatePks.values());
1536
1612
  this.pendingAnyUpdatePks.clear();
1537
- this.isAnyUpdateClearPending = false;
1538
1613
  const snapshot = Array.from(this.anyUpdateHandlers);
1539
1614
  for (let i = 0; i < snapshot.length; i++) snapshot[i](pks);
1540
1615
  };
@@ -1574,30 +1649,43 @@ var OIMReactiveCollection = class extends OIMCollection {
1574
1649
  this.anyUpdateHandlers.delete(handler);
1575
1650
  };
1576
1651
  }
1577
- // The legacy `emitter` (EOIMCollectionEventType.UPDATE) coexists with the
1578
- // keyed `updateEmitter`. Build its payload only when something is actually
1579
- // subscribed — otherwise every upsert/remove allocates a throwaway
1580
- // `{ pks }` object (and array) that nobody reads.
1581
- notifyLegacy(pk) {
1652
+ // Two notification channels coexist on a reactive collection; they serve
1653
+ // different audiences and NEITHER is obsolete:
1654
+ // • the keyed `updateEmitter` — per-key/per-carrier pings ("did MY key
1655
+ // change?", payload-less `void`), for leaf subscribers like React hooks
1656
+ // that declare the keys they care about up front;
1657
+ // • this `emitter` UPDATE event — the aggregate/batch channel that carries
1658
+ // the full list of changed pks in one SYNCHRONOUS callback, for
1659
+ // consumers that do NOT know their keys ahead of time and must re-derive
1660
+ // over the batch: derived indexes/collections, changed-fields, persist,
1661
+ // snapshot-manager. `pks` always enumerates exactly what changed — even
1662
+ // clear() lists every removed key, so an empty array never occurs.
1663
+ // It is not a per-key emitter with fewer features — it is the opposite
1664
+ // shape (enumerated batch, not "my key"), which is why it stays.
1665
+ //
1666
+ // Emit on the batch channel only when something is actually subscribed —
1667
+ // otherwise every upsert/remove allocates a throwaway `{ pks }` object (and
1668
+ // array) that nobody reads.
1669
+ notifyBatch(pk) {
1582
1670
  if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1583
1671
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1584
1672
  }
1585
1673
  }
1586
- notifyLegacyMany(pks) {
1674
+ notifyBatchMany(pks) {
1587
1675
  if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1588
1676
  this.emitter.emit(0 /* UPDATE */, { pks });
1589
1677
  }
1590
1678
  }
1591
1679
  upsertOneByPk(pk, entity) {
1592
1680
  const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
1593
- this.notifyLegacy(pk);
1681
+ this.notifyBatch(pk);
1594
1682
  this.updateEmitter.markUpdatedCarrier(slot);
1595
1683
  this.trackAnyUpdatePk(pk);
1596
1684
  return slot;
1597
1685
  }
1598
1686
  upsertOne(entity) {
1599
1687
  const slot = this.upsertOneWithoutNotifications(entity);
1600
- this.notifyLegacy(slot.pk);
1688
+ this.notifyBatch(slot.pk);
1601
1689
  this.updateEmitter.markUpdatedCarrier(slot);
1602
1690
  this.trackAnyUpdatePk(slot.pk);
1603
1691
  return slot;
@@ -1608,7 +1696,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1608
1696
  (entity) => this.upsertOneWithoutNotifications(entity)
1609
1697
  );
1610
1698
  const pks = slots.map((slot) => slot.pk);
1611
- this.notifyLegacyMany(pks);
1699
+ this.notifyBatchMany(pks);
1612
1700
  for (let i = 0; i < slots.length; i++) {
1613
1701
  this.updateEmitter.markUpdatedCarrier(slots[i]);
1614
1702
  }
@@ -1618,7 +1706,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1618
1706
  removeOne(entity) {
1619
1707
  const pk = this.selectPk(entity);
1620
1708
  this.store.removeOneByPk(pk);
1621
- this.notifyLegacy(pk);
1709
+ this.notifyBatch(pk);
1622
1710
  this.updateEmitter.markUpdatedKey(pk);
1623
1711
  this.trackAnyUpdatePk(pk);
1624
1712
  }
@@ -1626,30 +1714,32 @@ var OIMReactiveCollection = class extends OIMCollection {
1626
1714
  if (entities.length === 0) return;
1627
1715
  const pks = entities.map(this.selectPk);
1628
1716
  this.store.removeManyByPks(pks);
1629
- this.notifyLegacyMany(pks);
1717
+ this.notifyBatchMany(pks);
1630
1718
  this.updateEmitter.markUpdatedKeys(pks);
1631
1719
  this.trackAnyUpdatePks(pks);
1632
1720
  }
1633
1721
  removeOneByPk(pk) {
1634
1722
  this.store.removeOneByPk(pk);
1635
- this.notifyLegacy(pk);
1723
+ this.notifyBatch(pk);
1636
1724
  this.updateEmitter.markUpdatedKey(pk);
1637
1725
  this.trackAnyUpdatePk(pk);
1638
1726
  }
1639
1727
  removeManyByPks(pks) {
1640
1728
  if (pks.length === 0) return;
1641
1729
  this.store.removeManyByPks(pks);
1642
- this.notifyLegacyMany(pks);
1730
+ this.notifyBatchMany(pks);
1643
1731
  this.updateEmitter.markUpdatedKeys(pks);
1644
1732
  this.trackAnyUpdatePks(pks);
1645
1733
  }
1646
1734
  clear() {
1735
+ const needsPks = this.emitter.hasHandlers(0 /* UPDATE */) || this.anyUpdateHandlers.size > 0;
1736
+ const pks = needsPks ? this.store.getAllPks() : void 0;
1647
1737
  this.store.clear();
1648
- if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1649
- this.emitter.emit(0 /* UPDATE */, { pks: [] });
1738
+ if (pks && pks.length > 0) {
1739
+ this.notifyBatchMany(pks);
1740
+ this.trackAnyUpdatePks(pks);
1650
1741
  }
1651
1742
  this.updateEmitter.markAllUpdated();
1652
- this.trackAnyUpdateClear();
1653
1743
  }
1654
1744
  trackAnyUpdatePk(pk) {
1655
1745
  if (this.anyUpdateHandlers.size === 0) return;
@@ -1658,14 +1748,9 @@ var OIMReactiveCollection = class extends OIMCollection {
1658
1748
  }
1659
1749
  trackAnyUpdatePks(pks) {
1660
1750
  if (this.anyUpdateHandlers.size === 0) return;
1661
- for (let i = 0; i < pks.length; i++)
1751
+ for (let i = 0; i < pks.length; i++) {
1662
1752
  this.pendingAnyUpdatePks.add(pks[i]);
1663
- this.ensureAnyUpdateEnqueued();
1664
- }
1665
- trackAnyUpdateClear() {
1666
- if (this.anyUpdateHandlers.size === 0) return;
1667
- this.isAnyUpdateClearPending = true;
1668
- this.pendingAnyUpdatePks.clear();
1753
+ }
1669
1754
  this.ensureAnyUpdateEnqueued();
1670
1755
  }
1671
1756
  ensureAnyUpdateEnqueued() {
@@ -2098,8 +2183,13 @@ var OIMUpdateEventEmitter = class {
2098
2183
  var OIMOrderedListCommandBuffer = class {
2099
2184
  constructor(queue) {
2100
2185
  this.commandsByKey = /* @__PURE__ */ new Map();
2186
+ // Keys whose buffered leading `reset` is stale: an edit after the reset
2187
+ // should be folded into it, but we defer the (O(list-size)) rebuild to the
2188
+ // next read instead of redoing it on every subsequent edit in the batch.
2189
+ this.staleResetKeys = /* @__PURE__ */ new Set();
2101
2190
  this.onAfterFlush = () => {
2102
2191
  this.commandsByKey.clear();
2192
+ this.staleResetKeys.clear();
2103
2193
  };
2104
2194
  this.commandsEventEmitter = new OIMUpdateEventEmitter(
2105
2195
  queue,
@@ -2114,9 +2204,11 @@ var OIMOrderedListCommandBuffer = class {
2114
2204
  return this.commandsEventEmitter.subscribeOnKey(key, handler);
2115
2205
  }
2116
2206
  getBufferedCommands(key) {
2207
+ this.materializeStaleReset(key);
2117
2208
  return this.commandsByKey.get(key) ?? [];
2118
2209
  }
2119
2210
  consumeCommands(key) {
2211
+ this.materializeStaleReset(key);
2120
2212
  const cmds = this.commandsByKey.get(key);
2121
2213
  if (!cmds || cmds.length === 0) return [];
2122
2214
  return cmds.slice();
@@ -2125,6 +2217,20 @@ var OIMOrderedListCommandBuffer = class {
2125
2217
  this.commandsEventEmitter.destroy();
2126
2218
  this.unsubscribeAfterFlush();
2127
2219
  this.commandsByKey.clear();
2220
+ this.staleResetKeys.clear();
2221
+ }
2222
+ /**
2223
+ * Rebuild a deferred leading `reset` from the current order, once, at read
2224
+ * time. All the post-reset edits that were folded in are already reflected
2225
+ * in the driver's backing order, so a single rebuild here is equivalent to
2226
+ * having recomputed the reset on each of those edits.
2227
+ */
2228
+ materializeStaleReset(key) {
2229
+ if (!this.staleResetKeys.delete(key)) return;
2230
+ const cmds = this.commandsByKey.get(key);
2231
+ if (cmds && cmds.length > 0 && cmds[0].type === "reset") {
2232
+ cmds[0] = this.createResetCommand(this.getItemsByKey(key));
2233
+ }
2128
2234
  }
2129
2235
  appendCommand(key, cmd) {
2130
2236
  let cmds = this.commandsByKey.get(key);
@@ -2135,8 +2241,9 @@ var OIMOrderedListCommandBuffer = class {
2135
2241
  if (cmd.type === "reset") {
2136
2242
  cmds.length = 0;
2137
2243
  cmds.push(cmd);
2244
+ this.staleResetKeys.delete(key);
2138
2245
  } else if (cmds.length > 0 && cmds[0].type === "reset") {
2139
- cmds[0] = this.createResetCommand(this.getItemsByKey(key));
2246
+ this.staleResetKeys.add(key);
2140
2247
  } else {
2141
2248
  cmds.push(cmd);
2142
2249
  }
@@ -3178,10 +3285,6 @@ var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndex
3178
3285
  });
3179
3286
  this.keysByPk = /* @__PURE__ */ new Map();
3180
3287
  this.onCollectionUpdate = (pks) => {
3181
- if (pks.length === 0) {
3182
- this.rebuildFromCollection();
3183
- return;
3184
- }
3185
3288
  for (const pk of pks) {
3186
3289
  this.syncPk(pk);
3187
3290
  }
@@ -3283,10 +3386,6 @@ var OIMDerivedCollectionIndexArrayBased = class extends OIMReactiveCollectionInd
3283
3386
  // touching an unrelated field can skip re-sorting when the value is unchanged.
3284
3387
  this.orderValueByPk = /* @__PURE__ */ new Map();
3285
3388
  this.onCollectionUpdate = (pks) => {
3286
- if (pks.length === 0) {
3287
- this.rebuildFromCollection();
3288
- return;
3289
- }
3290
3389
  const ordered = !!(this.compareEntities || this.orderBy);
3291
3390
  const keysToSort = ordered ? /* @__PURE__ */ new Set() : null;
3292
3391
  for (const pk of pks) {
@@ -3703,10 +3802,6 @@ var OIMDerivedCollectionGlobalIndexArrayBased = class extends OIMReactiveCollect
3703
3802
  // an unrelated field can skip re-sorting when the value is unchanged.
3704
3803
  this.orderValueByPk = /* @__PURE__ */ new Map();
3705
3804
  this.onCollectionUpdate = (pks) => {
3706
- if (pks.length === 0) {
3707
- this.rebuildFromCollection();
3708
- return;
3709
- }
3710
3805
  const ordered = !!(this.compareEntities || this.orderBy);
3711
3806
  let needsSort = false;
3712
3807
  for (const pk of pks) {
@@ -3804,10 +3899,6 @@ var OIMDerivedCollectionGlobalIndexSetBased = class extends OIMReactiveCollectio
3804
3899
  super(queue, { collection, indexOptions: opts.indexOptions });
3805
3900
  this.presentPks = /* @__PURE__ */ new Set();
3806
3901
  this.onCollectionUpdate = (pks) => {
3807
- if (pks.length === 0) {
3808
- this.rebuildFromCollection();
3809
- return;
3810
- }
3811
3902
  for (const pk of pks) {
3812
3903
  const prev = this.presentPks.has(pk);
3813
3904
  const slot = this.collection.getSlotByPk(pk);
@@ -3983,6 +4074,55 @@ function createOIMCollectionIndexFactory(queue, collection) {
3983
4074
  return new OIMCollectionIndexFactory(queue, collection);
3984
4075
  }
3985
4076
 
4077
+ // src/core/OIMDerivedCollection.ts
4078
+ var OIMDerivedCollection = class extends OIMReactiveCollection {
4079
+ constructor(queue, source, derive, opts) {
4080
+ super(queue, {
4081
+ store: opts?.store,
4082
+ // Each sync recomputes the WHOLE derived entity → replace, not merge.
4083
+ updateEntity: (draft) => draft
4084
+ });
4085
+ this.onSourceUpdate = (pks) => {
4086
+ for (let i = 0; i < pks.length; i++) this.syncPk(pks[i]);
4087
+ };
4088
+ this.source = source;
4089
+ this.derive = derive;
4090
+ this.compareDerived = opts?.compare ?? Object.is;
4091
+ this.unsubscribeFromSource = source.emitter.on(
4092
+ 0 /* UPDATE */,
4093
+ (payload) => this.onSourceUpdate(payload.pks)
4094
+ );
4095
+ if (opts?.buildInitial !== false) {
4096
+ this.rebuildFromSource();
4097
+ }
4098
+ }
4099
+ rebuildFromSource() {
4100
+ for (const pk of this.getAllPks()) {
4101
+ if (this.source.getOneByPk(pk) === void 0) {
4102
+ this.removeOneByPk(pk);
4103
+ }
4104
+ }
4105
+ for (const slot of this.source.getAllSlots()) {
4106
+ if (slot.item !== void 0) this.syncPk(slot.pk);
4107
+ }
4108
+ }
4109
+ destroy() {
4110
+ this.unsubscribeFromSource();
4111
+ super.destroy();
4112
+ }
4113
+ syncPk(pk) {
4114
+ const sourceEntity = this.source.getOneByPk(pk);
4115
+ if (sourceEntity === void 0) {
4116
+ if (this.getOneByPk(pk) !== void 0) this.removeOneByPk(pk);
4117
+ return;
4118
+ }
4119
+ const next = this.derive(sourceEntity, pk);
4120
+ const prev = this.getOneByPk(pk);
4121
+ if (prev !== void 0 && this.compareDerived(prev, next)) return;
4122
+ this.upsertOneByPk(pk, next);
4123
+ }
4124
+ };
4125
+
3986
4126
  // src/core/OIMObjectStoreRecordDriven.ts
3987
4127
  var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
3988
4128
  constructor(initial) {
@@ -4055,8 +4195,11 @@ var OIMObject = class {
4055
4195
  return this.store.getAll();
4056
4196
  }
4057
4197
  clear() {
4198
+ const keys = this.store.keys();
4058
4199
  this.store.clear();
4059
- this.onUpdatedKeys([]);
4200
+ if (keys.length > 0) {
4201
+ this.onUpdatedKeys(keys);
4202
+ }
4060
4203
  }
4061
4204
  /**
4062
4205
  * Write fan-out hooks. Split single-key vs multi-key so a reactive subclass
@@ -4359,8 +4502,9 @@ var OIMComparatorFactory = class {
4359
4502
  if (ka.length !== kb.length) return true;
4360
4503
  for (let i = 0; i < ka.length; i++) {
4361
4504
  const k = ka[i];
4362
- if (b[k] !== a[k])
4505
+ if (b[k] !== a[k]) {
4363
4506
  return true;
4507
+ }
4364
4508
  }
4365
4509
  return false;
4366
4510
  };
@@ -4410,10 +4554,14 @@ var OIMEventQueue = class {
4410
4554
  return this.isFlushing;
4411
4555
  }
4412
4556
  /**
4413
- * Execute all pending tasks.
4557
+ * Execute the tasks pending at the moment the flush starts — a single pass,
4558
+ * not a drain-to-empty.
4414
4559
  *
4415
- * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
4416
- * executed within the same flush.
4560
+ * The pending set is swapped out for an empty spare up front and the flush
4561
+ * iterates that captured set. Tasks enqueued during the flush land in the
4562
+ * fresh set and run on the NEXT flush, not this one. (Contrast
4563
+ * `OIMComputeRuntime.flush()`, which re-reads its bucket per level and so
4564
+ * does drain same-level tasks scheduled mid-flush.)
4417
4565
  */
4418
4566
  flush() {
4419
4567
  this.scheduler?.cancel();
@@ -4687,20 +4835,92 @@ var OIMPkCodecKeyPath = class {
4687
4835
  }
4688
4836
  };
4689
4837
 
4838
+ // src/modules/effect/core/OIMEffectDependencyComputed.ts
4839
+ var OIMEffectDependencyComputed = class {
4840
+ constructor(source) {
4841
+ this.source = source;
4842
+ }
4843
+ subscribe(onUpdate) {
4844
+ return this.source.emitter.on("update", onUpdate);
4845
+ }
4846
+ };
4847
+
4690
4848
  // src/modules/compute/core/OIMComputeRuntime.ts
4691
4849
  var OIMComputeRuntime = class {
4692
4850
  constructor(queue) {
4693
4851
  this.queue = queue;
4694
4852
  this.isFlushScheduled = false;
4695
4853
  this.isFlushing = false;
4854
+ // Active level range (min > max means nothing pending).
4696
4855
  this.minLevel = Infinity;
4697
4856
  this.maxLevel = -Infinity;
4698
- this.dirtyByLevel = /* @__PURE__ */ new Map();
4857
+ this.dirtyByLevel = [];
4699
4858
  this.afterFlushTasks = /* @__PURE__ */ new Set();
4700
- this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
4701
- if (!this.isFlushScheduled) return;
4702
- this.flush();
4703
- });
4859
+ // Topological depth per computed node — the engine is the ONLY owner of
4860
+ // levels; nodes never expose their own. A node's dependents look up its depth
4861
+ // here (by the computed reference their dependency wraps). Written once at
4862
+ // construction, never on the hot flush path.
4863
+ this.levelByComputed = /* @__PURE__ */ new WeakMap();
4864
+ this._isDestroyed = false;
4865
+ this.unsubscribeFromQueue = this.queue.emitter.on(
4866
+ 0 /* AFTER_FLUSH */,
4867
+ () => {
4868
+ if (!this.isFlushScheduled) return;
4869
+ this.flush();
4870
+ }
4871
+ );
4872
+ }
4873
+ /**
4874
+ * Whether {@link destroy} has run. A destroyed runtime is detached from its
4875
+ * queue and must not be reused — `getOIMComputeRuntime` reads this to hand
4876
+ * out a fresh runtime instead of a dead one.
4877
+ */
4878
+ get isDestroyed() {
4879
+ return this._isDestroyed;
4880
+ }
4881
+ /**
4882
+ * Detach from the queue and drop everything pending.
4883
+ *
4884
+ * Without this the AFTER_FLUSH subscription lives as long as the queue does,
4885
+ * even once every computed / effect on this runtime is gone — harmless (the
4886
+ * handler early-returns) but impossible to unwind. Destroying does NOT
4887
+ * destroy the nodes scheduled on it; destroy them first, then the runtime.
4888
+ * Idempotent.
4889
+ */
4890
+ destroy() {
4891
+ if (this._isDestroyed) return;
4892
+ this._isDestroyed = true;
4893
+ this.unsubscribeFromQueue();
4894
+ for (let i = 0; i < this.dirtyByLevel.length; i++) {
4895
+ this.dirtyByLevel[i]?.clear();
4896
+ }
4897
+ this.afterFlushTasks.clear();
4898
+ this.minLevel = Infinity;
4899
+ this.maxLevel = -Infinity;
4900
+ this.isFlushScheduled = false;
4901
+ }
4902
+ /**
4903
+ * Topological depth for a node with these dependencies: one above the deepest
4904
+ * computed dependency (source-only → 0). The engine owns this — callers pass
4905
+ * their dependencies and get back a level, they never compute it themselves.
4906
+ */
4907
+ computeLevel(deps) {
4908
+ let max = -1;
4909
+ for (const dep of deps) {
4910
+ if (dep instanceof OIMEffectDependencyComputed) {
4911
+ const depLevel = this.levelByComputed.get(dep.source) ?? 0;
4912
+ if (depLevel > max) max = depLevel;
4913
+ }
4914
+ }
4915
+ return max + 1;
4916
+ }
4917
+ /** Record a computed's depth so its dependents can look it up. */
4918
+ registerLevel(computed, level) {
4919
+ this.levelByComputed.set(computed, level);
4920
+ }
4921
+ /** The recorded topological depth of a computed (0 if unknown). Introspection. */
4922
+ getLevel(computed) {
4923
+ return this.levelByComputed.get(computed) ?? 0;
4704
4924
  }
4705
4925
  /**
4706
4926
  * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
@@ -4712,10 +4932,10 @@ var OIMComputeRuntime = class {
4712
4932
  if (!isActive) return;
4713
4933
  task();
4714
4934
  };
4715
- let bucket = this.dirtyByLevel.get(level);
4716
- if (!bucket) {
4935
+ let bucket = this.dirtyByLevel[level];
4936
+ if (bucket === void 0) {
4717
4937
  bucket = /* @__PURE__ */ new Set();
4718
- this.dirtyByLevel.set(level, bucket);
4938
+ this.dirtyByLevel[level] = bucket;
4719
4939
  }
4720
4940
  bucket.add(wrapped);
4721
4941
  if (level < this.minLevel) this.minLevel = level;
@@ -4747,7 +4967,7 @@ var OIMComputeRuntime = class {
4747
4967
  }
4748
4968
  flush() {
4749
4969
  this.isFlushScheduled = false;
4750
- if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
4970
+ if (this.minLevel > this.maxLevel && this.afterFlushTasks.size === 0)
4751
4971
  return;
4752
4972
  this.isFlushing = true;
4753
4973
  try {
@@ -4756,15 +4976,15 @@ var OIMComputeRuntime = class {
4756
4976
  this.afterFlushTasks.clear();
4757
4977
  for (let i = 0; i < snapshot.length; i++) snapshot[i]();
4758
4978
  }
4759
- if (this.dirtyByLevel.size === 0) return;
4979
+ if (this.minLevel > this.maxLevel) return;
4760
4980
  let level = this.minLevel;
4761
4981
  let max = this.maxLevel;
4762
4982
  while (level <= max) {
4763
4983
  for (; ; ) {
4764
- const bucket = this.dirtyByLevel.get(level);
4765
- if (!bucket || bucket.size === 0) break;
4766
- this.dirtyByLevel.delete(level);
4984
+ const bucket = this.dirtyByLevel[level];
4985
+ if (bucket === void 0 || bucket.size === 0) break;
4767
4986
  const snapshot = Array.from(bucket);
4987
+ bucket.clear();
4768
4988
  for (let i = 0; i < snapshot.length; i++) snapshot[i]();
4769
4989
  }
4770
4990
  if (this.maxLevel > max) max = this.maxLevel;
@@ -4772,24 +4992,38 @@ var OIMComputeRuntime = class {
4772
4992
  }
4773
4993
  } finally {
4774
4994
  this.isFlushing = false;
4775
- if (this.dirtyByLevel.size === 0) {
4776
- this.minLevel = Infinity;
4777
- this.maxLevel = -Infinity;
4778
- } else {
4779
- let nextMin = Infinity;
4780
- let nextMax = -Infinity;
4781
- this.dirtyByLevel.forEach((_bucket, lvl) => {
4782
- if (lvl < nextMin) nextMin = lvl;
4783
- if (lvl > nextMax) nextMax = lvl;
4784
- });
4785
- this.minLevel = nextMin;
4786
- this.maxLevel = nextMax;
4787
- this.isFlushScheduled = true;
4995
+ this.recomputePendingRange();
4996
+ }
4997
+ }
4998
+ recomputePendingRange() {
4999
+ let nextMin = Infinity;
5000
+ let nextMax = -Infinity;
5001
+ const buckets = this.dirtyByLevel;
5002
+ for (let i = 0; i < buckets.length; i++) {
5003
+ const bucket = buckets[i];
5004
+ if (bucket !== void 0 && bucket.size > 0) {
5005
+ if (i < nextMin) nextMin = i;
5006
+ if (i > nextMax) nextMax = i;
4788
5007
  }
4789
5008
  }
5009
+ this.minLevel = nextMin;
5010
+ this.maxLevel = nextMax;
5011
+ if (nextMin <= nextMax) this.isFlushScheduled = true;
4790
5012
  }
4791
5013
  };
4792
5014
 
5015
+ // src/dx/oimComputeRuntimeByQueue.ts
5016
+ var oimComputeRuntimeByQueue = /* @__PURE__ */ new WeakMap();
5017
+
5018
+ // src/dx/getOIMComputeRuntime.ts
5019
+ function getOIMComputeRuntime(queue) {
5020
+ const runtime = oimComputeRuntimeByQueue.get(queue);
5021
+ if (runtime !== void 0 && !runtime.isDestroyed) return runtime;
5022
+ const nextRuntime = new OIMComputeRuntime(queue);
5023
+ oimComputeRuntimeByQueue.set(queue, nextRuntime);
5024
+ return nextRuntime;
5025
+ }
5026
+
4793
5027
  // src/modules/selector/core/OIMSelector.ts
4794
5028
  var OIMSelector = class {
4795
5029
  constructor(runtime, sourceDeps, computedDeps = []) {
@@ -5050,8 +5284,9 @@ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
5050
5284
  areEqual(prev, next) {
5051
5285
  if (prev === next) return true;
5052
5286
  if (prev.length !== next.length) return false;
5053
- for (let i = 0; i < prev.length; i++)
5287
+ for (let i = 0; i < prev.length; i++) {
5054
5288
  if (prev[i] !== next[i]) return false;
5289
+ }
5055
5290
  return true;
5056
5291
  }
5057
5292
  };
@@ -5121,8 +5356,9 @@ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
5121
5356
  areEqual(prev, next) {
5122
5357
  if (prev === next) return true;
5123
5358
  if (prev.length !== next.length) return false;
5124
- for (let i = 0; i < prev.length; i++)
5359
+ for (let i = 0; i < prev.length; i++) {
5125
5360
  if (prev[i] !== next[i]) return false;
5361
+ }
5126
5362
  return true;
5127
5363
  }
5128
5364
  };
@@ -5186,8 +5422,9 @@ var OIMEntitiesByGlobalIndexArrayBasedSelector = class extends OIMSelector {
5186
5422
  areEqual(prev, next) {
5187
5423
  if (prev === next) return true;
5188
5424
  if (prev.length !== next.length) return false;
5189
- for (let i = 0; i < prev.length; i++)
5425
+ for (let i = 0; i < prev.length; i++) {
5190
5426
  if (prev[i] !== next[i]) return false;
5427
+ }
5191
5428
  return true;
5192
5429
  }
5193
5430
  };
@@ -5252,8 +5489,9 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
5252
5489
  areEqual(prev, next) {
5253
5490
  if (prev === next) return true;
5254
5491
  if (prev.length !== next.length) return false;
5255
- for (let i = 0; i < prev.length; i++)
5492
+ for (let i = 0; i < prev.length; i++) {
5256
5493
  if (prev[i] !== next[i]) return false;
5494
+ }
5257
5495
  return true;
5258
5496
  }
5259
5497
  };
@@ -5262,7 +5500,7 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
5262
5500
  var OIMCollectionSelectors = class {
5263
5501
  constructor(queue, collection) {
5264
5502
  this.collection = collection;
5265
- this.runtime = new OIMComputeRuntime(queue);
5503
+ this.runtime = getOIMComputeRuntime(queue);
5266
5504
  }
5267
5505
  byPk(pk) {
5268
5506
  return new OIMCollectionByPkSelector(this.runtime, this.collection, pk);
@@ -5329,120 +5567,401 @@ var OIMCollectionSelectors = class {
5329
5567
  }
5330
5568
  };
5331
5569
 
5332
- // src/dx/OIMCollectionKit.ts
5333
- function createOIMCollectionKit(queue, opts) {
5334
- const collection = new OIMReactiveCollection(queue, opts);
5335
- const indexFactory = new OIMCollectionIndexFactory(queue, collection);
5336
- const select = new OIMCollectionSelectors(queue, collection);
5337
- const scope = new OIMDisposeScope();
5338
- scope.add(collection);
5339
- return {
5340
- queue,
5341
- collection,
5342
- indexFactory,
5343
- select,
5344
- scope,
5345
- destroy: () => scope.destroy()
5346
- };
5347
- }
5570
+ // src/modules/effect/core/OIMEffectDependencyKeyed.ts
5571
+ var OIMEffectDependencyKeyed = class {
5572
+ constructor(source, key) {
5573
+ this.source = source;
5574
+ this.key = key;
5575
+ }
5576
+ subscribe(onUpdate) {
5577
+ return this.source.subscribeOnKey(this.key, onUpdate);
5578
+ }
5579
+ };
5348
5580
 
5349
- // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
5350
- var OIMCollectionChangedFields = class {
5351
- constructor(queue, collection, opts) {
5352
- this.isInWrite = false;
5353
- this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
5354
- // PKs written through this wrapper during the current flush. Their fields are
5355
- // already tracked accurately via trackPatch, so the flush-time external-write
5356
- // handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
5357
- this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
5358
- this.changedFieldsByPk = /* @__PURE__ */ new Map();
5359
- this.changedPksByField = /* @__PURE__ */ new Map();
5360
- this.onAfterFlush = () => {
5361
- this.clear();
5362
- this.writtenThroughThisFlush.clear();
5363
- };
5364
- this.onCollectionUpdate = (payload) => {
5365
- if (this.isInWrite) return;
5366
- if (payload.pks.length === 0) return;
5367
- for (let i = 0; i < payload.pks.length; i++) {
5368
- const pk = payload.pks[i];
5369
- if (this.writtenThroughThisFlush.has(pk)) continue;
5370
- this.trackExternalMutationByPk(pk);
5371
- }
5372
- };
5373
- this.collection = collection;
5374
- this.detectExternalMutations = opts?.detectExternalMutations !== false;
5375
- this.selectPk = opts?.selectPk ?? (() => {
5376
- throw new Error(
5377
- `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
5378
- );
5379
- });
5380
- this.changedPksEventEmitter = new OIMUpdateEventEmitter(
5381
- queue,
5382
- "after_flush"
5383
- );
5384
- this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
5385
- this.unsubscribeAfterFlush = queue.emitter.on(
5386
- 0 /* AFTER_FLUSH */,
5387
- this.onAfterFlush
5388
- );
5389
- if (this.detectExternalMutations) {
5390
- const maybeReactive = collection;
5391
- this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
5392
- this.onCollectionUpdate({
5393
- pks
5394
- });
5395
- }) : collection.emitter.on(
5396
- 0 /* UPDATE */,
5397
- this.onCollectionUpdate
5398
- );
5399
- }
5581
+ // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
5582
+ var OIMEffectDependencyKeyedCollection = class {
5583
+ constructor(collection, pk) {
5584
+ this.dep = new OIMEffectDependencyKeyed(collection, pk);
5400
5585
  }
5401
- upsertOneByPk(pk, patch) {
5402
- const slot = this.withWrite(() => {
5403
- this.trackPatch(pk, patch);
5404
- return this.collection.upsertOneByPk(pk, patch);
5405
- });
5406
- this.updateEntitySnapshot(pk);
5407
- return slot;
5586
+ get source() {
5587
+ return this.dep.source;
5408
5588
  }
5409
- upsertOne(entity) {
5410
- const slot = this.withWrite(() => {
5411
- const pk = this.selectPk(entity);
5412
- this.trackPatch(pk, entity);
5413
- return this.collection.upsertOne(entity);
5414
- });
5415
- this.updateEntitySnapshot(slot.pk);
5416
- return slot;
5589
+ subscribe(onUpdate) {
5590
+ return this.dep.subscribe(onUpdate);
5417
5591
  }
5418
- upsertMany(entities) {
5419
- const pks = [];
5420
- const slots = this.withWrite(() => {
5421
- for (const entity of entities) {
5422
- const pk = this.selectPk(entity);
5423
- pks.push(pk);
5424
- this.trackPatch(pk, entity);
5425
- }
5426
- return this.collection.upsertMany(entities);
5427
- });
5428
- for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
5429
- return slots;
5592
+ };
5593
+
5594
+ // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
5595
+ var OIMEffectDependencyKeyedIndex = class {
5596
+ constructor(index, key) {
5597
+ this.dep = new OIMEffectDependencyKeyed(index, key);
5430
5598
  }
5431
- getChangedFieldsByPk(pk) {
5432
- return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
5599
+ get source() {
5600
+ return this.dep.source;
5433
5601
  }
5434
- consumeChangedFieldsByPk(pk) {
5435
- const existing = this.changedFieldsByPk.get(pk);
5436
- if (!existing) return /* @__PURE__ */ new Set();
5437
- return new Set(existing);
5602
+ subscribe(onUpdate) {
5603
+ return this.dep.subscribe(onUpdate);
5438
5604
  }
5439
- getChangedPksByField(field) {
5440
- return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
5605
+ };
5606
+
5607
+ // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
5608
+ var OIMEffectDependencyKeyedObject = class {
5609
+ constructor(object, key) {
5610
+ this.dep = new OIMEffectDependencyKeyed(object, key);
5441
5611
  }
5442
- consumeChangedPksByField(field) {
5443
- const existing = this.changedPksByField.get(field);
5444
- if (!existing) return /* @__PURE__ */ new Set();
5445
- return new Set(existing);
5612
+ get source() {
5613
+ return this.dep.source;
5614
+ }
5615
+ subscribe(onUpdate) {
5616
+ return this.dep.subscribe(onUpdate);
5617
+ }
5618
+ };
5619
+
5620
+ // src/dx/on.ts
5621
+ var on = {
5622
+ /** Depend on one PK of a collection. */
5623
+ collection: (collection, pk) => new OIMEffectDependencyKeyedCollection(collection, pk),
5624
+ /** Depend on one key of an index. */
5625
+ index: (index, key) => new OIMEffectDependencyKeyedIndex(index, key),
5626
+ /** Depend on one key of a reactive object. */
5627
+ object: (object, key) => new OIMEffectDependencyKeyedObject(object, key),
5628
+ /** Depend on another computed. */
5629
+ computed: (computed) => new OIMEffectDependencyComputed(computed)
5630
+ };
5631
+
5632
+ // src/modules/effect/core/OIMEffect.ts
5633
+ var OIMEffect = class {
5634
+ constructor(runtime, opts, level) {
5635
+ this.sourceDeps = [];
5636
+ this.computedDeps = [];
5637
+ this.sourceUnsubscribers = [];
5638
+ this.computedUnsubscribers = [];
5639
+ this.isResubscribeEnqueued = false;
5640
+ this.isTemporarilyUnsubscribedForFlush = false;
5641
+ this.isScheduled = false;
5642
+ this.runTask = () => {
5643
+ this.isScheduled = false;
5644
+ this.dequeueScheduled = void 0;
5645
+ this.runOnce();
5646
+ };
5647
+ this.onSourceInvalidate = () => {
5648
+ this.onUpdate?.();
5649
+ if (!this.isTemporarilyUnsubscribedForFlush) {
5650
+ this.isTemporarilyUnsubscribedForFlush = true;
5651
+ this.unsubscribeFromSourceDeps();
5652
+ this.ensureResubscribeAfterFlush();
5653
+ }
5654
+ if (this.isScheduled) return;
5655
+ this.isScheduled = true;
5656
+ this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
5657
+ };
5658
+ this.onComputedInvalidate = () => {
5659
+ this.onUpdate?.();
5660
+ if (this.isScheduled) return;
5661
+ this.isScheduled = true;
5662
+ this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
5663
+ };
5664
+ this.onAfterFlush = () => {
5665
+ if (!this.isResubscribeEnqueued) return;
5666
+ this.isResubscribeEnqueued = false;
5667
+ this.dequeueResubscribe = void 0;
5668
+ this.isTemporarilyUnsubscribedForFlush = false;
5669
+ if (this.sourceUnsubscribers.length === 0) {
5670
+ for (const dep of this.sourceDeps) {
5671
+ this.sourceUnsubscribers.push(
5672
+ dep.subscribe(this.onSourceInvalidate)
5673
+ );
5674
+ }
5675
+ }
5676
+ };
5677
+ this.runtime = runtime;
5678
+ this.onUpdate = opts.onUpdate;
5679
+ this.run = opts.run;
5680
+ this.deps = opts.deps ?? [];
5681
+ for (const dep of this.deps) {
5682
+ if (dep instanceof OIMEffectDependencyComputed) {
5683
+ this.computedDeps.push(dep);
5684
+ } else {
5685
+ this.sourceDeps.push(dep);
5686
+ }
5687
+ }
5688
+ this.level = level ?? this.runtime.computeLevel(this.deps);
5689
+ this.subscribeToDeps();
5690
+ }
5691
+ destroy() {
5692
+ if (this.dequeueScheduled) {
5693
+ this.dequeueScheduled();
5694
+ this.dequeueScheduled = void 0;
5695
+ }
5696
+ this.unsubscribeFromAllDeps();
5697
+ if (this.dequeueResubscribe) {
5698
+ this.dequeueResubscribe();
5699
+ this.dequeueResubscribe = void 0;
5700
+ }
5701
+ this.isResubscribeEnqueued = false;
5702
+ this.isTemporarilyUnsubscribedForFlush = false;
5703
+ }
5704
+ runOnce() {
5705
+ this.run();
5706
+ }
5707
+ subscribeToDeps() {
5708
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
5709
+ return;
5710
+ for (const dep of this.sourceDeps) {
5711
+ this.sourceUnsubscribers.push(
5712
+ dep.subscribe(this.onSourceInvalidate)
5713
+ );
5714
+ }
5715
+ for (const dep of this.computedDeps) {
5716
+ this.computedUnsubscribers.push(
5717
+ dep.subscribe(this.onComputedInvalidate)
5718
+ );
5719
+ }
5720
+ }
5721
+ unsubscribeFromSourceDeps() {
5722
+ if (this.sourceUnsubscribers.length === 0) return;
5723
+ for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
5724
+ this.sourceUnsubscribers = [];
5725
+ }
5726
+ unsubscribeFromAllDeps() {
5727
+ this.unsubscribeFromSourceDeps();
5728
+ if (this.computedUnsubscribers.length === 0) return;
5729
+ for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
5730
+ this.computedUnsubscribers = [];
5731
+ }
5732
+ ensureResubscribeAfterFlush() {
5733
+ if (this.isResubscribeEnqueued) return;
5734
+ this.isResubscribeEnqueued = true;
5735
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
5736
+ this.onAfterFlush
5737
+ );
5738
+ }
5739
+ };
5740
+
5741
+ // src/modules/computed/enums/EOIMComputedEventType.ts
5742
+ var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
5743
+ EOIMComputedEventType2["UPDATE"] = "update";
5744
+ return EOIMComputedEventType2;
5745
+ })(EOIMComputedEventType || {});
5746
+
5747
+ // src/modules/computed/core/OIMComputed.ts
5748
+ var OIMComputed = class {
5749
+ constructor(runtime, opts) {
5750
+ this.emitter = new OIMEventEmitter();
5751
+ this.hasValue = false;
5752
+ this.isDirty = true;
5753
+ this.runtime = runtime;
5754
+ this.compute = opts.compute;
5755
+ this.compare = opts.compare ?? Object.is;
5756
+ this._deps = opts.deps ?? [];
5757
+ this.updateEventEmitter = new OIMUpdateEventEmitter(
5758
+ this.runtime.queue,
5759
+ "immediate"
5760
+ );
5761
+ const level = this.runtime.computeLevel(this._deps);
5762
+ this.runtime.registerLevel(this, level);
5763
+ this.effect = new OIMEffect(
5764
+ this.runtime,
5765
+ {
5766
+ deps: this._deps,
5767
+ onUpdate: () => {
5768
+ this.isDirty = true;
5769
+ },
5770
+ run: () => {
5771
+ this.recomputeAndEmitIfChanged();
5772
+ }
5773
+ },
5774
+ level
5775
+ );
5776
+ }
5777
+ get needsRecompute() {
5778
+ return this.isDirty;
5779
+ }
5780
+ get isReady() {
5781
+ return this.hasValue;
5782
+ }
5783
+ get deps() {
5784
+ return this._deps;
5785
+ }
5786
+ get() {
5787
+ this.recomputeAndEmitIfChanged();
5788
+ return this.value;
5789
+ }
5790
+ getIfReady() {
5791
+ return this.hasValue ? this.value : void 0;
5792
+ }
5793
+ destroy() {
5794
+ this.effect.destroy();
5795
+ this.updateEventEmitter.destroy();
5796
+ this.emitter.offAll();
5797
+ }
5798
+ recomputeAndEmitIfChanged() {
5799
+ if (!this.isDirty) return;
5800
+ const prev = this.value;
5801
+ const hadPrev = this.hasValue;
5802
+ const next = this.compute();
5803
+ this.value = next;
5804
+ this.hasValue = true;
5805
+ this.isDirty = false;
5806
+ const changed = !hadPrev || !this.compare(prev, next);
5807
+ if (!changed) return;
5808
+ this.emitter.emit("update" /* UPDATE */, {
5809
+ keys: ["value"]
5810
+ });
5811
+ this.updateEventEmitter.markUpdatedKey("value");
5812
+ }
5813
+ };
5814
+
5815
+ // src/dx/OIMCollectionKit.ts
5816
+ function createOIMCollectionKit(queue, opts) {
5817
+ const collection = new OIMReactiveCollection(queue, opts);
5818
+ const indexFactory = new OIMCollectionIndexFactory(queue, collection);
5819
+ const select = new OIMCollectionSelectors(queue, collection);
5820
+ const runtime = getOIMComputeRuntime(queue);
5821
+ const scope = new OIMDisposeScope();
5822
+ scope.add(collection);
5823
+ return {
5824
+ queue,
5825
+ collection,
5826
+ indexFactory,
5827
+ select,
5828
+ on,
5829
+ computed: (deps, compute, options) => {
5830
+ const computed = new OIMComputed(runtime, {
5831
+ deps,
5832
+ compute,
5833
+ compare: options?.compare
5834
+ });
5835
+ scope.add(computed);
5836
+ return computed;
5837
+ },
5838
+ effect: (deps, run, options) => {
5839
+ const effect = new OIMEffect(runtime, {
5840
+ deps,
5841
+ run,
5842
+ onUpdate: options?.onUpdate
5843
+ });
5844
+ scope.add(effect);
5845
+ return effect;
5846
+ },
5847
+ scope,
5848
+ destroy: () => scope.destroy()
5849
+ };
5850
+ }
5851
+
5852
+ // src/dx/peekOIMComputeRuntime.ts
5853
+ function peekOIMComputeRuntime(queue) {
5854
+ const runtime = oimComputeRuntimeByQueue.get(queue);
5855
+ return runtime === void 0 || runtime.isDestroyed ? void 0 : runtime;
5856
+ }
5857
+
5858
+ // src/dx/setOIMComputeRuntime.ts
5859
+ function setOIMComputeRuntime(queue, runtime) {
5860
+ if (runtime.queue !== queue) {
5861
+ throw new Error(
5862
+ "setOIMComputeRuntime: the runtime is attached to a different queue. A runtime drains on its own queue and would never run here."
5863
+ );
5864
+ }
5865
+ oimComputeRuntimeByQueue.set(queue, runtime);
5866
+ }
5867
+
5868
+ // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
5869
+ var OIMCollectionChangedFields = class {
5870
+ constructor(queue, collection, opts) {
5871
+ this.isInWrite = false;
5872
+ this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
5873
+ // PKs written through this wrapper during the current flush. Their fields are
5874
+ // already tracked accurately via trackPatch, so the flush-time external-write
5875
+ // handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
5876
+ this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
5877
+ this.changedFieldsByPk = /* @__PURE__ */ new Map();
5878
+ this.changedPksByField = /* @__PURE__ */ new Map();
5879
+ this.onAfterFlush = () => {
5880
+ this.clear();
5881
+ this.writtenThroughThisFlush.clear();
5882
+ };
5883
+ this.onCollectionUpdate = (payload) => {
5884
+ if (this.isInWrite) return;
5885
+ if (payload.pks.length === 0) return;
5886
+ for (let i = 0; i < payload.pks.length; i++) {
5887
+ const pk = payload.pks[i];
5888
+ if (this.writtenThroughThisFlush.has(pk)) continue;
5889
+ this.trackExternalMutationByPk(pk);
5890
+ }
5891
+ };
5892
+ this.collection = collection;
5893
+ this.detectExternalMutations = opts?.detectExternalMutations !== false;
5894
+ this.selectPk = opts?.selectPk ?? (() => {
5895
+ throw new Error(
5896
+ `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
5897
+ );
5898
+ });
5899
+ this.changedPksEventEmitter = new OIMUpdateEventEmitter(
5900
+ queue,
5901
+ "after_flush"
5902
+ );
5903
+ this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
5904
+ this.unsubscribeAfterFlush = queue.emitter.on(
5905
+ 0 /* AFTER_FLUSH */,
5906
+ this.onAfterFlush
5907
+ );
5908
+ if (this.detectExternalMutations) {
5909
+ const maybeReactive = collection;
5910
+ this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
5911
+ this.onCollectionUpdate({
5912
+ pks
5913
+ });
5914
+ }) : collection.emitter.on(
5915
+ 0 /* UPDATE */,
5916
+ this.onCollectionUpdate
5917
+ );
5918
+ }
5919
+ }
5920
+ upsertOneByPk(pk, patch) {
5921
+ const slot = this.withWrite(() => {
5922
+ this.trackPatch(pk, patch);
5923
+ return this.collection.upsertOneByPk(pk, patch);
5924
+ });
5925
+ this.updateEntitySnapshot(pk);
5926
+ return slot;
5927
+ }
5928
+ upsertOne(entity) {
5929
+ const slot = this.withWrite(() => {
5930
+ const pk = this.selectPk(entity);
5931
+ this.trackPatch(pk, entity);
5932
+ return this.collection.upsertOne(entity);
5933
+ });
5934
+ this.updateEntitySnapshot(slot.pk);
5935
+ return slot;
5936
+ }
5937
+ upsertMany(entities) {
5938
+ const pks = [];
5939
+ const slots = this.withWrite(() => {
5940
+ for (const entity of entities) {
5941
+ const pk = this.selectPk(entity);
5942
+ pks.push(pk);
5943
+ this.trackPatch(pk, entity);
5944
+ }
5945
+ return this.collection.upsertMany(entities);
5946
+ });
5947
+ for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
5948
+ return slots;
5949
+ }
5950
+ getChangedFieldsByPk(pk) {
5951
+ return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
5952
+ }
5953
+ consumeChangedFieldsByPk(pk) {
5954
+ const existing = this.changedFieldsByPk.get(pk);
5955
+ if (!existing) return /* @__PURE__ */ new Set();
5956
+ return new Set(existing);
5957
+ }
5958
+ getChangedPksByField(field) {
5959
+ return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
5960
+ }
5961
+ consumeChangedPksByField(field) {
5962
+ const existing = this.changedPksByField.get(field);
5963
+ if (!existing) return /* @__PURE__ */ new Set();
5964
+ return new Set(existing);
5446
5965
  }
5447
5966
  clear() {
5448
5967
  this.changedFieldsByPk.clear();
@@ -5488,8 +6007,9 @@ var OIMCollectionChangedFields = class {
5488
6007
  Object.keys(nextObj).forEach((k) => keys.add(k));
5489
6008
  const changed = [];
5490
6009
  keys.forEach((k) => {
5491
- if (prevObj[k] !== nextObj[k])
6010
+ if (prevObj[k] !== nextObj[k]) {
5492
6011
  changed.push(k);
6012
+ }
5493
6013
  });
5494
6014
  return changed;
5495
6015
  }
@@ -5655,10 +6175,15 @@ var OIMOrderedListMappedCommandStream = class _OIMOrderedListMappedCommandStream
5655
6175
  try {
5656
6176
  const subs = this.subscribers.get(key);
5657
6177
  if (subs && subs.size > 0) {
5658
- const snapshot = Array.from(subs);
5659
- for (let s = 0; s < snapshot.length; s++) {
5660
- const handler = snapshot[s];
5661
- if (subs.has(handler)) handler();
6178
+ if (subs.size === 1) {
6179
+ const only = subs.values().next().value;
6180
+ if (only) only();
6181
+ } else {
6182
+ const snapshot = Array.from(subs);
6183
+ for (let s = 0; s < snapshot.length; s++) {
6184
+ const handler = snapshot[s];
6185
+ if (subs.has(handler)) handler();
6186
+ }
5662
6187
  }
5663
6188
  }
5664
6189
  } finally {
@@ -5952,10 +6477,13 @@ var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler
5952
6477
  this.flush();
5953
6478
  });
5954
6479
  } else {
5955
- this.frameId = setTimeout(() => {
5956
- this.frameId = void 0;
5957
- this.flush();
5958
- }, 16);
6480
+ this.frameId = this.unref(
6481
+ setTimeout(() => {
6482
+ this.frameId = void 0;
6483
+ this.flush();
6484
+ }, 16)
6485
+ // ~60fps
6486
+ );
5959
6487
  }
5960
6488
  }
5961
6489
  cancel() {
@@ -6001,10 +6529,12 @@ var OIMEventQueueSchedulerTimeout = class extends OIMEventQueueScheduler {
6001
6529
  }
6002
6530
  schedule() {
6003
6531
  if (this.timeoutId !== void 0) return;
6004
- this.timeoutId = setTimeout(() => {
6005
- this.timeoutId = void 0;
6006
- this.flush();
6007
- }, this.delay);
6532
+ this.timeoutId = this.unref(
6533
+ setTimeout(() => {
6534
+ this.timeoutId = void 0;
6535
+ this.flush();
6536
+ }, this.delay)
6537
+ );
6008
6538
  }
6009
6539
  cancel() {
6010
6540
  if (this.timeoutId === void 0) return;
@@ -6038,13 +6568,13 @@ var OIMEventQueueSchedulerImmediate = class extends OIMEventQueueScheduler {
6038
6568
  this.flush();
6039
6569
  };
6040
6570
  if (this.useSetImmediate) {
6041
- this.immediateId = setImmediate(callback);
6571
+ this.immediateId = this.unref(setImmediate(callback));
6042
6572
  } else if (this.useMessageChannel && this.messageChannel) {
6043
6573
  this.immediateId = 1;
6044
6574
  this.pendingCallback = callback;
6045
6575
  this.messageChannel.port1.postMessage(null);
6046
6576
  } else {
6047
- this.immediateId = setTimeout(callback, 0);
6577
+ this.immediateId = this.unref(setTimeout(callback, 0));
6048
6578
  }
6049
6579
  }
6050
6580
  cancel() {
@@ -6137,242 +6667,6 @@ var OIMDBSettings = class {
6137
6667
  OIMDBSettings.UPDATED_KEYS_INDEX_KEY = "__updatedKeys__";
6138
6668
  OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
6139
6669
 
6140
- // src/modules/effect/core/OIMEffectDependencyComputed.ts
6141
- var OIMEffectDependencyComputed = class {
6142
- constructor(source) {
6143
- this.source = source;
6144
- }
6145
- subscribe(onUpdate) {
6146
- return this.source.emitter.on("update", onUpdate);
6147
- }
6148
- };
6149
-
6150
- // src/modules/effect/core/OIMEffect.ts
6151
- var OIMEffect = class {
6152
- constructor(runtime, opts) {
6153
- this.sourceDeps = [];
6154
- this.computedDeps = [];
6155
- this.sourceUnsubscribers = [];
6156
- this.computedUnsubscribers = [];
6157
- this.isResubscribeEnqueued = false;
6158
- this.isTemporarilyUnsubscribedForFlush = false;
6159
- this.isScheduled = false;
6160
- this.runTask = () => {
6161
- this.isScheduled = false;
6162
- this.dequeueScheduled = void 0;
6163
- this.runOnce();
6164
- };
6165
- this.onSourceInvalidate = () => {
6166
- this.onUpdate?.();
6167
- if (!this.isTemporarilyUnsubscribedForFlush) {
6168
- this.isTemporarilyUnsubscribedForFlush = true;
6169
- this.unsubscribeFromSourceDeps();
6170
- this.ensureResubscribeAfterFlush();
6171
- }
6172
- if (this.isScheduled) return;
6173
- this.isScheduled = true;
6174
- this.dequeueScheduled = this.runtime.schedule(this.runTask);
6175
- };
6176
- this.onComputedInvalidate = () => {
6177
- this.onUpdate?.();
6178
- if (this.isScheduled) return;
6179
- this.isScheduled = true;
6180
- this.dequeueScheduled = this.runtime.schedule(this.runTask);
6181
- };
6182
- this.onAfterFlush = () => {
6183
- if (!this.isResubscribeEnqueued) return;
6184
- this.isResubscribeEnqueued = false;
6185
- this.dequeueResubscribe = void 0;
6186
- this.isTemporarilyUnsubscribedForFlush = false;
6187
- if (this.sourceUnsubscribers.length === 0) {
6188
- for (const dep of this.sourceDeps) {
6189
- this.sourceUnsubscribers.push(
6190
- dep.subscribe(this.onSourceInvalidate)
6191
- );
6192
- }
6193
- }
6194
- };
6195
- this.runtime = runtime;
6196
- this.onUpdate = opts.onUpdate;
6197
- this.run = opts.run;
6198
- this.deps = opts.deps ?? [];
6199
- for (const dep of this.deps) {
6200
- if (dep instanceof OIMEffectDependencyComputed) {
6201
- this.computedDeps.push(dep);
6202
- } else {
6203
- this.sourceDeps.push(dep);
6204
- }
6205
- }
6206
- this.subscribeToDeps();
6207
- }
6208
- destroy() {
6209
- if (this.dequeueScheduled) {
6210
- this.dequeueScheduled();
6211
- this.dequeueScheduled = void 0;
6212
- }
6213
- this.unsubscribeFromAllDeps();
6214
- if (this.dequeueResubscribe) {
6215
- this.dequeueResubscribe();
6216
- this.dequeueResubscribe = void 0;
6217
- }
6218
- this.isResubscribeEnqueued = false;
6219
- this.isTemporarilyUnsubscribedForFlush = false;
6220
- }
6221
- runOnce() {
6222
- this.run();
6223
- }
6224
- subscribeToDeps() {
6225
- if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
6226
- return;
6227
- for (const dep of this.sourceDeps) {
6228
- this.sourceUnsubscribers.push(
6229
- dep.subscribe(this.onSourceInvalidate)
6230
- );
6231
- }
6232
- for (const dep of this.computedDeps) {
6233
- this.computedUnsubscribers.push(
6234
- dep.subscribe(this.onComputedInvalidate)
6235
- );
6236
- }
6237
- }
6238
- unsubscribeFromSourceDeps() {
6239
- if (this.sourceUnsubscribers.length === 0) return;
6240
- for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
6241
- this.sourceUnsubscribers = [];
6242
- }
6243
- unsubscribeFromAllDeps() {
6244
- this.unsubscribeFromSourceDeps();
6245
- if (this.computedUnsubscribers.length === 0) return;
6246
- for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
6247
- this.computedUnsubscribers = [];
6248
- }
6249
- ensureResubscribeAfterFlush() {
6250
- if (this.isResubscribeEnqueued) return;
6251
- this.isResubscribeEnqueued = true;
6252
- this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
6253
- this.onAfterFlush
6254
- );
6255
- }
6256
- };
6257
-
6258
- // src/modules/computed/enums/EOIMComputedEventType.ts
6259
- var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
6260
- EOIMComputedEventType2["UPDATE"] = "update";
6261
- return EOIMComputedEventType2;
6262
- })(EOIMComputedEventType || {});
6263
-
6264
- // src/modules/computed/core/OIMComputed.ts
6265
- var OIMComputed = class {
6266
- constructor(runtime, opts) {
6267
- this.emitter = new OIMEventEmitter();
6268
- this.hasValue = false;
6269
- this.isDirty = true;
6270
- this.runtime = runtime;
6271
- this.compute = opts.compute;
6272
- this.compare = opts.compare ?? Object.is;
6273
- this._deps = opts.deps ?? [];
6274
- this.updateEventEmitter = new OIMUpdateEventEmitter(
6275
- this.runtime.queue,
6276
- "immediate"
6277
- );
6278
- this.effect = new OIMEffect(this.runtime, {
6279
- deps: this._deps,
6280
- onUpdate: () => {
6281
- this.isDirty = true;
6282
- },
6283
- run: () => {
6284
- this.recomputeAndEmitIfChanged();
6285
- }
6286
- });
6287
- }
6288
- get needsRecompute() {
6289
- return this.isDirty;
6290
- }
6291
- get isReady() {
6292
- return this.hasValue;
6293
- }
6294
- get deps() {
6295
- return this._deps;
6296
- }
6297
- get() {
6298
- this.recomputeAndEmitIfChanged();
6299
- return this.value;
6300
- }
6301
- getIfReady() {
6302
- return this.hasValue ? this.value : void 0;
6303
- }
6304
- destroy() {
6305
- this.effect.destroy();
6306
- this.updateEventEmitter.destroy();
6307
- this.emitter.offAll();
6308
- }
6309
- recomputeAndEmitIfChanged() {
6310
- if (!this.isDirty) return;
6311
- const prev = this.value;
6312
- const hadPrev = this.hasValue;
6313
- const next = this.compute();
6314
- this.value = next;
6315
- this.hasValue = true;
6316
- this.isDirty = false;
6317
- const changed = !hadPrev || !this.compare(prev, next);
6318
- if (!changed) return;
6319
- this.emitter.emit("update" /* UPDATE */, {
6320
- keys: ["value"]
6321
- });
6322
- this.updateEventEmitter.markUpdatedKey("value");
6323
- }
6324
- };
6325
-
6326
- // src/modules/effect/core/OIMEffectDependencyKeyed.ts
6327
- var OIMEffectDependencyKeyed = class {
6328
- constructor(source, key) {
6329
- this.source = source;
6330
- this.key = key;
6331
- }
6332
- subscribe(onUpdate) {
6333
- return this.source.subscribeOnKey(this.key, onUpdate);
6334
- }
6335
- };
6336
-
6337
- // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
6338
- var OIMEffectDependencyKeyedObject = class {
6339
- constructor(object, key) {
6340
- this.dep = new OIMEffectDependencyKeyed(object, key);
6341
- }
6342
- get source() {
6343
- return this.dep.source;
6344
- }
6345
- subscribe(onUpdate) {
6346
- return this.dep.subscribe(onUpdate);
6347
- }
6348
- };
6349
-
6350
- // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
6351
- var OIMEffectDependencyKeyedCollection = class {
6352
- constructor(collection, pk) {
6353
- this.dep = new OIMEffectDependencyKeyed(collection, pk);
6354
- }
6355
- get source() {
6356
- return this.dep.source;
6357
- }
6358
- subscribe(onUpdate) {
6359
- return this.dep.subscribe(onUpdate);
6360
- }
6361
- };
6362
-
6363
- // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
6364
- var OIMEffectDependencyKeyedIndex = class {
6365
- constructor(index, key) {
6366
- this.dep = new OIMEffectDependencyKeyed(index, key);
6367
- }
6368
- get source() {
6369
- return this.dep.source;
6370
- }
6371
- subscribe(onUpdate) {
6372
- return this.dep.subscribe(onUpdate);
6373
- }
6374
- };
6375
-
6376
6670
  // src/modules/selector/core/OIMObjectValueByKeySelector.ts
6377
6671
  var OIMObjectValueByKeySelector = class extends OIMSelector {
6378
6672
  constructor(runtime, obj, key) {
@@ -6450,8 +6744,9 @@ var OIMIndexPksByKeyArrayBasedSelector = class extends OIMSelector {
6450
6744
  areEqual(prev, next) {
6451
6745
  if (prev === next) return true;
6452
6746
  if (prev.length !== next.length) return false;
6453
- for (let i = 0; i < prev.length; i++)
6747
+ for (let i = 0; i < prev.length; i++) {
6454
6748
  if (prev[i] !== next[i]) return false;
6749
+ }
6455
6750
  return true;
6456
6751
  }
6457
6752
  };
@@ -6474,8 +6769,9 @@ var OIMIndexPksByKeysSetBasedSelector = class extends OIMSelector {
6474
6769
  areEqual(prev, next) {
6475
6770
  if (prev === next) return true;
6476
6771
  if (prev.length !== next.length) return false;
6477
- for (let i = 0; i < prev.length; i++)
6772
+ for (let i = 0; i < prev.length; i++) {
6478
6773
  if (prev[i] !== next[i]) return false;
6774
+ }
6479
6775
  return true;
6480
6776
  }
6481
6777
  };
@@ -6498,8 +6794,9 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
6498
6794
  areEqual(prev, next) {
6499
6795
  if (prev === next) return true;
6500
6796
  if (prev.length !== next.length) return false;
6501
- for (let i = 0; i < prev.length; i++)
6797
+ for (let i = 0; i < prev.length; i++) {
6502
6798
  if (prev[i] !== next[i]) return false;
6799
+ }
6503
6800
  return true;
6504
6801
  }
6505
6802
  };
@@ -6529,6 +6826,7 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
6529
6826
  OIMComputeRuntime,
6530
6827
  OIMComputed,
6531
6828
  OIMDBSettings,
6829
+ OIMDerivedCollection,
6532
6830
  OIMDerivedCollectionGlobalIndexArrayBased,
6533
6831
  OIMDerivedCollectionGlobalIndexSetBased,
6534
6832
  OIMDerivedCollectionIndexArrayBased,
@@ -6624,5 +6922,9 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
6624
6922
  createOIMIndexSlotMap,
6625
6923
  createOIMOrderedListCommandStreamDiffDriven,
6626
6924
  createOIMOrderedListMappedCommandStream,
6627
- diffOrderedListByPk
6925
+ diffOrderedListByPk,
6926
+ getOIMComputeRuntime,
6927
+ on,
6928
+ peekOIMComputeRuntime,
6929
+ setOIMComputeRuntime
6628
6930
  });