@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.js CHANGED
@@ -225,6 +225,25 @@ var OIMEventQueueScheduler = class {
225
225
  flush() {
226
226
  this.emitter.emit(0 /* FLUSH */, void 0);
227
227
  }
228
+ /**
229
+ * Detach a Node timer handle from the event loop, returning it unchanged.
230
+ *
231
+ * A pending flush must never be a reason for a process to stay alive: if
232
+ * nothing else is running there is no UI left to update, and the flush is
233
+ * moot. Without this a scheduled-but-not-yet-run flush keeps Node awake —
234
+ * which is why a test suite that leaves a queue scheduled reports
235
+ * "a worker process has failed to exit gracefully".
236
+ *
237
+ * In the browser `setTimeout` returns a number, which has no `unref`, so the
238
+ * call is guarded and the handle passes straight through.
239
+ */
240
+ unref(handle) {
241
+ const maybeUnrefable = handle;
242
+ if (typeof maybeUnrefable?.unref === "function") {
243
+ maybeUnrefable.unref();
244
+ }
245
+ return handle;
246
+ }
228
247
  };
229
248
 
230
249
  // src/core/OIMKeyedBucketSetBased.ts
@@ -564,14 +583,17 @@ var OIMCarrierKeyedEmitter = class {
564
583
  if (this.dirtyCarriers.length === 0) return;
565
584
  const flushing = this.dirtyCarriers;
566
585
  this.dirtyCarriers = [];
586
+ const multiCarrierHandlers = this.multiCarrierHandlers;
587
+ const delivered = multiCarrierHandlers !== void 0 && multiCarrierHandlers.size > 0 ? this.deliveredPool ?? (this.deliveredPool = /* @__PURE__ */ new Set()) : void 0;
567
588
  let i = 0;
568
589
  try {
569
590
  for (; i < flushing.length; i++) {
570
591
  flushing[i].dirty = false;
571
- this.notify(flushing[i]);
592
+ this.notify(flushing[i], delivered);
572
593
  }
573
594
  } finally {
574
595
  for (; i < flushing.length; i++) flushing[i].dirty = false;
596
+ delivered?.clear();
575
597
  }
576
598
  };
577
599
  this.queue = queue;
@@ -589,7 +611,15 @@ var OIMCarrierKeyedEmitter = class {
589
611
  this.addHandler(carrier, handler);
590
612
  carriers.push(carrier);
591
613
  }
