@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.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
@@ -559,19 +578,28 @@ var OIMCarrierKeyedEmitter = class {
559
578
  // doubles as membership: a carrier is in this array iff `carrier.dirty`.
560
579
  this.dirtyCarriers = [];
561
580
  this.isFlushEnqueued = false;
581
+ // Number of live subscriptions whose single handler spans 2+ distinct
582
+ // carriers (a `subscribeOnKeys` over distinct keys). While this is > 0 a
583
+ // flush coalesces delivery so such a handler fires at most ONCE per flush,
584
+ // not once per changed carrier — the callback carries no key, so extra
585
+ // fires are pure waste. At 0 the hot single-subscriber path pays nothing:
586
+ // no per-flush Set, no membership checks.
587
+ this.multiCarrierSubscriptions = 0;
562
588
  this.onFlush = () => {
563
589
  this.isFlushEnqueued = false;
564
590
  if (this.dirtyCarriers.length === 0) return;
565
591
  const flushing = this.dirtyCarriers;
566
592
  this.dirtyCarriers = [];
593
+ const delivered = this.multiCarrierSubscriptions > 0 ? this.deliveredPool ?? (this.deliveredPool = /* @__PURE__ */ new Set()) : void 0;
567
594
  let i = 0;
568
595
  try {
569
596
  for (; i < flushing.length; i++) {
570
597
  flushing[i].dirty = false;
571
- this.notify(flushing[i]);
598
+ this.notify(flushing[i], delivered);
572
599
  }
573
600
  } finally {
574
601
  for (; i < flushing.length; i++) flushing[i].dirty = false;
602
+ delivered?.clear();
575
603
  }
576
604
  };
577
605
  this.queue = queue;
@@ -589,7 +617,13 @@ var OIMCarrierKeyedEmitter = class {
589
617
  this.addHandler(carrier, handler);
590
618
  carriers.push(carrier);
591
619
  }
620
+ const spansMultipleCarriers = carriers.length > 1;
621
+ if (spansMultipleCarriers) this.multiCarrierSubscriptions++;
622
+ let unsubscribed = false;
592
623
  return () => {
624
+ if (unsubscribed) return;
625
+ unsubscribed = true;
626
+ if (spansMultipleCarriers) this.multiCarrierSubscriptions--;
593
627
  for (let i = 0; i < carriers.length; i++) {
594
628
  this.removeHandler(carriers[i], handler);
595
629
  }
@@ -705,19 +739,31 @@ var OIMCarrierKeyedEmitter = class {
705
739
  this.isFlushEnqueued = true;
706
740
  this.queue.enqueue(this.onFlush);
707
741
  }
708
- notify(carrier) {
742
+ notify(carrier, delivered) {
709
743
  const subscribers = carrier.subscribers;
710
744
  if (!subscribers || subscribers.size === 0) return;
711
745
  if (subscribers.size === 1) {
712
746
  const only = subscribers.values().next().value;
713
- if (only) only();
747
+ if (!only) return;
748
+ if (delivered) {
749
+ const size = delivered.size;
750
+ delivered.add(only);
751
+ if (delivered.size === size) return;
752
+ }
753
+ only();
714
754
  return;
715
755
  }
716
756
  const snapshot = [];
717
757
  subscribers.forEach((h) => snapshot.push(h));
718
758
  for (let i = 0; i < snapshot.length; i++) {
719
759
  const handler = snapshot[i];
720
- if (subscribers.has(handler)) handler();
760
+ if (!subscribers.has(handler)) continue;
761
+ if (delivered) {
762
+ const size = delivered.size;
763
+ delivered.add(handler);
764
+ if (delivered.size === size) continue;
765
+ }
766
+ handler();
721
767
  }
722
768
  }
723
769
  };
@@ -1339,8 +1385,11 @@ var OIMCollection = class {
1339
1385
  this.emitter.emit(0 /* UPDATE */, { pks });
1340
1386
  }
1341
1387
  clear() {
1388
+ const pks = this.store.getAllPks();
1342
1389
  this.store.clear();
1343
- this.emitter.emit(0 /* UPDATE */, { pks: [] });
1390
+ if (pks.length > 0) {
1391
+ this.emitter.emit(0 /* UPDATE */, { pks });
1392
+ }
1344
1393
  }
1345
1394
  countAll() {
1346
1395
  return this.store.countAll();
@@ -1382,14 +1431,12 @@ var OIMReactiveCollection = class extends OIMCollection {
1382
1431
  // Only ever holds `slot.pk` (one canonical reference per logical key), so a
1383
1432
  // native `Set` dedups correctly by reference even for composite pks.
1384
1433
  this.pendingAnyUpdatePks = /* @__PURE__ */ new Set();
1385
- this.isAnyUpdateClearPending = false;
1386
1434
  this.isAnyUpdateScheduled = false;
1387
1435
  this.runAnyUpdate = () => {
1388
1436
  if (!this.isAnyUpdateScheduled) return;
1389
1437
  this.isAnyUpdateScheduled = false;
1390
- const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks.values());
1438
+ const pks = Array.from(this.pendingAnyUpdatePks.values());
1391
1439
  this.pendingAnyUpdatePks.clear();
1392
- this.isAnyUpdateClearPending = false;
1393
1440
  const snapshot = Array.from(this.anyUpdateHandlers);
1394
1441
  for (let i = 0; i < snapshot.length; i++) snapshot[i](pks);
1395
1442
  };
@@ -1429,30 +1476,43 @@ var OIMReactiveCollection = class extends OIMCollection {
1429
1476
  this.anyUpdateHandlers.delete(handler);
1430
1477
  };
1431
1478
  }
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) {
1479
+ // Two notification channels coexist on a reactive collection; they serve
1480
+ // different audiences and NEITHER is obsolete:
1481
+ // • the keyed `updateEmitter` — per-key/per-carrier pings ("did MY key
1482
+ // change?", payload-less `void`), for leaf subscribers like React hooks
1483
+ // that declare the keys they care about up front;
1484
+ // • this `emitter` UPDATE event — the aggregate/batch channel that carries
1485
+ // the full list of changed pks in one SYNCHRONOUS callback, for
1486
+ // consumers that do NOT know their keys ahead of time and must re-derive
1487
+ // over the batch: derived indexes/collections, changed-fields, persist,
1488
+ // snapshot-manager. `pks` always enumerates exactly what changed — even
1489
+ // clear() lists every removed key, so an empty array never occurs.
1490
+ // It is not a per-key emitter with fewer features — it is the opposite
1491
+ // shape (enumerated batch, not "my key"), which is why it stays.
1492
+ //
1493
+ // Emit on the batch channel only when something is actually subscribed —
1494
+ // otherwise every upsert/remove allocates a throwaway `{ pks }` object (and
1495
+ // array) that nobody reads.
1496
+ notifyBatch(pk) {
1437
1497
  if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1438
1498
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1439
1499
  }
1440
1500
  }
1441
- notifyLegacyMany(pks) {
1501
+ notifyBatchMany(pks) {
1442
1502
  if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1443
1503
  this.emitter.emit(0 /* UPDATE */, { pks });
1444
1504
  }
1445
1505
  }
1446
1506
  upsertOneByPk(pk, entity) {
1447
1507
  const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
1448
- this.notifyLegacy(pk);
1508
+ this.notifyBatch(pk);
1449
1509
  this.updateEmitter.markUpdatedCarrier(slot);
1450
1510
  this.trackAnyUpdatePk(pk);
1451
1511
  return slot;
1452
1512
  }
1453
1513
  upsertOne(entity) {
1454
1514
  const slot = this.upsertOneWithoutNotifications(entity);
1455
- this.notifyLegacy(slot.pk);
1515
+ this.notifyBatch(slot.pk);
1456
1516
  this.updateEmitter.markUpdatedCarrier(slot);
1457
1517
  this.trackAnyUpdatePk(slot.pk);
1458
1518
  return slot;
@@ -1463,7 +1523,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1463
1523
  (entity) => this.upsertOneWithoutNotifications(entity)
1464
1524
  );
1465
1525
  const pks = slots.map((slot) => slot.pk);
1466
- this.notifyLegacyMany(pks);
1526
+ this.notifyBatchMany(pks);
1467
1527
  for (let i = 0; i < slots.length; i++) {
1468
1528
  this.updateEmitter.markUpdatedCarrier(slots[i]);
1469
1529
  }
@@ -1473,7 +1533,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1473
1533
  removeOne(entity) {
1474
1534
  const pk = this.selectPk(entity);
1475
1535
  this.store.removeOneByPk(pk);
1476
- this.notifyLegacy(pk);
1536
+ this.notifyBatch(pk);
1477
1537
  this.updateEmitter.markUpdatedKey(pk);
1478
1538
  this.trackAnyUpdatePk(pk);
1479
1539
  }
@@ -1481,30 +1541,32 @@ var OIMReactiveCollection = class extends OIMCollection {
1481
1541
  if (entities.length === 0) return;
1482
1542
  const pks = entities.map(this.selectPk);
1483
1543
  this.store.removeManyByPks(pks);
1484
- this.notifyLegacyMany(pks);
1544
+ this.notifyBatchMany(pks);
1485
1545
  this.updateEmitter.markUpdatedKeys(pks);
1486
1546
  this.trackAnyUpdatePks(pks);
1487
1547
  }
1488
1548
  removeOneByPk(pk) {
1489
1549
  this.store.removeOneByPk(pk);
1490
- this.notifyLegacy(pk);
1550
+ this.notifyBatch(pk);
1491
1551
  this.updateEmitter.markUpdatedKey(pk);
1492
1552
  this.trackAnyUpdatePk(pk);
1493
1553
  }
1494
1554
  removeManyByPks(pks) {
1495
1555
  if (pks.length === 0) return;
1496
1556
  this.store.removeManyByPks(pks);
1497
- this.notifyLegacyMany(pks);
1557
+ this.notifyBatchMany(pks);
1498
1558
  this.updateEmitter.markUpdatedKeys(pks);
1499
1559
  this.trackAnyUpdatePks(pks);
1500
1560
  }
1501
1561
  clear() {
1562
+ const needsPks = this.emitter.hasHandlers(0 /* UPDATE */) || this.anyUpdateHandlers.size > 0;
1563
+ const pks = needsPks ? this.store.getAllPks() : void 0;
1502
1564
  this.store.clear();
1503
- if (this.emitter.hasHandlers(0 /* UPDATE */)) {
1504
- this.emitter.emit(0 /* UPDATE */, { pks: [] });
1565
+ if (pks && pks.length > 0) {
1566
+ this.notifyBatchMany(pks);
1567
+ this.trackAnyUpdatePks(pks);
1505
1568
  }
1506
1569
  this.updateEmitter.markAllUpdated();
1507
- this.trackAnyUpdateClear();
1508
1570
  }
1509
1571
  trackAnyUpdatePk(pk) {
1510
1572
  if (this.anyUpdateHandlers.size === 0) return;
@@ -1513,14 +1575,9 @@ var OIMReactiveCollection = class extends OIMCollection {
1513
1575
  }
1514
1576
  trackAnyUpdatePks(pks) {
1515
1577
  if (this.anyUpdateHandlers.size === 0) return;
1516
- for (let i = 0; i < pks.length; i++)
1578
+ for (let i = 0; i < pks.length; i++) {
1517
1579
  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();
1580
+ }
1524
1581
  this.ensureAnyUpdateEnqueued();
1525
1582
  }
1526
1583
  ensureAnyUpdateEnqueued() {
@@ -1699,6 +1756,7 @@ var OIMCollectionIndexManualOrderedArrayBased = class extends OIMIndexManualOrde
1699
1756
  var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
1700
1757
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
1701
1758
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
1759
+ EOIMEventQueueEventType2[EOIMEventQueueEventType2["FLUSH_ERROR"] = 2] = "FLUSH_ERROR";
1702
1760
  return EOIMEventQueueEventType2;
1703
1761
  })(EOIMEventQueueEventType || {});
1704
1762
 
@@ -1952,8 +2010,13 @@ var OIMUpdateEventEmitter = class {
1952
2010
  var OIMOrderedListCommandBuffer = class {
1953
2011
  constructor(queue) {
1954
2012
  this.commandsByKey = /* @__PURE__ */ new Map();
2013
+ // Keys whose buffered leading `reset` is stale: an edit after the reset
2014
+ // should be folded into it, but we defer the (O(list-size)) rebuild to the
2015
+ // next read instead of redoing it on every subsequent edit in the batch.
2016
+ this.staleResetKeys = /* @__PURE__ */ new Set();
1955
2017
  this.onAfterFlush = () => {
1956
2018
  this.commandsByKey.clear();
2019
+ this.staleResetKeys.clear();
1957
2020
  };
1958
2021
  this.commandsEventEmitter = new OIMUpdateEventEmitter(
1959
2022
  queue,
@@ -1968,9 +2031,11 @@ var OIMOrderedListCommandBuffer = class {
1968
2031
  return this.commandsEventEmitter.subscribeOnKey(key, handler);
1969
2032
  }
1970
2033
  getBufferedCommands(key) {
2034
+ this.materializeStaleReset(key);
1971
2035
  return this.commandsByKey.get(key) ?? [];
1972
2036
  }
1973
2037
  consumeCommands(key) {
2038
+ this.materializeStaleReset(key);
1974
2039
  const cmds = this.commandsByKey.get(key);
1975
2040
  if (!cmds || cmds.length === 0) return [];
1976
2041
  return cmds.slice();
@@ -1979,6 +2044,20 @@ var OIMOrderedListCommandBuffer = class {
1979
2044
  this.commandsEventEmitter.destroy();
1980
2045
  this.unsubscribeAfterFlush();
1981
2046
  this.commandsByKey.clear();
2047
+ this.staleResetKeys.clear();
2048
+ }
2049
+ /**
2050
+ * Rebuild a deferred leading `reset` from the current order, once, at read
2051
+ * time. All the post-reset edits that were folded in are already reflected
2052
+ * in the driver's backing order, so a single rebuild here is equivalent to
2053
+ * having recomputed the reset on each of those edits.
2054
+ */
2055
+ materializeStaleReset(key) {
2056
+ if (!this.staleResetKeys.delete(key)) return;
2057
+ const cmds = this.commandsByKey.get(key);
2058
+ if (cmds && cmds.length > 0 && cmds[0].type === "reset") {
2059
+ cmds[0] = this.createResetCommand(this.getItemsByKey(key));
2060
+ }
1982
2061
  }
1983
2062
  appendCommand(key, cmd) {
1984
2063
  let cmds = this.commandsByKey.get(key);
@@ -1989,8 +2068,9 @@ var OIMOrderedListCommandBuffer = class {
1989
2068
  if (cmd.type === "reset") {
1990
2069
  cmds.length = 0;
1991
2070
  cmds.push(cmd);
2071
+ this.staleResetKeys.delete(key);
1992
2072
  } else if (cmds.length > 0 && cmds[0].type === "reset") {
1993
- cmds[0] = this.createResetCommand(this.getItemsByKey(key));
2073
+ this.staleResetKeys.add(key);
1994
2074
  } else {
1995
2075
  cmds.push(cmd);
1996
2076
  }
@@ -3032,10 +3112,6 @@ var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndex
3032
3112
  });
3033
3113
  this.keysByPk = /* @__PURE__ */ new Map();
3034
3114
  this.onCollectionUpdate = (pks) => {
3035
- if (pks.length === 0) {
3036
- this.rebuildFromCollection();
3037
- return;
3038
- }
3039
3115
  for (const pk of pks) {
3040
3116
  this.syncPk(pk);
3041
3117
  }
@@ -3137,10 +3213,6 @@ var OIMDerivedCollectionIndexArrayBased = class extends OIMReactiveCollectionInd
3137
3213
  // touching an unrelated field can skip re-sorting when the value is unchanged.
3138
3214
  this.orderValueByPk = /* @__PURE__ */ new Map();
3139
3215
  this.onCollectionUpdate = (pks) => {
3140
- if (pks.length === 0) {
3141
- this.rebuildFromCollection();
3142
- return;
3143
- }
3144
3216
  const ordered = !!(this.compareEntities || this.orderBy);
3145
3217
  const keysToSort = ordered ? /* @__PURE__ */ new Set() : null;
3146
3218
  for (const pk of pks) {
@@ -3557,10 +3629,6 @@ var OIMDerivedCollectionGlobalIndexArrayBased = class extends OIMReactiveCollect
3557
3629
  // an unrelated field can skip re-sorting when the value is unchanged.
3558
3630
  this.orderValueByPk = /* @__PURE__ */ new Map();
3559
3631
  this.onCollectionUpdate = (pks) => {
3560
- if (pks.length === 0) {
3561
- this.rebuildFromCollection();
3562
- return;
3563
- }
3564
3632
  const ordered = !!(this.compareEntities || this.orderBy);
3565
3633
  let needsSort = false;
3566
3634
  for (const pk of pks) {
@@ -3658,10 +3726,6 @@ var OIMDerivedCollectionGlobalIndexSetBased = class extends OIMReactiveCollectio
3658
3726
  super(queue, { collection, indexOptions: opts.indexOptions });
3659
3727
  this.presentPks = /* @__PURE__ */ new Set();
3660
3728
  this.onCollectionUpdate = (pks) => {
3661
- if (pks.length === 0) {
3662
- this.rebuildFromCollection();
3663
- return;
3664
- }
3665
3729
  for (const pk of pks) {
3666
3730
  const prev = this.presentPks.has(pk);
3667
3731
  const slot = this.collection.getSlotByPk(pk);
@@ -3837,6 +3901,55 @@ function createOIMCollectionIndexFactory(queue, collection) {
3837
3901
  return new OIMCollectionIndexFactory(queue, collection);
3838
3902
  }
3839
3903
 
3904
+ // src/core/OIMDerivedCollection.ts
3905
+ var OIMDerivedCollection = class extends OIMReactiveCollection {
3906
+ constructor(queue, source, derive, opts) {
3907
+ super(queue, {
3908
+ store: opts?.store,
3909
+ // Each sync recomputes the WHOLE derived entity → replace, not merge.
3910
+ updateEntity: (draft) => draft
3911
+ });
3912
+ this.onSourceUpdate = (pks) => {
3913
+ for (let i = 0; i < pks.length; i++) this.syncPk(pks[i]);
3914
+ };
3915
+ this.source = source;
3916
+ this.derive = derive;
3917
+ this.compareDerived = opts?.compare ?? Object.is;
3918
+ this.unsubscribeFromSource = source.emitter.on(
3919
+ 0 /* UPDATE */,
3920
+ (payload) => this.onSourceUpdate(payload.pks)
3921
+ );
3922
+ if (opts?.buildInitial !== false) {
3923
+ this.rebuildFromSource();
3924
+ }
3925
+ }
3926
+ rebuildFromSource() {
3927
+ for (const pk of this.getAllPks()) {
3928
+ if (this.source.getOneByPk(pk) === void 0) {
3929
+ this.removeOneByPk(pk);
3930
+ }
3931
+ }
3932
+ for (const slot of this.source.getAllSlots()) {
3933
+ if (slot.item !== void 0) this.syncPk(slot.pk);
3934
+ }
3935
+ }
3936
+ destroy() {
3937
+ this.unsubscribeFromSource();
3938
+ super.destroy();
3939
+ }
3940
+ syncPk(pk) {
3941
+ const sourceEntity = this.source.getOneByPk(pk);
3942
+ if (sourceEntity === void 0) {
3943
+ if (this.getOneByPk(pk) !== void 0) this.removeOneByPk(pk);
3944
+ return;
3945
+ }
3946
+ const next = this.derive(sourceEntity, pk);
3947
+ const prev = this.getOneByPk(pk);
3948
+ if (prev !== void 0 && this.compareDerived(prev, next)) return;
3949
+ this.upsertOneByPk(pk, next);
3950
+ }
3951
+ };
3952
+
3840
3953
  // src/core/OIMObjectStoreRecordDriven.ts
3841
3954
  var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
3842
3955
  constructor(initial) {
@@ -3909,8 +4022,11 @@ var OIMObject = class {
3909
4022
  return this.store.getAll();
3910
4023
  }
3911
4024
  clear() {
4025
+ const keys = this.store.keys();
3912
4026
  this.store.clear();
3913
- this.onUpdatedKeys([]);
4027
+ if (keys.length > 0) {
4028
+ this.onUpdatedKeys(keys);
4029
+ }
3914
4030
  }
3915
4031
  /**
3916
4032
  * Write fan-out hooks. Split single-key vs multi-key so a reactive subclass
@@ -4213,8 +4329,9 @@ var OIMComparatorFactory = class {
4213
4329
  if (ka.length !== kb.length) return true;
4214
4330
  for (let i = 0; i < ka.length; i++) {
4215
4331
  const k = ka[i];
4216
- if (b[k] !== a[k])
4332
+ if (b[k] !== a[k]) {
4217
4333
  return true;
4334
+ }
4218
4335
  }
4219
4336
  return false;
4220
4337
  };
@@ -4236,6 +4353,7 @@ var OIMEventQueue = class {
4236
4353
  this.tasksSpare = /* @__PURE__ */ new Set();
4237
4354
  this.isFlushing = false;
4238
4355
  this.scheduler = options.scheduler;
4356
+ this.onError = options.onError;
4239
4357
  if (this.scheduler) {
4240
4358
  this.flushBound = () => this.flush();
4241
4359
  this.scheduler.on(
@@ -4263,10 +4381,14 @@ var OIMEventQueue = class {
4263
4381
  return this.isFlushing;
4264
4382
  }
4265
4383
  /**
4266
- * Execute all pending tasks.
4384
+ * Execute the tasks pending at the moment the flush starts — a single pass,
4385
+ * not a drain-to-empty.
4267
4386
  *
4268
- * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
4269
- * executed within the same flush.
4387
+ * The pending set is swapped out for an empty spare up front and the flush
4388
+ * iterates that captured set. Tasks enqueued during the flush land in the
4389
+ * fresh set and run on the NEXT flush, not this one. (Contrast
4390
+ * `OIMComputeRuntime.flush()`, which re-reads its bucket per level and so
4391
+ * does drain same-level tasks scheduled mid-flush.)
4270
4392
  */
4271
4393
  flush() {
4272
4394
  this.scheduler?.cancel();
@@ -4275,12 +4397,34 @@ var OIMEventQueue = class {
4275
4397
  const flushing = this.tasks;
4276
4398
  this.tasks = this.tasksSpare;
4277
4399
  this.flushing = flushing;
4278
- for (const task of flushing) task();
4279
- flushing.clear();
4280
- this.tasksSpare = flushing;
4281
- this.flushing = void 0;
4282
- this.isFlushing = false;
4400
+ let errors;
4401
+ try {
4402
+ for (const task of flushing) {
4403
+ try {
4404
+ task();
4405
+ } catch (error) {
4406
+ (errors ?? (errors = [])).push(error);
4407
+ this.emitter.emit(2 /* FLUSH_ERROR */, {
4408
+ error
4409
+ });
4410
+ this.onError?.(error);
4411
+ }
4412
+ }
4413
+ } finally {
4414
+ flushing.clear();
4415
+ this.tasksSpare = flushing;
4416
+ this.flushing = void 0;
4417
+ this.isFlushing = false;
4418
+ }
4283
4419
  this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
4420
+ if (errors && this.onError === void 0) {
4421
+ if (errors.length === 1) throw errors[0];
4422
+ const AggregateErrorCtor = globalThis.AggregateError;
4423
+ throw AggregateErrorCtor ? new AggregateErrorCtor(
4424
+ errors,
4425
+ "Errors thrown during queue flush"
4426
+ ) : errors[0];
4427
+ }
4284
4428
  }
4285
4429
  /**
4286
4430
  * Get the current number of queued functions.
@@ -4518,20 +4662,92 @@ var OIMPkCodecKeyPath = class {
4518
4662
  }
4519
4663
  };
4520
4664
 
4665
+ // src/modules/effect/core/OIMEffectDependencyComputed.ts
4666
+ var OIMEffectDependencyComputed = class {
4667
+ constructor(source) {
4668
+ this.source = source;
4669
+ }
4670
+ subscribe(onUpdate) {
4671
+ return this.source.emitter.on("update", onUpdate);
4672
+ }
4673
+ };
4674
+
4521
4675
  // src/modules/compute/core/OIMComputeRuntime.ts
4522
4676
  var OIMComputeRuntime = class {
4523
4677
  constructor(queue) {
4524
4678
  this.queue = queue;
4525
4679
  this.isFlushScheduled = false;
4526
4680
  this.isFlushing = false;
4681
+ // Active level range (min > max means nothing pending).
4527
4682
  this.minLevel = Infinity;
4528
4683
  this.maxLevel = -Infinity;
4529
- this.dirtyByLevel = /* @__PURE__ */ new Map();
4684
+ this.dirtyByLevel = [];
4530
4685
  this.afterFlushTasks = /* @__PURE__ */ new Set();
4531
- this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
4532
- if (!this.isFlushScheduled) return;
4533
- this.flush();
4534
- });
4686
+ // Topological depth per computed node — the engine is the ONLY owner of
4687
+ // levels; nodes never expose their own. A node's dependents look up its depth
4688
+ // here (by the computed reference their dependency wraps). Written once at
4689
+ // construction, never on the hot flush path.
4690
+ this.levelByComputed = /* @__PURE__ */ new WeakMap();
4691
+ this._isDestroyed = false;
4692
+ this.unsubscribeFromQueue = this.queue.emitter.on(
4693
+ 0 /* AFTER_FLUSH */,
4694
+ () => {
4695
+ if (!this.isFlushScheduled) return;
4696
+ this.flush();
4697
+ }
4698
+ );
4699
+ }
4700
+ /**
4701
+ * Whether {@link destroy} has run. A destroyed runtime is detached from its
4702
+ * queue and must not be reused — `getOIMComputeRuntime` reads this to hand
4703
+ * out a fresh runtime instead of a dead one.
4704
+ */
4705
+ get isDestroyed() {
4706
+ return this._isDestroyed;
4707
+ }
4708
+ /**
4709
+ * Detach from the queue and drop everything pending.
4710
+ *
4711
+ * Without this the AFTER_FLUSH subscription lives as long as the queue does,
4712
+ * even once every computed / effect on this runtime is gone — harmless (the
4713
+ * handler early-returns) but impossible to unwind. Destroying does NOT
4714
+ * destroy the nodes scheduled on it; destroy them first, then the runtime.
4715
+ * Idempotent.
4716
+ */
4717
+ destroy() {
4718
+ if (this._isDestroyed) return;
4719
+ this._isDestroyed = true;
4720
+ this.unsubscribeFromQueue();
4721
+ for (let i = 0; i < this.dirtyByLevel.length; i++) {
4722
+ this.dirtyByLevel[i]?.clear();
4723
+ }
4724
+ this.afterFlushTasks.clear();
4725
+ this.minLevel = Infinity;
4726
+ this.maxLevel = -Infinity;
4727
+ this.isFlushScheduled = false;
4728
+ }
4729
+ /**
4730
+ * Topological depth for a node with these dependencies: one above the deepest
4731
+ * computed dependency (source-only → 0). The engine owns this — callers pass
4732
+ * their dependencies and get back a level, they never compute it themselves.
4733
+ */
4734
+ computeLevel(deps) {
4735
+ let max = -1;
4736
+ for (const dep of deps) {
4737
+ if (dep instanceof OIMEffectDependencyComputed) {
4738
+ const depLevel = this.levelByComputed.get(dep.source) ?? 0;
4739
+ if (depLevel > max) max = depLevel;
4740
+ }
4741
+ }
4742
+ return max + 1;
4743
+ }
4744
+ /** Record a computed's depth so its dependents can look it up. */
4745
+ registerLevel(computed, level) {
4746
+ this.levelByComputed.set(computed, level);
4747
+ }
4748
+ /** The recorded topological depth of a computed (0 if unknown). Introspection. */
4749
+ getLevel(computed) {
4750
+ return this.levelByComputed.get(computed) ?? 0;
4535
4751
  }
4536
4752
  /**
4537
4753
  * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
@@ -4543,10 +4759,10 @@ var OIMComputeRuntime = class {
4543
4759
  if (!isActive) return;
4544
4760
  task();
4545
4761
  };
4546
- let bucket = this.dirtyByLevel.get(level);
4547
- if (!bucket) {
4762
+ let bucket = this.dirtyByLevel[level];
4763
+ if (bucket === void 0) {
4548
4764
  bucket = /* @__PURE__ */ new Set();
4549
- this.dirtyByLevel.set(level, bucket);
4765
+ this.dirtyByLevel[level] = bucket;
4550
4766
  }
4551
4767
  bucket.add(wrapped);
4552
4768
  if (level < this.minLevel) this.minLevel = level;
@@ -4578,7 +4794,7 @@ var OIMComputeRuntime = class {
4578
4794
  }
4579
4795
  flush() {
4580
4796
  this.isFlushScheduled = false;
4581
- if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
4797
+ if (this.minLevel > this.maxLevel && this.afterFlushTasks.size === 0)
4582
4798
  return;
4583
4799
  this.isFlushing = true;
4584
4800
  try {
@@ -4587,15 +4803,15 @@ var OIMComputeRuntime = class {
4587
4803
  this.afterFlushTasks.clear();
4588
4804
  for (let i = 0; i < snapshot.length; i++) snapshot[i]();
4589
4805
  }
4590
- if (this.dirtyByLevel.size === 0) return;
4806
+ if (this.minLevel > this.maxLevel) return;
4591
4807
  let level = this.minLevel;
4592
4808
  let max = this.maxLevel;
4593
4809
  while (level <= max) {
4594
4810
  for (; ; ) {
4595
- const bucket = this.dirtyByLevel.get(level);
4596
- if (!bucket || bucket.size === 0) break;
4597
- this.dirtyByLevel.delete(level);
4811
+ const bucket = this.dirtyByLevel[level];
4812
+ if (bucket === void 0 || bucket.size === 0) break;
4598
4813
  const snapshot = Array.from(bucket);
4814
+ bucket.clear();
4599
4815
  for (let i = 0; i < snapshot.length; i++) snapshot[i]();
4600
4816
  }
4601
4817
  if (this.maxLevel > max) max = this.maxLevel;
@@ -4603,24 +4819,38 @@ var OIMComputeRuntime = class {
4603
4819
  }
4604
4820
  } finally {
4605
4821
  this.isFlushing = false;
4606
- if (this.dirtyByLevel.size === 0) {
4607
- this.minLevel = Infinity;
4608
- this.maxLevel = -Infinity;
4609
- } else {
4610
- let nextMin = Infinity;
4611
- let nextMax = -Infinity;
4612
- this.dirtyByLevel.forEach((_bucket, lvl) => {
4613
- if (lvl < nextMin) nextMin = lvl;
4614
- if (lvl > nextMax) nextMax = lvl;
4615
- });
4616
- this.minLevel = nextMin;
4617
- this.maxLevel = nextMax;
4618
- this.isFlushScheduled = true;
4822
+ this.recomputePendingRange();
4823
+ }
4824
+ }
4825
+ recomputePendingRange() {
4826
+ let nextMin = Infinity;
4827
+ let nextMax = -Infinity;
4828
+ const buckets = this.dirtyByLevel;
4829
+ for (let i = 0; i < buckets.length; i++) {
4830
+ const bucket = buckets[i];
4831
+ if (bucket !== void 0 && bucket.size > 0) {
4832
+ if (i < nextMin) nextMin = i;
4833
+ if (i > nextMax) nextMax = i;
4619
4834
  }
4620
4835
  }
4836
+ this.minLevel = nextMin;
4837
+ this.maxLevel = nextMax;
4838
+ if (nextMin <= nextMax) this.isFlushScheduled = true;
4621
4839
  }
4622
4840
  };
4623
4841
 
4842
+ // src/dx/oimComputeRuntimeByQueue.ts
4843
+ var oimComputeRuntimeByQueue = /* @__PURE__ */ new WeakMap();
4844
+
4845
+ // src/dx/getOIMComputeRuntime.ts
4846
+ function getOIMComputeRuntime(queue) {
4847
+ const runtime = oimComputeRuntimeByQueue.get(queue);
4848
+ if (runtime !== void 0 && !runtime.isDestroyed) return runtime;
4849
+ const nextRuntime = new OIMComputeRuntime(queue);
4850
+ oimComputeRuntimeByQueue.set(queue, nextRuntime);
4851
+ return nextRuntime;
4852
+ }
4853
+
4624
4854
  // src/modules/selector/core/OIMSelector.ts
4625
4855
  var OIMSelector = class {
4626
4856
  constructor(runtime, sourceDeps, computedDeps = []) {
@@ -4881,8 +5111,9 @@ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
4881
5111
  areEqual(prev, next) {
4882
5112
  if (prev === next) return true;
4883
5113
  if (prev.length !== next.length) return false;
4884
- for (let i = 0; i < prev.length; i++)
5114
+ for (let i = 0; i < prev.length; i++) {
4885
5115
  if (prev[i] !== next[i]) return false;
5116
+ }
4886
5117
  return true;
4887
5118
  }
4888
5119
  };
@@ -4952,8 +5183,9 @@ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
4952
5183
  areEqual(prev, next) {
4953
5184
  if (prev === next) return true;
4954
5185
  if (prev.length !== next.length) return false;
4955
- for (let i = 0; i < prev.length; i++)
5186
+ for (let i = 0; i < prev.length; i++) {
4956
5187
  if (prev[i] !== next[i]) return false;
5188
+ }
4957
5189
  return true;
4958
5190
  }
4959
5191
  };
@@ -5017,8 +5249,9 @@ var OIMEntitiesByGlobalIndexArrayBasedSelector = class extends OIMSelector {
5017
5249
  areEqual(prev, next) {
5018
5250
  if (prev === next) return true;
5019
5251
  if (prev.length !== next.length) return false;
5020
- for (let i = 0; i < prev.length; i++)
5252
+ for (let i = 0; i < prev.length; i++) {
5021
5253
  if (prev[i] !== next[i]) return false;
5254
+ }
5022
5255
  return true;
5023
5256
  }
5024
5257
  };
@@ -5083,8 +5316,9 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
5083
5316
  areEqual(prev, next) {
5084
5317
  if (prev === next) return true;
5085
5318
  if (prev.length !== next.length) return false;
5086
- for (let i = 0; i < prev.length; i++)
5319
+ for (let i = 0; i < prev.length; i++) {
5087
5320
  if (prev[i] !== next[i]) return false;
5321
+ }
5088
5322
  return true;
5089
5323
  }
5090
5324
  };
@@ -5093,7 +5327,7 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
5093
5327
  var OIMCollectionSelectors = class {
5094
5328
  constructor(queue, collection) {
5095
5329
  this.collection = collection;
5096
- this.runtime = new OIMComputeRuntime(queue);
5330
+ this.runtime = getOIMComputeRuntime(queue);
5097
5331
  }
5098
5332
  byPk(pk) {
5099
5333
  return new OIMCollectionByPkSelector(this.runtime, this.collection, pk);
@@ -5160,127 +5394,408 @@ var OIMCollectionSelectors = class {
5160
5394
  }
5161
5395
  };
5162
5396
 
5163
- // src/dx/OIMCollectionKit.ts
5164
- function createOIMCollectionKit(queue, opts) {
5165
- const collection = new OIMReactiveCollection(queue, opts);
5166
- const indexFactory = new OIMCollectionIndexFactory(queue, collection);
5167
- const select = new OIMCollectionSelectors(queue, collection);
5168
- const scope = new OIMDisposeScope();
5169
- scope.add(collection);
5170
- return {
5171
- queue,
5172
- collection,
5173
- indexFactory,
5174
- select,
5175
- scope,
5176
- destroy: () => scope.destroy()
5177
- };
5178
- }
5179
-
5180
- // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
5181
- var OIMCollectionChangedFields = class {
5182
- constructor(queue, collection, opts) {
5183
- this.isInWrite = false;
5184
- this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
5185
- // PKs written through this wrapper during the current flush. Their fields are
5186
- // already tracked accurately via trackPatch, so the flush-time external-write
5187
- // handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
5188
- this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
5189
- this.changedFieldsByPk = /* @__PURE__ */ new Map();
5190
- this.changedPksByField = /* @__PURE__ */ new Map();
5191
- this.onAfterFlush = () => {
5192
- this.clear();
5193
- this.writtenThroughThisFlush.clear();
5194
- };
5195
- this.onCollectionUpdate = (payload) => {
5196
- if (this.isInWrite) return;
5197
- if (payload.pks.length === 0) return;
5198
- for (let i = 0; i < payload.pks.length; i++) {
5199
- const pk = payload.pks[i];
5200
- if (this.writtenThroughThisFlush.has(pk)) continue;
5201
- this.trackExternalMutationByPk(pk);
5202
- }
5203
- };
5204
- this.collection = collection;
5205
- this.detectExternalMutations = opts?.detectExternalMutations !== false;
5206
- this.selectPk = opts?.selectPk ?? (() => {
5207
- throw new Error(
5208
- `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
5209
- );
5210
- });
5211
- this.changedPksEventEmitter = new OIMUpdateEventEmitter(
5212
- queue,
5213
- "after_flush"
5214
- );
5215
- this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
5216
- this.unsubscribeAfterFlush = queue.emitter.on(
5217
- 0 /* AFTER_FLUSH */,
5218
- this.onAfterFlush
5219
- );
5220
- if (this.detectExternalMutations) {
5221
- const maybeReactive = collection;
5222
- this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
5223
- this.onCollectionUpdate({
5224
- pks
5225
- });
5226
- }) : collection.emitter.on(
5227
- 0 /* UPDATE */,
5228
- this.onCollectionUpdate
5229
- );
5230
- }
5231
- }
5232
- upsertOneByPk(pk, patch) {
5233
- const slot = this.withWrite(() => {
5234
- this.trackPatch(pk, patch);
5235
- return this.collection.upsertOneByPk(pk, patch);
5236
- });
5237
- this.updateEntitySnapshot(pk);
5238
- return slot;
5397
+ // src/modules/effect/core/OIMEffectDependencyKeyed.ts
5398
+ var OIMEffectDependencyKeyed = class {
5399
+ constructor(source, key) {
5400
+ this.source = source;
5401
+ this.key = key;
5239
5402
  }
5240
- upsertOne(entity) {
5241
- const slot = this.withWrite(() => {
5242
- const pk = this.selectPk(entity);
5243
- this.trackPatch(pk, entity);
5244
- return this.collection.upsertOne(entity);
5245
- });
5246
- this.updateEntitySnapshot(slot.pk);
5247
- return slot;
5403
+ subscribe(onUpdate) {
5404
+ return this.source.subscribeOnKey(this.key, onUpdate);
5248
5405
  }
5249
- upsertMany(entities) {
5250
- const pks = [];
5251
- const slots = this.withWrite(() => {
5252
- for (const entity of entities) {
5253
- const pk = this.selectPk(entity);
5254
- pks.push(pk);
5255
- this.trackPatch(pk, entity);
5256
- }
5257
- return this.collection.upsertMany(entities);
5258
- });
5259
- for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
5260
- return slots;
5406
+ };
5407
+
5408
+ // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
5409
+ var OIMEffectDependencyKeyedCollection = class {
5410
+ constructor(collection, pk) {
5411
+ this.dep = new OIMEffectDependencyKeyed(collection, pk);
5261
5412
  }
5262
- getChangedFieldsByPk(pk) {
5263
- return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
5413
+ get source() {
5414
+ return this.dep.source;
5264
5415
  }
5265
- consumeChangedFieldsByPk(pk) {
5266
- const existing = this.changedFieldsByPk.get(pk);
5267
- if (!existing) return /* @__PURE__ */ new Set();
5268
- return new Set(existing);
5416
+ subscribe(onUpdate) {
5417
+ return this.dep.subscribe(onUpdate);
5269
5418
  }
5270
- getChangedPksByField(field) {
5271
- return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
5419
+ };
5420
+
5421
+ // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
5422
+ var OIMEffectDependencyKeyedIndex = class {
5423
+ constructor(index, key) {
5424
+ this.dep = new OIMEffectDependencyKeyed(index, key);
5272
5425
  }
5273
- consumeChangedPksByField(field) {
5274
- const existing = this.changedPksByField.get(field);
5275
- if (!existing) return /* @__PURE__ */ new Set();
5276
- return new Set(existing);
5426
+ get source() {
5427
+ return this.dep.source;
5277
5428
  }
5278
- clear() {
5279
- this.changedFieldsByPk.clear();
5280
- this.changedPksByField.clear();
5429
+ subscribe(onUpdate) {
5430
+ return this.dep.subscribe(onUpdate);
5281
5431
  }
5282
- destroy() {
5283
- this.changedPksEventEmitter.destroy();
5432
+ };
5433
+
5434
+ // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
5435
+ var OIMEffectDependencyKeyedObject = class {
5436
+ constructor(object, key) {
5437
+ this.dep = new OIMEffectDependencyKeyed(object, key);
5438
+ }
5439
+ get source() {
5440
+ return this.dep.source;
5441
+ }
5442
+ subscribe(onUpdate) {
5443
+ return this.dep.subscribe(onUpdate);
5444
+ }
5445
+ };
5446
+
5447
+ // src/dx/on.ts
5448
+ var on = {
5449
+ /** Depend on one PK of a collection. */
5450
+ collection: (collection, pk) => new OIMEffectDependencyKeyedCollection(collection, pk),
5451
+ /** Depend on one key of an index. */
5452
+ index: (index, key) => new OIMEffectDependencyKeyedIndex(index, key),
5453
+ /** Depend on one key of a reactive object. */
5454
+ object: (object, key) => new OIMEffectDependencyKeyedObject(object, key),
5455
+ /** Depend on another computed. */
5456
+ computed: (computed) => new OIMEffectDependencyComputed(computed)
5457
+ };
5458
+
5459
+ // src/modules/effect/core/OIMEffect.ts
5460
+ var OIMEffect = class {
5461
+ constructor(runtime, opts, level) {
5462
+ this.sourceDeps = [];
5463
+ this.computedDeps = [];
5464
+ this.sourceUnsubscribers = [];
5465
+ this.computedUnsubscribers = [];
5466
+ this.isResubscribeEnqueued = false;
5467
+ this.isTemporarilyUnsubscribedForFlush = false;
5468
+ this.isScheduled = false;
5469
+ this.runTask = () => {
5470
+ this.isScheduled = false;
5471
+ this.dequeueScheduled = void 0;
5472
+ this.runOnce();
5473
+ };
5474
+ this.onSourceInvalidate = () => {
5475
+ this.onUpdate?.();
5476
+ if (!this.isTemporarilyUnsubscribedForFlush) {
5477
+ this.isTemporarilyUnsubscribedForFlush = true;
5478
+ this.unsubscribeFromSourceDeps();
5479
+ this.ensureResubscribeAfterFlush();
5480
+ }
5481
+ if (this.isScheduled) return;
5482
+ this.isScheduled = true;
5483
+ this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
5484
+ };
5485
+ this.onComputedInvalidate = () => {
5486
+ this.onUpdate?.();
5487
+ if (this.isScheduled) return;
5488
+ this.isScheduled = true;
5489
+ this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
5490
+ };
5491
+ this.onAfterFlush = () => {
5492
+ if (!this.isResubscribeEnqueued) return;
5493
+ this.isResubscribeEnqueued = false;
5494
+ this.dequeueResubscribe = void 0;
5495
+ this.isTemporarilyUnsubscribedForFlush = false;
5496
+ if (this.sourceUnsubscribers.length === 0) {
5497
+ for (const dep of this.sourceDeps) {
5498
+ this.sourceUnsubscribers.push(
5499
+ dep.subscribe(this.onSourceInvalidate)
5500
+ );
5501
+ }
5502
+ }
5503
+ };
5504
+ this.runtime = runtime;
5505
+ this.onUpdate = opts.onUpdate;
5506
+ this.run = opts.run;
5507
+ this.deps = opts.deps ?? [];
5508
+ for (const dep of this.deps) {
5509
+ if (dep instanceof OIMEffectDependencyComputed) {
5510
+ this.computedDeps.push(dep);
5511
+ } else {
5512
+ this.sourceDeps.push(dep);
5513
+ }
5514
+ }
5515
+ this.level = level ?? this.runtime.computeLevel(this.deps);
5516
+ this.subscribeToDeps();
5517
+ }
5518
+ destroy() {
5519
+ if (this.dequeueScheduled) {
5520
+ this.dequeueScheduled();
5521
+ this.dequeueScheduled = void 0;
5522
+ }
5523
+ this.unsubscribeFromAllDeps();
5524
+ if (this.dequeueResubscribe) {
5525
+ this.dequeueResubscribe();
5526
+ this.dequeueResubscribe = void 0;
5527
+ }
5528
+ this.isResubscribeEnqueued = false;
5529
+ this.isTemporarilyUnsubscribedForFlush = false;
5530
+ }
5531
+ runOnce() {
5532
+ this.run();
5533
+ }
5534
+ subscribeToDeps() {
5535
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
5536
+ return;
5537
+ for (const dep of this.sourceDeps) {
5538
+ this.sourceUnsubscribers.push(
5539
+ dep.subscribe(this.onSourceInvalidate)
5540
+ );
5541
+ }
5542
+ for (const dep of this.computedDeps) {
5543
+ this.computedUnsubscribers.push(
5544
+ dep.subscribe(this.onComputedInvalidate)
5545
+ );
5546
+ }
5547
+ }
5548
+ unsubscribeFromSourceDeps() {
5549
+ if (this.sourceUnsubscribers.length === 0) return;
5550
+ for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
5551
+ this.sourceUnsubscribers = [];
5552
+ }
5553
+ unsubscribeFromAllDeps() {
5554
+ this.unsubscribeFromSourceDeps();
5555
+ if (this.computedUnsubscribers.length === 0) return;
5556
+ for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
5557
+ this.computedUnsubscribers = [];
5558
+ }
5559
+ ensureResubscribeAfterFlush() {
5560
+ if (this.isResubscribeEnqueued) return;
5561
+ this.isResubscribeEnqueued = true;
5562
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
5563
+ this.onAfterFlush
5564
+ );
5565
+ }
5566
+ };
5567
+
5568
+ // src/modules/computed/enums/EOIMComputedEventType.ts
5569
+ var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
5570
+ EOIMComputedEventType2["UPDATE"] = "update";
5571
+ return EOIMComputedEventType2;
5572
+ })(EOIMComputedEventType || {});
5573
+
5574
+ // src/modules/computed/core/OIMComputed.ts
5575
+ var OIMComputed = class {
5576
+ constructor(runtime, opts) {
5577
+ this.emitter = new OIMEventEmitter();
5578
+ this.hasValue = false;
5579
+ this.isDirty = true;
5580
+ this.runtime = runtime;
5581
+ this.compute = opts.compute;
5582
+ this.compare = opts.compare ?? Object.is;
5583
+ this._deps = opts.deps ?? [];
5584
+ this.updateEventEmitter = new OIMUpdateEventEmitter(
5585
+ this.runtime.queue,
5586
+ "immediate"
5587
+ );
5588
+ const level = this.runtime.computeLevel(this._deps);
5589
+ this.runtime.registerLevel(this, level);
5590
+ this.effect = new OIMEffect(
5591
+ this.runtime,
5592
+ {
5593
+ deps: this._deps,
5594
+ onUpdate: () => {
5595
+ this.isDirty = true;
5596
+ },
5597
+ run: () => {
5598
+ this.recomputeAndEmitIfChanged();
5599
+ }
5600
+ },
5601
+ level
5602
+ );
5603
+ }
5604
+ get needsRecompute() {
5605
+ return this.isDirty;
5606
+ }
5607
+ get isReady() {
5608
+ return this.hasValue;
5609
+ }
5610
+ get deps() {
5611
+ return this._deps;
5612
+ }
5613
+ get() {
5614
+ this.recomputeAndEmitIfChanged();
5615
+ return this.value;
5616
+ }
5617
+ getIfReady() {
5618
+ return this.hasValue ? this.value : void 0;
5619
+ }
5620
+ destroy() {
5621
+ this.effect.destroy();
5622
+ this.updateEventEmitter.destroy();
5623
+ this.emitter.offAll();
5624
+ }
5625
+ recomputeAndEmitIfChanged() {
5626
+ if (!this.isDirty) return;
5627
+ const prev = this.value;
5628
+ const hadPrev = this.hasValue;
5629
+ const next = this.compute();
5630
+ this.value = next;
5631
+ this.hasValue = true;
5632
+ this.isDirty = false;
5633
+ const changed = !hadPrev || !this.compare(prev, next);
5634
+ if (!changed) return;
5635
+ this.emitter.emit("update" /* UPDATE */, {
5636
+ keys: ["value"]
5637
+ });
5638
+ this.updateEventEmitter.markUpdatedKey("value");
5639
+ }
5640
+ };
5641
+
5642
+ // src/dx/OIMCollectionKit.ts
5643
+ function createOIMCollectionKit(queue, opts) {
5644
+ const collection = new OIMReactiveCollection(queue, opts);
5645
+ const indexFactory = new OIMCollectionIndexFactory(queue, collection);
5646
+ const select = new OIMCollectionSelectors(queue, collection);
5647
+ const runtime = getOIMComputeRuntime(queue);
5648
+ const scope = new OIMDisposeScope();
5649
+ scope.add(collection);
5650
+ return {
5651
+ queue,
5652
+ collection,
5653
+ indexFactory,
5654
+ select,
5655
+ on,
5656
+ computed: (deps, compute, options) => {
5657
+ const computed = new OIMComputed(runtime, {
5658
+ deps,
5659
+ compute,
5660
+ compare: options?.compare
5661
+ });
5662
+ scope.add(computed);
5663
+ return computed;
5664
+ },
5665
+ effect: (deps, run, options) => {
5666
+ const effect = new OIMEffect(runtime, {
5667
+ deps,
5668
+ run,
5669
+ onUpdate: options?.onUpdate
5670
+ });
5671
+ scope.add(effect);
5672
+ return effect;
5673
+ },
5674
+ scope,
5675
+ destroy: () => scope.destroy()
5676
+ };
5677
+ }
5678
+
5679
+ // src/dx/peekOIMComputeRuntime.ts
5680
+ function peekOIMComputeRuntime(queue) {
5681
+ const runtime = oimComputeRuntimeByQueue.get(queue);
5682
+ return runtime === void 0 || runtime.isDestroyed ? void 0 : runtime;
5683
+ }
5684
+
5685
+ // src/dx/setOIMComputeRuntime.ts
5686
+ function setOIMComputeRuntime(queue, runtime) {
5687
+ if (runtime.queue !== queue) {
5688
+ throw new Error(
5689
+ "setOIMComputeRuntime: the runtime is attached to a different queue. A runtime drains on its own queue and would never run here."
5690
+ );
5691
+ }
5692
+ oimComputeRuntimeByQueue.set(queue, runtime);
5693
+ }
5694
+
5695
+ // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
5696
+ var OIMCollectionChangedFields = class {
5697
+ constructor(queue, collection, opts) {
5698
+ this.isInWrite = false;
5699
+ this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
5700
+ // PKs written through this wrapper during the current flush. Their fields are
5701
+ // already tracked accurately via trackPatch, so the flush-time external-write
5702
+ // handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
5703
+ this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
5704
+ this.changedFieldsByPk = /* @__PURE__ */ new Map();
5705
+ this.changedPksByField = /* @__PURE__ */ new Map();
5706
+ this.onAfterFlush = () => {
5707
+ this.clear();
5708
+ this.writtenThroughThisFlush.clear();
5709
+ };
5710
+ this.onCollectionUpdate = (payload) => {
5711
+ if (this.isInWrite) return;
5712
+ if (payload.pks.length === 0) return;
5713
+ for (let i = 0; i < payload.pks.length; i++) {
5714
+ const pk = payload.pks[i];
5715
+ if (this.writtenThroughThisFlush.has(pk)) continue;
5716
+ this.trackExternalMutationByPk(pk);
5717
+ }
5718
+ };
5719
+ this.collection = collection;
5720
+ this.detectExternalMutations = opts?.detectExternalMutations !== false;
5721
+ this.selectPk = opts?.selectPk ?? (() => {
5722
+ throw new Error(
5723
+ `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
5724
+ );
5725
+ });
5726
+ this.changedPksEventEmitter = new OIMUpdateEventEmitter(
5727
+ queue,
5728
+ "after_flush"
5729
+ );
5730
+ this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
5731
+ this.unsubscribeAfterFlush = queue.emitter.on(
5732
+ 0 /* AFTER_FLUSH */,
5733
+ this.onAfterFlush
5734
+ );
5735
+ if (this.detectExternalMutations) {
5736
+ const maybeReactive = collection;
5737
+ this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
5738
+ this.onCollectionUpdate({
5739
+ pks
5740
+ });
5741
+ }) : collection.emitter.on(
5742
+ 0 /* UPDATE */,
5743
+ this.onCollectionUpdate
5744
+ );
5745
+ }
5746
+ }
5747
+ upsertOneByPk(pk, patch) {
5748
+ const slot = this.withWrite(() => {
5749
+ this.trackPatch(pk, patch);
5750
+ return this.collection.upsertOneByPk(pk, patch);
5751
+ });
5752
+ this.updateEntitySnapshot(pk);
5753
+ return slot;
5754
+ }
5755
+ upsertOne(entity) {
5756
+ const slot = this.withWrite(() => {
5757
+ const pk = this.selectPk(entity);
5758
+ this.trackPatch(pk, entity);
5759
+ return this.collection.upsertOne(entity);
5760
+ });
5761
+ this.updateEntitySnapshot(slot.pk);
5762
+ return slot;
5763
+ }
5764
+ upsertMany(entities) {
5765
+ const pks = [];
5766
+ const slots = this.withWrite(() => {
5767
+ for (const entity of entities) {
5768
+ const pk = this.selectPk(entity);
5769
+ pks.push(pk);
5770
+ this.trackPatch(pk, entity);
5771
+ }
5772
+ return this.collection.upsertMany(entities);
5773
+ });
5774
+ for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
5775
+ return slots;
5776
+ }
5777
+ getChangedFieldsByPk(pk) {
5778
+ return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
5779
+ }
5780
+ consumeChangedFieldsByPk(pk) {
5781
+ const existing = this.changedFieldsByPk.get(pk);
5782
+ if (!existing) return /* @__PURE__ */ new Set();
5783
+ return new Set(existing);
5784
+ }
5785
+ getChangedPksByField(field) {
5786
+ return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
5787
+ }
5788
+ consumeChangedPksByField(field) {
5789
+ const existing = this.changedPksByField.get(field);
5790
+ if (!existing) return /* @__PURE__ */ new Set();
5791
+ return new Set(existing);
5792
+ }
5793
+ clear() {
5794
+ this.changedFieldsByPk.clear();
5795
+ this.changedPksByField.clear();
5796
+ }
5797
+ destroy() {
5798
+ this.changedPksEventEmitter.destroy();
5284
5799
  this.changedFieldsEventEmitter.destroy();
5285
5800
  this.unsubscribeAfterFlush();
5286
5801
  this.unsubscribeFromCollectionEmitter?.();
@@ -5319,8 +5834,9 @@ var OIMCollectionChangedFields = class {
5319
5834
  Object.keys(nextObj).forEach((k) => keys.add(k));
5320
5835
  const changed = [];
5321
5836
  keys.forEach((k) => {
5322
- if (prevObj[k] !== nextObj[k])
5837
+ if (prevObj[k] !== nextObj[k]) {
5323
5838
  changed.push(k);
5839
+ }
5324
5840
  });
5325
5841
  return changed;
5326
5842
  }
@@ -5486,10 +6002,15 @@ var OIMOrderedListMappedCommandStream = class _OIMOrderedListMappedCommandStream
5486
6002
  try {
5487
6003
  const subs = this.subscribers.get(key);
5488
6004
  if (subs && subs.size > 0) {
5489
- const snapshot = Array.from(subs);
5490
- for (let s = 0; s < snapshot.length; s++) {
5491
- const handler = snapshot[s];
5492
- if (subs.has(handler)) handler();
6005
+ if (subs.size === 1) {
6006
+ const only = subs.values().next().value;
6007
+ if (only) only();
6008
+ } else {
6009
+ const snapshot = Array.from(subs);
6010
+ for (let s = 0; s < snapshot.length; s++) {
6011
+ const handler = snapshot[s];
6012
+ if (subs.has(handler)) handler();
6013
+ }
5493
6014
  }
5494
6015
  }
5495
6016
  } finally {
@@ -5783,10 +6304,13 @@ var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler
5783
6304
  this.flush();
5784
6305
  });
5785
6306
  } else {
5786
- this.frameId = setTimeout(() => {
5787
- this.frameId = void 0;
5788
- this.flush();
5789
- }, 16);
6307
+ this.frameId = this.unref(
6308
+ setTimeout(() => {
6309
+ this.frameId = void 0;
6310
+ this.flush();
6311
+ }, 16)
6312
+ // ~60fps
6313
+ );
5790
6314
  }
5791
6315
  }
5792
6316
  cancel() {
@@ -5832,10 +6356,12 @@ var OIMEventQueueSchedulerTimeout = class extends OIMEventQueueScheduler {
5832
6356
  }
5833
6357
  schedule() {
5834
6358
  if (this.timeoutId !== void 0) return;
5835
- this.timeoutId = setTimeout(() => {
5836
- this.timeoutId = void 0;
5837
- this.flush();
5838
- }, this.delay);
6359
+ this.timeoutId = this.unref(
6360
+ setTimeout(() => {
6361
+ this.timeoutId = void 0;
6362
+ this.flush();
6363
+ }, this.delay)
6364
+ );
5839
6365
  }
5840
6366
  cancel() {
5841
6367
  if (this.timeoutId === void 0) return;
@@ -5869,13 +6395,13 @@ var OIMEventQueueSchedulerImmediate = class extends OIMEventQueueScheduler {
5869
6395
  this.flush();
5870
6396
  };
5871
6397
  if (this.useSetImmediate) {
5872
- this.immediateId = setImmediate(callback);
6398
+ this.immediateId = this.unref(setImmediate(callback));
5873
6399
  } else if (this.useMessageChannel && this.messageChannel) {
5874
6400
  this.immediateId = 1;
5875
6401
  this.pendingCallback = callback;
5876
6402
  this.messageChannel.port1.postMessage(null);
5877
6403
  } else {
5878
- this.immediateId = setTimeout(callback, 0);
6404
+ this.immediateId = this.unref(setTimeout(callback, 0));
5879
6405
  }
5880
6406
  }
5881
6407
  cancel() {
@@ -5968,242 +6494,6 @@ var OIMDBSettings = class {
5968
6494
  OIMDBSettings.UPDATED_KEYS_INDEX_KEY = "__updatedKeys__";
5969
6495
  OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
5970
6496
 
5971
- // src/modules/effect/core/OIMEffectDependencyComputed.ts
5972
- var OIMEffectDependencyComputed = class {
5973
- constructor(source) {
5974
- this.source = source;
5975
- }
5976
- subscribe(onUpdate) {
5977
- return this.source.emitter.on("update", onUpdate);
5978
- }
5979
- };
5980
-
5981
- // src/modules/effect/core/OIMEffect.ts
5982
- var OIMEffect = class {
5983
- constructor(runtime, opts) {
5984
- this.sourceDeps = [];
5985
- this.computedDeps = [];
5986
- this.sourceUnsubscribers = [];
5987
- this.computedUnsubscribers = [];
5988
- this.isResubscribeEnqueued = false;
5989
- this.isTemporarilyUnsubscribedForFlush = false;
5990
- this.isScheduled = false;
5991
- this.runTask = () => {
5992
- this.isScheduled = false;
5993
- this.dequeueScheduled = void 0;
5994
- this.runOnce();
5995
- };
5996
- this.onSourceInvalidate = () => {
5997
- this.onUpdate?.();
5998
- if (!this.isTemporarilyUnsubscribedForFlush) {
5999
- this.isTemporarilyUnsubscribedForFlush = true;
6000
- this.unsubscribeFromSourceDeps();
6001
- this.ensureResubscribeAfterFlush();
6002
- }
6003
- if (this.isScheduled) return;
6004
- this.isScheduled = true;
6005
- this.dequeueScheduled = this.runtime.schedule(this.runTask);
6006
- };
6007
- this.onComputedInvalidate = () => {
6008
- this.onUpdate?.();
6009
- if (this.isScheduled) return;
6010
- this.isScheduled = true;
6011
- this.dequeueScheduled = this.runtime.schedule(this.runTask);
6012
- };
6013
- this.onAfterFlush = () => {
6014
- if (!this.isResubscribeEnqueued) return;
6015
- this.isResubscribeEnqueued = false;
6016
- this.dequeueResubscribe = void 0;
6017
- this.isTemporarilyUnsubscribedForFlush = false;
6018
- if (this.sourceUnsubscribers.length === 0) {
6019
- for (const dep of this.sourceDeps) {
6020
- this.sourceUnsubscribers.push(
6021
- dep.subscribe(this.onSourceInvalidate)
6022
- );
6023
- }
6024
- }
6025
- };
6026
- this.runtime = runtime;
6027
- this.onUpdate = opts.onUpdate;
6028
- this.run = opts.run;
6029
- this.deps = opts.deps ?? [];
6030
- for (const dep of this.deps) {
6031
- if (dep instanceof OIMEffectDependencyComputed) {
6032
- this.computedDeps.push(dep);
6033
- } else {
6034
- this.sourceDeps.push(dep);
6035
- }
6036
- }
6037
- this.subscribeToDeps();
6038
- }
6039
- destroy() {
6040
- if (this.dequeueScheduled) {
6041
- this.dequeueScheduled();
6042
- this.dequeueScheduled = void 0;
6043
- }
6044
- this.unsubscribeFromAllDeps();
6045
- if (this.dequeueResubscribe) {
6046
- this.dequeueResubscribe();
6047
- this.dequeueResubscribe = void 0;
6048
- }
6049
- this.isResubscribeEnqueued = false;
6050
- this.isTemporarilyUnsubscribedForFlush = false;
6051
- }
6052
- runOnce() {
6053
- this.run();
6054
- }
6055
- subscribeToDeps() {
6056
- if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
6057
- return;
6058
- for (const dep of this.sourceDeps) {
6059
- this.sourceUnsubscribers.push(
6060
- dep.subscribe(this.onSourceInvalidate)
6061
- );
6062
- }
6063
- for (const dep of this.computedDeps) {
6064
- this.computedUnsubscribers.push(
6065
- dep.subscribe(this.onComputedInvalidate)
6066
- );
6067
- }
6068
- }
6069
- unsubscribeFromSourceDeps() {
6070
- if (this.sourceUnsubscribers.length === 0) return;
6071
- for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
6072
- this.sourceUnsubscribers = [];
6073
- }
6074
- unsubscribeFromAllDeps() {
6075
- this.unsubscribeFromSourceDeps();
6076
- if (this.computedUnsubscribers.length === 0) return;
6077
- for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
6078
- this.computedUnsubscribers = [];
6079
- }
6080
- ensureResubscribeAfterFlush() {
6081
- if (this.isResubscribeEnqueued) return;
6082
- this.isResubscribeEnqueued = true;
6083
- this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
6084
- this.onAfterFlush
6085
- );
6086
- }
6087
- };
6088
-
6089
- // src/modules/computed/enums/EOIMComputedEventType.ts
6090
- var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
6091
- EOIMComputedEventType2["UPDATE"] = "update";
6092
- return EOIMComputedEventType2;
6093
- })(EOIMComputedEventType || {});
6094
-
6095
- // src/modules/computed/core/OIMComputed.ts
6096
- var OIMComputed = class {
6097
- constructor(runtime, opts) {
6098
- this.emitter = new OIMEventEmitter();
6099
- this.hasValue = false;
6100
- this.isDirty = true;
6101
- this.runtime = runtime;
6102
- this.compute = opts.compute;
6103
- this.compare = opts.compare ?? Object.is;
6104
- this._deps = opts.deps ?? [];
6105
- this.updateEventEmitter = new OIMUpdateEventEmitter(
6106
- this.runtime.queue,
6107
- "immediate"
6108
- );
6109
- this.effect = new OIMEffect(this.runtime, {
6110
- deps: this._deps,
6111
- onUpdate: () => {
6112
- this.isDirty = true;
6113
- },
6114
- run: () => {
6115
- this.recomputeAndEmitIfChanged();
6116
- }
6117
- });
6118
- }
6119
- get needsRecompute() {
6120
- return this.isDirty;
6121
- }
6122
- get isReady() {
6123
- return this.hasValue;
6124
- }
6125
- get deps() {
6126
- return this._deps;
6127
- }
6128
- get() {
6129
- this.recomputeAndEmitIfChanged();
6130
- return this.value;
6131
- }
6132
- getIfReady() {
6133
- return this.hasValue ? this.value : void 0;
6134
- }
6135
- destroy() {
6136
- this.effect.destroy();
6137
- this.updateEventEmitter.destroy();
6138
- this.emitter.offAll();
6139
- }
6140
- recomputeAndEmitIfChanged() {
6141
- if (!this.isDirty) return;
6142
- const prev = this.value;
6143
- const hadPrev = this.hasValue;
6144
- const next = this.compute();
6145
- this.value = next;
6146
- this.hasValue = true;
6147
- this.isDirty = false;
6148
- const changed = !hadPrev || !this.compare(prev, next);
6149
- if (!changed) return;
6150
- this.emitter.emit("update" /* UPDATE */, {
6151
- keys: ["value"]
6152
- });
6153
- this.updateEventEmitter.markUpdatedKey("value");
6154
- }
6155
- };
6156
-
6157
- // src/modules/effect/core/OIMEffectDependencyKeyed.ts
6158
- var OIMEffectDependencyKeyed = class {
6159
- constructor(source, key) {
6160
- this.source = source;
6161
- this.key = key;
6162
- }
6163
- subscribe(onUpdate) {
6164
- return this.source.subscribeOnKey(this.key, onUpdate);
6165
- }
6166
- };
6167
-
6168
- // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
6169
- var OIMEffectDependencyKeyedObject = class {
6170
- constructor(object, key) {
6171
- this.dep = new OIMEffectDependencyKeyed(object, key);
6172
- }
6173
- get source() {
6174
- return this.dep.source;
6175
- }
6176
- subscribe(onUpdate) {
6177
- return this.dep.subscribe(onUpdate);
6178
- }
6179
- };
6180
-
6181
- // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
6182
- var OIMEffectDependencyKeyedCollection = class {
6183
- constructor(collection, pk) {
6184
- this.dep = new OIMEffectDependencyKeyed(collection, pk);
6185
- }
6186
- get source() {
6187
- return this.dep.source;
6188
- }
6189
- subscribe(onUpdate) {
6190
- return this.dep.subscribe(onUpdate);
6191
- }
6192
- };
6193
-
6194
- // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
6195
- var OIMEffectDependencyKeyedIndex = class {
6196
- constructor(index, key) {
6197
- this.dep = new OIMEffectDependencyKeyed(index, key);
6198
- }
6199
- get source() {
6200
- return this.dep.source;
6201
- }
6202
- subscribe(onUpdate) {
6203
- return this.dep.subscribe(onUpdate);
6204
- }
6205
- };
6206
-
6207
6497
  // src/modules/selector/core/OIMObjectValueByKeySelector.ts
6208
6498
  var OIMObjectValueByKeySelector = class extends OIMSelector {
6209
6499
  constructor(runtime, obj, key) {
@@ -6281,8 +6571,9 @@ var OIMIndexPksByKeyArrayBasedSelector = class extends OIMSelector {
6281
6571
  areEqual(prev, next) {
6282
6572
  if (prev === next) return true;
6283
6573
  if (prev.length !== next.length) return false;
6284
- for (let i = 0; i < prev.length; i++)
6574
+ for (let i = 0; i < prev.length; i++) {
6285
6575
  if (prev[i] !== next[i]) return false;
6576
+ }
6286
6577
  return true;
6287
6578
  }
6288
6579
  };
@@ -6305,8 +6596,9 @@ var OIMIndexPksByKeysSetBasedSelector = class extends OIMSelector {
6305
6596
  areEqual(prev, next) {
6306
6597
  if (prev === next) return true;
6307
6598
  if (prev.length !== next.length) return false;
6308
- for (let i = 0; i < prev.length; i++)
6599
+ for (let i = 0; i < prev.length; i++) {
6309
6600
  if (prev[i] !== next[i]) return false;
6601
+ }
6310
6602
  return true;
6311
6603
  }
6312
6604
  };
@@ -6329,8 +6621,9 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
6329
6621
  areEqual(prev, next) {
6330
6622
  if (prev === next) return true;
6331
6623
  if (prev.length !== next.length) return false;
6332
- for (let i = 0; i < prev.length; i++)
6624
+ for (let i = 0; i < prev.length; i++) {
6333
6625
  if (prev[i] !== next[i]) return false;
6626
+ }
6334
6627
  return true;
6335
6628
  }
6336
6629
  };
@@ -6359,6 +6652,7 @@ export {
6359
6652
  OIMComputeRuntime,
6360
6653
  OIMComputed,
6361
6654
  OIMDBSettings,
6655
+ OIMDerivedCollection,
6362
6656
  OIMDerivedCollectionGlobalIndexArrayBased,
6363
6657
  OIMDerivedCollectionGlobalIndexSetBased,
6364
6658
  OIMDerivedCollectionIndexArrayBased,
@@ -6454,5 +6748,9 @@ export {
6454
6748
  createOIMIndexSlotMap,
6455
6749
  createOIMOrderedListCommandStreamDiffDriven,
6456
6750
  createOIMOrderedListMappedCommandStream,
6457
- diffOrderedListByPk
6751
+ diffOrderedListByPk,
6752
+ getOIMComputeRuntime,
6753
+ on,
6754
+ peekOIMComputeRuntime,
6755
+ setOIMComputeRuntime
6458
6756
  };