@oimdb/core 3.9.0 → 3.11.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/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
@@ -704,19 +728,28 @@ var OIMCarrierKeyedEmitter = class {
704
728
  // doubles as membership: a carrier is in this array iff `carrier.dirty`.
705
729
  this.dirtyCarriers = [];
706
730
  this.isFlushEnqueued = false;
731
+ // Number of live subscriptions whose single handler spans 2+ distinct
732
+ // carriers (a `subscribeOnKeys` over distinct keys). While this is > 0 a
733
+ // flush coalesces delivery so such a handler fires at most ONCE per flush,
734
+ // not once per changed carrier — the callback carries no key, so extra
735
+ // fires are pure waste. At 0 the hot single-subscriber path pays nothing:
736
+ // no per-flush Set, no membership checks.
737
+ this.multiCarrierSubscriptions = 0;
707
738
  this.onFlush = () => {
708
739
  this.isFlushEnqueued = false;
709
740
  if (this.dirtyCarriers.length === 0) return;
710
741
  const flushing = this.dirtyCarriers;
711
742
  this.dirtyCarriers = [];
743
+ const delivered = this.multiCarrierSubscriptions > 0 ? this.deliveredPool ?? (this.deliveredPool = /* @__PURE__ */ new Set()) : void 0;
712
744
  let i = 0;
713
745
  try {
714
746
  for (; i < flushing.length; i++) {
715
747
  flushing[i].dirty = false;
716
- this.notify(flushing[i]);
748
+ this.notify(flushing[i], delivered);
717
749
  }
718
750
  } finally {
719
751
  for (; i < flushing.length; i++) flushing[i].dirty = false;
752
+ delivered?.clear();
720
753
  }
721
754
  };
722
755
  this.queue = queue;
@@ -734,7 +767,13 @@ var OIMCarrierKeyedEmitter = class {
734
767
  this.addHandler(carrier, handler);
735
768
  carriers.push(carrier);
736
769
  }
770
+ const spansMultipleCarriers = carriers.length > 1;
771
+ if (spansMultipleCarriers) this.multiCarrierSubscriptions++;
772
+ let unsubscribed = false;
737
773
  return () => {
774
+ if (unsubscribed) return;
775
+ unsubscribed = true;
776
+ if (spansMultipleCarriers) this.multiCarrierSubscriptions--;
738
777
  for (let i = 0; i < carriers.length; i++) {
739
778
  this.removeHandler(carriers[i], handler);
740
779
  }
@@ -850,19 +889,31 @@ var OIMCarrierKeyedEmitter = class {
850
889
  this.isFlushEnqueued = true;
851
890
  this.queue.enqueue(this.onFlush);
852
891
  }
853
- notify(carrier) {
892
+ notify(carrier, delivered) {
854
893
  const subscribers = carrier.subscribers;
855
894
  if (!subscribers || subscribers.size === 0) return;
856
895
  if (subscribers.size === 1) {
857
896
  const only = subscribers.values().next().value;
858
- if (only) only();
897
+ if (!only) return;
898
+ if (delivered) {
899
+ const size = delivered.size;
900
+ delivered.add(only);
901
+ if (delivered.size === size) return;
902
+ }
903
+ only();
859
904
  return;
860
905
  }
861
906
  const snapshot = [];
862
907
  subscribers.forEach((h) => snapshot.push(h));
863
908
  for (let i = 0; i < snapshot.length; i++) {
864
909
  const handler = snapshot[i];
865
- if (subscribers.has(handler)) handler();
910
+ if (!subscribers.has(handler)) continue;
911
+ if (delivered) {
912
+ const size = delivered.size;
913
+ delivered.add(handler);
914
+ if (delivered.size === size) continue;
915
+ }
916
+ handler();
866
917
  }
867
918
  }
868
919
  };
@@ -1484,8 +1535,11 @@ var OIMCollection = class {
1484
1535
  this.emitter.emit(0 /* UPDATE */, { pks });
1485
1536
  }
1486
1537
  clear() {
1538
+ const pks = this.store.getAllPks();
1487
1539
  this.store.clear();
1488
- this.emitter.emit(0 /* UPDATE */, { pks: [] });
1540
+ if (pks.length > 0) {
1541
+ this.emitter.emit(0 /* UPDATE */, { pks });
1542
+ }
1489
1543
  }
1490
1544
  countAll() {
1491
1545
  return this.store.countAll();
@@ -1527,14 +1581,12 @@ var OIMReactiveCollection = class extends OIMCollection {
1527
1581
  // Only ever holds `slot.pk` (one canonical reference per logical key), so a
1528
1582
  // native `Set` dedups correctly by reference even for composite pks.
1529
1583
  this.pendingAnyUpdatePks = /* @__PURE__ */ new Set();
1530
- this.isAnyUpdateClearPending = false;
1531
1584
  this.isAnyUpdateScheduled = false;
1532
1585
  this.runAnyUpdate = () => {
1533
1586
  if (!this.isAnyUpdateScheduled) return;
1534
1587
  this.isAnyUpdateScheduled = false;
1535
- const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks.values());
1588
+ const pks = Array.from(this.pendingAnyUpdatePks.values());
1536
1589
  this.pendingAnyUpdatePks.clear();
1537
- this.isAnyUpdateClearPending = false;
1538
1590
  const snapshot = Array.from(this.anyUpdateHandlers);
1539
1591
  for (let i = 0; i < snapshot.length; i++) snapshot[i](pks);
1540
1592
  };
@@ -1574,30 +1626,43 @@ var OIMReactiveCollection = class extends OIMCollection {
1574
1626
  this.anyUpdateHandlers.delete(handler);
1575
1627
  };
1576
1628
  }
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) {
1629
+ // Two notification channels coexist on a reactive collection; they serve
1630
+ // different audiences and NEITHER is obsolete:
1631
+ // • the keyed `updateEmitter` — per-key/per-carrier pings ("did MY key
1632
+ // change?", payload-less `void`), for leaf subscribers like React hooks
1633
+ // that declare the keys they care about up front;
1634
+ // • this `emitter` UPDATE event — the aggregate/batch channel that carries
1635
+ // the full list of changed pks in one SYNCHRONOUS callback, for
1636
+ // consumers that do NOT know their keys ahead of time and must re-derive
1637
+ // over the batch: derived indexes/collections, changed-fields, persist,
1638
+ // snapshot-manager. `pks` always enumerates exactly what changed — even
1639
+ // clear() lists every removed key, so an empty array never occurs.
1640
+ // It is not a per-key emitter with fewer features — it is the opposite
1641
+ // shape (enumerated batch, not "my key"), which is why it stays.
1642
+ //
1643
+ // Emit on the batch channel only when something is actually subscribed —
1644
+ // otherwise every upsert/remove allocates a throwaway `{ pks }` object (and
1645
+ // array) that nobody reads.
1646
+ notifyBatch(pk) {
1582
1647
  if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1583
1648
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1584
1649
  }
1585
1650
  }
1586
- notifyLegacyMany(pks) {
1651
+ notifyBatchMany(pks) {
1587
1652
  if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1588
1653
  this.emitter.emit(0 /* UPDATE */, { pks });
1589
1654
  }
1590
1655
  }
1591
1656
  upsertOneByPk(pk, entity) {
1592
1657
  const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
1593
- this.notifyLegacy(pk);
1658
+ this.notifyBatch(pk);
1594
1659
  this.updateEmitter.markUpdatedCarrier(slot);
1595
1660
  this.trackAnyUpdatePk(pk);
1596
1661
  return slot;
1597
1662
  }
1598
1663
  upsertOne(entity) {
1599
1664
  const slot = this.upsertOneWithoutNotifications(entity);
1600
- this.notifyLegacy(slot.pk);
1665
+ this.notifyBatch(slot.pk);
1601
1666
  this.updateEmitter.markUpdatedCarrier(slot);
1602
1667
  this.trackAnyUpdatePk(slot.pk);
1603
1668
  return slot;
@@ -1608,7 +1673,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1608
1673
  (entity) => this.upsertOneWithoutNotifications(entity)
1609
1674
  );
1610
1675
  const pks = slots.map((slot) => slot.pk);
1611
- this.notifyLegacyMany(pks);
1676
+ this.notifyBatchMany(pks);
1612
1677
  for (let i = 0; i < slots.length; i++) {
1613
1678
  this.updateEmitter.markUpdatedCarrier(slots[i]);
1614
1679
  }
@@ -1618,7 +1683,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1618
1683
  removeOne(entity) {
1619
1684
  const pk = this.selectPk(entity);
1620
1685
  this.store.removeOneByPk(pk);
1621
- this.notifyLegacy(pk);
1686
+ this.notifyBatch(pk);
1622
1687
  this.updateEmitter.markUpdatedKey(pk);
1623
1688
  this.trackAnyUpdatePk(pk);
1624
1689
  }
@@ -1626,30 +1691,32 @@ var OIMReactiveCollection = class extends OIMCollection {
1626
1691
  if (entities.length === 0) return;
1627
1692
  const pks = entities.map(this.selectPk);
1628
1693
  this.store.removeManyByPks(pks);
1629
- this.notifyLegacyMany(pks);
1694
+ this.notifyBatchMany(pks);
1630
1695
  this.updateEmitter.markUpdatedKeys(pks);
1631
1696
  this.trackAnyUpdatePks(pks);
1632
1697
  }
1633
1698
  removeOneByPk(pk) {
1634
1699
  this.store.removeOneByPk(pk);
1635
- this.notifyLegacy(pk);
1700
+ this.notifyBatch(pk);
1636
1701
  this.updateEmitter.markUpdatedKey(pk);
1637
1702
  this.trackAnyUpdatePk(pk);
1638
1703
  }
1639
1704
  removeManyByPks(pks) {
1640
1705
  if (pks.length === 0) return;
1641
1706
  this.store.removeManyByPks(pks);
1642
- this.notifyLegacyMany(pks);
1707
+ this.notifyBatchMany(pks);
1643
1708
  this.updateEmitter.markUpdatedKeys(pks);
1644
1709
  this.trackAnyUpdatePks(pks);
1645
1710
  }
1646
1711
  clear() {
1712
+ const needsPks = this.emitter.hasHandlers(0 /* UPDATE */) || this.anyUpdateHandlers.size > 0;
1713
+ const pks = needsPks ? this.store.getAllPks() : void 0;
1647
1714
  this.store.clear();
1648
- if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1649
- this.emitter.emit(0 /* UPDATE */, { pks: [] });
1715
+ if (pks && pks.length > 0) {
1716
+ this.notifyBatchMany(pks);
1717
+ this.trackAnyUpdatePks(pks);
1650
1718
  }
1651
1719
  this.updateEmitter.markAllUpdated();
1652
- this.trackAnyUpdateClear();
1653
1720
  }
1654
1721
  trackAnyUpdatePk(pk) {
1655
1722
  if (this.anyUpdateHandlers.size === 0) return;
@@ -1658,14 +1725,9 @@ var OIMReactiveCollection = class extends OIMCollection {
1658
1725
  }
1659
1726
  trackAnyUpdatePks(pks) {
1660
1727
  if (this.anyUpdateHandlers.size === 0) return;
1661
- for (let i = 0; i < pks.length; i++)
1728
+ for (let i = 0; i < pks.length; i++) {
1662
1729
  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();
1730
+ }
1669
1731
  this.ensureAnyUpdateEnqueued();
1670
1732
  }
1671
1733
  ensureAnyUpdateEnqueued() {
@@ -1844,6 +1906,7 @@ var OIMCollectionIndexManualOrderedArrayBased = class extends OIMIndexManualOrde
1844
1906
  var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
1845
1907
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
1846
1908
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
1909
+ EOIMEventQueueEventType2[EOIMEventQueueEventType2["FLUSH_ERROR"] = 2] = "FLUSH_ERROR";
1847
1910
  return EOIMEventQueueEventType2;
1848
1911
  })(EOIMEventQueueEventType || {});
1849
1912
 
@@ -2097,8 +2160,13 @@ var OIMUpdateEventEmitter = class {
2097
2160
  var OIMOrderedListCommandBuffer = class {
2098
2161
  constructor(queue) {
2099
2162
  this.commandsByKey = /* @__PURE__ */ new Map();
2163
+ // Keys whose buffered leading `reset` is stale: an edit after the reset
2164
+ // should be folded into it, but we defer the (O(list-size)) rebuild to the
2165
+ // next read instead of redoing it on every subsequent edit in the batch.
2166
+ this.staleResetKeys = /* @__PURE__ */ new Set();
2100
2167
  this.onAfterFlush = () => {
2101
2168
  this.commandsByKey.clear();
2169
+ this.staleResetKeys.clear();
2102
2170
  };
2103
2171
  this.commandsEventEmitter = new OIMUpdateEventEmitter(
2104
2172
  queue,
@@ -2113,9 +2181,11 @@ var OIMOrderedListCommandBuffer = class {
2113
2181
  return this.commandsEventEmitter.subscribeOnKey(key, handler);
2114
2182
  }
2115
2183
  getBufferedCommands(key) {
2184
+ this.materializeStaleReset(key);
2116
2185
  return this.commandsByKey.get(key) ?? [];
2117
2186
  }
2118
2187
  consumeCommands(key) {
2188
+ this.materializeStaleReset(key);
2119
2189
  const cmds = this.commandsByKey.get(key);
2120
2190
  if (!cmds || cmds.length === 0) return [];
2121
2191
  return cmds.slice();
@@ -2124,6 +2194,20 @@ var OIMOrderedListCommandBuffer = class {
2124
2194
  this.commandsEventEmitter.destroy();
2125
2195
  this.unsubscribeAfterFlush();
2126
2196
  this.commandsByKey.clear();
2197
+ this.staleResetKeys.clear();
2198
+ }
2199
+ /**
2200
+ * Rebuild a deferred leading `reset` from the current order, once, at read
2201
+ * time. All the post-reset edits that were folded in are already reflected
2202
+ * in the driver's backing order, so a single rebuild here is equivalent to
2203
+ * having recomputed the reset on each of those edits.
2204
+ */
2205
+ materializeStaleReset(key) {
2206
+ if (!this.staleResetKeys.delete(key)) return;
2207
+ const cmds = this.commandsByKey.get(key);
2208
+ if (cmds && cmds.length > 0 && cmds[0].type === "reset") {
2209
+ cmds[0] = this.createResetCommand(this.getItemsByKey(key));
2210
+ }
2127
2211
  }
2128
2212
  appendCommand(key, cmd) {
2129
2213
  let cmds = this.commandsByKey.get(key);
@@ -2134,8 +2218,9 @@ var OIMOrderedListCommandBuffer = class {
2134
2218
  if (cmd.type === "reset") {
2135
2219
  cmds.length = 0;
2136
2220
  cmds.push(cmd);
2221
+ this.staleResetKeys.delete(key);
2137
2222
  } else if (cmds.length > 0 && cmds[0].type === "reset") {
2138
- cmds[0] = this.createResetCommand(this.getItemsByKey(key));
2223
+ this.staleResetKeys.add(key);
2139
2224
  } else {
2140
2225
  cmds.push(cmd);
2141
2226
  }
@@ -3177,10 +3262,6 @@ var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndex
3177
3262
  });
3178
3263
  this.keysByPk = /* @__PURE__ */ new Map();
3179
3264
  this.onCollectionUpdate = (pks) => {
3180
- if (pks.length === 0) {
3181
- this.rebuildFromCollection();
3182
- return;
3183
- }
3184
3265
  for (const pk of pks) {
3185
3266
  this.syncPk(pk);
3186
3267
  }
@@ -3282,10 +3363,6 @@ var OIMDerivedCollectionIndexArrayBased = class extends OIMReactiveCollectionInd
3282
3363
  // touching an unrelated field can skip re-sorting when the value is unchanged.
3283
3364
  this.orderValueByPk = /* @__PURE__ */ new Map();
3284
3365
  this.onCollectionUpdate = (pks) => {
3285
- if (pks.length === 0) {
3286
- this.rebuildFromCollection();
3287
- return;
3288
- }
3289
3366
  const ordered = !!(this.compareEntities || this.orderBy);
3290
3367
  const keysToSort = ordered ? /* @__PURE__ */ new Set() : null;
3291
3368
  for (const pk of pks) {
@@ -3702,10 +3779,6 @@ var OIMDerivedCollectionGlobalIndexArrayBased = class extends OIMReactiveCollect
3702
3779
  // an unrelated field can skip re-sorting when the value is unchanged.
3703
3780
  this.orderValueByPk = /* @__PURE__ */ new Map();
3704
3781
  this.onCollectionUpdate = (pks) => {
3705
- if (pks.length === 0) {
3706
- this.rebuildFromCollection();
3707
- return;
3708
- }
3709
3782
  const ordered = !!(this.compareEntities || this.orderBy);
3710
3783
  let needsSort = false;
3711
3784
  for (const pk of pks) {
@@ -3803,10 +3876,6 @@ var OIMDerivedCollectionGlobalIndexSetBased = class extends OIMReactiveCollectio
3803
3876
  super(queue, { collection, indexOptions: opts.indexOptions });
3804
3877
  this.presentPks = /* @__PURE__ */ new Set();
3805
3878
  this.onCollectionUpdate = (pks) => {
3806
- if (pks.length === 0) {
3807
- this.rebuildFromCollection();
3808
- return;
3809
- }
3810
3879
  for (const pk of pks) {
3811
3880
  const prev = this.presentPks.has(pk);
3812
3881
  const slot = this.collection.getSlotByPk(pk);
@@ -3982,6 +4051,55 @@ function createOIMCollectionIndexFactory(queue, collection) {
3982
4051
  return new OIMCollectionIndexFactory(queue, collection);
3983
4052
  }
3984
4053
 
4054
+ // src/core/OIMDerivedCollection.ts
4055
+ var OIMDerivedCollection = class extends OIMReactiveCollection {
4056
+ constructor(queue, source, derive, opts) {
4057
+ super(queue, {
4058
+ store: opts?.store,
4059
+ // Each sync recomputes the WHOLE derived entity → replace, not merge.
4060
+ updateEntity: (draft) => draft
4061
+ });
4062
+ this.onSourceUpdate = (pks) => {
4063
+ for (let i = 0; i < pks.length; i++) this.syncPk(pks[i]);
4064
+ };
4065
+ this.source = source;
4066
+ this.derive = derive;
4067
+ this.compareDerived = opts?.compare ?? Object.is;
4068
+ this.unsubscribeFromSource = source.emitter.on(
4069
+ 0 /* UPDATE */,
4070
+ (payload) => this.onSourceUpdate(payload.pks)
4071
+ );
4072
+ if (opts?.buildInitial !== false) {
4073
+ this.rebuildFromSource();
4074
+ }
4075
+ }
4076
+ rebuildFromSource() {
4077
+ for (const pk of this.getAllPks()) {
4078
+ if (this.source.getOneByPk(pk) === void 0) {
4079
+ this.removeOneByPk(pk);
4080
+ }
4081
+ }
4082
+ for (const slot of this.source.getAllSlots()) {
4083
+ if (slot.item !== void 0) this.syncPk(slot.pk);
4084
+ }
4085
+ }
4086
+ destroy() {
4087
+ this.unsubscribeFromSource();
4088
+ super.destroy();
4089
+ }
4090
+ syncPk(pk) {
4091
+ const sourceEntity = this.source.getOneByPk(pk);
4092
+ if (sourceEntity === void 0) {
4093
+ if (this.getOneByPk(pk) !== void 0) this.removeOneByPk(pk);
4094
+ return;
4095
+ }
4096
+ const next = this.derive(sourceEntity, pk);
4097
+ const prev = this.getOneByPk(pk);
4098
+ if (prev !== void 0 && this.compareDerived(prev, next)) return;
4099
+ this.upsertOneByPk(pk, next);
4100
+ }
4101
+ };
4102
+
3985
4103
  // src/core/OIMObjectStoreRecordDriven.ts
3986
4104
  var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
3987
4105
  constructor(initial) {
@@ -4054,8 +4172,11 @@ var OIMObject = class {
4054
4172
  return this.store.getAll();
4055
4173
  }
4056
4174
  clear() {
4175
+ const keys = this.store.keys();
4057
4176
  this.store.clear();
4058
- this.onUpdatedKeys([]);
4177
+ if (keys.length > 0) {
4178
+ this.onUpdatedKeys(keys);
4179
+ }
4059
4180
  }
4060
4181
  /**
4061
4182
  * Write fan-out hooks. Split single-key vs multi-key so a reactive subclass
@@ -4358,8 +4479,9 @@ var OIMComparatorFactory = class {
4358
4479
  if (ka.length !== kb.length) return true;
4359
4480
  for (let i = 0; i < ka.length; i++) {
4360
4481
  const k = ka[i];
4361
- if (b[k] !== a[k])
4482
+ if (b[k] !== a[k]) {
4362
4483
  return true;
4484
+ }
4363
4485
  }
4364
4486
  return false;
4365
4487
  };
@@ -4381,6 +4503,7 @@ var OIMEventQueue = class {
4381
4503
  this.tasksSpare = /* @__PURE__ */ new Set();
4382
4504
  this.isFlushing = false;
4383
4505
  this.scheduler = options.scheduler;
4506
+ this.onError = options.onError;
4384
4507
  if (this.scheduler) {
4385
4508
  this.flushBound = () => this.flush();
4386
4509
  this.scheduler.on(
@@ -4408,10 +4531,14 @@ var OIMEventQueue = class {
4408
4531
  return this.isFlushing;
4409
4532
  }
4410
4533
  /**
4411
- * Execute all pending tasks.
4534
+ * Execute the tasks pending at the moment the flush starts — a single pass,
4535
+ * not a drain-to-empty.
4412
4536
  *
4413
- * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
4414
- * executed within the same flush.
4537
+ * The pending set is swapped out for an empty spare up front and the flush
4538
+ * iterates that captured set. Tasks enqueued during the flush land in the
4539
+ * fresh set and run on the NEXT flush, not this one. (Contrast
4540
+ * `OIMComputeRuntime.flush()`, which re-reads its bucket per level and so
4541
+ * does drain same-level tasks scheduled mid-flush.)
4415
4542
  */
4416
4543
  flush() {
4417
4544
  this.scheduler?.cancel();
@@ -4420,12 +4547,34 @@ var OIMEventQueue = class {
4420
4547
  const flushing = this.tasks;
4421
4548
  this.tasks = this.tasksSpare;
4422
4549
  this.flushing = flushing;
4423
- for (const task of flushing) task();
4424
- flushing.clear();
4425
- this.tasksSpare = flushing;
4426
- this.flushing = void 0;
4427
- this.isFlushing = false;
4550
+ let errors;
4551
+ try {
4552
+ for (const task of flushing) {
4553
+ try {
4554
+ task();
4555
+ } catch (error) {
4556
+ (errors ?? (errors = [])).push(error);
4557
+ this.emitter.emit(2 /* FLUSH_ERROR */, {
4558
+ error
4559
+ });
4560
+ this.onError?.(error);
4561
+ }
4562
+ }
4563
+ } finally {
4564
+ flushing.clear();
4565
+ this.tasksSpare = flushing;
4566
+ this.flushing = void 0;
4567
+ this.isFlushing = false;
4568
+ }
4428
4569
  this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
4570
+ if (errors && this.onError === void 0) {
4571
+ if (errors.length === 1) throw errors[0];
4572
+ const AggregateErrorCtor = globalThis.AggregateError;
4573
+ throw AggregateErrorCtor ? new AggregateErrorCtor(
4574
+ errors,
4575
+ "Errors thrown during queue flush"
4576
+ ) : errors[0];
4577
+ }
4429
4578
  }
4430
4579
  /**
4431
4580
  * Get the current number of queued functions.
@@ -4663,20 +4812,92 @@ var OIMPkCodecKeyPath = class {
4663
4812
  }
4664
4813
  };
4665
4814
 
4815
+ // src/modules/effect/core/OIMEffectDependencyComputed.ts
4816
+ var OIMEffectDependencyComputed = class {
4817
+ constructor(source) {
4818
+ this.source = source;
4819
+ }
4820
+ subscribe(onUpdate) {
4821
+ return this.source.emitter.on("update", onUpdate);
4822
+ }
4823
+ };
4824
+
4666
4825
  // src/modules/compute/core/OIMComputeRuntime.ts
4667
4826
  var OIMComputeRuntime = class {
4668
4827
  constructor(queue) {
4669
4828
  this.queue = queue;
4670
4829
  this.isFlushScheduled = false;
4671
4830
  this.isFlushing = false;
4831
+ // Active level range (min > max means nothing pending).
4672
4832
  this.minLevel = Infinity;
4673
4833
  this.maxLevel = -Infinity;
4674
- this.dirtyByLevel = /* @__PURE__ */ new Map();
4834
+ this.dirtyByLevel = [];
4675
4835
  this.afterFlushTasks = /* @__PURE__ */ new Set();
4676
- this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
4677
- if (!this.isFlushScheduled) return;
4678
- this.flush();
4679
- });
4836
+ // Topological depth per computed node — the engine is the ONLY owner of
4837
+ // levels; nodes never expose their own. A node's dependents look up its depth
4838
+ // here (by the computed reference their dependency wraps). Written once at
4839
+ // construction, never on the hot flush path.
4840
+ this.levelByComputed = /* @__PURE__ */ new WeakMap();
4841
+ this._isDestroyed = false;
4842
+ this.unsubscribeFromQueue = this.queue.emitter.on(
4843
+ 0 /* AFTER_FLUSH */,
4844
+ () => {
4845
+ if (!this.isFlushScheduled) return;
4846
+ this.flush();
4847
+ }
4848
+ );
4849
+ }
4850
+ /**
4851
+ * Whether {@link destroy} has run. A destroyed runtime is detached from its
4852
+ * queue and must not be reused — `getOIMComputeRuntime` reads this to hand
4853
+ * out a fresh runtime instead of a dead one.
4854
+ */
4855
+ get isDestroyed() {
4856
+ return this._isDestroyed;
4857
+ }
4858
+ /**
4859
+ * Detach from the queue and drop everything pending.
4860
+ *
4861
+ * Without this the AFTER_FLUSH subscription lives as long as the queue does,
4862
+ * even once every computed / effect on this runtime is gone — harmless (the
4863
+ * handler early-returns) but impossible to unwind. Destroying does NOT
4864
+ * destroy the nodes scheduled on it; destroy them first, then the runtime.
4865
+ * Idempotent.
4866
+ */
4867
+ destroy() {
4868
+ if (this._isDestroyed) return;
4869
+ this._isDestroyed = true;
4870
+ this.unsubscribeFromQueue();
4871
+ for (let i = 0; i < this.dirtyByLevel.length; i++) {
4872
+ this.dirtyByLevel[i]?.clear();
4873
+ }
4874
+ this.afterFlushTasks.clear();
4875
+ this.minLevel = Infinity;
4876
+ this.maxLevel = -Infinity;
4877
+ this.isFlushScheduled = false;
4878
+ }
4879
+ /**
4880
+ * Topological depth for a node with these dependencies: one above the deepest
4881
+ * computed dependency (source-only → 0). The engine owns this — callers pass
4882
+ * their dependencies and get back a level, they never compute it themselves.
4883
+ */
4884
+ computeLevel(deps) {
4885
+ let max = -1;
4886
+ for (const dep of deps) {
4887
+ if (dep instanceof OIMEffectDependencyComputed) {
4888
+ const depLevel = this.levelByComputed.get(dep.source) ?? 0;
4889
+ if (depLevel > max) max = depLevel;
4890
+ }
4891
+ }
4892
+ return max + 1;
4893
+ }
4894
+ /** Record a computed's depth so its dependents can look it up. */
4895
+ registerLevel(computed, level) {
4896
+ this.levelByComputed.set(computed, level);
4897
+ }
4898
+ /** The recorded topological depth of a computed (0 if unknown). Introspection. */
4899
+ getLevel(computed) {
4900
+ return this.levelByComputed.get(computed) ?? 0;
4680
4901
  }
4681
4902
  /**
4682
4903
  * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
@@ -4688,10 +4909,10 @@ var OIMComputeRuntime = class {
4688
4909
  if (!isActive) return;
4689
4910
  task();
4690
4911
  };
4691
- let bucket = this.dirtyByLevel.get(level);
4692
- if (!bucket) {
4912
+ let bucket = this.dirtyByLevel[level];
4913
+ if (bucket === void 0) {
4693
4914
  bucket = /* @__PURE__ */ new Set();
4694
- this.dirtyByLevel.set(level, bucket);
4915
+ this.dirtyByLevel[level] = bucket;
4695
4916
  }
4696
4917
  bucket.add(wrapped);
4697
4918
  if (level < this.minLevel) this.minLevel = level;
@@ -4723,7 +4944,7 @@ var OIMComputeRuntime = class {
4723
4944
  }
4724
4945
  flush() {
4725
4946
  this.isFlushScheduled = false;
4726
- if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
4947
+ if (this.minLevel > this.maxLevel && this.afterFlushTasks.size === 0)
4727
4948
  return;
4728
4949
  this.isFlushing = true;
4729
4950
  try {
@@ -4732,15 +4953,15 @@ var OIMComputeRuntime = class {
4732
4953
  this.afterFlushTasks.clear();
4733
4954
  for (let i = 0; i < snapshot.length; i++) snapshot[i]();
4734
4955
  }
4735
- if (this.dirtyByLevel.size === 0) return;
4956
+ if (this.minLevel > this.maxLevel) return;
4736
4957
  let level = this.minLevel;
4737
4958
  let max = this.maxLevel;
4738
4959
  while (level <= max) {
4739
4960
  for (; ; ) {
4740
- const bucket = this.dirtyByLevel.get(level);
4741
- if (!bucket || bucket.size === 0) break;
4742
- this.dirtyByLevel.delete(level);
4961
+ const bucket = this.dirtyByLevel[level];
4962
+ if (bucket === void 0 || bucket.size === 0) break;
4743
4963
  const snapshot = Array.from(bucket);
4964
+ bucket.clear();
4744
4965
  for (let i = 0; i < snapshot.length; i++) snapshot[i]();
4745
4966
  }
4746
4967
  if (this.maxLevel > max) max = this.maxLevel;
@@ -4748,24 +4969,38 @@ var OIMComputeRuntime = class {
4748
4969
  }
4749
4970
  } finally {
4750
4971
  this.isFlushing = false;
4751
- if (this.dirtyByLevel.size === 0) {
4752
- this.minLevel = Infinity;
4753
- this.maxLevel = -Infinity;
4754
- } else {
4755
- let nextMin = Infinity;
4756
- let nextMax = -Infinity;
4757
- this.dirtyByLevel.forEach((_bucket, lvl) => {
4758
- if (lvl < nextMin) nextMin = lvl;
4759
- if (lvl > nextMax) nextMax = lvl;
4760
- });
4761
- this.minLevel = nextMin;
4762
- this.maxLevel = nextMax;
4763
- this.isFlushScheduled = true;
4972
+ this.recomputePendingRange();
4973
+ }
4974
+ }
4975
+ recomputePendingRange() {
4976
+ let nextMin = Infinity;
4977
+ let nextMax = -Infinity;
4978
+ const buckets = this.dirtyByLevel;
4979
+ for (let i = 0; i < buckets.length; i++) {
4980
+ const bucket = buckets[i];
4981
+ if (bucket !== void 0 && bucket.size > 0) {
4982
+ if (i < nextMin) nextMin = i;
4983
+ if (i > nextMax) nextMax = i;
4764
4984
  }
4765
4985
  }
4986
+ this.minLevel = nextMin;
4987
+ this.maxLevel = nextMax;
4988
+ if (nextMin <= nextMax) this.isFlushScheduled = true;
4766
4989
  }
4767
4990
  };
4768
4991
 
4992
+ // src/dx/oimComputeRuntimeByQueue.ts
4993
+ var oimComputeRuntimeByQueue = /* @__PURE__ */ new WeakMap();
4994
+
4995
+ // src/dx/getOIMComputeRuntime.ts
4996
+ function getOIMComputeRuntime(queue) {
4997
+ const runtime = oimComputeRuntimeByQueue.get(queue);
4998
+ if (runtime !== void 0 && !runtime.isDestroyed) return runtime;
4999
+ const nextRuntime = new OIMComputeRuntime(queue);
5000
+ oimComputeRuntimeByQueue.set(queue, nextRuntime);
5001
+ return nextRuntime;
5002
+ }
5003
+
4769
5004
  // src/modules/selector/core/OIMSelector.ts
4770
5005
  var OIMSelector = class {
4771
5006
  constructor(runtime, sourceDeps, computedDeps = []) {
@@ -5026,8 +5261,9 @@ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
5026
5261
  areEqual(prev, next) {
5027
5262
  if (prev === next) return true;
5028
5263
  if (prev.length !== next.length) return false;
5029
- for (let i = 0; i < prev.length; i++)
5264
+ for (let i = 0; i < prev.length; i++) {
5030
5265
  if (prev[i] !== next[i]) return false;
5266
+ }
5031
5267
  return true;
5032
5268
  }
5033
5269
  };
@@ -5097,8 +5333,9 @@ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
5097
5333
  areEqual(prev, next) {
5098
5334
  if (prev === next) return true;
5099
5335
  if (prev.length !== next.length) return false;
5100
- for (let i = 0; i < prev.length; i++)
5336
+ for (let i = 0; i < prev.length; i++) {
5101
5337
  if (prev[i] !== next[i]) return false;
5338
+ }
5102
5339
  return true;
5103
5340
  }
5104
5341
  };
@@ -5162,8 +5399,9 @@ var OIMEntitiesByGlobalIndexArrayBasedSelector = class extends OIMSelector {
5162
5399
  areEqual(prev, next) {
5163
5400
  if (prev === next) return true;
5164
5401
  if (prev.length !== next.length) return false;
5165
- for (let i = 0; i < prev.length; i++)
5402
+ for (let i = 0; i < prev.length; i++) {
5166
5403
  if (prev[i] !== next[i]) return false;
5404
+ }
5167
5405
  return true;
5168
5406
  }
5169
5407
  };
@@ -5228,8 +5466,9 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
5228
5466
  areEqual(prev, next) {
5229
5467
  if (prev === next) return true;
5230
5468
  if (prev.length !== next.length) return false;
5231
- for (let i = 0; i < prev.length; i++)
5469
+ for (let i = 0; i < prev.length; i++) {
5232
5470
  if (prev[i] !== next[i]) return false;
5471
+ }
5233
5472
  return true;
5234
5473
  }
5235
5474
  };
@@ -5238,7 +5477,7 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
5238
5477
  var OIMCollectionSelectors = class {
5239
5478
  constructor(queue, collection) {
5240
5479
  this.collection = collection;
5241
- this.runtime = new OIMComputeRuntime(queue);
5480
+ this.runtime = getOIMComputeRuntime(queue);
5242
5481
  }
5243
5482
  byPk(pk) {
5244
5483
  return new OIMCollectionByPkSelector(this.runtime, this.collection, pk);
@@ -5305,127 +5544,408 @@ var OIMCollectionSelectors = class {
5305
5544
  }
5306
5545
  };
5307
5546
 
5308
- // src/dx/OIMCollectionKit.ts
5309
- function createOIMCollectionKit(queue, opts) {
5310
- const collection = new OIMReactiveCollection(queue, opts);
5311
- const indexFactory = new OIMCollectionIndexFactory(queue, collection);
5312
- const select = new OIMCollectionSelectors(queue, collection);
5313
- const scope = new OIMDisposeScope();
5314
- scope.add(collection);
5315
- return {
5316
- queue,
5317
- collection,
5318
- indexFactory,
5319
- select,
5320
- scope,
5321
- destroy: () => scope.destroy()
5322
- };
5323
- }
5324
-
5325
- // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
5326
- var OIMCollectionChangedFields = class {
5327
- constructor(queue, collection, opts) {
5328
- this.isInWrite = false;
5329
- this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
5330
- // PKs written through this wrapper during the current flush. Their fields are
5331
- // already tracked accurately via trackPatch, so the flush-time external-write
5332
- // handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
5333
- this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
5334
- this.changedFieldsByPk = /* @__PURE__ */ new Map();
5335
- this.changedPksByField = /* @__PURE__ */ new Map();
5336
- this.onAfterFlush = () => {
5337
- this.clear();
5338
- this.writtenThroughThisFlush.clear();
5339
- };
5340
- this.onCollectionUpdate = (payload) => {
5341
- if (this.isInWrite) return;
5342
- if (payload.pks.length === 0) return;
5343
- for (let i = 0; i < payload.pks.length; i++) {
5344
- const pk = payload.pks[i];
5345
- if (this.writtenThroughThisFlush.has(pk)) continue;
5346
- this.trackExternalMutationByPk(pk);
5347
- }
5348
- };
5349
- this.collection = collection;
5350
- this.detectExternalMutations = opts?.detectExternalMutations !== false;
5351
- this.selectPk = opts?.selectPk ?? (() => {
5352
- throw new Error(
5353
- `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
5354
- );
5355
- });
5356
- this.changedPksEventEmitter = new OIMUpdateEventEmitter(
5357
- queue,
5358
- "after_flush"
5359
- );
5360
- this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
5361
- this.unsubscribeAfterFlush = queue.emitter.on(
5362
- 0 /* AFTER_FLUSH */,
5363
- this.onAfterFlush
5364
- );
5365
- if (this.detectExternalMutations) {
5366
- const maybeReactive = collection;
5367
- this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
5368
- this.onCollectionUpdate({
5369
- pks
5370
- });
5371
- }) : collection.emitter.on(
5372
- 0 /* UPDATE */,
5373
- this.onCollectionUpdate
5374
- );
5375
- }
5376
- }
5377
- upsertOneByPk(pk, patch) {
5378
- const slot = this.withWrite(() => {
5379
- this.trackPatch(pk, patch);
5380
- return this.collection.upsertOneByPk(pk, patch);
5381
- });
5382
- this.updateEntitySnapshot(pk);
5383
- return slot;
5547
+ // src/modules/effect/core/OIMEffectDependencyKeyed.ts
5548
+ var OIMEffectDependencyKeyed = class {
5549
+ constructor(source, key) {
5550
+ this.source = source;
5551
+ this.key = key;
5384
5552
  }
5385
- upsertOne(entity) {
5386
- const slot = this.withWrite(() => {
5387
- const pk = this.selectPk(entity);
5388
- this.trackPatch(pk, entity);
5389
- return this.collection.upsertOne(entity);
5390
- });
5391
- this.updateEntitySnapshot(slot.pk);
5392
- return slot;
5553
+ subscribe(onUpdate) {
5554
+ return this.source.subscribeOnKey(this.key, onUpdate);
5393
5555
  }
5394
- upsertMany(entities) {
5395
- const pks = [];
5396
- const slots = this.withWrite(() => {
5397
- for (const entity of entities) {
5398
- const pk = this.selectPk(entity);
5399
- pks.push(pk);
5400
- this.trackPatch(pk, entity);
5401
- }
5402
- return this.collection.upsertMany(entities);
5403
- });
5404
- for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
5405
- return slots;
5556
+ };
5557
+
5558
+ // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
5559
+ var OIMEffectDependencyKeyedCollection = class {
5560
+ constructor(collection, pk) {
5561
+ this.dep = new OIMEffectDependencyKeyed(collection, pk);
5406
5562
  }
5407
- getChangedFieldsByPk(pk) {
5408
- return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
5563
+ get source() {
5564
+ return this.dep.source;
5409
5565
  }
5410
- consumeChangedFieldsByPk(pk) {
5411
- const existing = this.changedFieldsByPk.get(pk);
5412
- if (!existing) return /* @__PURE__ */ new Set();
5413
- return new Set(existing);
5566
+ subscribe(onUpdate) {
5567
+ return this.dep.subscribe(onUpdate);
5414
5568
  }
5415
- getChangedPksByField(field) {
5416
- return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
5569
+ };
5570
+
5571
+ // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
5572
+ var OIMEffectDependencyKeyedIndex = class {
5573
+ constructor(index, key) {
5574
+ this.dep = new OIMEffectDependencyKeyed(index, key);
5417
5575
  }
5418
- consumeChangedPksByField(field) {
5419
- const existing = this.changedPksByField.get(field);
5420
- if (!existing) return /* @__PURE__ */ new Set();
5421
- return new Set(existing);
5576
+ get source() {
5577
+ return this.dep.source;
5422
5578
  }
5423
- clear() {
5424
- this.changedFieldsByPk.clear();
5425
- this.changedPksByField.clear();
5579
+ subscribe(onUpdate) {
5580
+ return this.dep.subscribe(onUpdate);
5426
5581
  }
5427
- destroy() {
5428
- this.changedPksEventEmitter.destroy();
5582
+ };
5583
+
5584
+ // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
5585
+ var OIMEffectDependencyKeyedObject = class {
5586
+ constructor(object, key) {
5587
+ this.dep = new OIMEffectDependencyKeyed(object, key);
5588
+ }
5589
+ get source() {
5590
+ return this.dep.source;
5591
+ }
5592
+ subscribe(onUpdate) {
5593
+ return this.dep.subscribe(onUpdate);
5594
+ }
5595
+ };
5596
+
5597
+ // src/dx/on.ts
5598
+ var on = {
5599
+ /** Depend on one PK of a collection. */
5600
+ collection: (collection, pk) => new OIMEffectDependencyKeyedCollection(collection, pk),
5601
+ /** Depend on one key of an index. */
5602
+ index: (index, key) => new OIMEffectDependencyKeyedIndex(index, key),
5603
+ /** Depend on one key of a reactive object. */
5604
+ object: (object, key) => new OIMEffectDependencyKeyedObject(object, key),
5605
+ /** Depend on another computed. */
5606
+ computed: (computed) => new OIMEffectDependencyComputed(computed)
5607
+ };
5608
+
5609
+ // src/modules/effect/core/OIMEffect.ts
5610
+ var OIMEffect = class {
5611
+ constructor(runtime, opts, level) {
5612
+ this.sourceDeps = [];
5613
+ this.computedDeps = [];
5614
+ this.sourceUnsubscribers = [];
5615
+ this.computedUnsubscribers = [];
5616
+ this.isResubscribeEnqueued = false;
5617
+ this.isTemporarilyUnsubscribedForFlush = false;
5618
+ this.isScheduled = false;
5619
+ this.runTask = () => {
5620
+ this.isScheduled = false;
5621
+ this.dequeueScheduled = void 0;
5622
+ this.runOnce();
5623
+ };
5624
+ this.onSourceInvalidate = () => {
5625
+ this.onUpdate?.();
5626
+ if (!this.isTemporarilyUnsubscribedForFlush) {
5627
+ this.isTemporarilyUnsubscribedForFlush = true;
5628
+ this.unsubscribeFromSourceDeps();
5629
+ this.ensureResubscribeAfterFlush();
5630
+ }
5631
+ if (this.isScheduled) return;
5632
+ this.isScheduled = true;
5633
+ this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
5634
+ };
5635
+ this.onComputedInvalidate = () => {
5636
+ this.onUpdate?.();
5637
+ if (this.isScheduled) return;
5638
+ this.isScheduled = true;
5639
+ this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
5640
+ };
5641
+ this.onAfterFlush = () => {
5642
+ if (!this.isResubscribeEnqueued) return;
5643
+ this.isResubscribeEnqueued = false;
5644
+ this.dequeueResubscribe = void 0;
5645
+ this.isTemporarilyUnsubscribedForFlush = false;
5646
+ if (this.sourceUnsubscribers.length === 0) {
5647
+ for (const dep of this.sourceDeps) {
5648
+ this.sourceUnsubscribers.push(
5649
+ dep.subscribe(this.onSourceInvalidate)
5650
+ );
5651
+ }
5652
+ }
5653
+ };
5654
+ this.runtime = runtime;
5655
+ this.onUpdate = opts.onUpdate;
5656
+ this.run = opts.run;
5657
+ this.deps = opts.deps ?? [];
5658
+ for (const dep of this.deps) {
5659
+ if (dep instanceof OIMEffectDependencyComputed) {
5660
+ this.computedDeps.push(dep);
5661
+ } else {
5662
+ this.sourceDeps.push(dep);
5663
+ }
5664
+ }
5665
+ this.level = level ?? this.runtime.computeLevel(this.deps);
5666
+ this.subscribeToDeps();
5667
+ }
5668
+ destroy() {
5669
+ if (this.dequeueScheduled) {
5670
+ this.dequeueScheduled();
5671
+ this.dequeueScheduled = void 0;
5672
+ }
5673
+ this.unsubscribeFromAllDeps();
5674
+ if (this.dequeueResubscribe) {
5675
+ this.dequeueResubscribe();
5676
+ this.dequeueResubscribe = void 0;
5677
+ }
5678
+ this.isResubscribeEnqueued = false;
5679
+ this.isTemporarilyUnsubscribedForFlush = false;
5680
+ }
5681
+ runOnce() {
5682
+ this.run();
5683
+ }
5684
+ subscribeToDeps() {
5685
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
5686
+ return;
5687
+ for (const dep of this.sourceDeps) {
5688
+ this.sourceUnsubscribers.push(
5689
+ dep.subscribe(this.onSourceInvalidate)
5690
+ );
5691
+ }
5692
+ for (const dep of this.computedDeps) {
5693
+ this.computedUnsubscribers.push(
5694
+ dep.subscribe(this.onComputedInvalidate)
5695
+ );
5696
+ }
5697
+ }
5698
+ unsubscribeFromSourceDeps() {
5699
+ if (this.sourceUnsubscribers.length === 0) return;
5700
+ for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
5701
+ this.sourceUnsubscribers = [];
5702
+ }
5703
+ unsubscribeFromAllDeps() {
5704
+ this.unsubscribeFromSourceDeps();
5705
+ if (this.computedUnsubscribers.length === 0) return;
5706
+ for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
5707
+ this.computedUnsubscribers = [];
5708
+ }
5709
+ ensureResubscribeAfterFlush() {
5710
+ if (this.isResubscribeEnqueued) return;
5711
+ this.isResubscribeEnqueued = true;
5712
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
5713
+ this.onAfterFlush
5714
+ );
5715
+ }
5716
+ };
5717
+
5718
+ // src/modules/computed/enums/EOIMComputedEventType.ts
5719
+ var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
5720
+ EOIMComputedEventType2["UPDATE"] = "update";
5721
+ return EOIMComputedEventType2;
5722
+ })(EOIMComputedEventType || {});
5723
+
5724
+ // src/modules/computed/core/OIMComputed.ts
5725
+ var OIMComputed = class {
5726
+ constructor(runtime, opts) {
5727
+ this.emitter = new OIMEventEmitter();
5728
+ this.hasValue = false;
5729
+ this.isDirty = true;
5730
+ this.runtime = runtime;
5731
+ this.compute = opts.compute;
5732
+ this.compare = opts.compare ?? Object.is;
5733
+ this._deps = opts.deps ?? [];
5734
+ this.updateEventEmitter = new OIMUpdateEventEmitter(
5735
+ this.runtime.queue,
5736
+ "immediate"
5737
+ );
5738
+ const level = this.runtime.computeLevel(this._deps);
5739
+ this.runtime.registerLevel(this, level);
5740
+ this.effect = new OIMEffect(
5741
+ this.runtime,
5742
+ {
5743
+ deps: this._deps,
5744
+ onUpdate: () => {
5745
+ this.isDirty = true;
5746
+ },
5747
+ run: () => {
5748
+ this.recomputeAndEmitIfChanged();
5749
+ }
5750
+ },
5751
+ level
5752
+ );
5753
+ }
5754
+ get needsRecompute() {
5755
+ return this.isDirty;
5756
+ }
5757
+ get isReady() {
5758
+ return this.hasValue;
5759
+ }
5760
+ get deps() {
5761
+ return this._deps;
5762
+ }
5763
+ get() {
5764
+ this.recomputeAndEmitIfChanged();
5765
+ return this.value;
5766
+ }
5767
+ getIfReady() {
5768
+ return this.hasValue ? this.value : void 0;
5769
+ }
5770
+ destroy() {
5771
+ this.effect.destroy();
5772
+ this.updateEventEmitter.destroy();
5773
+ this.emitter.offAll();
5774
+ }
5775
+ recomputeAndEmitIfChanged() {
5776
+ if (!this.isDirty) return;
5777
+ const prev = this.value;
5778
+ const hadPrev = this.hasValue;
5779
+ const next = this.compute();
5780
+ this.value = next;
5781
+ this.hasValue = true;
5782
+ this.isDirty = false;
5783
+ const changed = !hadPrev || !this.compare(prev, next);
5784
+ if (!changed) return;
5785
+ this.emitter.emit("update" /* UPDATE */, {
5786
+ keys: ["value"]
5787
+ });
5788
+ this.updateEventEmitter.markUpdatedKey("value");
5789
+ }
5790
+ };
5791
+
5792
+ // src/dx/OIMCollectionKit.ts
5793
+ function createOIMCollectionKit(queue, opts) {
5794
+ const collection = new OIMReactiveCollection(queue, opts);
5795
+ const indexFactory = new OIMCollectionIndexFactory(queue, collection);
5796
+ const select = new OIMCollectionSelectors(queue, collection);
5797
+ const runtime = getOIMComputeRuntime(queue);
5798
+ const scope = new OIMDisposeScope();
5799
+ scope.add(collection);
5800
+ return {
5801
+ queue,
5802
+ collection,
5803
+ indexFactory,
5804
+ select,
5805
+ on,
5806
+ computed: (deps, compute, options) => {
5807
+ const computed = new OIMComputed(runtime, {
5808
+ deps,
5809
+ compute,
5810
+ compare: options?.compare
5811
+ });
5812
+ scope.add(computed);
5813
+ return computed;
5814
+ },
5815
+ effect: (deps, run, options) => {
5816
+ const effect = new OIMEffect(runtime, {
5817
+ deps,
5818
+ run,
5819
+ onUpdate: options?.onUpdate
5820
+ });
5821
+ scope.add(effect);
5822
+ return effect;
5823
+ },
5824
+ scope,
5825
+ destroy: () => scope.destroy()
5826
+ };
5827
+ }
5828
+
5829
+ // src/dx/peekOIMComputeRuntime.ts
5830
+ function peekOIMComputeRuntime(queue) {
5831
+ const runtime = oimComputeRuntimeByQueue.get(queue);
5832
+ return runtime === void 0 || runtime.isDestroyed ? void 0 : runtime;
5833
+ }
5834
+
5835
+ // src/dx/setOIMComputeRuntime.ts
5836
+ function setOIMComputeRuntime(queue, runtime) {
5837
+ if (runtime.queue !== queue) {
5838
+ throw new Error(
5839
+ "setOIMComputeRuntime: the runtime is attached to a different queue. A runtime drains on its own queue and would never run here."
5840
+ );
5841
+ }
5842
+ oimComputeRuntimeByQueue.set(queue, runtime);
5843
+ }
5844
+
5845
+ // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
5846
+ var OIMCollectionChangedFields = class {
5847
+ constructor(queue, collection, opts) {
5848
+ this.isInWrite = false;
5849
+ this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
5850
+ // PKs written through this wrapper during the current flush. Their fields are
5851
+ // already tracked accurately via trackPatch, so the flush-time external-write
5852
+ // handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
5853
+ this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
5854
+ this.changedFieldsByPk = /* @__PURE__ */ new Map();
5855
+ this.changedPksByField = /* @__PURE__ */ new Map();
5856
+ this.onAfterFlush = () => {
5857
+ this.clear();
5858
+ this.writtenThroughThisFlush.clear();
5859
+ };
5860
+ this.onCollectionUpdate = (payload) => {
5861
+ if (this.isInWrite) return;
5862
+ if (payload.pks.length === 0) return;
5863
+ for (let i = 0; i < payload.pks.length; i++) {
5864
+ const pk = payload.pks[i];
5865
+ if (this.writtenThroughThisFlush.has(pk)) continue;
5866
+ this.trackExternalMutationByPk(pk);
5867
+ }
5868
+ };
5869
+ this.collection = collection;
5870
+ this.detectExternalMutations = opts?.detectExternalMutations !== false;
5871
+ this.selectPk = opts?.selectPk ?? (() => {
5872
+ throw new Error(
5873
+ `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
5874
+ );
5875
+ });
5876
+ this.changedPksEventEmitter = new OIMUpdateEventEmitter(
5877
+ queue,
5878
+ "after_flush"
5879
+ );
5880
+ this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
5881
+ this.unsubscribeAfterFlush = queue.emitter.on(
5882
+ 0 /* AFTER_FLUSH */,
5883
+ this.onAfterFlush
5884
+ );
5885
+ if (this.detectExternalMutations) {
5886
+ const maybeReactive = collection;
5887
+ this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
5888
+ this.onCollectionUpdate({
5889
+ pks
5890
+ });
5891
+ }) : collection.emitter.on(
5892
+ 0 /* UPDATE */,
5893
+ this.onCollectionUpdate
5894
+ );
5895
+ }
5896
+ }
5897
+ upsertOneByPk(pk, patch) {
5898
+ const slot = this.withWrite(() => {
5899
+ this.trackPatch(pk, patch);
5900
+ return this.collection.upsertOneByPk(pk, patch);
5901
+ });
5902
+ this.updateEntitySnapshot(pk);
5903
+ return slot;
5904
+ }
5905
+ upsertOne(entity) {
5906
+ const slot = this.withWrite(() => {
5907
+ const pk = this.selectPk(entity);
5908
+ this.trackPatch(pk, entity);
5909
+ return this.collection.upsertOne(entity);
5910
+ });
5911
+ this.updateEntitySnapshot(slot.pk);
5912
+ return slot;
5913
+ }
5914
+ upsertMany(entities) {
5915
+ const pks = [];
5916
+ const slots = this.withWrite(() => {
5917
+ for (const entity of entities) {
5918
+ const pk = this.selectPk(entity);
5919
+ pks.push(pk);
5920
+ this.trackPatch(pk, entity);
5921
+ }
5922
+ return this.collection.upsertMany(entities);
5923
+ });
5924
+ for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
5925
+ return slots;
5926
+ }
5927
+ getChangedFieldsByPk(pk) {
5928
+ return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
5929
+ }
5930
+ consumeChangedFieldsByPk(pk) {
5931
+ const existing = this.changedFieldsByPk.get(pk);
5932
+ if (!existing) return /* @__PURE__ */ new Set();
5933
+ return new Set(existing);
5934
+ }
5935
+ getChangedPksByField(field) {
5936
+ return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
5937
+ }
5938
+ consumeChangedPksByField(field) {
5939
+ const existing = this.changedPksByField.get(field);
5940
+ if (!existing) return /* @__PURE__ */ new Set();
5941
+ return new Set(existing);
5942
+ }
5943
+ clear() {
5944
+ this.changedFieldsByPk.clear();
5945
+ this.changedPksByField.clear();
5946
+ }
5947
+ destroy() {
5948
+ this.changedPksEventEmitter.destroy();
5429
5949
  this.changedFieldsEventEmitter.destroy();
5430
5950
  this.unsubscribeAfterFlush();
5431
5951
  this.unsubscribeFromCollectionEmitter?.();
@@ -5464,8 +5984,9 @@ var OIMCollectionChangedFields = class {
5464
5984
  Object.keys(nextObj).forEach((k) => keys.add(k));
5465
5985
  const changed = [];
5466
5986
  keys.forEach((k) => {
5467
- if (prevObj[k] !== nextObj[k])
5987
+ if (prevObj[k] !== nextObj[k]) {
5468
5988
  changed.push(k);
5989
+ }
5469
5990
  });
5470
5991
  return changed;
5471
5992
  }
@@ -5631,10 +6152,15 @@ var OIMOrderedListMappedCommandStream = class _OIMOrderedListMappedCommandStream
5631
6152
  try {
5632
6153
  const subs = this.subscribers.get(key);
5633
6154
  if (subs && subs.size > 0) {
5634
- const snapshot = Array.from(subs);
5635
- for (let s = 0; s < snapshot.length; s++) {
5636
- const handler = snapshot[s];
5637
- if (subs.has(handler)) handler();
6155
+ if (subs.size === 1) {
6156
+ const only = subs.values().next().value;
6157
+ if (only) only();
6158
+ } else {
6159
+ const snapshot = Array.from(subs);
6160
+ for (let s = 0; s < snapshot.length; s++) {
6161
+ const handler = snapshot[s];
6162
+ if (subs.has(handler)) handler();
6163
+ }
5638
6164
  }
5639
6165
  }
5640
6166
  } finally {
@@ -5928,10 +6454,13 @@ var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler
5928
6454
  this.flush();
5929
6455
  });
5930
6456
  } else {
5931
- this.frameId = setTimeout(() => {
5932
- this.frameId = void 0;
5933
- this.flush();
5934
- }, 16);
6457
+ this.frameId = this.unref(
6458
+ setTimeout(() => {
6459
+ this.frameId = void 0;
6460
+ this.flush();
6461
+ }, 16)
6462
+ // ~60fps
6463
+ );
5935
6464
  }
5936
6465
  }
5937
6466
  cancel() {
@@ -5977,10 +6506,12 @@ var OIMEventQueueSchedulerTimeout = class extends OIMEventQueueScheduler {
5977
6506
  }
5978
6507
  schedule() {
5979
6508
  if (this.timeoutId !== void 0) return;
5980
- this.timeoutId = setTimeout(() => {
5981
- this.timeoutId = void 0;
5982
- this.flush();
5983
- }, this.delay);
6509
+ this.timeoutId = this.unref(
6510
+ setTimeout(() => {
6511
+ this.timeoutId = void 0;
6512
+ this.flush();
6513
+ }, this.delay)
6514
+ );
5984
6515
  }
5985
6516
  cancel() {
5986
6517
  if (this.timeoutId === void 0) return;
@@ -6014,13 +6545,13 @@ var OIMEventQueueSchedulerImmediate = class extends OIMEventQueueScheduler {
6014
6545
  this.flush();
6015
6546
  };
6016
6547
  if (this.useSetImmediate) {
6017
- this.immediateId = setImmediate(callback);
6548
+ this.immediateId = this.unref(setImmediate(callback));
6018
6549
  } else if (this.useMessageChannel && this.messageChannel) {
6019
6550
  this.immediateId = 1;
6020
6551
  this.pendingCallback = callback;
6021
6552
  this.messageChannel.port1.postMessage(null);
6022
6553
  } else {
6023
- this.immediateId = setTimeout(callback, 0);
6554
+ this.immediateId = this.unref(setTimeout(callback, 0));
6024
6555
  }
6025
6556
  }
6026
6557
  cancel() {
@@ -6113,242 +6644,6 @@ var OIMDBSettings = class {
6113
6644
  OIMDBSettings.UPDATED_KEYS_INDEX_KEY = "__updatedKeys__";
6114
6645
  OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
6115
6646
 
6116
- // src/modules/effect/core/OIMEffectDependencyComputed.ts
6117
- var OIMEffectDependencyComputed = class {
6118
- constructor(source) {
6119
- this.source = source;
6120
- }
6121
- subscribe(onUpdate) {
6122
- return this.source.emitter.on("update", onUpdate);
6123
- }
6124
- };
6125
-
6126
- // src/modules/effect/core/OIMEffect.ts
6127
- var OIMEffect = class {
6128
- constructor(runtime, opts) {
6129
- this.sourceDeps = [];
6130
- this.computedDeps = [];
6131
- this.sourceUnsubscribers = [];
6132
- this.computedUnsubscribers = [];
6133
- this.isResubscribeEnqueued = false;
6134
- this.isTemporarilyUnsubscribedForFlush = false;
6135
- this.isScheduled = false;
6136
- this.runTask = () => {
6137
- this.isScheduled = false;
6138
- this.dequeueScheduled = void 0;
6139
- this.runOnce();
6140
- };
6141
- this.onSourceInvalidate = () => {
6142
- this.onUpdate?.();
6143
- if (!this.isTemporarilyUnsubscribedForFlush) {
6144
- this.isTemporarilyUnsubscribedForFlush = true;
6145
- this.unsubscribeFromSourceDeps();
6146
- this.ensureResubscribeAfterFlush();
6147
- }
6148
- if (this.isScheduled) return;
6149
- this.isScheduled = true;
6150
- this.dequeueScheduled = this.runtime.schedule(this.runTask);
6151
- };
6152
- this.onComputedInvalidate = () => {
6153
- this.onUpdate?.();
6154
- if (this.isScheduled) return;
6155
- this.isScheduled = true;
6156
- this.dequeueScheduled = this.runtime.schedule(this.runTask);
6157
- };
6158
- this.onAfterFlush = () => {
6159
- if (!this.isResubscribeEnqueued) return;
6160
- this.isResubscribeEnqueued = false;
6161
- this.dequeueResubscribe = void 0;
6162
- this.isTemporarilyUnsubscribedForFlush = false;
6163
- if (this.sourceUnsubscribers.length === 0) {
6164
- for (const dep of this.sourceDeps) {
6165
- this.sourceUnsubscribers.push(
6166
- dep.subscribe(this.onSourceInvalidate)
6167
- );
6168
- }
6169
- }
6170
- };
6171
- this.runtime = runtime;
6172
- this.onUpdate = opts.onUpdate;
6173
- this.run = opts.run;
6174
- this.deps = opts.deps ?? [];
6175
- for (const dep of this.deps) {
6176
- if (dep instanceof OIMEffectDependencyComputed) {
6177
- this.computedDeps.push(dep);
6178
- } else {
6179
- this.sourceDeps.push(dep);
6180
- }
6181
- }
6182
- this.subscribeToDeps();
6183
- }
6184
- destroy() {
6185
- if (this.dequeueScheduled) {
6186
- this.dequeueScheduled();
6187
- this.dequeueScheduled = void 0;
6188
- }
6189
- this.unsubscribeFromAllDeps();
6190
- if (this.dequeueResubscribe) {
6191
- this.dequeueResubscribe();
6192
- this.dequeueResubscribe = void 0;
6193
- }
6194
- this.isResubscribeEnqueued = false;
6195
- this.isTemporarilyUnsubscribedForFlush = false;
6196
- }
6197
- runOnce() {
6198
- this.run();
6199
- }
6200
- subscribeToDeps() {
6201
- if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
6202
- return;
6203
- for (const dep of this.sourceDeps) {
6204
- this.sourceUnsubscribers.push(
6205
- dep.subscribe(this.onSourceInvalidate)
6206
- );
6207
- }
6208
- for (const dep of this.computedDeps) {
6209
- this.computedUnsubscribers.push(
6210
- dep.subscribe(this.onComputedInvalidate)
6211
- );
6212
- }
6213
- }
6214
- unsubscribeFromSourceDeps() {
6215
- if (this.sourceUnsubscribers.length === 0) return;
6216
- for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
6217
- this.sourceUnsubscribers = [];
6218
- }
6219
- unsubscribeFromAllDeps() {
6220
- this.unsubscribeFromSourceDeps();
6221
- if (this.computedUnsubscribers.length === 0) return;
6222
- for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
6223
- this.computedUnsubscribers = [];
6224
- }
6225
- ensureResubscribeAfterFlush() {
6226
- if (this.isResubscribeEnqueued) return;
6227
- this.isResubscribeEnqueued = true;
6228
- this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
6229
- this.onAfterFlush
6230
- );
6231
- }
6232
- };
6233
-
6234
- // src/modules/computed/enums/EOIMComputedEventType.ts
6235
- var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
6236
- EOIMComputedEventType2["UPDATE"] = "update";
6237
- return EOIMComputedEventType2;
6238
- })(EOIMComputedEventType || {});
6239
-
6240
- // src/modules/computed/core/OIMComputed.ts
6241
- var OIMComputed = class {
6242
- constructor(runtime, opts) {
6243
- this.emitter = new OIMEventEmitter();
6244
- this.hasValue = false;
6245
- this.isDirty = true;
6246
- this.runtime = runtime;
6247
- this.compute = opts.compute;
6248
- this.compare = opts.compare ?? Object.is;
6249
- this._deps = opts.deps ?? [];
6250
- this.updateEventEmitter = new OIMUpdateEventEmitter(
6251
- this.runtime.queue,
6252
- "immediate"
6253
- );
6254
- this.effect = new OIMEffect(this.runtime, {
6255
- deps: this._deps,
6256
- onUpdate: () => {
6257
- this.isDirty = true;
6258
- },
6259
- run: () => {
6260
- this.recomputeAndEmitIfChanged();
6261
- }
6262
- });
6263
- }
6264
- get needsRecompute() {
6265
- return this.isDirty;
6266
- }
6267
- get isReady() {
6268
- return this.hasValue;
6269
- }
6270
- get deps() {
6271
- return this._deps;
6272
- }
6273
- get() {
6274
- this.recomputeAndEmitIfChanged();
6275
- return this.value;
6276
- }
6277
- getIfReady() {
6278
- return this.hasValue ? this.value : void 0;
6279
- }
6280
- destroy() {
6281
- this.effect.destroy();
6282
- this.updateEventEmitter.destroy();
6283
- this.emitter.offAll();
6284
- }
6285
- recomputeAndEmitIfChanged() {
6286
- if (!this.isDirty) return;
6287
- const prev = this.value;
6288
- const hadPrev = this.hasValue;
6289
- const next = this.compute();
6290
- this.value = next;
6291
- this.hasValue = true;
6292
- this.isDirty = false;
6293
- const changed = !hadPrev || !this.compare(prev, next);
6294
- if (!changed) return;
6295
- this.emitter.emit("update" /* UPDATE */, {
6296
- keys: ["value"]
6297
- });
6298
- this.updateEventEmitter.markUpdatedKey("value");
6299
- }
6300
- };
6301
-
6302
- // src/modules/effect/core/OIMEffectDependencyKeyed.ts
6303
- var OIMEffectDependencyKeyed = class {
6304
- constructor(source, key) {
6305
- this.source = source;
6306
- this.key = key;
6307
- }
6308
- subscribe(onUpdate) {
6309
- return this.source.subscribeOnKey(this.key, onUpdate);
6310
- }
6311
- };
6312
-
6313
- // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
6314
- var OIMEffectDependencyKeyedObject = class {
6315
- constructor(object, key) {
6316
- this.dep = new OIMEffectDependencyKeyed(object, key);
6317
- }
6318
- get source() {
6319
- return this.dep.source;
6320
- }
6321
- subscribe(onUpdate) {
6322
- return this.dep.subscribe(onUpdate);
6323
- }
6324
- };
6325
-
6326
- // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
6327
- var OIMEffectDependencyKeyedCollection = class {
6328
- constructor(collection, pk) {
6329
- this.dep = new OIMEffectDependencyKeyed(collection, pk);
6330
- }
6331
- get source() {
6332
- return this.dep.source;
6333
- }
6334
- subscribe(onUpdate) {
6335
- return this.dep.subscribe(onUpdate);
6336
- }
6337
- };
6338
-
6339
- // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
6340
- var OIMEffectDependencyKeyedIndex = class {
6341
- constructor(index, key) {
6342
- this.dep = new OIMEffectDependencyKeyed(index, key);
6343
- }
6344
- get source() {
6345
- return this.dep.source;
6346
- }
6347
- subscribe(onUpdate) {
6348
- return this.dep.subscribe(onUpdate);
6349
- }
6350
- };
6351
-
6352
6647
  // src/modules/selector/core/OIMObjectValueByKeySelector.ts
6353
6648
  var OIMObjectValueByKeySelector = class extends OIMSelector {
6354
6649
  constructor(runtime, obj, key) {
@@ -6426,8 +6721,9 @@ var OIMIndexPksByKeyArrayBasedSelector = class extends OIMSelector {
6426
6721
  areEqual(prev, next) {
6427
6722
  if (prev === next) return true;
6428
6723
  if (prev.length !== next.length) return false;
6429
- for (let i = 0; i < prev.length; i++)
6724
+ for (let i = 0; i < prev.length; i++) {
6430
6725
  if (prev[i] !== next[i]) return false;
6726
+ }
6431
6727
  return true;
6432
6728
  }
6433
6729
  };
@@ -6450,8 +6746,9 @@ var OIMIndexPksByKeysSetBasedSelector = class extends OIMSelector {
6450
6746
  areEqual(prev, next) {
6451
6747
  if (prev === next) return true;
6452
6748
  if (prev.length !== next.length) return false;
6453
- for (let i = 0; i < prev.length; i++)
6749
+ for (let i = 0; i < prev.length; i++) {
6454
6750
  if (prev[i] !== next[i]) return false;
6751
+ }
6455
6752
  return true;
6456
6753
  }
6457
6754
  };
@@ -6474,8 +6771,9 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
6474
6771
  areEqual(prev, next) {
6475
6772
  if (prev === next) return true;
6476
6773
  if (prev.length !== next.length) return false;
6477
- for (let i = 0; i < prev.length; i++)
6774
+ for (let i = 0; i < prev.length; i++) {
6478
6775
  if (prev[i] !== next[i]) return false;
6776
+ }
6479
6777
  return true;
6480
6778
  }
6481
6779
  };
@@ -6505,6 +6803,7 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
6505
6803
  OIMComputeRuntime,
6506
6804
  OIMComputed,
6507
6805
  OIMDBSettings,
6806
+ OIMDerivedCollection,
6508
6807
  OIMDerivedCollectionGlobalIndexArrayBased,
6509
6808
  OIMDerivedCollectionGlobalIndexSetBased,
6510
6809
  OIMDerivedCollectionIndexArrayBased,
@@ -6600,5 +6899,9 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
6600
6899
  createOIMIndexSlotMap,
6601
6900
  createOIMOrderedListCommandStreamDiffDriven,
6602
6901
  createOIMOrderedListMappedCommandStream,
6603
- diffOrderedListByPk
6902
+ diffOrderedListByPk,
6903
+ getOIMComputeRuntime,
6904
+ on,
6905
+ peekOIMComputeRuntime,
6906
+ setOIMComputeRuntime
6604
6907
  });