614
+ const spansMultipleCarriers = carriers.length > 1;
615
+ if (spansMultipleCarriers) this.retainMultiCarrierHandler(handler);
616
+ let unsubscribed = false;
592
617
  return () => {
618
+ if (unsubscribed) return;
619
+ unsubscribed = true;
620
+ if (spansMultipleCarriers) {
621
+ this.releaseMultiCarrierHandler(handler);
622
+ }
593
623
  for (let i = 0; i < carriers.length; i++) {
594
624
  this.removeHandler(carriers[i], handler);
595
625
  }
@@ -600,10 +630,25 @@ var OIMCarrierKeyedEmitter = class {
600
630
  if (carrier) this.removeHandler(carrier, handler);
601
631
  }
602
632
  unsubscribeFromKeys(keys, handler) {
633
+ if (keys.length > 1) this.releaseMultiCarrierHandler(handler);
603
634
  for (let i = 0; i < keys.length; i++) {
604
635
  this.unsubscribeFromKey(keys[i], handler);
605
636
  }
606
637
  }
638
+ /** Take a reference for a handler whose subscription spans several carriers. */
639
+ retainMultiCarrierHandler(handler) {
640
+ const handlers = this.multiCarrierHandlers ?? (this.multiCarrierHandlers = /* @__PURE__ */ new Map());
641
+ handlers.set(handler, (handlers.get(handler) ?? 0) + 1);
642
+ }
643
+ /** Drop one reference; the handler leaves the set when none are left. */
644
+ releaseMultiCarrierHandler(handler) {
645
+ const handlers = this.multiCarrierHandlers;
646
+ if (handlers === void 0) return;
647
+ const count = handlers.get(handler);
648
+ if (count === void 0) return;
649
+ if (count > 1) handlers.set(handler, count - 1);
650
+ else handlers.delete(handler);
651
+ }
607
652
  addHandler(carrier, handler) {
608
653
  let subscribers = carrier.subscribers;
609
654
  if (!subscribers) {
@@ -692,6 +737,8 @@ var OIMCarrierKeyedEmitter = class {
692
737
  }
693
738
  this.dirtyCarriers = [];
694
739
  this.isFlushEnqueued = false;
740
+ this.multiCarrierHandlers?.clear();
741
+ this.deliveredPool?.clear();
695
742
  }
696
743
  assertNotInFlush() {
697
744
  if (this.queue.isInFlush) {
@@ -705,21 +752,43 @@ var OIMCarrierKeyedEmitter = class {
705
752
  this.isFlushEnqueued = true;
706
753
  this.queue.enqueue(this.onFlush);
707
754
  }
708
- notify(carrier) {
755
+ notify(carrier, delivered) {
709
756
  const subscribers = carrier.subscribers;
710
757
  if (!subscribers || subscribers.size === 0) return;
758
+ const multiCarrierHandlers = delivered ? this.multiCarrierHandlers : void 0;
711
759
  if (subscribers.size === 1) {
712
760
  const only = subscribers.values().next().value;
713
- if (only) only();
761
+ if (!only) return;
762
+ if (this.isAlreadyDelivered(only, delivered, multiCarrierHandlers)) {
763
+ return;
764
+ }
765
+ only();
714
766
  return;
715
767
  }
716
768
  const snapshot = [];
717
769
  subscribers.forEach((h) => snapshot.push(h));
718
770
  for (let i = 0; i < snapshot.length; i++) {
719
771
  const handler = snapshot[i];
720
- if (subscribers.has(handler)) handler();
772
+ if (!subscribers.has(handler)) continue;
773
+ if (this.isAlreadyDelivered(handler, delivered, multiCarrierHandlers)) {
774
+ continue;
775
+ }
776
+ handler();
721
777
  }
722
778
  }
779
+ /**
780
+ * Whether this handler already fired in the current flush and must be
781
+ * skipped. Only multi-carrier handlers are tracked; everything else always
782
+ * delivers. Membership is recorded via the size delta of a single `add` —
783
+ * one hash probe instead of `has` + `add`.
784
+ */
785
+ isAlreadyDelivered(handler, delivered, multiCarrierHandlers) {
786
+ if (delivered === void 0) return false;
787
+ if (multiCarrierHandlers?.has(handler) !== true) return false;
788
+ const size = delivered.size;
789
+ delivered.add(handler);
790
+ return delivered.size === size;
791
+ }
723
792
  };
724
793
 
725
794
  // src/core/OIMKeyedCarrierProvider.ts
@@ -1339,8 +1408,11 @@ var OIMCollection = class {
1339
1408
  this.emitter.emit(0 /* UPDATE */, { pks });
1340
1409
  }
1341
1410
  clear() {
1411
+ const pks = this.store.getAllPks();
1342
1412
  this.store.clear();
1343
- this.emitter.emit(0 /* UPDATE */, { pks: [] });
1413
+ if (pks.length > 0) {
1414
+ this.emitter.emit(0 /* UPDATE */, { pks });
1415
+ }
1344
1416
  }
1345
1417
  countAll() {
1346
1418
  return this.store.countAll();
@@ -1382,14 +1454,12 @@ var OIMReactiveCollection = class extends OIMCollection {
1382
1454
  // Only ever holds `slot.pk` (one canonical reference per logical key), so a
1383
1455
  // native `Set` dedups correctly by reference even for composite pks.
1384
1456
  this.pendingAnyUpdatePks = /* @__PURE__ */ new Set();
1385
- this.isAnyUpdateClearPending = false;
1386
1457
  this.isAnyUpdateScheduled = false;
1387
1458
  this.runAnyUpdate = () => {
1388
1459
  if (!this.isAnyUpdateScheduled) return;
1389
1460
  this.isAnyUpdateScheduled = false;
1390
- const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks.values());
1461
+ const pks = Array.from(this.pendingAnyUpdatePks.values());
1391
1462
  this.pendingAnyUpdatePks.clear();
1392
- this.isAnyUpdateClearPending = false;
1393
1463
  const snapshot = Array.from(this.anyUpdateHandlers);
1394
1464
  for (let i = 0; i < snapshot.length; i++) snapshot[i](pks);
1395
1465
  };
@@ -1429,30 +1499,43 @@ var OIMReactiveCollection = class extends OIMCollection {
1429
1499
  this.anyUpdateHandlers.delete(handler);
1430
1500
  };
1431
1501
  }
1432
- // The legacy `emitter` (EOIMCollectionEventType.UPDATE) coexists with the
1433
- // keyed `updateEmitter`. Build its payload only when something is actually
1434
- // subscribed — otherwise every upsert/remove allocates a throwaway
1435
- // `{ pks }` object (and array) that nobody reads.
1436
- notifyLegacy(pk) {
1502
+ // Two notification channels coexist on a reactive collection; they serve
1503
+ // different audiences and NEITHER is obsolete:
1504
+ // • the keyed `updateEmitter` — per-key/per-carrier pings ("did MY key
1505
+ // change?", payload-less `void`), for leaf subscribers like React hooks
1506
+ // that declare the keys they care about up front;
1507
+ // • this `emitter` UPDATE event — the aggregate/batch channel that carries
1508
+ // the full list of changed pks in one SYNCHRONOUS callback, for
1509
+ // consumers that do NOT know their keys ahead of time and must re-derive
1510
+ // over the batch: derived indexes/collections, changed-fields, persist,
1511
+ // snapshot-manager. `pks` always enumerates exactly what changed — even
1512
+ // clear() lists every removed key, so an empty array never occurs.
1513
+ // It is not a per-key emitter with fewer features — it is the opposite
1514
+ // shape (enumerated batch, not "my key"), which is why it stays.
1515
+ //
1516
+ // Emit on the batch channel only when something is actually subscribed —
1517
+ // otherwise every upsert/remove allocates a throwaway `{ pks }` object (and
1518
+ // array) that nobody reads.
1519
+ notifyBatch(pk) {
1437
1520
  if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1438
1521
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1439
1522
  }
1440
1523
  }
1441
- notifyLegacyMany(pks) {
1524
+ notifyBatchMany(pks) {
1442
1525
  if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1443
1526
  this.emitter.emit(0 /* UPDATE */, { pks });
1444
1527
  }
1445
1528
  }
1446
1529
  upsertOneByPk(pk, entity) {
1447
1530
  const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
1448
- this.notifyLegacy(pk);
1531
+ this.notifyBatch(pk);
1449
1532
  this.updateEmitter.markUpdatedCarrier(slot);
1450
1533
  this.trackAnyUpdatePk(pk);
1451
1534
  return slot;
1452
1535
  }
1453
1536
  upsertOne(entity) {
1454
1537
  const slot = this.upsertOneWithoutNotifications(entity);
1455
- this.notifyLegacy(slot.pk);
1538
+ this.notifyBatch(slot.pk);
1456
1539
  this.updateEmitter.markUpdatedCarrier(slot);
1457
1540
  this.trackAnyUpdatePk(slot.pk);
1458
1541
  return slot;
@@ -1463,7 +1546,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1463
1546
  (entity) => this.upsertOneWithoutNotifications(entity)
1464
1547
  );
1465
1548
  const pks = slots.map((slot) => slot.pk);
1466
- this.notifyLegacyMany(pks);
1549
+ this.notifyBatchMany(pks);
1467
1550
  for (let i = 0; i < slots.length; i++) {
1468
1551
  this.updateEmitter.markUpdatedCarrier(slots[i]);
1469
1552
  }
@@ -1473,7 +1556,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1473
1556
  removeOne(entity) {
1474
1557
  const pk = this.selectPk(entity);
1475
1558
  this.store.removeOneByPk(pk);
1476
- this.notifyLegacy(pk);
1559
+ this.notifyBatch(pk);
1477
1560
  this.updateEmitter.markUpdatedKey(pk);
1478
1561
  this.trackAnyUpdatePk(pk);
1479
1562
  }
@@ -1481,30 +1564,32 @@ var OIMReactiveCollection = class extends OIMCollection {
1481
1564
  if (entities.length === 0) return;
1482
1565
  const pks = entities.map(this.selectPk);
1483
1566
  this.store.removeManyByPks(pks);
1484
- this.notifyLegacyMany(pks);
1567
+ this.notifyBatchMany(pks);
1485
1568
  this.updateEmitter.markUpdatedKeys(pks);
1486
1569
  this.trackAnyUpdatePks(pks);
1487
1570
  }
1488
1571
  removeOneByPk(pk) {
1489
1572
  this.store.removeOneByPk(pk);
1490
- this.notifyLegacy(pk);
1573
+ this.notifyBatch(pk);
1491
1574
  this.updateEmitter.markUpdatedKey(pk);
1492
1575
  this.trackAnyUpdatePk(pk);
1493
1576
  }
1494
1577
  removeManyByPks(pks) {
1495
1578
  if (pks.length === 0) return;
1496
1579
  this.store.removeManyByPks(pks);
1497
- this.notifyLegacyMany(pks);
1580
+ this.notifyBatchMany(pks);
1498
1581
  this.updateEmitter.markUpdatedKeys(pks);
1499
1582
  this.trackAnyUpdatePks(pks);
1500
1583
  }
1501
1584
  clear() {
1585
+ const needsPks = this.emitter.hasHandlers(0 /* UPDATE */) || this.anyUpdateHandlers.size > 0;
1586
+ const pks = needsPks ? this.store.getAllPks() : void 0;
1502
1587
  this.store.clear();
1503
- if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1504
- this.emitter.emit(0 /* UPDATE */, { pks: [] });
1588
+ if (pks && pks.length > 0) {
1589
+ this.notifyBatchMany(pks);
1590
+ this.trackAnyUpdatePks(pks);
1505
1591
  }
1506
1592
  this.updateEmitter.markAllUpdated();
1507
- this.trackAnyUpdateClear();
1508
1593
  }
1509
1594
  trackAnyUpdatePk(pk) {
1510
1595
  if (this.anyUpdateHandlers.size === 0) return;
@@ -1513,14 +1598,9 @@ var OIMReactiveCollection = class extends OIMCollection {
1513
1598
  }
1514
1599
  trackAnyUpdatePks(pks) {
1515
1600
  if (this.anyUpdateHandlers.size === 0) return;
1516
- for (let i = 0; i < pks.length; i++)
1601
+ for (let i = 0; i < pks.length; i++) {
1517
1602
  this.pendingAnyUpdatePks.add(pks[i]);
1518
- this.ensureAnyUpdateEnqueued();
1519
- }
1520
- trackAnyUpdateClear() {
1521
- if (this.anyUpdateHandlers.size === 0) return;
1522
- this.isAnyUpdateClearPending = true;
1523
- this.pendingAnyUpdatePks.clear();
1603
+ }
1524
1604
  this.ensureAnyUpdateEnqueued();
1525
1605
  }
1526
1606
  ensureAnyUpdateEnqueued() {
@@ -1953,8 +2033,13 @@ var OIMUpdateEventEmitter = class {
1953
2033
  var OIMOrderedListCommandBuffer = class {
1954
2034
  constructor(queue) {
1955
2035
  this.commandsByKey = /* @__PURE__ */ new Map();
2036
+ // Keys whose buffered leading `reset` is stale: an edit after the reset
2037
+ // should be folded into it, but we defer the (O(list-size)) rebuild to the
2038
+ // next read instead of redoing it on every subsequent edit in the batch.
2039
+ this.staleResetKeys = /* @__PURE__ */ new Set();
1956
2040
  this.onAfterFlush = () => {
1957
2041
  this.commandsByKey.clear();
2042
+ this.staleResetKeys.clear();
1958
2043
  };
1959
2044
  this.commandsEventEmitter = new OIMUpdateEventEmitter(
1960
2045
  queue,
@@ -1969,9 +2054,11 @@ var OIMOrderedListCommandBuffer = class {
1969
2054
  return this.commandsEventEmitter.subscribeOnKey(key, handler);
1970
2055
  }
1971
2056
  getBufferedCommands(key) {
2057
+ this.materializeStaleReset(key);
1972
2058
  return this.commandsByKey.get(key) ?? [];
1973
2059
  }
1974
2060
  consumeCommands(key) {
2061
+ this.materializeStaleReset(key);
1975
2062
  const cmds = this.commandsByKey.get(key);
1976
2063
  if (!cmds || cmds.length === 0) return [];
1977
2064
  return cmds.slice();
@@ -1980,6 +2067,20 @@ var OIMOrderedListCommandBuffer = class {
1980
2067
  this.commandsEventEmitter.destroy();
1981
2068
  this.unsubscribeAfterFlush();
1982
2069
  this.commandsByKey.clear();
2070
+ this.staleResetKeys.clear();
2071
+ }
2072
+ /**
2073
+ * Rebuild a deferred leading `reset` from the current order, once, at read
2074
+ * time. All the post-reset edits that were folded in are already reflected
2075
+ * in the driver's backing order, so a single rebuild here is equivalent to
2076
+ * having recomputed the reset on each of those edits.
2077
+ */
2078
+ materializeStaleReset(key) {
2079
+ if (!this.staleResetKeys.delete(key)) return;
2080
+ const cmds = this.commandsByKey.get(key);
2081
+ if (cmds && cmds.length > 0 && cmds[0].type === "reset") {
2082
+ cmds[0] = this.createResetCommand(this.getItemsByKey(key));
2083
+ }
1983
2084
  }
1984
2085
  appendCommand(key, cmd) {
1985
2086
  let cmds = this.commandsByKey.get(key);
@@ -1990,8 +2091,9 @@ var OIMOrderedListCommandBuffer = class {
1990
2091
  if (cmd.type === "reset") {
1991
2092
  cmds.length = 0;
1992
2093
  cmds.push(cmd);
2094
+ this.staleResetKeys.delete(key);
1993
2095
  } else if (cmds.length > 0 && cmds[0].type === "reset") {
1994
- cmds[0] = this.createResetCommand(this.getItemsByKey(key));
2096
+ this.staleResetKeys.add(key);
1995
2097
  } else {
1996
2098
  cmds.push(cmd);
1997
2099
  }
@@ -3033,10 +3135,6 @@ var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndex
3033
3135
  });
3034
3136
  this.keysByPk = /* @__PURE__ */ new Map();
3035
3137
  this.onCollectionUpdate = (pks) => {
3036
- if (pks.length === 0) {
3037
- this.rebuildFromCollection();
3038
- return;
3039
- }
3040
3138
  for (const pk of pks) {
3041
3139
  this.syncPk(pk);
3042
3140
  }
@@ -3138,10 +3236,6 @@ var OIMDerivedCollectionIndexArrayBased = class extends OIMReactiveCollectionInd
3138
3236
  // touching an unrelated field can skip re-sorting when the value is unchanged.
3139
3237
  this.orderValueByPk = /* @__PURE__ */ new Map();
3140
3238
  this.onCollectionUpdate = (pks) => {
3141
- if (pks.length === 0) {
3142
- this.rebuildFromCollection();
3143
- return;
3144
- }
3145
3239
  const ordered = !!(this.compareEntities || this.orderBy);
3146
3240
  const keysToSort = ordered ? /* @__PURE__ */ new Set() : null;
3147
3241
  for (const pk of pks) {
@@ -3558,10 +3652,6 @@ var OIMDerivedCollectionGlobalIndexArrayBased = class extends OIMReactiveCollect
3558
3652
  // an unrelated field can skip re-sorting when the value is unchanged.
3559
3653
  this.orderValueByPk = /* @__PURE__ */ new Map();
3560
3654
  this.onCollectionUpdate = (pks) => {
3561
- if (pks.length === 0) {
3562
- this.rebuildFromCollection();
3563
- return;
3564
- }
3565
3655
  const ordered = !!(this.compareEntities || this.orderBy);
3566
3656
  let needsSort = false;
3567
3657
  for (const pk of pks) {
@@ -3659,10 +3749,6 @@ var OIMDerivedCollectionGlobalIndexSetBased = class extends OIMReactiveCollectio
3659
3749
  super(queue, { collection, indexOptions: opts.indexOptions });
3660
3750
  this.presentPks = /* @__PURE__ */ new Set();
3661
3751
  this.onCollectionUpdate = (pks) => {
3662
- if (pks.length === 0) {
3663
- this.rebuildFromCollection();
3664
- return;
3665
- }
3666
3752
  for (const pk of pks) {
3667
3753
  const prev = this.presentPks.has(pk);
3668
3754
  const slot = this.collection.getSlotByPk(pk);
@@ -3838,6 +3924,55 @@ function createOIMCollectionIndexFactory(queue, collection) {
3838
3924
  return new OIMCollectionIndexFactory(queue, collection);
3839
3925
  }
3840
3926
 
3927
+ // src/core/OIMDerivedCollection.ts
3928
+ var OIMDerivedCollection = class extends OIMReactiveCollection {
3929
+ constructor(queue, source, derive, opts) {
3930
+ super(queue, {
3931
+ store: opts?.store,
3932
+ // Each sync recomputes the WHOLE derived entity → replace, not merge.
3933
+ updateEntity: (draft) => draft
3934
+ });
3935
+ this.onSourceUpdate = (pks) => {
3936
+ for (let i = 0; i < pks.length; i++) this.syncPk(pks[i]);
3937
+ };
3938
+ this.source = source;
3939
+ this.derive = derive;
3940
+ this.compareDerived = opts?.compare ?? Object.is;
3941
+ this.unsubscribeFromSource = source.emitter.on(
3942
+ 0 /* UPDATE */,
3943
+ (payload) => this.onSourceUpdate(payload.pks)
3944
+ );
3945
+ if (opts?.buildInitial !== false) {
3946
+ this.rebuildFromSource();
3947
+ }
3948
+ }
3949
+ rebuildFromSource() {
3950
+ for (const pk of this.getAllPks()) {
3951
+ if (this.source.getOneByPk(pk) === void 0) {
3952
+ this.removeOneByPk(pk);
3953
+ }
3954
+ }
3955
+ for (const slot of this.source.getAllSlots()) {
3956
+ if (slot.item !== void 0) this.syncPk(slot.pk);
3957
+ }
3958
+ }
3959
+ destroy() {
3960
+ this.unsubscribeFromSource();
3961
+ super.destroy();
3962
+ }
3963
+ syncPk(pk) {
3964
+ const sourceEntity = this.source.getOneByPk(pk);
3965
+ if (sourceEntity === void 0) {
3966
+ if (this.getOneByPk(pk) !== void 0) this.removeOneByPk(pk);
3967
+ return;
3968
+ }
3969
+ const next = this.derive(sourceEntity, pk);
3970
+ const prev = this.getOneByPk(pk);
3971
+ if (prev !== void 0 && this.compareDerived(prev, next)) return;
3972
+ this.upsertOneByPk(pk, next);
3973
+ }
3974
+ };
3975
+
3841
3976
  // src/core/OIMObjectStoreRecordDriven.ts
3842
3977
  var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
3843
3978
  constructor(initial) {
@@ -3910,8 +4045,11 @@ var OIMObject = class {
3910
4045
  return this.store.getAll();
3911
4046
  }
3912
4047
  clear() {
4048
+ const keys = this.store.keys();
3913
4049
  this.store.clear();
3914
- this.onUpdatedKeys([]);
4050
+ if (keys.length > 0) {
4051
+ this.onUpdatedKeys(keys);
4052
+ }
3915
4053
  }
3916
4054
  /**
3917
4055
  * Write fan-out hooks. Split single-key vs multi-key so a reactive subclass
@@ -4214,8 +4352,9 @@ var OIMComparatorFactory = class {
4214
4352
  if (ka.length !== kb.length) return true;
4215
4353
  for (let i = 0; i < ka.length; i++) {
4216
4354
  const k = ka[i];
4217
- if (b[k] !== a[k])
4355
+ if (b[k] !== a[k]) {
4218
4356
  return true;
4357
+ }
4219
4358
  }
4220
4359
  return false;
4221
4360
  };
@@ -4265,10 +4404,14 @@ var OIMEventQueue = class {
4265
4404
  return this.isFlushing;
4266
4405
  }
4267
4406
  /**
4268
- * Execute all pending tasks.
4407
+ * Execute the tasks pending at the moment the flush starts — a single pass,
4408
+ * not a drain-to-empty.
4269
4409
  *
4270
- * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
4271
- * executed within the same flush.
4410
+ * The pending set is swapped out for an empty spare up front and the flush
4411
+ * iterates that captured set. Tasks enqueued during the flush land in the
4412
+ * fresh set and run on the NEXT flush, not this one. (Contrast
4413
+ * `OIMComputeRuntime.flush()`, which re-reads its bucket per level and so
4414
+ * does drain same-level tasks scheduled mid-flush.)
4272
4415
  */
4273
4416
  flush() {
4274
4417
  this.scheduler?.cancel();
@@ -4542,20 +4685,92 @@ var OIMPkCodecKeyPath = class {
4542
4685
  }
4543
4686
  };
4544
4687
 
4688
+ // src/modules/effect/core/OIMEffectDependencyComputed.ts
4689
+ var OIMEffectDependencyComputed = class {
4690
+ constructor(source) {
4691
+ this.source = source;
4692
+ }
4693
+ subscribe(onUpdate) {
4694
+ return this.source.emitter.on("update", onUpdate);
4695
+ }
4696
+ };
4697
+
4545
4698
  // src/modules/compute/core/OIMComputeRuntime.ts
4546
4699
  var OIMComputeRuntime = class {
4547
4700
  constructor(queue) {
4548
4701
  this.queue = queue;
4549
4702
  this.isFlushScheduled = false;
4550
4703
  this.isFlushing = false;
4704
+ // Active level range (min > max means nothing pending).
4551
4705
  this.minLevel = Infinity;
4552
4706
  this.maxLevel = -Infinity;
4553
- this.dirtyByLevel = /* @__PURE__ */ new Map();
4707
+ this.dirtyByLevel = [];
4554
4708
  this.afterFlushTasks = /* @__PURE__ */ new Set();
4555
- this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
4556
- if (!this.isFlushScheduled) return;
4557
- this.flush();
4558
- });
4709
+ // Topological depth per computed node — the engine is the ONLY owner of
4710
+ // levels; nodes never expose their own. A node's dependents look up its depth
4711
+ // here (by the computed reference their dependency wraps). Written once at
4712
+ // construction, never on the hot flush path.
4713
+ this.levelByComputed = /* @__PURE__ */ new WeakMap();
4714
+ this._isDestroyed = false;
4715
+ this.unsubscribeFromQueue = this.queue.emitter.on(
4716
+ 0 /* AFTER_FLUSH */,
4717
+ () => {
4718
+ if (!this.isFlushScheduled) return;
4719
+ this.flush();
4720
+ }
4721
+ );
4722
+ }
4723
+ /**
4724
+ * Whether {@link destroy} has run. A destroyed runtime is detached from its
4725
+ * queue and must not be reused — `getOIMComputeRuntime` reads this to hand
4726
+ * out a fresh runtime instead of a dead one.
4727
+ */
4728
+ get isDestroyed() {
4729
+ return this._isDestroyed;
4730
+ }
4731
+ /**
4732
+ * Detach from the queue and drop everything pending.
4733
+ *
4734
+ * Without this the AFTER_FLUSH subscription lives as long as the queue does,
4735
+ * even once every computed / effect on this runtime is gone — harmless (the
4736
+ * handler early-returns) but impossible to unwind. Destroying does NOT
4737
+ * destroy the nodes scheduled on it; destroy them first, then the runtime.
4738
+ * Idempotent.
4739
+ */
4740
+ destroy() {
4741
+ if (this._isDestroyed) return;
4742
+ this._isDestroyed = true;
4743
+ this.unsubscribeFromQueue();
4744
+ for (let i = 0; i < this.dirtyByLevel.length; i++) {
4745
+ this.dirtyByLevel[i]?.clear();
4746
+ }
4747
+ this.afterFlushTasks.clear();
4748
+ this.minLevel = Infinity;
4749
+ this.maxLevel = -Infinity;
4750
+ this.isFlushScheduled = false;
4751
+ }
4752
+ /**
4753
+ * Topological depth for a node with these dependencies: one above the deepest
4754
+ * computed dependency (source-only → 0). The engine owns this — callers pass
4755
+ * their dependencies and get back a level, they never compute it themselves.
4756
+ */
4757
+ computeLevel(deps) {
4758
+ let max = -1;
4759
+ for (const dep of deps) {
4760
+ if (dep instanceof OIMEffectDependencyComputed) {
4761
+ const depLevel = this.levelByComputed.get(dep.source) ?? 0;
4762
+ if (depLevel > max) max = depLevel;
4763
+ }
4764
+ }
4765
+ return max + 1;
4766
+ }
4767
+ /** Record a computed's depth so its dependents can look it up. */
4768
+ registerLevel(computed, level) {
4769
+ this.levelByComputed.set(computed, level);
4770
+ }
4771
+ /** The recorded topological depth of a computed (0 if unknown). Introspection. */
4772
+ getLevel(computed) {
4773
+ return this.levelByComputed.get(computed) ?? 0;
4559
4774
  }
4560
4775
  /**
4561
4776
  * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
@@ -4567,10 +4782,10 @@ var OIMComputeRuntime = class {
4567
4782
  if (!isActive) return;
4568
4783
  task();
4569
4784
  };
4570
- let bucket = this.dirtyByLevel.get(level);
4571
- if (!bucket) {
4785
+ let bucket = this.dirtyByLevel[level];
4786
+ if (bucket === void 0) {
4572
4787
  bucket = /* @__PURE__ */ new Set();
4573
- this.dirtyByLevel.set(level, bucket);
4788
+ this.dirtyByLevel[level] = bucket;
4574
4789
  }
4575
4790
  bucket.add(wrapped);
4576
4791
  if (level < this.minLevel) this.minLevel = level;
@@ -4602,7 +4817,7 @@ var OIMComputeRuntime = class {
4602
4817
  }
4603
4818
  flush() {
4604
4819
  this.isFlushScheduled = false;
4605
- if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
4820
+ if (this.minLevel > this.maxLevel && this.afterFlushTasks.size === 0)
4606
4821
  return;
4607
4822
  this.isFlushing = true;
4608
4823
  try {
@@ -4611,15 +4826,15 @@ var OIMComputeRuntime = class {
4611
4826
  this.afterFlushTasks.clear();
4612
4827
  for (let i = 0; i < snapshot.length; i++) snapshot[i]();
4613
4828
  }
4614
- if (this.dirtyByLevel.size === 0) return;
4829
+ if (this.minLevel > this.maxLevel) return;
4615
4830
  let level = this.minLevel;
4616
4831
  let max = this.maxLevel;
4617
4832
  while (level <= max) {
4618
4833
  for (; ; ) {
4619
- const bucket = this.dirtyByLevel.get(level);
4620
- if (!bucket || bucket.size === 0) break;
4621
- this.dirtyByLevel.delete(level);
4834
+ const bucket = this.dirtyByLevel[level];
4835
+ if (bucket === void 0 || bucket.size === 0) break;
4622
4836
  const snapshot = Array.from(bucket);
4837
+ bucket.clear();
4623
4838
  for (let i = 0; i < snapshot.length; i++) snapshot[i]();
4624
4839
  }
4625
4840
  if (this.maxLevel > max) max = this.maxLevel;
@@ -4627,24 +4842,38 @@ var OIMComputeRuntime = class {
4627
4842
  }
4628
4843
  } finally {
4629
4844
  this.isFlushing = false;
4630
- if (this.dirtyByLevel.size === 0) {
4631
- this.minLevel = Infinity;
4632
- this.maxLevel = -Infinity;
4633
- } else {
4634
- let nextMin = Infinity;
4635
- let nextMax = -Infinity;
4636
- this.dirtyByLevel.forEach((_bucket, lvl) => {
4637
- if (lvl < nextMin) nextMin = lvl;
4638
- if (lvl > nextMax) nextMax = lvl;
4639
- });
4640
- this.minLevel = nextMin;
4641
- this.maxLevel = nextMax;
4642
- this.isFlushScheduled = true;
4845
+ this.recomputePendingRange();
4846
+ }
4847
+ }
4848
+ recomputePendingRange() {
4849
+ let nextMin = Infinity;
4850
+ let nextMax = -Infinity;
4851
+ const buckets = this.dirtyByLevel;
4852
+ for (let i = 0; i < buckets.length; i++) {
4853
+ const bucket = buckets[i];
4854
+ if (bucket !== void 0 && bucket.size > 0) {
4855
+ if (i < nextMin) nextMin = i;
4856
+ if (i > nextMax) nextMax = i;
4643
4857
  }
4644
4858
  }
4859
+ this.minLevel = nextMin;
4860
+ this.maxLevel = nextMax;
4861
+ if (nextMin <= nextMax) this.isFlushScheduled = true;
4645
4862
  }
4646
4863
  };
4647
4864
 
4865
+ // src/dx/oimComputeRuntimeByQueue.ts
4866
+ var oimComputeRuntimeByQueue = /* @__PURE__ */ new WeakMap();
4867
+
4868
+ // src/dx/getOIMComputeRuntime.ts
4869
+ function getOIMComputeRuntime(queue) {
4870
+ const runtime = oimComputeRuntimeByQueue.get(queue);
4871
+ if (runtime !== void 0 && !runtime.isDestroyed) return runtime;
4872
+ const nextRuntime = new OIMComputeRuntime(queue);
4873
+ oimComputeRuntimeByQueue.set(queue, nextRuntime);
4874
+ return nextRuntime;
4875
+ }
4876
+
4648
4877
  // src/modules/selector/core/OIMSelector.ts
4649
4878
  var OIMSelector = class {
4650
4879
  constructor(runtime, sourceDeps, computedDeps = []) {
@@ -4905,8 +5134,9 @@ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
4905
5134
  areEqual(prev, next) {
4906
5135
  if (prev === next) return true;
4907
5136
  if (prev.length !== next.length) return false;
4908
- for (let i = 0; i < prev.length; i++)
5137
+ for (let i = 0; i < prev.length; i++) {
4909
5138
  if (prev[i] !== next[i]) return false;
5139
+ }
4910
5140
  return true;
4911
5141
  }
4912
5142
  };
@@ -4976,8 +5206,9 @@ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
4976
5206
  areEqual(prev, next) {
4977
5207
  if (prev === next) return true;
4978
5208
  if (prev.length !== next.length) return false;
4979
- for (let i = 0; i < prev.length; i++)
5209
+ for (let i = 0; i < prev.length; i++) {
4980
5210
  if (prev[i] !== next[i]) return false;
5211
+ }
4981
5212
  return true;
4982
5213
  }
4983
5214
  };
@@ -5041,8 +5272,9 @@ var OIMEntitiesByGlobalIndexArrayBasedSelector = class extends OIMSelector {
5041
5272
  areEqual(prev, next) {
5042
5273
  if (prev === next) return true;
5043
5274
  if (prev.length !== next.length) return false;
5044
- for (let i = 0; i < prev.length; i++)
5275
+ for (let i = 0; i < prev.length; i++) {
5045
5276
  if (prev[i] !== next[i]) return false;
5277
+ }
5046
5278
  return true;
5047
5279
  }
5048
5280
  };
@@ -5107,8 +5339,9 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
5107
5339
  areEqual(prev, next) {
5108
5340
  if (prev === next) return true;
5109
5341
  if (prev.length !== next.length) return false;
5110
- for (let i = 0; i < prev.length; i++)
5342
+ for (let i = 0; i < prev.length; i++) {
5111
5343
  if (prev[i] !== next[i]) return false;
5344
+ }
5112
5345
  return true;
5113
5346
  }
5114
5347
  };
@@ -5117,7 +5350,7 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
5117
5350
  var OIMCollectionSelectors = class {
5118
5351
  constructor(queue, collection) {
5119
5352
  this.collection = collection;
5120
- this.runtime = new OIMComputeRuntime(queue);
5353
+ this.runtime = getOIMComputeRuntime(queue);
5121
5354
  }
5122
5355
  byPk(pk) {
5123
5356
  return new OIMCollectionByPkSelector(this.runtime, this.collection, pk);
@@ -5184,120 +5417,401 @@ var OIMCollectionSelectors = class {
5184
5417
  }
5185
5418
  };
5186
5419
 
5187
- // src/dx/OIMCollectionKit.ts
5188
- function createOIMCollectionKit(queue, opts) {
5189
- const collection = new OIMReactiveCollection(queue, opts);
5190
- const indexFactory = new OIMCollectionIndexFactory(queue, collection);
5191
- const select = new OIMCollectionSelectors(queue, collection);
5192
- const scope = new OIMDisposeScope();
5193
- scope.add(collection);
5194
- return {
5195
- queue,
5196
- collection,
5197
- indexFactory,
5198
- select,
5199
- scope,
5200
- destroy: () => scope.destroy()
5201
- };
5202
- }
5420
+ // src/modules/effect/core/OIMEffectDependencyKeyed.ts
5421
+ var OIMEffectDependencyKeyed = class {
5422
+ constructor(source, key) {
5423
+ this.source = source;
5424
+ this.key = key;
5425
+ }
5426
+ subscribe(onUpdate) {
5427
+ return this.source.subscribeOnKey(this.key, onUpdate);
5428
+ }
5429
+ };
5203
5430
 
5204
- // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
5205
- var OIMCollectionChangedFields = class {
5206
- constructor(queue, collection, opts) {
5207
- this.isInWrite = false;
5208
- this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
5209
- // PKs written through this wrapper during the current flush. Their fields are
5210
- // already tracked accurately via trackPatch, so the flush-time external-write
5211
- // handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
5212
- this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
5213
- this.changedFieldsByPk = /* @__PURE__ */ new Map();
5214
- this.changedPksByField = /* @__PURE__ */ new Map();
5215
- this.onAfterFlush = () => {
5216
- this.clear();
5217
- this.writtenThroughThisFlush.clear();
5218
- };
5219
- this.onCollectionUpdate = (payload) => {
5220
- if (this.isInWrite) return;
5221
- if (payload.pks.length === 0) return;
5222
- for (let i = 0; i < payload.pks.length; i++) {
5223
- const pk = payload.pks[i];
5224
- if (this.writtenThroughThisFlush.has(pk)) continue;
5225
- this.trackExternalMutationByPk(pk);
5226
- }
5227
- };
5228
- this.collection = collection;
5229
- this.detectExternalMutations = opts?.detectExternalMutations !== false;
5230
- this.selectPk = opts?.selectPk ?? (() => {
5231
- throw new Error(
5232
- `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
5233
- );
5234
- });
5235
- this.changedPksEventEmitter = new OIMUpdateEventEmitter(
5236
- queue,
5237
- "after_flush"
5238
- );
5239
- this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
5240
- this.unsubscribeAfterFlush = queue.emitter.on(
5241
- 0 /* AFTER_FLUSH */,
5242
- this.onAfterFlush
5243
- );
5244
- if (this.detectExternalMutations) {
5245
- const maybeReactive = collection;
5246
- this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
5247
- this.onCollectionUpdate({
5248
- pks
5249
- });
5250
- }) : collection.emitter.on(
5251
- 0 /* UPDATE */,
5252
- this.onCollectionUpdate
5253
- );
5254
- }
5431
+ // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
5432
+ var OIMEffectDependencyKeyedCollection = class {
5433
+ constructor(collection, pk) {
5434
+ this.dep = new OIMEffectDependencyKeyed(collection, pk);
5255
5435
  }
5256
- upsertOneByPk(pk, patch) {
5257
- const slot = this.withWrite(() => {
5258
- this.trackPatch(pk, patch);
5259
- return this.collection.upsertOneByPk(pk, patch);
5260
- });
5261
- this.updateEntitySnapshot(pk);
5262
- return slot;
5436
+ get source() {
5437
+ return this.dep.source;
5263
5438
  }
5264
- upsertOne(entity) {
5265
- const slot = this.withWrite(() => {
5266
- const pk = this.selectPk(entity);
5267
- this.trackPatch(pk, entity);
5268
- return this.collection.upsertOne(entity);
5269
- });
5270
- this.updateEntitySnapshot(slot.pk);
5271
- return slot;
5439
+ subscribe(onUpdate) {
5440
+ return this.dep.subscribe(onUpdate);
5272
5441
  }
5273
- upsertMany(entities) {
5274
- const pks = [];
5275
- const slots = this.withWrite(() => {
5276
- for (const entity of entities) {
5277
- const pk = this.selectPk(entity);
5278
- pks.push(pk);
5279
- this.trackPatch(pk, entity);
5280
- }
5281
- return this.collection.upsertMany(entities);
5282
- });
5283
- for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
5284
- return slots;
5442
+ };
5443
+
5444
+ // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
5445
+ var OIMEffectDependencyKeyedIndex = class {
5446
+ constructor(index, key) {
5447
+ this.dep = new OIMEffectDependencyKeyed(index, key);
5285
5448
  }
5286
- getChangedFieldsByPk(pk) {
5287
- return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
5449
+ get source() {
5450
+ return this.dep.source;
5288
5451
  }
5289
- consumeChangedFieldsByPk(pk) {
5290
- const existing = this.changedFieldsByPk.get(pk);
5291
- if (!existing) return /* @__PURE__ */ new Set();
5292
- return new Set(existing);
5452
+ subscribe(onUpdate) {
5453
+ return this.dep.subscribe(onUpdate);
5293
5454
  }
5294
- getChangedPksByField(field) {
5295
- return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
5455
+ };
5456
+
5457
+ // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
5458
+ var OIMEffectDependencyKeyedObject = class {
5459
+ constructor(object, key) {
5460
+ this.dep = new OIMEffectDependencyKeyed(object, key);
5296
5461
  }
5297
- consumeChangedPksByField(field) {
5298
- const existing = this.changedPksByField.get(field);
5299
- if (!existing) return /* @__PURE__ */ new Set();
5300
- return new Set(existing);
5462
+ get source() {
5463
+ return this.dep.source;
5464
+ }
5465
+ subscribe(onUpdate) {
5466
+ return this.dep.subscribe(onUpdate);
5467
+ }
5468
+ };
5469
+
5470
+ // src/dx/on.ts
5471
+ var on = {
5472
+ /** Depend on one PK of a collection. */
5473
+ collection: (collection, pk) => new OIMEffectDependencyKeyedCollection(collection, pk),
5474
+ /** Depend on one key of an index. */
5475
+ index: (index, key) => new OIMEffectDependencyKeyedIndex(index, key),
5476
+ /** Depend on one key of a reactive object. */
5477
+ object: (object, key) => new OIMEffectDependencyKeyedObject(object, key),
5478
+ /** Depend on another computed. */
5479
+ computed: (computed) => new OIMEffectDependencyComputed(computed)
5480
+ };
5481
+
5482
+ // src/modules/effect/core/OIMEffect.ts
5483
+ var OIMEffect = class {
5484
+ constructor(runtime, opts, level) {
5485
+ this.sourceDeps = [];
5486
+ this.computedDeps = [];
5487
+ this.sourceUnsubscribers = [];
5488
+ this.computedUnsubscribers = [];
5489
+ this.isResubscribeEnqueued = false;
5490
+ this.isTemporarilyUnsubscribedForFlush = false;
5491
+ this.isScheduled = false;
5492
+ this.runTask = () => {
5493
+ this.isScheduled = false;
5494
+ this.dequeueScheduled = void 0;
5495
+ this.runOnce();
5496
+ };
5497
+ this.onSourceInvalidate = () => {
5498
+ this.onUpdate?.();
5499
+ if (!this.isTemporarilyUnsubscribedForFlush) {
5500
+ this.isTemporarilyUnsubscribedForFlush = true;
5501
+ this.unsubscribeFromSourceDeps();
5502
+ this.ensureResubscribeAfterFlush();
5503
+ }
5504
+ if (this.isScheduled) return;
5505
+ this.isScheduled = true;
5506
+ this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
5507
+ };
5508
+ this.onComputedInvalidate = () => {
5509
+ this.onUpdate?.();
5510
+ if (this.isScheduled) return;
5511
+ this.isScheduled = true;
5512
+ this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
5513
+ };
5514
+ this.onAfterFlush = () => {
5515
+ if (!this.isResubscribeEnqueued) return;
5516
+ this.isResubscribeEnqueued = false;
5517
+ this.dequeueResubscribe = void 0;
5518
+ this.isTemporarilyUnsubscribedForFlush = false;
5519
+ if (this.sourceUnsubscribers.length === 0) {
5520
+ for (const dep of this.sourceDeps) {
5521
+ this.sourceUnsubscribers.push(
5522
+ dep.subscribe(this.onSourceInvalidate)
5523
+ );
5524
+ }
5525
+ }
5526
+ };
5527
+ this.runtime = runtime;
5528
+ this.onUpdate = opts.onUpdate;
5529
+ this.run = opts.run;
5530
+ this.deps = opts.deps ?? [];
5531
+ for (const dep of this.deps) {
5532
+ if (dep instanceof OIMEffectDependencyComputed) {
5533
+ this.computedDeps.push(dep);
5534
+ } else {
5535
+ this.sourceDeps.push(dep);
5536
+ }
5537
+ }
5538
+ this.level = level ?? this.runtime.computeLevel(this.deps);
5539
+ this.subscribeToDeps();
5540
+ }
5541
+ destroy() {
5542
+ if (this.dequeueScheduled) {
5543
+ this.dequeueScheduled();
5544
+ this.dequeueScheduled = void 0;
5545
+ }
5546
+ this.unsubscribeFromAllDeps();
5547
+ if (this.dequeueResubscribe) {
5548
+ this.dequeueResubscribe();
5549
+ this.dequeueResubscribe = void 0;
5550
+ }
5551
+ this.isResubscribeEnqueued = false;
5552
+ this.isTemporarilyUnsubscribedForFlush = false;
5553
+ }
5554
+ runOnce() {
5555
+ this.run();
5556
+ }
5557
+ subscribeToDeps() {
5558
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
5559
+ return;
5560
+ for (const dep of this.sourceDeps) {
5561
+ this.sourceUnsubscribers.push(
5562
+ dep.subscribe(this.onSourceInvalidate)
5563
+ );
5564
+ }
5565
+ for (const dep of this.computedDeps) {
5566
+ this.computedUnsubscribers.push(
5567
+ dep.subscribe(this.onComputedInvalidate)
5568
+ );
5569
+ }
5570
+ }
5571
+ unsubscribeFromSourceDeps() {
5572
+ if (this.sourceUnsubscribers.length === 0) return;
5573
+ for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
5574
+ this.sourceUnsubscribers = [];
5575
+ }
5576
+ unsubscribeFromAllDeps() {
5577
+ this.unsubscribeFromSourceDeps();
5578
+ if (this.computedUnsubscribers.length === 0) return;
5579
+ for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
5580
+ this.computedUnsubscribers = [];
5581
+ }
5582
+ ensureResubscribeAfterFlush() {
5583
+ if (this.isResubscribeEnqueued) return;
5584
+ this.isResubscribeEnqueued = true;
5585
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
5586
+ this.onAfterFlush
5587
+ );
5588
+ }
5589
+ };
5590
+
5591
+ // src/modules/computed/enums/EOIMComputedEventType.ts
5592
+ var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
5593
+ EOIMComputedEventType2["UPDATE"] = "update";
5594
+ return EOIMComputedEventType2;
5595
+ })(EOIMComputedEventType || {});
5596
+
5597
+ // src/modules/computed/core/OIMComputed.ts
5598
+ var OIMComputed = class {
5599
+ constructor(runtime, opts) {
5600
+ this.emitter = new OIMEventEmitter();
5601
+ this.hasValue = false;
5602
+ this.isDirty = true;
5603
+ this.runtime = runtime;
5604
+ this.compute = opts.compute;
5605
+ this.compare = opts.compare ?? Object.is;
5606
+ this._deps = opts.deps ?? [];
5607
+ this.updateEventEmitter = new OIMUpdateEventEmitter(
5608
+ this.runtime.queue,
5609
+ "immediate"
5610
+ );
5611
+ const level = this.runtime.computeLevel(this._deps);
5612
+ this.runtime.registerLevel(this, level);
5613
+ this.effect = new OIMEffect(
5614
+ this.runtime,
5615
+ {
5616
+ deps: this._deps,
5617
+ onUpdate: () => {
5618
+ this.isDirty = true;
5619
+ },
5620
+ run: () => {
5621
+ this.recomputeAndEmitIfChanged();
5622
+ }
5623
+ },
5624
+ level
5625
+ );
5626
+ }
5627
+ get needsRecompute() {
5628
+ return this.isDirty;
5629
+ }
5630
+ get isReady() {
5631
+ return this.hasValue;
5632
+ }
5633
+ get deps() {
5634
+ return this._deps;
5635
+ }
5636
+ get() {
5637
+ this.recomputeAndEmitIfChanged();
5638
+ return this.value;
5639
+ }
5640
+ getIfReady() {
5641
+ return this.hasValue ? this.value : void 0;
5642
+ }
5643
+ destroy() {
5644
+ this.effect.destroy();
5645
+ this.updateEventEmitter.destroy();
5646
+ this.emitter.offAll();
5647
+ }
5648
+ recomputeAndEmitIfChanged() {
5649
+ if (!this.isDirty) return;
5650
+ const prev = this.value;
5651
+ const hadPrev = this.hasValue;
5652
+ const next = this.compute();
5653
+ this.value = next;
5654
+ this.hasValue = true;
5655
+ this.isDirty = false;
5656
+ const changed = !hadPrev || !this.compare(prev, next);
5657
+ if (!changed) return;
5658
+ this.emitter.emit("update" /* UPDATE */, {
5659
+ keys: ["value"]
5660
+ });
5661
+ this.updateEventEmitter.markUpdatedKey("value");
5662
+ }
5663
+ };
5664
+
5665
+ // src/dx/OIMCollectionKit.ts
5666
+ function createOIMCollectionKit(queue, opts) {
5667
+ const collection = new OIMReactiveCollection(queue, opts);
5668
+ const indexFactory = new OIMCollectionIndexFactory(queue, collection);
5669
+ const select = new OIMCollectionSelectors(queue, collection);
5670
+ const runtime = getOIMComputeRuntime(queue);
5671
+ const scope = new OIMDisposeScope();
5672
+ scope.add(collection);
5673
+ return {
5674
+ queue,
5675
+ collection,
5676
+ indexFactory,
5677
+ select,
5678
+ on,
5679
+ computed: (deps, compute, options) => {
5680
+ const computed = new OIMComputed(runtime, {
5681
+ deps,
5682
+ compute,
5683
+ compare: options?.compare
5684
+ });
5685
+ scope.add(computed);
5686
+ return computed;
5687
+ },
5688
+ effect: (deps, run, options) => {
5689
+ const effect = new OIMEffect(runtime, {
5690
+ deps,
5691
+ run,
5692
+ onUpdate: options?.onUpdate
5693
+ });
5694
+ scope.add(effect);
5695
+ return effect;
5696
+ },
5697
+ scope,
5698
+ destroy: () => scope.destroy()
5699
+ };
5700
+ }
5701
+
5702
+ // src/dx/peekOIMComputeRuntime.ts
5703
+ function peekOIMComputeRuntime(queue) {
5704
+ const runtime = oimComputeRuntimeByQueue.get(queue);
5705
+ return runtime === void 0 || runtime.isDestroyed ? void 0 : runtime;
5706
+ }
5707
+
5708
+ // src/dx/setOIMComputeRuntime.ts
5709
+ function setOIMComputeRuntime(queue, runtime) {
5710
+ if (runtime.queue !== queue) {
5711
+ throw new Error(
5712
+ "setOIMComputeRuntime: the runtime is attached to a different queue. A runtime drains on its own queue and would never run here."
5713
+ );
5714
+ }
5715
+ oimComputeRuntimeByQueue.set(queue, runtime);
5716
+ }
5717
+
5718
+ // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
5719
+ var OIMCollectionChangedFields = class {
5720
+ constructor(queue, collection, opts) {
5721
+ this.isInWrite = false;
5722
+ this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
5723
+ // PKs written through this wrapper during the current flush. Their fields are
5724
+ // already tracked accurately via trackPatch, so the flush-time external-write
5725
+ // handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
5726
+ this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
5727
+ this.changedFieldsByPk = /* @__PURE__ */ new Map();
5728
+ this.changedPksByField = /* @__PURE__ */ new Map();
5729
+ this.onAfterFlush = () => {
5730
+ this.clear();
5731
+ this.writtenThroughThisFlush.clear();
5732
+ };
5733
+ this.onCollectionUpdate = (payload) => {
5734
+ if (this.isInWrite) return;
5735
+ if (payload.pks.length === 0) return;
5736
+ for (let i = 0; i < payload.pks.length; i++) {
5737
+ const pk = payload.pks[i];
5738
+ if (this.writtenThroughThisFlush.has(pk)) continue;
5739
+ this.trackExternalMutationByPk(pk);
5740
+ }
5741
+ };
5742
+ this.collection = collection;
5743
+ this.detectExternalMutations = opts?.detectExternalMutations !== false;
5744
+ this.selectPk = opts?.selectPk ?? (() => {
5745
+ throw new Error(
5746
+ `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
5747
+ );
5748
+ });
5749
+ this.changedPksEventEmitter = new OIMUpdateEventEmitter(
5750
+ queue,
5751
+ "after_flush"
5752
+ );
5753
+ this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
5754
+ this.unsubscribeAfterFlush = queue.emitter.on(
5755
+ 0 /* AFTER_FLUSH */,
5756
+ this.onAfterFlush
5757
+ );
5758
+ if (this.detectExternalMutations) {
5759
+ const maybeReactive = collection;
5760
+ this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
5761
+ this.onCollectionUpdate({
5762
+ pks
5763
+ });
5764
+ }) : collection.emitter.on(
5765
+ 0 /* UPDATE */,
5766
+ this.onCollectionUpdate
5767
+ );
5768
+ }
5769
+ }
5770
+ upsertOneByPk(pk, patch) {
5771
+ const slot = this.withWrite(() => {
5772
+ this.trackPatch(pk, patch);
5773
+ return this.collection.upsertOneByPk(pk, patch);
5774
+ });
5775
+ this.updateEntitySnapshot(pk);
5776
+ return slot;
5777
+ }
5778
+ upsertOne(entity) {
5779
+ const slot = this.withWrite(() => {
5780
+ const pk = this.selectPk(entity);
5781
+ this.trackPatch(pk, entity);
5782
+ return this.collection.upsertOne(entity);
5783
+ });
5784
+ this.updateEntitySnapshot(slot.pk);
5785
+ return slot;
5786
+ }
5787
+ upsertMany(entities) {
5788
+ const pks = [];
5789
+ const slots = this.withWrite(() => {
5790
+ for (const entity of entities) {
5791
+ const pk = this.selectPk(entity);
5792
+ pks.push(pk);
5793
+ this.trackPatch(pk, entity);
5794
+ }
5795
+ return this.collection.upsertMany(entities);
5796
+ });
5797
+ for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
5798
+ return slots;
5799
+ }
5800
+ getChangedFieldsByPk(pk) {
5801
+ return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
5802
+ }
5803
+ consumeChangedFieldsByPk(pk) {
5804
+ const existing = this.changedFieldsByPk.get(pk);
5805
+ if (!existing) return /* @__PURE__ */ new Set();
5806
+ return new Set(existing);
5807
+ }
5808
+ getChangedPksByField(field) {
5809
+ return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
5810
+ }
5811
+ consumeChangedPksByField(field) {
5812
+ const existing = this.changedPksByField.get(field);
5813
+ if (!existing) return /* @__PURE__ */ new Set();
5814
+ return new Set(existing);
5301
5815
  }
5302
5816
  clear() {
5303
5817
  this.changedFieldsByPk.clear();
@@ -5343,8 +5857,9 @@ var OIMCollectionChangedFields = class {
5343
5857
  Object.keys(nextObj).forEach((k) => keys.add(k));
5344
5858
  const changed = [];
5345
5859
  keys.forEach((k) => {
5346
- if (prevObj[k] !== nextObj[k])
5860
+ if (prevObj[k] !== nextObj[k]) {
5347
5861
  changed.push(k);
5862
+ }
5348
5863
  });
5349
5864
  return changed;
5350
5865
  }
@@ -5510,10 +6025,15 @@ var OIMOrderedListMappedCommandStream = class _OIMOrderedListMappedCommandStream
5510
6025
  try {
5511
6026
  const subs = this.subscribers.get(key);
5512
6027
  if (subs && subs.size > 0) {
5513
- const snapshot = Array.from(subs);
5514
- for (let s = 0; s < snapshot.length; s++) {
5515
- const handler = snapshot[s];
5516
- if (subs.has(handler)) handler();
6028
+ if (subs.size === 1) {
6029
+ const only = subs.values().next().value;
6030
+ if (only) only();
6031
+ } else {
6032
+ const snapshot = Array.from(subs);
6033
+ for (let s = 0; s < snapshot.length; s++) {
6034
+ const handler = snapshot[s];
6035
+ if (subs.has(handler)) handler();
6036
+ }
5517
6037
  }
5518
6038
  }
5519
6039
  } finally {
@@ -5807,10 +6327,13 @@ var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler
5807
6327
  this.flush();
5808
6328
  });
5809
6329
  } else {
5810
- this.frameId = setTimeout(() => {
5811
- this.frameId = void 0;
5812
- this.flush();
5813
- }, 16);
6330
+ this.frameId = this.unref(
6331
+ setTimeout(() => {
6332
+ this.frameId = void 0;
6333
+ this.flush();
6334
+ }, 16)
6335
+ // ~60fps
6336
+ );
5814
6337
  }
5815
6338
  }
5816
6339
  cancel() {
@@ -5856,10 +6379,12 @@ var OIMEventQueueSchedulerTimeout = class extends OIMEventQueueScheduler {
5856
6379
  }
5857
6380
  schedule() {
5858
6381
  if (this.timeoutId !== void 0) return;
5859
- this.timeoutId = setTimeout(() => {
5860
- this.timeoutId = void 0;
5861
- this.flush();
5862
- }, this.delay);
6382
+ this.timeoutId = this.unref(
6383
+ setTimeout(() => {
6384
+ this.timeoutId = void 0;
6385
+ this.flush();
6386
+ }, this.delay)
6387
+ );
5863
6388
  }
5864
6389
  cancel() {
5865
6390
  if (this.timeoutId === void 0) return;
@@ -5893,13 +6418,13 @@ var OIMEventQueueSchedulerImmediate = class extends OIMEventQueueScheduler {
5893
6418
  this.flush();
5894
6419
  };
5895
6420
  if (this.useSetImmediate) {
5896
- this.immediateId = setImmediate(callback);
6421
+ this.immediateId = this.unref(setImmediate(callback));
5897
6422
  } else if (this.useMessageChannel && this.messageChannel) {
5898
6423
  this.immediateId = 1;
5899
6424
  this.pendingCallback = callback;
5900
6425
  this.messageChannel.port1.postMessage(null);
5901
6426
  } else {
5902
- this.immediateId = setTimeout(callback, 0);
6427
+ this.immediateId = this.unref(setTimeout(callback, 0));
5903
6428
  }
5904
6429
  }
5905
6430
  cancel() {
@@ -5992,242 +6517,6 @@ var OIMDBSettings = class {
5992
6517
  OIMDBSettings.UPDATED_KEYS_INDEX_KEY = "__updatedKeys__";
5993
6518
  OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
5994
6519
 
5995
- // src/modules/effect/core/OIMEffectDependencyComputed.ts
5996
- var OIMEffectDependencyComputed = class {
5997
- constructor(source) {
5998
- this.source = source;
5999
- }
6000
- subscribe(onUpdate) {
6001
- return this.source.emitter.on("update", onUpdate);
6002
- }
6003
- };
6004
-
6005
- // src/modules/effect/core/OIMEffect.ts
6006
- var OIMEffect = class {
6007
- constructor(runtime, opts) {
6008
- this.sourceDeps = [];
6009
- this.computedDeps = [];
6010
- this.sourceUnsubscribers = [];
6011
- this.computedUnsubscribers = [];
6012
- this.isResubscribeEnqueued = false;
6013
- this.isTemporarilyUnsubscribedForFlush = false;
6014
- this.isScheduled = false;
6015
- this.runTask = () => {
6016
- this.isScheduled = false;
6017
- this.dequeueScheduled = void 0;
6018
- this.runOnce();
6019
- };
6020
- this.onSourceInvalidate = () => {
6021
- this.onUpdate?.();
6022
- if (!this.isTemporarilyUnsubscribedForFlush) {
6023
- this.isTemporarilyUnsubscribedForFlush = true;
6024
- this.unsubscribeFromSourceDeps();
6025
- this.ensureResubscribeAfterFlush();
6026
- }
6027
- if (this.isScheduled) return;
6028
- this.isScheduled = true;
6029
- this.dequeueScheduled = this.runtime.schedule(this.runTask);
6030
- };
6031
- this.onComputedInvalidate = () => {
6032
- this.onUpdate?.();
6033
- if (this.isScheduled) return;
6034
- this.isScheduled = true;
6035
- this.dequeueScheduled = this.runtime.schedule(this.runTask);
6036
- };
6037
- this.onAfterFlush = () => {
6038
- if (!this.isResubscribeEnqueued) return;
6039
- this.isResubscribeEnqueued = false;
6040
- this.dequeueResubscribe = void 0;
6041
- this.isTemporarilyUnsubscribedForFlush = false;
6042
- if (this.sourceUnsubscribers.length === 0) {
6043
- for (const dep of this.sourceDeps) {
6044
- this.sourceUnsubscribers.push(
6045
- dep.subscribe(this.onSourceInvalidate)
6046
- );
6047
- }
6048
- }
6049
- };
6050
- this.runtime = runtime;
6051
- this.onUpdate = opts.onUpdate;
6052
- this.run = opts.run;
6053
- this.deps = opts.deps ?? [];
6054
- for (const dep of this.deps) {
6055
- if (dep instanceof OIMEffectDependencyComputed) {
6056
- this.computedDeps.push(dep);
6057
- } else {
6058
- this.sourceDeps.push(dep);
6059
- }
6060
- }
6061
- this.subscribeToDeps();
6062
- }
6063
- destroy() {
6064
- if (this.dequeueScheduled) {
6065
- this.dequeueScheduled();
6066
- this.dequeueScheduled = void 0;
6067
- }
6068
- this.unsubscribeFromAllDeps();
6069
- if (this.dequeueResubscribe) {
6070
- this.dequeueResubscribe();
6071
- this.dequeueResubscribe = void 0;
6072
- }
6073
- this.isResubscribeEnqueued = false;
6074
- this.isTemporarilyUnsubscribedForFlush = false;
6075
- }
6076
- runOnce() {
6077
- this.run();
6078
- }
6079
- subscribeToDeps() {
6080
- if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
6081
- return;
6082
- for (const dep of this.sourceDeps) {
6083
- this.sourceUnsubscribers.push(
6084
- dep.subscribe(this.onSourceInvalidate)
6085
- );
6086
- }
6087
- for (const dep of this.computedDeps) {
6088
- this.computedUnsubscribers.push(
6089
- dep.subscribe(this.onComputedInvalidate)
6090
- );
6091
- }
6092
- }
6093
- unsubscribeFromSourceDeps() {
6094
- if (this.sourceUnsubscribers.length === 0) return;
6095
- for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
6096
- this.sourceUnsubscribers = [];
6097
- }
6098
- unsubscribeFromAllDeps() {
6099
- this.unsubscribeFromSourceDeps();
6100
- if (this.computedUnsubscribers.length === 0) return;
6101
- for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
6102
- this.computedUnsubscribers = [];
6103
- }
6104
- ensureResubscribeAfterFlush() {
6105
- if (this.isResubscribeEnqueued) return;
6106
- this.isResubscribeEnqueued = true;
6107
- this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
6108
- this.onAfterFlush
6109
- );
6110
- }
6111
- };
6112
-
6113
- // src/modules/computed/enums/EOIMComputedEventType.ts
6114
- var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
6115
- EOIMComputedEventType2["UPDATE"] = "update";
6116
- return EOIMComputedEventType2;
6117
- })(EOIMComputedEventType || {});
6118
-
6119
- // src/modules/computed/core/OIMComputed.ts
6120
- var OIMComputed = class {
6121
- constructor(runtime, opts) {
6122
- this.emitter = new OIMEventEmitter();
6123
- this.hasValue = false;
6124
- this.isDirty = true;
6125
- this.runtime = runtime;
6126
- this.compute = opts.compute;
6127
- this.compare = opts.compare ?? Object.is;
6128
- this._deps = opts.deps ?? [];
6129
- this.updateEventEmitter = new OIMUpdateEventEmitter(
6130
- this.runtime.queue,
6131
- "immediate"
6132
- );
6133
- this.effect = new OIMEffect(this.runtime, {
6134
- deps: this._deps,
6135
- onUpdate: () => {
6136
- this.isDirty = true;
6137
- },
6138
- run: () => {
6139
- this.recomputeAndEmitIfChanged();
6140
- }
6141
- });
6142
- }
6143
- get needsRecompute() {
6144
- return this.isDirty;
6145
- }
6146
- get isReady() {
6147
- return this.hasValue;
6148
- }
6149
- get deps() {
6150
- return this._deps;
6151
- }
6152
- get() {
6153
- this.recomputeAndEmitIfChanged();
6154
- return this.value;
6155
- }
6156
- getIfReady() {
6157
- return this.hasValue ? this.value : void 0;
6158
- }
6159
- destroy() {
6160
- this.effect.destroy();
6161
- this.updateEventEmitter.destroy();
6162
- this.emitter.offAll();
6163
- }
6164
- recomputeAndEmitIfChanged() {
6165
- if (!this.isDirty) return;
6166
- const prev = this.value;
6167
- const hadPrev = this.hasValue;
6168
- const next = this.compute();
6169
- this.value = next;
6170
- this.hasValue = true;
6171
- this.isDirty = false;
6172
- const changed = !hadPrev || !this.compare(prev, next);
6173
- if (!changed) return;
6174
- this.emitter.emit("update" /* UPDATE */, {
6175
- keys: ["value"]
6176
- });
6177
- this.updateEventEmitter.markUpdatedKey("value");
6178
- }
6179
- };
6180
-
6181
- // src/modules/effect/core/OIMEffectDependencyKeyed.ts
6182
- var OIMEffectDependencyKeyed = class {
6183
- constructor(source, key) {
6184
- this.source = source;
6185
- this.key = key;
6186
- }
6187
- subscribe(onUpdate) {
6188
- return this.source.subscribeOnKey(this.key, onUpdate);
6189
- }
6190
- };
6191
-
6192
- // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
6193
- var OIMEffectDependencyKeyedObject = class {
6194
- constructor(object, key) {
6195
- this.dep = new OIMEffectDependencyKeyed(object, key);
6196
- }
6197
- get source() {
6198
- return this.dep.source;
6199
- }
6200
- subscribe(onUpdate) {
6201
- return this.dep.subscribe(onUpdate);
6202
- }
6203
- };
6204
-
6205
- // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
6206
- var OIMEffectDependencyKeyedCollection = class {
6207
- constructor(collection, pk) {
6208
- this.dep = new OIMEffectDependencyKeyed(collection, pk);
6209
- }
6210
- get source() {
6211
- return this.dep.source;
6212
- }
6213
- subscribe(onUpdate) {
6214
- return this.dep.subscribe(onUpdate);
6215
- }
6216
- };
6217
-
6218
- // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
6219
- var OIMEffectDependencyKeyedIndex = class {
6220
- constructor(index, key) {
6221
- this.dep = new OIMEffectDependencyKeyed(index, key);
6222
- }
6223
- get source() {
6224
- return this.dep.source;
6225
- }
6226
- subscribe(onUpdate) {
6227
- return this.dep.subscribe(onUpdate);
6228
- }
6229
- };
6230
-
6231
6520
  // src/modules/selector/core/OIMObjectValueByKeySelector.ts
6232
6521
  var OIMObjectValueByKeySelector = class extends OIMSelector {
6233
6522
  constructor(runtime, obj, key) {
@@ -6305,8 +6594,9 @@ var OIMIndexPksByKeyArrayBasedSelector = class extends OIMSelector {
6305
6594
  areEqual(prev, next) {
6306
6595
  if (prev === next) return true;
6307
6596
  if (prev.length !== next.length) return false;
6308
- for (let i = 0; i < prev.length; i++)
6597
+ for (let i = 0; i < prev.length; i++) {
6309
6598
  if (prev[i] !== next[i]) return false;
6599
+ }
6310
6600
  return true;
6311
6601
  }
6312
6602
  };
@@ -6329,8 +6619,9 @@ var OIMIndexPksByKeysSetBasedSelector = class extends OIMSelector {
6329
6619
  areEqual(prev, next) {
6330
6620
  if (prev === next) return true;
6331
6621
  if (prev.length !== next.length) return false;
6332
- for (let i = 0; i < prev.length; i++)
6622
+ for (let i = 0; i < prev.length; i++) {
6333
6623
  if (prev[i] !== next[i]) return false;
6624
+ }
6334
6625
  return true;
6335
6626
  }
6336
6627
  };
@@ -6353,8 +6644,9 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
6353
6644
  areEqual(prev, next) {
6354
6645
  if (prev === next) return true;
6355
6646
  if (prev.length !== next.length) return false;
6356
- for (let i = 0; i < prev.length; i++)
6647
+ for (let i = 0; i < prev.length; i++) {
6357
6648
  if (prev[i] !== next[i]) return false;
6649
+ }
6358
6650
  return true;
6359
6651
  }
6360
6652
  };
@@ -6383,6 +6675,7 @@ export {
6383
6675
  OIMComputeRuntime,
6384
6676
  OIMComputed,
6385
6677
  OIMDBSettings,
6678
+ OIMDerivedCollection,
6386
6679
  OIMDerivedCollectionGlobalIndexArrayBased,
6387
6680
  OIMDerivedCollectionGlobalIndexSetBased,
6388
6681
  OIMDerivedCollectionIndexArrayBased,
@@ -6478,5 +6771,9 @@ export {
6478
6771
  createOIMIndexSlotMap,
6479
6772
  createOIMOrderedListCommandStreamDiffDriven,
6480
6773
  createOIMOrderedListMappedCommandStream,
6481
- diffOrderedListByPk
6774
+ diffOrderedListByPk,
6775
+ getOIMComputeRuntime,
6776
+ on,
6777
+ peekOIMComputeRuntime,
6778
+ setOIMComputeRuntime
6482
6779
  };