@oimdb/core 1.3.1 → 1.4.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
@@ -15,6 +15,11 @@ var OIMEventEmitter = class {
15
15
  constructor() {
16
16
  this.buckets = {};
17
17
  }
18
+ hasHandlers(event) {
19
+ const bucket = this.buckets[event];
20
+ if (!bucket) return false;
21
+ return bucket.indexByHandler.size > 0;
22
+ }
18
23
  getOrCreateBucket(event) {
19
24
  let bucket = this.buckets[event];
20
25
  if (!bucket) {
@@ -28,39 +33,40 @@ var OIMEventEmitter = class {
28
33
  }
29
34
  return bucket;
30
35
  }
36
+ /**
37
+ * Compacts the handlers array by removing null entries (tombstones) left by handler removal.
38
+ * Rebuilds the indexByHandler map with updated indices and resets the tombstone count.
39
+ * This reduces array fragmentation and improves iteration performance.
40
+ */
31
41
  compactBucket(bucket) {
32
42
  const handlers = bucket.handlers;
33
43
  const length = handlers.length;
34
44
  const newHandlers = [];
35
45
  bucket.indexByHandler.clear();
36
- if (bucket.tombstoneCount < length >> 2) {
37
- newHandlers.length = length - bucket.tombstoneCount;
38
- }
39
- let writeIndex = 0;
40
46
  for (let i = 0; i < length; i++) {
41
47
  const handler = handlers[i];
42
48
  if (handler) {
43
- newHandlers[writeIndex] = handler;
49
+ const writeIndex = newHandlers.length;
50
+ newHandlers.push(handler);
44
51
  bucket.indexByHandler.set(handler, writeIndex);
45
- writeIndex++;
46
52
  }
47
53
  }
48
- if (newHandlers.length !== writeIndex) {
49
- newHandlers.length = writeIndex;
50
- }
51
54
  bucket.handlers = newHandlers;
52
55
  bucket.tombstoneCount = 0;
53
56
  }
54
57
  on(event, handler) {
55
58
  const bucket = this.getOrCreateBucket(event);
56
- if (bucket.indexByHandler.has(handler))
57
- return;
59
+ if (bucket.indexByHandler.has(handler)) {
60
+ return () => {
61
+ this.off(event, handler);
62
+ };
63
+ }
58
64
  const index = bucket.handlers.length;
59
65
  bucket.handlers.push(handler);
60
- bucket.indexByHandler.set(
61
- handler,
62
- index
63
- );
66
+ bucket.indexByHandler.set(handler, index);
67
+ return () => {
68
+ this.off(event, handler);
69
+ };
64
70
  }
65
71
  emit(event, payload) {
66
72
  const bucket = this.buckets[event];
@@ -75,10 +81,11 @@ var OIMEventEmitter = class {
75
81
  }
76
82
  bucket.isEmitting--;
77
83
  if (bucket.isEmitting === 0 && bucket.tombstoneCount > 0) {
78
- if (bucket.tombstoneCount >= length >> 1) {
79
- this.compactBucket(bucket);
80
- } else if (length === bucket.tombstoneCount) {
84
+ const currentLength = bucket.handlers.length;
85
+ if (bucket.tombstoneCount === currentLength) {
81
86
  delete this.buckets[event];
87
+ } else if (bucket.tombstoneCount >= currentLength >> 1) {
88
+ this.compactBucket(bucket);
82
89
  }
83
90
  }
84
91
  }
@@ -92,10 +99,10 @@ var OIMEventEmitter = class {
92
99
  bucket.indexByHandler.delete(handler);
93
100
  if (bucket.isEmitting === 0) {
94
101
  const length = bucket.handlers.length;
95
- if (bucket.tombstoneCount >= length >> 1) {
96
- this.compactBucket(bucket);
97
- } else if (length === bucket.tombstoneCount) {
102
+ if (bucket.tombstoneCount === length) {
98
103
  delete this.buckets[event];
104
+ } else if (bucket.tombstoneCount >= length >> 1) {
105
+ this.compactBucket(bucket);
99
106
  }
100
107
  }
101
108
  }
@@ -295,6 +302,12 @@ var OIMIndexSetBased = class {
295
302
  emitUpdate(keys) {
296
303
  this.emitter.emit(0 /* UPDATE */, { keys });
297
304
  }
305
+ /**
306
+ * Emit update event for a single key (fast-path override point).
307
+ */
308
+ emitUpdateOne(key) {
309
+ this.emitUpdate([key]);
310
+ }
298
311
  };
299
312
 
300
313
  // src/core/OIMIndexStoreMapDrivenArrayBased.ts
@@ -444,70 +457,195 @@ var OIMIndexArrayBased = class {
444
457
  emitUpdate(keys) {
445
458
  this.emitter.emit(0 /* UPDATE */, { keys });
446
459
  }
460
+ /**
461
+ * Emit update event for a single key (fast-path override point).
462
+ */
463
+ emitUpdateOne(key) {
464
+ this.emitUpdate([key]);
465
+ }
447
466
  };
448
467
 
449
- // src/enum/EOIMUpdateEventCoalescerEventType.ts
450
- var EOIMUpdateEventCoalescerEventType = /* @__PURE__ */ ((EOIMUpdateEventCoalescerEventType2) => {
451
- EOIMUpdateEventCoalescerEventType2[EOIMUpdateEventCoalescerEventType2["HAS_CHANGES"] = 0] = "HAS_CHANGES";
452
- EOIMUpdateEventCoalescerEventType2[EOIMUpdateEventCoalescerEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
453
- EOIMUpdateEventCoalescerEventType2[EOIMUpdateEventCoalescerEventType2["AFTER_FLUSH"] = 2] = "AFTER_FLUSH";
454
- return EOIMUpdateEventCoalescerEventType2;
455
- })(EOIMUpdateEventCoalescerEventType || {});
468
+ // src/enum/EOIMEventQueueEventType.ts
469
+ var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
470
+ EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
471
+ EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
472
+ return EOIMEventQueueEventType2;
473
+ })(EOIMEventQueueEventType || {});
456
474
 
457
475
  // src/core/OIMUpdateEventEmitter.ts
458
476
  var OIMUpdateEventEmitter = class {
459
- constructor(opts) {
477
+ constructor(queue, deliveryMode = "queue") {
460
478
  this.keyHandlers = /* @__PURE__ */ new Map();
461
- this.handleHasChanges = () => {
462
- this.queue.enqueue(() => {
463
- const updatedKeys = this.coalescer.getUpdatedKeys();
464
- if (this.keyHandlers.size === 0) {
465
- this.coalescer.clearUpdatedKeys();
466
- return;
479
+ this.isFlushEnqueued = false;
480
+ this.updatedKeys = /* @__PURE__ */ new Set();
481
+ this.isAfterFlushDeliveryScheduled = false;
482
+ // Stable handler reference to avoid allocating a new closure on every HAS_CHANGES.
483
+ this.onFlush = () => {
484
+ this.isFlushEnqueued = false;
485
+ this.dequeueFlush = void 0;
486
+ if (this.keyHandlers.size === 0) {
487
+ this.updatedKeys.clear();
488
+ return;
489
+ }
490
+ const makeHandlerSnapshot = (handlers) => {
491
+ const snapshot = [];
492
+ handlers.forEach((h) => snapshot.push(h));
493
+ return snapshot;
494
+ };
495
+ this.runSinglePass(makeHandlerSnapshot);
496
+ };
497
+ this.onAfterFlush = () => {
498
+ if (!this.isAfterFlushDeliveryScheduled) return;
499
+ this.isAfterFlushDeliveryScheduled = false;
500
+ this.deliverImmediate();
501
+ };
502
+ this.queue = queue;
503
+ this.deliveryMode = deliveryMode;
504
+ if (this.deliveryMode === "after_flush") {
505
+ this.unsubscribeAfterFlushDelivery = this.queue.emitter.on(
506
+ 0 /* AFTER_FLUSH */,
507
+ this.onAfterFlush
508
+ );
509
+ }
510
+ }
511
+ /**
512
+ * Mark keys as updated and schedule delivery.
513
+ *
514
+ * If there are no subscriptions, this is a no-op and does not allocate or retain keys.
515
+ */
516
+ markUpdatedKeys(keys) {
517
+ if (keys.length === 0) return;
518
+ if (this.deliveryMode === "queue" && this.queue.isInFlush) {
519
+ throw new Error(
520
+ "OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
521
+ );
522
+ }
523
+ if (this.keyHandlers.size === 0) return;
524
+ let didAddAny = false;
525
+ for (let i = 0; i < keys.length; i++) {
526
+ const key = keys[i];
527
+ const handlers = this.keyHandlers.get(key);
528
+ if (!handlers || handlers.size === 0) continue;
529
+ this.updatedKeys.add(key);
530
+ didAddAny = true;
531
+ }
532
+ if (!didAddAny) return;
533
+ if (this.deliveryMode === "immediate") {
534
+ this.deliverImmediate();
535
+ return;
536
+ }
537
+ if (this.deliveryMode === "after_flush") {
538
+ this.isAfterFlushDeliveryScheduled = true;
539
+ return;
540
+ }
541
+ if (this.isFlushEnqueued) return;
542
+ this.isFlushEnqueued = true;
543
+ this.dequeueFlush = this.queue.enqueue(this.onFlush);
544
+ }
545
+ markUpdatedKey(key) {
546
+ if (this.deliveryMode === "queue" && this.queue.isInFlush) {
547
+ throw new Error(
548
+ "OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
549
+ );
550
+ }
551
+ if (this.keyHandlers.size === 0) return;
552
+ const handlers = this.keyHandlers.get(key);
553
+ if (!handlers || handlers.size === 0) return;
554
+ this.updatedKeys.add(key);
555
+ if (this.deliveryMode === "immediate") {
556
+ this.deliverImmediate();
557
+ return;
558
+ }
559
+ if (this.deliveryMode === "after_flush") {
560
+ this.isAfterFlushDeliveryScheduled = true;
561
+ return;
562
+ }
563
+ if (this.isFlushEnqueued) return;
564
+ this.isFlushEnqueued = true;
565
+ this.dequeueFlush = this.queue.enqueue(this.onFlush);
566
+ }
567
+ markAllUpdated() {
568
+ if (this.deliveryMode === "queue" && this.queue.isInFlush) {
569
+ throw new Error(
570
+ "OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
571
+ );
572
+ }
573
+ if (this.keyHandlers.size === 0) return;
574
+ this.keyHandlers.forEach((_handlers, key) => {
575
+ this.updatedKeys.add(key);
576
+ });
577
+ if (this.deliveryMode === "immediate") {
578
+ this.deliverImmediate();
579
+ return;
580
+ }
581
+ if (this.deliveryMode === "after_flush") {
582
+ this.isAfterFlushDeliveryScheduled = true;
583
+ return;
584
+ }
585
+ if (this.isFlushEnqueued) return;
586
+ this.isFlushEnqueued = true;
587
+ this.dequeueFlush = this.queue.enqueue(this.onFlush);
588
+ }
589
+ deliverImmediate() {
590
+ const makeHandlerSnapshot = (handlers) => {
591
+ const snapshot = [];
592
+ handlers.forEach((h) => snapshot.push(h));
593
+ return snapshot;
594
+ };
595
+ this.runSinglePass(makeHandlerSnapshot);
596
+ }
597
+ /**
598
+ * Deliver updated keys and notify handlers once.
599
+ * Re-entrant updates during delivery are forbidden and will throw in markUpdatedKeys().
600
+ */
601
+ runSinglePass(makeHandlerSnapshot) {
602
+ if (this.updatedKeys.size === 0) return;
603
+ const flushingKeys = this.updatedKeys;
604
+ this.updatedKeys = /* @__PURE__ */ new Set();
605
+ if (this.keyHandlers.size === 0 || flushingKeys.size === 0) return;
606
+ if (flushingKeys.size * 2 < this.keyHandlers.size) {
607
+ flushingKeys.forEach((key) => {
608
+ const handlers = this.keyHandlers.get(key);
609
+ if (!handlers || handlers.size === 0) return;
610
+ const handlerSnapshot = makeHandlerSnapshot(handlers);
611
+ for (let i = 0; i < handlerSnapshot.length; i++) {
612
+ const handler = handlerSnapshot[i];
613
+ if (handlers.has(handler)) handler();
467
614
  }
468
- if (updatedKeys.size * 2 < this.keyHandlers.size) {
469
- for (const key of updatedKeys.values()) {
470
- const handlers = this.keyHandlers.get(key);
471
- if (handlers) {
472
- for (const handler of handlers) {
473
- handler();
474
- }
475
- }
476
- }
477
- } else {
478
- for (const [key, handlers] of this.keyHandlers) {
479
- if (updatedKeys.has(key)) {
480
- for (const handler of handlers) {
481
- handler();
482
- }
483
- }
484
- }
615
+ });
616
+ } else {
617
+ this.keyHandlers.forEach((handlers, key) => {
618
+ if (!flushingKeys.has(key)) return;
619
+ if (!handlers || handlers.size === 0) return;
620
+ const handlerSnapshot = makeHandlerSnapshot(handlers);
621
+ for (let i = 0; i < handlerSnapshot.length; i++) {
622
+ const handler = handlerSnapshot[i];
623
+ if (handlers.has(handler)) handler();
485
624
  }
486
- this.coalescer.clearUpdatedKeys();
487
625
  });
488
- };
489
- this.coalescer = opts.coalescer;
490
- this.coalescer.emitter.on(
491
- 0 /* HAS_CHANGES */,
492
- this.handleHasChanges
493
- );
494
- this.queue = opts.queue;
626
+ }
495
627
  }
496
628
  destroy() {
497
- this.coalescer.emitter.off(
498
- 0 /* HAS_CHANGES */,
499
- this.handleHasChanges
500
- );
629
+ if (this.dequeueFlush) {
630
+ this.dequeueFlush();
631
+ this.dequeueFlush = void 0;
632
+ }
633
+ if (this.unsubscribeAfterFlushDelivery) {
634
+ this.unsubscribeAfterFlushDelivery();
635
+ this.unsubscribeAfterFlushDelivery = void 0;
636
+ }
637
+ this.keyHandlers.forEach((handlers) => handlers.clear());
501
638
  this.keyHandlers.clear();
639
+ this.updatedKeys.clear();
502
640
  }
503
641
  /**
504
642
  * Get performance metrics for monitoring and debugging
505
643
  */
506
644
  getMetrics() {
507
645
  let totalHandlers = 0;
508
- for (const handlers of this.keyHandlers.values()) {
646
+ this.keyHandlers.forEach((handlers) => {
509
647
  totalHandlers += handlers.size;
510
- }
648
+ });
511
649
  return {
512
650
  totalKeys: this.keyHandlers.size,
513
651
  totalHandlers,
@@ -544,6 +682,15 @@ var OIMUpdateEventEmitter = class {
544
682
  handlers.delete(handler);
545
683
  if (handlers.size === 0) {
546
684
  this.keyHandlers.delete(key);
685
+ this.updatedKeys.delete(key);
686
+ }
687
+ if (this.keyHandlers.size === 0) {
688
+ this.updatedKeys.clear();
689
+ if (this.dequeueFlush) {
690
+ this.dequeueFlush();
691
+ this.dequeueFlush = void 0;
692
+ }
693
+ this.isFlushEnqueued = false;
547
694
  }
548
695
  }
549
696
  subscribeOnKeys(keys, handler) {
@@ -572,181 +719,11 @@ var OIMUpdateEventEmitter = class {
572
719
  }
573
720
  };
574
721
 
575
- // src/core/OIMIndexManualSetBased.ts
576
- var OIMIndexManualSetBased = class extends OIMIndexSetBased {
577
- constructor(options = {}) {
578
- super(options);
579
- }
580
- /**
581
- * Set primary keys for a specific index key, replacing any existing values.
582
- * Uses optional comparator to skip updates if PKs haven't actually changed.
583
- */
584
- setPks(key, pks) {
585
- const hasChanges = this.setPksWithComparison(key, new Set(pks));
586
- if (hasChanges) {
587
- this.emitUpdate([key]);
588
- }
589
- }
590
- /**
591
- * Add primary keys to a specific index key
592
- */
593
- addPks(key, pks) {
594
- if (pks.length === 0) return;
595
- let pksSet = this.store.getOneByKey(key);
596
- if (!pksSet) {
597
- pksSet = /* @__PURE__ */ new Set();
598
- this.store.setOneByKey(key, pksSet);
599
- }
600
- let hasChanges = false;
601
- for (const pk of pks) {
602
- if (!pksSet.has(pk)) {
603
- pksSet.add(pk);
604
- hasChanges = true;
605
- }
606
- }
607
- if (hasChanges) {
608
- this.store.setOneByKey(key, pksSet);
609
- this.emitUpdate([key]);
610
- }
611
- }
612
- /**
613
- * Remove primary keys from a specific index key
614
- */
615
- removePks(key, pks) {
616
- if (pks.length === 0) return;
617
- const pksSet = this.store.getOneByKey(key);
618
- if (!pksSet) return;
619
- let hasChanges = false;
620
- for (const pk of pks) {
621
- if (pksSet.delete(pk)) {
622
- hasChanges = true;
623
- }
624
- }
625
- if (pksSet.size === 0) {
626
- this.store.removeOneByKey(key);
627
- hasChanges = true;
628
- } else if (hasChanges) {
629
- this.store.setOneByKey(key, pksSet);
630
- }
631
- if (hasChanges) {
632
- this.emitUpdate([key]);
633
- }
634
- }
635
- /**
636
- * Clear all primary keys for a specific index key, or all keys if no key specified
637
- */
638
- clear(key) {
639
- if (key === void 0) {
640
- const allKeys = this.store.getAllKeys();
641
- if (allKeys.length > 0) {
642
- this.store.clear();
643
- this.emitUpdate(allKeys);
644
- }
645
- } else {
646
- if (this.store.getOneByKey(key) !== void 0) {
647
- this.store.removeOneByKey(key);
648
- this.emitUpdate([key]);
649
- }
650
- }
651
- }
652
- };
653
-
654
- // src/const/OIMDBSettings.ts
655
- var OIMDBSettings = class {
656
- };
657
- /**
658
- * Fixed key used in the manual index to store updated keys set in coalescers.
659
- * This constant is exported so users can create their own indexes and pass them to coalescers.
660
- */
661
- OIMDBSettings.UPDATED_KEYS_INDEX_KEY = "__updatedKeys__";
662
-
663
- // src/core/OIMUpdateEventCoalescer.ts
664
- var OIMUpdateEventCoalescer = class {
665
- constructor(index) {
666
- this.emitter = new OIMEventEmitter();
667
- this.hasEmittedChanges = false;
668
- this.updatedKeysIndex = index ?? new OIMIndexManualSetBased({
669
- store: new OIMIndexStoreMapDrivenSetBased()
670
- });
671
- if (!this.updatedKeysIndex.hasKey(OIMDBSettings.UPDATED_KEYS_INDEX_KEY)) {
672
- this.updatedKeysIndex.setPks(
673
- OIMDBSettings.UPDATED_KEYS_INDEX_KEY,
674
- []
675
- );
676
- }
677
- }
678
- /**
679
- * Get the set of keys that have been updated since last clear
680
- */
681
- getUpdatedKeys() {
682
- return this.updatedKeysIndex.getPksByKey(
683
- OIMDBSettings.UPDATED_KEYS_INDEX_KEY
684
- );
685
- }
686
- /**
687
- * Clear all updated keys and reset the changed state
688
- */
689
- clearUpdatedKeys() {
690
- this.emitter.emit(
691
- 1 /* BEFORE_FLUSH */,
692
- void 0
693
- );
694
- this.updatedKeysIndex.setPks(OIMDBSettings.UPDATED_KEYS_INDEX_KEY, []);
695
- this.hasEmittedChanges = false;
696
- this.emitter.emit(
697
- 2 /* AFTER_FLUSH */,
698
- void 0
699
- );
700
- }
701
- /**
702
- * Add keys to the updated set and emit HAS_CHANGES event if needed
703
- */
704
- addUpdatedKeys(keys) {
705
- this.updatedKeysIndex.addPks(
706
- OIMDBSettings.UPDATED_KEYS_INDEX_KEY,
707
- keys
708
- );
709
- if (!this.hasEmittedChanges) {
710
- this.hasEmittedChanges = true;
711
- this.emitter.emit(
712
- 0 /* HAS_CHANGES */,
713
- void 0
714
- );
715
- }
716
- }
717
- };
718
-
719
- // src/core/OIMUpdateEventCoalescerIndex.ts
720
- var OIMUpdateEventCoalescerIndex = class extends OIMUpdateEventCoalescer {
721
- constructor(indexEmitter) {
722
- super();
723
- this.indexEmitter = indexEmitter;
724
- /**
725
- * When the index is updated, we keep track of the updated keys.
726
- */
727
- this.handleUpdate = (payload) => {
728
- this.addUpdatedKeys(payload.keys);
729
- };
730
- this.indexEmitter.on(0 /* UPDATE */, this.handleUpdate);
731
- }
732
- destroy() {
733
- this.indexEmitter.off(0 /* UPDATE */, this.handleUpdate);
734
- this.emitter.offAll();
735
- this.clearUpdatedKeys();
736
- }
737
- };
738
-
739
722
  // src/abstract/OIMReactiveIndexSetBased.ts
740
723
  var OIMReactiveIndexSetBased = class {
741
- constructor(queue, opts) {
742
- this.index = opts?.index ?? this.createDefaultIndex();
743
- this.coalescer = new OIMUpdateEventCoalescerIndex(
744
- this.index.emitter
745
- );
746
- this.updateEventEmitter = new OIMUpdateEventEmitter({
747
- coalescer: this.coalescer,
748
- queue
749
- });
724
+ constructor(queue, createIndex) {
725
+ this.updateEmitter = new OIMUpdateEventEmitter(queue);
726
+ this.index = createIndex(this.updateEmitter);
750
727
  }
751
728
  getPksByKey(key) {
752
729
  return this.index.getPksByKey(key);
@@ -772,22 +749,38 @@ var OIMReactiveIndexSetBased = class {
772
749
  getMetrics() {
773
750
  return this.index.getMetrics();
774
751
  }
752
+ subscribeOnKey(key, handler) {
753
+ return this.updateEmitter.subscribeOnKey(key, handler);
754
+ }
755
+ subscribeOnKeys(keys, handler) {
756
+ return this.updateEmitter.subscribeOnKeys(keys, handler);
757
+ }
758
+ unsubscribeFromKey(key, handler) {
759
+ this.updateEmitter.unsubscribeFromKey(key, handler);
760
+ }
761
+ unsubscribeFromKeys(keys, handler) {
762
+ this.updateEmitter.unsubscribeFromKeys(keys, handler);
763
+ }
764
+ hasSubscriptions() {
765
+ return this.updateEmitter.hasSubscriptions();
766
+ }
767
+ getHandlerCount(key) {
768
+ return this.updateEmitter.getHandlerCount(key);
769
+ }
770
+ getSubscriptionMetrics() {
771
+ return this.updateEmitter.getMetrics();
772
+ }
775
773
  destroy() {
774
+ this.updateEmitter.destroy();
776
775
  this.index.destroy();
777
776
  }
778
777
  };
779
778
 
780
779
  // src/abstract/OIMReactiveIndexArrayBased.ts
781
780
  var OIMReactiveIndexArrayBased = class {
782
- constructor(queue, opts) {
783
- this.index = opts?.index ?? this.createDefaultIndex();
784
- this.coalescer = new OIMUpdateEventCoalescerIndex(
785
- this.index.emitter
786
- );
787
- this.updateEventEmitter = new OIMUpdateEventEmitter({
788
- coalescer: this.coalescer,
789
- queue
790
- });
781
+ constructor(queue, createIndex) {
782
+ this.updateEmitter = new OIMUpdateEventEmitter(queue);
783
+ this.index = createIndex(this.updateEmitter);
791
784
  }
792
785
  getPksByKey(key) {
793
786
  return this.index.getPksByKey(key);
@@ -813,11 +806,37 @@ var OIMReactiveIndexArrayBased = class {
813
806
  getMetrics() {
814
807
  return this.index.getMetrics();
815
808
  }
809
+ subscribeOnKey(key, handler) {
810
+ return this.updateEmitter.subscribeOnKey(key, handler);
811
+ }
812
+ subscribeOnKeys(keys, handler) {
813
+ return this.updateEmitter.subscribeOnKeys(keys, handler);
814
+ }
815
+ unsubscribeFromKey(key, handler) {
816
+ this.updateEmitter.unsubscribeFromKey(key, handler);
817
+ }
818
+ unsubscribeFromKeys(keys, handler) {
819
+ this.updateEmitter.unsubscribeFromKeys(keys, handler);
820
+ }
821
+ hasSubscriptions() {
822
+ return this.updateEmitter.hasSubscriptions();
823
+ }
824
+ getHandlerCount(key) {
825
+ return this.updateEmitter.getHandlerCount(key);
826
+ }
827
+ getSubscriptionMetrics() {
828
+ return this.updateEmitter.getMetrics();
829
+ }
816
830
  destroy() {
831
+ this.updateEmitter.destroy();
817
832
  this.index.destroy();
818
833
  }
819
834
  };
820
835
 
836
+ // src/abstract/OIMObjectStore.ts
837
+ var OIMObjectStore = class {
838
+ };
839
+
821
840
  // src/core/OIMPkSelectorFactory.ts
822
841
  var OIMPkSelectorFactory = class {
823
842
  createIdSelector() {
@@ -875,6 +894,9 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
875
894
  getAllPks() {
876
895
  return Array.from(this.entities.keys());
877
896
  }
897
+ destroy() {
898
+ this.clear();
899
+ }
878
900
  };
879
901
 
880
902
  // src/core/OIMEntityUpdaterFactory.ts
@@ -972,31 +994,9 @@ var OIMCollection = class {
972
994
  this.store.setOneByPk(pk, entity);
973
995
  }
974
996
  }
975
- };
976
-
977
- // src/core/OIMUpdateEventCoalescerCollection.ts
978
- var OIMUpdateEventCoalescerCollection = class extends OIMUpdateEventCoalescer {
979
- constructor(collectionEmitter) {
980
- super();
981
- this.collectionEmitter = collectionEmitter;
982
- /**
983
- * When the collection is updated, we keep track of the updated pks.
984
- */
985
- this.handleUpdate = (payload) => {
986
- this.addUpdatedKeys(payload.pks);
987
- };
988
- this.collectionEmitter.on(
989
- 0 /* UPDATE */,
990
- this.handleUpdate
991
- );
992
- }
993
997
  destroy() {
994
- this.collectionEmitter.off(
995
- 0 /* UPDATE */,
996
- this.handleUpdate
997
- );
998
+ this.store.destroy();
998
999
  this.emitter.offAll();
999
- this.clearUpdatedKeys();
1000
1000
  }
1001
1001
  };
1002
1002
 
@@ -1004,28 +1004,367 @@ var OIMUpdateEventCoalescerCollection = class extends OIMUpdateEventCoalescer {
1004
1004
  var OIMReactiveCollection = class extends OIMCollection {
1005
1005
  constructor(queue, opts) {
1006
1006
  super(opts);
1007
- this.coalescer = new OIMUpdateEventCoalescerCollection(
1008
- this.emitter
1009
- );
1010
- this.updateEventEmitter = new OIMUpdateEventEmitter({
1011
- coalescer: this.coalescer,
1012
- queue
1013
- });
1007
+ this.anyUpdateHandlers = /* @__PURE__ */ new Set();
1008
+ this.pendingAnyUpdatePks = /* @__PURE__ */ new Set();
1009
+ this.isAnyUpdateClearPending = false;
1010
+ this.isAnyUpdateScheduled = false;
1011
+ this.runAnyUpdate = () => {
1012
+ if (!this.isAnyUpdateScheduled) return;
1013
+ this.isAnyUpdateScheduled = false;
1014
+ this.dequeueAnyUpdate = void 0;
1015
+ const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks);
1016
+ this.pendingAnyUpdatePks.clear();
1017
+ this.isAnyUpdateClearPending = false;
1018
+ const snapshot = Array.from(this.anyUpdateHandlers);
1019
+ for (let i = 0; i < snapshot.length; i++) snapshot[i](pks);
1020
+ };
1021
+ this.queue = queue;
1022
+ this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
1014
1023
  }
1015
- };
1016
-
1017
- // src/core/OIMReactiveIndexManualSetBased.ts
1018
- var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1019
- constructor(queue, opts) {
1020
- super(queue, opts);
1024
+ subscribeOnKey(pk, handler) {
1025
+ return this.updateEmitter.subscribeOnKey(pk, handler);
1021
1026
  }
1022
- createDefaultIndex() {
1023
- return new OIMIndexManualSetBased();
1027
+ subscribeOnKeys(pks, handler) {
1028
+ return this.updateEmitter.subscribeOnKeys(pks, handler);
1024
1029
  }
1025
- setPks(key, pks) {
1026
- this.index.setPks(key, pks);
1030
+ unsubscribeFromKey(pk, handler) {
1031
+ this.updateEmitter.unsubscribeFromKey(pk, handler);
1027
1032
  }
1028
- addPks(key, pks) {
1033
+ unsubscribeFromKeys(pks, handler) {
1034
+ this.updateEmitter.unsubscribeFromKeys(pks, handler);
1035
+ }
1036
+ destroySubscriptions() {
1037
+ this.updateEmitter.destroy();
1038
+ }
1039
+ hasSubscriptions() {
1040
+ return this.updateEmitter.hasSubscriptions();
1041
+ }
1042
+ getHandlerCount(pk) {
1043
+ return this.updateEmitter.getHandlerCount(pk);
1044
+ }
1045
+ getMetrics() {
1046
+ return this.updateEmitter.getMetrics();
1047
+ }
1048
+ subscribeOnAnyUpdate(handler) {
1049
+ this.anyUpdateHandlers.add(handler);
1050
+ return () => {
1051
+ this.anyUpdateHandlers.delete(handler);
1052
+ };
1053
+ }
1054
+ upsertOneByPk(pk, entity) {
1055
+ this.upsertOneWithoutNotificationsByPk(pk, entity);
1056
+ this.updateEmitter.markUpdatedKey(pk);
1057
+ this.trackAnyUpdatePk(pk);
1058
+ }
1059
+ upsertOne(entity) {
1060
+ const pk = this.upsertOneWithoutNotifications(entity);
1061
+ this.updateEmitter.markUpdatedKey(pk);
1062
+ this.trackAnyUpdatePk(pk);
1063
+ }
1064
+ upsertMany(entities) {
1065
+ const pks = entities.map(
1066
+ (entity) => this.upsertOneWithoutNotifications(entity)
1067
+ );
1068
+ this.updateEmitter.markUpdatedKeys(pks);
1069
+ this.trackAnyUpdatePks(pks);
1070
+ }
1071
+ removeOne(entity) {
1072
+ const pk = this.selectPk(entity);
1073
+ this.store.removeOneByPk(pk);
1074
+ this.updateEmitter.markUpdatedKey(pk);
1075
+ this.trackAnyUpdatePk(pk);
1076
+ }
1077
+ removeMany(entities) {
1078
+ const pks = entities.map(this.selectPk);
1079
+ this.store.removeManyByPks(pks);
1080
+ this.updateEmitter.markUpdatedKeys(pks);
1081
+ this.trackAnyUpdatePks(pks);
1082
+ }
1083
+ removeOneByPk(pk) {
1084
+ this.store.removeOneByPk(pk);
1085
+ this.updateEmitter.markUpdatedKey(pk);
1086
+ this.trackAnyUpdatePk(pk);
1087
+ }
1088
+ removeManyByPks(pks) {
1089
+ this.store.removeManyByPks(pks);
1090
+ this.updateEmitter.markUpdatedKeys(pks);
1091
+ this.trackAnyUpdatePks(pks);
1092
+ }
1093
+ clear() {
1094
+ this.store.clear();
1095
+ this.updateEmitter.markAllUpdated();
1096
+ this.trackAnyUpdateClear();
1097
+ }
1098
+ trackAnyUpdatePk(pk) {
1099
+ if (this.anyUpdateHandlers.size === 0) return;
1100
+ this.pendingAnyUpdatePks.add(pk);
1101
+ this.ensureAnyUpdateEnqueued();
1102
+ }
1103
+ trackAnyUpdatePks(pks) {
1104
+ if (this.anyUpdateHandlers.size === 0) return;
1105
+ for (let i = 0; i < pks.length; i++)
1106
+ this.pendingAnyUpdatePks.add(pks[i]);
1107
+ this.ensureAnyUpdateEnqueued();
1108
+ }
1109
+ trackAnyUpdateClear() {
1110
+ if (this.anyUpdateHandlers.size === 0) return;
1111
+ this.isAnyUpdateClearPending = true;
1112
+ this.pendingAnyUpdatePks.clear();
1113
+ this.ensureAnyUpdateEnqueued();
1114
+ }
1115
+ ensureAnyUpdateEnqueued() {
1116
+ if (this.isAnyUpdateScheduled) return;
1117
+ this.isAnyUpdateScheduled = true;
1118
+ this.dequeueAnyUpdate = this.queue.enqueue(this.runAnyUpdate);
1119
+ }
1120
+ destroy() {
1121
+ this.updateEmitter.destroy();
1122
+ if (this.dequeueAnyUpdate) {
1123
+ this.dequeueAnyUpdate();
1124
+ this.dequeueAnyUpdate = void 0;
1125
+ }
1126
+ this.anyUpdateHandlers.clear();
1127
+ this.pendingAnyUpdatePks.clear();
1128
+ super.destroy();
1129
+ }
1130
+ };
1131
+
1132
+ // src/core/OIMObjectStoreRecordDriven.ts
1133
+ var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
1134
+ constructor(initial) {
1135
+ super();
1136
+ this.properties = Object.assign(/* @__PURE__ */ Object.create(null), initial ?? {});
1137
+ }
1138
+ setProperty(key, value) {
1139
+ this.properties[key] = value;
1140
+ }
1141
+ merge(draft) {
1142
+ Object.assign(this.properties, draft);
1143
+ }
1144
+ delete(key) {
1145
+ delete this.properties[key];
1146
+ }
1147
+ get(key) {
1148
+ return this.properties[key];
1149
+ }
1150
+ getAll() {
1151
+ return { ...this.properties };
1152
+ }
1153
+ clear() {
1154
+ this.properties = /* @__PURE__ */ Object.create(null);
1155
+ }
1156
+ count() {
1157
+ return Object.keys(this.properties).length;
1158
+ }
1159
+ keys() {
1160
+ return Object.keys(this.properties);
1161
+ }
1162
+ values() {
1163
+ return Object.values(this.properties);
1164
+ }
1165
+ entries() {
1166
+ return Object.entries(this.properties);
1167
+ }
1168
+ destroy() {
1169
+ this.clear();
1170
+ }
1171
+ };
1172
+
1173
+ // src/enum/EOIMObjectEventType.ts
1174
+ var EOIMObjectEventType = /* @__PURE__ */ ((EOIMObjectEventType2) => {
1175
+ EOIMObjectEventType2[EOIMObjectEventType2["UPDATE"] = 0] = "UPDATE";
1176
+ return EOIMObjectEventType2;
1177
+ })(EOIMObjectEventType || {});
1178
+
1179
+ // src/core/OIMObject.ts
1180
+ var OIMObject = class {
1181
+ constructor(opts) {
1182
+ this.emitter = new OIMEventEmitter();
1183
+ this.store = opts?.store ?? new OIMObjectStoreRecordDriven();
1184
+ }
1185
+ setProperty(key, value) {
1186
+ this.store.setProperty(key, value);
1187
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1188
+ }
1189
+ merge(draft) {
1190
+ this.store.merge(draft);
1191
+ this.emitter.emit(0 /* UPDATE */, {
1192
+ keys: Object.keys(draft)
1193
+ });
1194
+ }
1195
+ delete(key) {
1196
+ this.store.delete(key);
1197
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1198
+ }
1199
+ get(key) {
1200
+ return this.store.get(key);
1201
+ }
1202
+ getAll() {
1203
+ return this.store.getAll();
1204
+ }
1205
+ clear() {
1206
+ this.store.clear();
1207
+ this.emitter.emit(0 /* UPDATE */, { keys: [] });
1208
+ }
1209
+ count() {
1210
+ return this.store.count();
1211
+ }
1212
+ keys() {
1213
+ return this.store.keys();
1214
+ }
1215
+ values() {
1216
+ return this.store.values();
1217
+ }
1218
+ entries() {
1219
+ return this.store.entries();
1220
+ }
1221
+ destroy() {
1222
+ this.store.destroy();
1223
+ this.emitter.offAll();
1224
+ }
1225
+ };
1226
+
1227
+ // src/core/OIMReactiveObject.ts
1228
+ var OIMReactiveObject = class extends OIMObject {
1229
+ constructor(queue, opts) {
1230
+ super(opts);
1231
+ this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
1232
+ this.emitter.on(0 /* UPDATE */, (payload) => {
1233
+ this.updateEmitter.markUpdatedKeys(payload.keys);
1234
+ });
1235
+ }
1236
+ subscribeOnKey(key, handler) {
1237
+ return this.updateEmitter.subscribeOnKey(key, handler);
1238
+ }
1239
+ subscribeOnKeys(keys, handler) {
1240
+ return this.updateEmitter.subscribeOnKeys(keys, handler);
1241
+ }
1242
+ unsubscribeFromKey(key, handler) {
1243
+ this.updateEmitter.unsubscribeFromKey(key, handler);
1244
+ }
1245
+ unsubscribeFromKeys(keys, handler) {
1246
+ this.updateEmitter.unsubscribeFromKeys(keys, handler);
1247
+ }
1248
+ hasSubscriptions() {
1249
+ return this.updateEmitter.hasSubscriptions();
1250
+ }
1251
+ getHandlerCount(key) {
1252
+ return this.updateEmitter.getHandlerCount(key);
1253
+ }
1254
+ getMetrics() {
1255
+ return this.updateEmitter.getMetrics();
1256
+ }
1257
+ destroy() {
1258
+ this.updateEmitter.destroy();
1259
+ super.destroy();
1260
+ }
1261
+ };
1262
+
1263
+ // src/core/OIMIndexManualSetBased.ts
1264
+ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1265
+ constructor(options = {}) {
1266
+ super(options);
1267
+ }
1268
+ /**
1269
+ * Set primary keys for a specific index key, replacing any existing values.
1270
+ * Uses optional comparator to skip updates if PKs haven't actually changed.
1271
+ */
1272
+ setPks(key, pks) {
1273
+ const hasChanges = this.setPksWithComparison(key, new Set(pks));
1274
+ if (hasChanges) {
1275
+ this.emitUpdateOne(key);
1276
+ }
1277
+ }
1278
+ /**
1279
+ * Add primary keys to a specific index key
1280
+ */
1281
+ addPks(key, pks) {
1282
+ if (pks.length === 0) return;
1283
+ let pksSet = this.store.getOneByKey(key);
1284
+ if (!pksSet) {
1285
+ pksSet = /* @__PURE__ */ new Set();
1286
+ this.store.setOneByKey(key, pksSet);
1287
+ }
1288
+ let hasChanges = false;
1289
+ for (const pk of pks) {
1290
+ if (!pksSet.has(pk)) {
1291
+ pksSet.add(pk);
1292
+ hasChanges = true;
1293
+ }
1294
+ }
1295
+ if (hasChanges) {
1296
+ this.store.setOneByKey(key, pksSet);
1297
+ this.emitUpdateOne(key);
1298
+ }
1299
+ }
1300
+ /**
1301
+ * Remove primary keys from a specific index key
1302
+ */
1303
+ removePks(key, pks) {
1304
+ if (pks.length === 0) return;
1305
+ const pksSet = this.store.getOneByKey(key);
1306
+ if (!pksSet) return;
1307
+ let hasChanges = false;
1308
+ for (const pk of pks) {
1309
+ if (pksSet.delete(pk)) {
1310
+ hasChanges = true;
1311
+ }
1312
+ }
1313
+ if (pksSet.size === 0) {
1314
+ this.store.removeOneByKey(key);
1315
+ hasChanges = true;
1316
+ } else if (hasChanges) {
1317
+ this.store.setOneByKey(key, pksSet);
1318
+ }
1319
+ if (hasChanges) {
1320
+ this.emitUpdateOne(key);
1321
+ }
1322
+ }
1323
+ /**
1324
+ * Clear all primary keys for a specific index key, or all keys if no key specified
1325
+ */
1326
+ clear(key) {
1327
+ if (key === void 0) {
1328
+ const allKeys = this.store.getAllKeys();
1329
+ if (allKeys.length > 0) {
1330
+ this.store.clear();
1331
+ this.emitUpdate(allKeys);
1332
+ }
1333
+ } else {
1334
+ if (this.store.getOneByKey(key) !== void 0) {
1335
+ this.store.removeOneByKey(key);
1336
+ this.emitUpdateOne(key);
1337
+ }
1338
+ }
1339
+ }
1340
+ };
1341
+
1342
+ // src/core/OIMReactiveIndexManualSetBased.ts
1343
+ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1344
+ constructor(updateEmitter, opts) {
1345
+ super(opts);
1346
+ this.updateEmitter = updateEmitter;
1347
+ }
1348
+ emitUpdate(keys) {
1349
+ this.updateEmitter.markUpdatedKeys(keys);
1350
+ }
1351
+ emitUpdateOne(key) {
1352
+ this.updateEmitter.markUpdatedKey(key);
1353
+ }
1354
+ };
1355
+ var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1356
+ constructor(queue, opts) {
1357
+ super(queue, (updateEmitter) => {
1358
+ return new OIMIndexManualSetBasedReactive(
1359
+ updateEmitter,
1360
+ opts?.indexOptions
1361
+ );
1362
+ });
1363
+ }
1364
+ setPks(key, pks) {
1365
+ this.index.setPks(key, pks);
1366
+ }
1367
+ addPks(key, pks) {
1029
1368
  this.index.addPks(key, pks);
1030
1369
  }
1031
1370
  removePks(key, pks) {
@@ -1048,7 +1387,7 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1048
1387
  setPks(key, pks) {
1049
1388
  const hasChanges = this.setPksWithComparison(key, pks);
1050
1389
  if (hasChanges) {
1051
- this.emitUpdate([key]);
1390
+ this.emitUpdateOne(key);
1052
1391
  }
1053
1392
  }
1054
1393
  /**
@@ -1071,7 +1410,7 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1071
1410
  }
1072
1411
  if (hasChanges) {
1073
1412
  this.store.setOneByKey(key, Array.from(pksSet));
1074
- this.emitUpdate([key]);
1413
+ this.emitUpdateOne(key);
1075
1414
  }
1076
1415
  }
1077
1416
  /**
@@ -1090,7 +1429,7 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1090
1429
  } else {
1091
1430
  this.store.setOneByKey(key, filtered);
1092
1431
  }
1093
- this.emitUpdate([key]);
1432
+ this.emitUpdateOne(key);
1094
1433
  }
1095
1434
  }
1096
1435
  /**
@@ -1106,19 +1445,33 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1106
1445
  } else {
1107
1446
  if (this.store.getOneByKey(key) !== void 0) {
1108
1447
  this.store.removeOneByKey(key);
1109
- this.emitUpdate([key]);
1448
+ this.emitUpdateOne(key);
1110
1449
  }
1111
1450
  }
1112
1451
  }
1113
1452
  };
1114
1453
 
1115
1454
  // src/core/OIMReactiveIndexManualArrayBased.ts
1455
+ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1456
+ constructor(updateEmitter, opts) {
1457
+ super(opts);
1458
+ this.updateEmitter = updateEmitter;
1459
+ }
1460
+ emitUpdate(keys) {
1461
+ this.updateEmitter.markUpdatedKeys(keys);
1462
+ }
1463
+ emitUpdateOne(key) {
1464
+ this.updateEmitter.markUpdatedKey(key);
1465
+ }
1466
+ };
1116
1467
  var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
1117
1468
  constructor(queue, opts) {
1118
- super(queue, opts);
1119
- }
1120
- createDefaultIndex() {
1121
- return new OIMIndexManualArrayBased();
1469
+ super(queue, (updateEmitter) => {
1470
+ return new OIMIndexManualArrayBasedReactive(
1471
+ updateEmitter,
1472
+ opts?.indexOptions
1473
+ );
1474
+ });
1122
1475
  }
1123
1476
  setPks(key, pks) {
1124
1477
  this.index.setPks(key, pks);
@@ -1142,6 +1495,53 @@ var OIMRICollection = class extends OIMReactiveCollection {
1142
1495
  }
1143
1496
  };
1144
1497
 
1498
+ // src/core/OIMObjectStoreMapDriven.ts
1499
+ var OIMObjectStoreMapDriven = class extends OIMObjectStore {
1500
+ constructor() {
1501
+ super(...arguments);
1502
+ this.properties = /* @__PURE__ */ new Map();
1503
+ }
1504
+ setProperty(key, value) {
1505
+ this.properties.set(key, value);
1506
+ }
1507
+ merge(draft) {
1508
+ for (const key in draft) {
1509
+ if (!Object.prototype.hasOwnProperty.call(draft, key)) continue;
1510
+ this.properties.set(
1511
+ key,
1512
+ draft[key]
1513
+ );
1514
+ }
1515
+ }
1516
+ delete(key) {
1517
+ this.properties.delete(key);
1518
+ }
1519
+ get(key) {
1520
+ return this.properties.get(key);
1521
+ }
1522
+ getAll() {
1523
+ return Object.fromEntries(this.properties.entries());
1524
+ }
1525
+ clear() {
1526
+ this.properties.clear();
1527
+ }
1528
+ count() {
1529
+ return this.properties.size;
1530
+ }
1531
+ keys() {
1532
+ return Array.from(this.properties.keys());
1533
+ }
1534
+ values() {
1535
+ return Array.from(this.properties.values());
1536
+ }
1537
+ entries() {
1538
+ return Array.from(this.properties.entries());
1539
+ }
1540
+ destroy() {
1541
+ this.properties.clear();
1542
+ }
1543
+ };
1544
+
1145
1545
  // src/core/OIMComparatorFactory.ts
1146
1546
  var OIMComparatorFactory = class {
1147
1547
  createShallowComparator() {
@@ -1160,18 +1560,12 @@ var OIMComparatorFactory = class {
1160
1560
  }
1161
1561
  };
1162
1562
 
1163
- // src/enum/EOIMEventQueueEventType.ts
1164
- var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
1165
- EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
1166
- EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
1167
- return EOIMEventQueueEventType2;
1168
- })(EOIMEventQueueEventType || {});
1169
-
1170
1563
  // src/core/OIMEventQueue.ts
1171
1564
  var OIMEventQueue = class {
1172
1565
  constructor(options = {}) {
1173
1566
  this.emitter = new OIMEventEmitter();
1174
- this.queue = [];
1567
+ this.tasks = /* @__PURE__ */ new Set();
1568
+ this.isFlushing = false;
1175
1569
  this.scheduler = options.scheduler;
1176
1570
  if (this.scheduler) {
1177
1571
  this.flushBound = () => this.flush();
@@ -1182,48 +1576,76 @@ var OIMEventQueue = class {
1182
1576
  }
1183
1577
  }
1184
1578
  /**
1185
- * Add a function to the queue. If scheduler is configured and autoSchedule is enabled,
1186
- * automatically schedules a flush operation.
1579
+ * Enqueue a one-shot task to be executed on the next flush.
1580
+ * Returns a dequeue function that can be called to cancel the task before it runs.
1187
1581
  */
1188
1582
  enqueue(fn) {
1189
- this.queue.push(fn);
1190
- if (this.scheduler && this.queue.length === 1) {
1191
- this.scheduler.schedule();
1192
- }
1583
+ let isActive = true;
1584
+ const task = () => {
1585
+ if (!isActive) return;
1586
+ fn();
1587
+ };
1588
+ this.tasks.add(task);
1589
+ this.ensureScheduled();
1590
+ return () => {
1591
+ if (!isActive) return;
1592
+ isActive = false;
1593
+ this.tasks.delete(task);
1594
+ };
1595
+ }
1596
+ get isInFlush() {
1597
+ return this.isFlushing;
1598
+ }
1599
+ /**
1600
+ * Dequeue (cancel) a previously enqueued task by its dequeue function.
1601
+ */
1602
+ dequeue(dequeueFn) {
1603
+ dequeueFn();
1193
1604
  }
1194
1605
  /**
1195
- * Execute all queued functions and clear the queue.
1196
- * This method is safe to call multiple times and handles reentrancy.
1606
+ * Execute all pending tasks.
1607
+ *
1608
+ * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
1609
+ * executed within the same flush.
1197
1610
  */
1198
1611
  flush() {
1199
- if (this.queue.length === 0) return;
1612
+ this.scheduler?.cancel();
1613
+ this.isFlushing = true;
1200
1614
  this.emitter.emit(1 /* BEFORE_FLUSH */, void 0);
1201
- const currentQueue = this.queue.slice();
1202
- this.queue.length = 0;
1203
- for (let i = 0; i < currentQueue.length; i++) {
1204
- currentQueue[i]();
1615
+ const snapshot = Array.from(this.tasks);
1616
+ this.tasks.clear();
1617
+ for (let i = 0; i < snapshot.length; i++) {
1618
+ snapshot[i]();
1205
1619
  }
1620
+ this.isFlushing = false;
1206
1621
  this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
1207
1622
  }
1208
1623
  /**
1209
1624
  * Get the current number of queued functions.
1210
1625
  */
1211
1626
  get length() {
1212
- return this.queue.length;
1627
+ return this.tasks.size;
1213
1628
  }
1214
1629
  /**
1215
1630
  * Check if the queue is empty.
1216
1631
  */
1217
1632
  get isEmpty() {
1218
- return this.queue.length === 0;
1633
+ return this.tasks.size === 0;
1219
1634
  }
1220
1635
  /**
1221
1636
  * Clear the queue without executing functions and cancel any scheduled flush.
1222
1637
  */
1223
1638
  clear() {
1224
- this.queue.length = 0;
1639
+ this.tasks.clear();
1225
1640
  this.scheduler?.cancel();
1226
1641
  }
1642
+ ensureScheduled() {
1643
+ if (!this.scheduler) return;
1644
+ if (this.isFlushing) return;
1645
+ if (this.tasks.size === 1) {
1646
+ this.scheduler.schedule();
1647
+ }
1648
+ }
1227
1649
  /**
1228
1650
  * Clean up scheduler subscription when queue is no longer needed.
1229
1651
  */
@@ -1271,7 +1693,7 @@ var OIMIndexComparatorFactory = class {
1271
1693
  if (existingSet.size !== newSet.size || existingSet.size !== existingPks.length) {
1272
1694
  return false;
1273
1695
  }
1274
- for (const pk of existingSet) {
1696
+ for (const pk of Array.from(existingSet)) {
1275
1697
  if (!newSet.has(pk)) {
1276
1698
  return false;
1277
1699
  }
@@ -1337,9 +1759,390 @@ var OIMMap2Keys = class {
1337
1759
  }
1338
1760
  };
1339
1761
 
1340
- // src/core/event-queue-scheduler/OIMEventQueueSchedulerAnimationFrame.ts
1341
- var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler {
1342
- constructor() {
1762
+ // src/modules/wrapper/collection/OIMCollectionChangedFieldsWrapper.ts
1763
+ var OIMCollectionChangedFieldsWrapper = class {
1764
+ constructor(queue, collection, opts) {
1765
+ this.isInWrite = false;
1766
+ this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
1767
+ this.changedFieldsByPk = /* @__PURE__ */ new Map();
1768
+ this.changedPksByField = /* @__PURE__ */ new Map();
1769
+ this.onAfterFlush = () => {
1770
+ this.clear();
1771
+ };
1772
+ this.onCollectionUpdate = (payload) => {
1773
+ if (this.isInWrite) return;
1774
+ if (payload.pks.length === 0) return;
1775
+ for (let i = 0; i < payload.pks.length; i++) {
1776
+ const pk = payload.pks[i];
1777
+ this.trackExternalMutationByPk(pk);
1778
+ }
1779
+ };
1780
+ this.collection = collection;
1781
+ this.selectPk = opts?.selectPk ?? (() => {
1782
+ throw new Error(
1783
+ `[OIMCollectionChangedFieldsWrapper]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
1784
+ );
1785
+ });
1786
+ this.changedPksEventEmitter = new OIMUpdateEventEmitter(
1787
+ queue,
1788
+ "after_flush"
1789
+ );
1790
+ this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
1791
+ this.unsubscribeAfterFlush = queue.emitter.on(
1792
+ 0 /* AFTER_FLUSH */,
1793
+ this.onAfterFlush
1794
+ );
1795
+ const maybeReactive = collection;
1796
+ this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
1797
+ this.onCollectionUpdate({
1798
+ pks
1799
+ });
1800
+ }) : collection.emitter.on(
1801
+ 0 /* UPDATE */,
1802
+ this.onCollectionUpdate
1803
+ );
1804
+ }
1805
+ upsertOneByPk(pk, patch) {
1806
+ this.withWrite(() => {
1807
+ this.trackPatch(pk, patch);
1808
+ this.collection.upsertOneByPk(pk, patch);
1809
+ });
1810
+ this.updateEntitySnapshot(pk);
1811
+ }
1812
+ upsertOne(entity) {
1813
+ this.withWrite(() => {
1814
+ const pk = this.selectPk(entity);
1815
+ this.trackPatch(pk, entity);
1816
+ this.collection.upsertOne(entity);
1817
+ });
1818
+ this.updateEntitySnapshot(this.selectPk(entity));
1819
+ }
1820
+ upsertMany(entities) {
1821
+ const pks = [];
1822
+ this.withWrite(() => {
1823
+ for (const entity of entities) {
1824
+ const pk = this.selectPk(entity);
1825
+ pks.push(pk);
1826
+ this.trackPatch(pk, entity);
1827
+ }
1828
+ this.collection.upsertMany(entities);
1829
+ });
1830
+ for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
1831
+ }
1832
+ getChangedFieldsByPk(pk) {
1833
+ return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
1834
+ }
1835
+ consumeChangedFieldsByPk(pk) {
1836
+ const existing = this.changedFieldsByPk.get(pk);
1837
+ if (!existing) return /* @__PURE__ */ new Set();
1838
+ return new Set(existing);
1839
+ }
1840
+ getChangedPksByField(field) {
1841
+ return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
1842
+ }
1843
+ consumeChangedPksByField(field) {
1844
+ const existing = this.changedPksByField.get(field);
1845
+ if (!existing) return /* @__PURE__ */ new Set();
1846
+ return new Set(existing);
1847
+ }
1848
+ clear() {
1849
+ this.changedFieldsByPk.clear();
1850
+ this.changedPksByField.clear();
1851
+ }
1852
+ destroy() {
1853
+ this.changedPksEventEmitter.destroy();
1854
+ this.changedFieldsEventEmitter.destroy();
1855
+ this.unsubscribeAfterFlush();
1856
+ this.unsubscribeFromCollectionEmitter();
1857
+ this.clear();
1858
+ }
1859
+ withWrite(fn) {
1860
+ if (this.isInWrite) {
1861
+ fn();
1862
+ return;
1863
+ }
1864
+ this.isInWrite = true;
1865
+ try {
1866
+ fn();
1867
+ } finally {
1868
+ this.isInWrite = false;
1869
+ }
1870
+ }
1871
+ trackExternalMutationByPk(pk) {
1872
+ const prev = this.lastEntitySnapshotByPk.get(pk);
1873
+ const next = this.collection.getOneByPk(pk);
1874
+ const changedKeys = this.diffChangedKeys(prev, next);
1875
+ this.updateEntitySnapshot(pk);
1876
+ if (changedKeys.length > 0) {
1877
+ this.trackChangedKeys(pk, changedKeys);
1878
+ } else {
1879
+ this.changedPksEventEmitter.markUpdatedKeys([pk]);
1880
+ }
1881
+ }
1882
+ diffChangedKeys(prev, next) {
1883
+ if (!prev && !next) return [];
1884
+ if (!prev && next) return Object.keys(next);
1885
+ if (prev && !next) return Object.keys(prev);
1886
+ const prevObj = prev;
1887
+ const nextObj = next;
1888
+ const keys = /* @__PURE__ */ new Set();
1889
+ Object.keys(prevObj).forEach((k) => keys.add(k));
1890
+ Object.keys(nextObj).forEach((k) => keys.add(k));
1891
+ const changed = [];
1892
+ keys.forEach((k) => {
1893
+ if (prevObj[k] !== nextObj[k])
1894
+ changed.push(k);
1895
+ });
1896
+ return changed;
1897
+ }
1898
+ updateEntitySnapshot(pk) {
1899
+ const entity = this.collection.getOneByPk(pk);
1900
+ if (!entity) {
1901
+ this.lastEntitySnapshotByPk.delete(pk);
1902
+ return;
1903
+ }
1904
+ this.lastEntitySnapshotByPk.set(pk, {
1905
+ ...entity
1906
+ });
1907
+ }
1908
+ trackChangedKeys(pk, keys) {
1909
+ let pkFields = this.changedFieldsByPk.get(pk);
1910
+ if (!pkFields) {
1911
+ pkFields = /* @__PURE__ */ new Set();
1912
+ this.changedFieldsByPk.set(pk, pkFields);
1913
+ }
1914
+ for (const key of keys) {
1915
+ pkFields.add(key);
1916
+ let fieldPks = this.changedPksByField.get(key);
1917
+ if (!fieldPks) {
1918
+ fieldPks = /* @__PURE__ */ new Set();
1919
+ this.changedPksByField.set(key, fieldPks);
1920
+ }
1921
+ fieldPks.add(pk);
1922
+ }
1923
+ this.changedPksEventEmitter.markUpdatedKeys([pk]);
1924
+ this.changedFieldsEventEmitter.markUpdatedKeys(keys);
1925
+ }
1926
+ trackPatch(pk, patch) {
1927
+ if (pk === void 0 || pk === null || pk === "") {
1928
+ throw new Error(
1929
+ `[OIMCollectionChangedFieldsWrapper]: PK is required to track changes.`
1930
+ );
1931
+ }
1932
+ const keys = Object.keys(patch);
1933
+ if (keys.length === 0) return;
1934
+ this.trackChangedKeys(pk, keys);
1935
+ }
1936
+ };
1937
+
1938
+ // src/modules/wrapper/index/OIMIndexManualOrderedArrayBased.ts
1939
+ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
1940
+ clear(key) {
1941
+ if (key === void 0) {
1942
+ const keys = this.getKeys();
1943
+ if (keys.length > 0) {
1944
+ this.store.clear();
1945
+ this.emitUpdate(Array.from(keys));
1946
+ }
1947
+ return;
1948
+ }
1949
+ if (this.store.getOneByKey(key) !== void 0) {
1950
+ this.store.removeOneByKey(key);
1951
+ this.emitUpdate([key]);
1952
+ }
1953
+ }
1954
+ push(key, itemKey) {
1955
+ const list = this.getOrCreateList(key);
1956
+ const index = list.length;
1957
+ list.push(itemKey);
1958
+ this.emitUpdate([key]);
1959
+ return index;
1960
+ }
1961
+ insertAt(key, index, itemKey) {
1962
+ const list = this.getOrCreateList(key);
1963
+ const safeIndex = Math.max(0, Math.min(index, list.length));
1964
+ list.splice(safeIndex, 0, itemKey);
1965
+ this.emitUpdate([key]);
1966
+ }
1967
+ removeAt(key, index) {
1968
+ const list = this.store.getOneByKey(key);
1969
+ if (!list) return void 0;
1970
+ if (index < 0 || index >= list.length) return void 0;
1971
+ const [removed] = list.splice(index, 1);
1972
+ if (list.length === 0) {
1973
+ this.store.removeOneByKey(key);
1974
+ }
1975
+ this.emitUpdate([key]);
1976
+ return removed;
1977
+ }
1978
+ move(key, fromIndex, toIndex) {
1979
+ const list = this.store.getOneByKey(key);
1980
+ if (!list) return void 0;
1981
+ if (fromIndex < 0 || fromIndex >= list.length) return void 0;
1982
+ if (toIndex < 0 || toIndex >= list.length) return void 0;
1983
+ if (fromIndex === toIndex) return list[fromIndex];
1984
+ const [itemKey] = list.splice(fromIndex, 1);
1985
+ if (itemKey === void 0) return void 0;
1986
+ list.splice(toIndex, 0, itemKey);
1987
+ this.emitUpdate([key]);
1988
+ return itemKey;
1989
+ }
1990
+ reset(key, itemKeys) {
1991
+ if (itemKeys.length === 0) {
1992
+ if (this.store.getOneByKey(key) !== void 0) {
1993
+ this.store.removeOneByKey(key);
1994
+ this.emitUpdate([key]);
1995
+ }
1996
+ return;
1997
+ }
1998
+ this.store.setOneByKey(key, Array.from(itemKeys));
1999
+ this.emitUpdate([key]);
2000
+ }
2001
+ getOrCreateList(key) {
2002
+ let list = this.store.getOneByKey(key);
2003
+ if (!list) {
2004
+ list = [];
2005
+ this.store.setOneByKey(key, list);
2006
+ }
2007
+ return list;
2008
+ }
2009
+ };
2010
+
2011
+ // src/modules/wrapper/index/OIMOrderedListIndexCommandStreamWrapper.ts
2012
+ var OIMOrderedListIndexCommandStreamWrapper = class {
2013
+ constructor(queue, index) {
2014
+ this.isInWrite = false;
2015
+ this.commandsByKey = /* @__PURE__ */ new Map();
2016
+ this.onAfterFlush = () => {
2017
+ this.commandsByKey.clear();
2018
+ };
2019
+ this.onIndexUpdate = (payload) => {
2020
+ if (this.isInWrite) return;
2021
+ if (payload.keys.length === 0) return;
2022
+ for (let i = 0; i < payload.keys.length; i++) {
2023
+ const key = payload.keys[i];
2024
+ const list = this.index.getPksByKey(key);
2025
+ this.appendCommand(key, { type: "set", keys: Array.from(list) });
2026
+ }
2027
+ };
2028
+ this.commandsEventEmitter = new OIMUpdateEventEmitter(
2029
+ queue,
2030
+ "after_flush"
2031
+ );
2032
+ this.index = index ?? new OIMIndexManualOrderedArrayBased();
2033
+ this.unsubscribeAfterFlush = queue.emitter.on(
2034
+ 0 /* AFTER_FLUSH */,
2035
+ this.onAfterFlush
2036
+ );
2037
+ this.unsubscribeFromIndexEmitter = this.index.emitter.on(
2038
+ 0 /* UPDATE */,
2039
+ this.onIndexUpdate
2040
+ );
2041
+ }
2042
+ getPksByKey(key) {
2043
+ return this.index.getPksByKey(key);
2044
+ }
2045
+ hasKey(key) {
2046
+ return this.index.hasKey(key);
2047
+ }
2048
+ getKeys() {
2049
+ return this.index.getKeys();
2050
+ }
2051
+ push(key, itemKey) {
2052
+ this.withWrite(() => {
2053
+ const index = this.index.push(key, itemKey);
2054
+ this.appendCommand(key, { type: "add", key: itemKey, index });
2055
+ });
2056
+ }
2057
+ removeAt(key, index) {
2058
+ this.withWrite(() => {
2059
+ const itemKey = this.index.removeAt(key, index);
2060
+ if (itemKey === void 0) return;
2061
+ this.appendCommand(key, { type: "remove", key: itemKey, index });
2062
+ });
2063
+ }
2064
+ move(key, fromIndex, toIndex) {
2065
+ this.withWrite(() => {
2066
+ const itemKey = this.index.move(key, fromIndex, toIndex);
2067
+ if (itemKey === void 0) return;
2068
+ this.appendCommand(key, {
2069
+ type: "move",
2070
+ key: itemKey,
2071
+ fromIndex,
2072
+ toIndex
2073
+ });
2074
+ });
2075
+ }
2076
+ set(key, keys) {
2077
+ this.withWrite(() => {
2078
+ this.index.reset(key, keys);
2079
+ this.appendCommand(key, { type: "set", keys });
2080
+ });
2081
+ }
2082
+ /**
2083
+ * Read-only peek at buffered commands without clearing them.
2084
+ */
2085
+ getBufferedCommands(key) {
2086
+ return this.commandsByKey.get(key) ?? [];
2087
+ }
2088
+ /**
2089
+ * Drain buffered commands for a key, preserving order.
2090
+ * Intended usage: call inside a `commandsEventEmitter.subscribeOnKey(key, ...)` handler.
2091
+ */
2092
+ consumeCommands(key) {
2093
+ const cmds = this.commandsByKey.get(key);
2094
+ if (!cmds || cmds.length === 0) return [];
2095
+ return cmds.slice();
2096
+ }
2097
+ clear(key) {
2098
+ if (key === void 0) {
2099
+ this.index.destroy();
2100
+ this.commandsByKey.clear();
2101
+ return;
2102
+ }
2103
+ this.index.clear(key);
2104
+ this.commandsByKey.delete(key);
2105
+ }
2106
+ destroy() {
2107
+ this.commandsEventEmitter.destroy();
2108
+ this.unsubscribeAfterFlush();
2109
+ this.unsubscribeFromIndexEmitter();
2110
+ this.clear();
2111
+ }
2112
+ withWrite(fn) {
2113
+ if (this.isInWrite) {
2114
+ fn();
2115
+ return;
2116
+ }
2117
+ this.isInWrite = true;
2118
+ try {
2119
+ fn();
2120
+ } finally {
2121
+ this.isInWrite = false;
2122
+ }
2123
+ }
2124
+ appendCommand(key, cmd) {
2125
+ let cmds = this.commandsByKey.get(key);
2126
+ if (!cmds) {
2127
+ cmds = [];
2128
+ this.commandsByKey.set(key, cmds);
2129
+ }
2130
+ if (cmd.type === "set") {
2131
+ cmds.length = 0;
2132
+ cmds.push(cmd);
2133
+ } else if (cmds.length > 0 && cmds[0].type === "set") {
2134
+ const setCmd = cmds[0];
2135
+ setCmd.keys = Array.from(this.index.getPksByKey(key));
2136
+ } else {
2137
+ cmds.push(cmd);
2138
+ }
2139
+ this.commandsEventEmitter.markUpdatedKeys([key]);
2140
+ }
2141
+ };
2142
+
2143
+ // src/core/event-queue-scheduler/OIMEventQueueSchedulerAnimationFrame.ts
2144
+ var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler {
2145
+ constructor() {
1343
2146
  super();
1344
2147
  this.useRequestAnimationFrame = typeof requestAnimationFrame !== "undefined";
1345
2148
  }
@@ -1505,17 +2308,842 @@ var OIMEventQueueSchedulerFactory = class {
1505
2308
  return new OIMEventQueueSchedulerImmediate();
1506
2309
  }
1507
2310
  };
2311
+
2312
+ // src/const/OIMDBSettings.ts
2313
+ var OIMDBSettings = class {
2314
+ };
2315
+ /**
2316
+ * Fixed key used in the manual index to store updated keys set in coalescers.
2317
+ * This constant is exported so users can create their own indexes and pass them to coalescers.
2318
+ */
2319
+ OIMDBSettings.UPDATED_KEYS_INDEX_KEY = "__updatedKeys__";
2320
+ OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
2321
+
2322
+ // src/modules/effect/core/OIMEffectDependencyComputed.ts
2323
+ var OIMEffectDependencyComputed = class {
2324
+ constructor(computed) {
2325
+ this.computed = computed;
2326
+ }
2327
+ subscribe(onUpdate) {
2328
+ return this.computed.emitter.on("update", onUpdate);
2329
+ }
2330
+ };
2331
+
2332
+ // src/modules/effect/core/OIMEffect.ts
2333
+ var OIMEffect = class {
2334
+ constructor(runtime, opts) {
2335
+ this.sourceDeps = [];
2336
+ this.computedDeps = [];
2337
+ this.sourceUnsubscribers = [];
2338
+ this.computedUnsubscribers = [];
2339
+ this.isResubscribeEnqueued = false;
2340
+ this.isTemporarilyUnsubscribedForFlush = false;
2341
+ this.isScheduled = false;
2342
+ this.runTask = () => {
2343
+ this.isScheduled = false;
2344
+ this.dequeueScheduled = void 0;
2345
+ this.runOnce();
2346
+ };
2347
+ this.onSourceInvalidate = () => {
2348
+ this.onUpdate?.();
2349
+ if (!this.isTemporarilyUnsubscribedForFlush) {
2350
+ this.isTemporarilyUnsubscribedForFlush = true;
2351
+ this.unsubscribeFromSourceDeps();
2352
+ this.ensureResubscribeAfterFlush();
2353
+ }
2354
+ if (this.isScheduled) return;
2355
+ this.isScheduled = true;
2356
+ this.dequeueScheduled = this.runtime.schedule(this.runTask);
2357
+ };
2358
+ this.onComputedInvalidate = () => {
2359
+ this.onUpdate?.();
2360
+ if (this.isScheduled) return;
2361
+ this.isScheduled = true;
2362
+ this.dequeueScheduled = this.runtime.schedule(this.runTask);
2363
+ };
2364
+ this.onAfterFlush = () => {
2365
+ if (!this.isResubscribeEnqueued) return;
2366
+ this.isResubscribeEnqueued = false;
2367
+ this.dequeueResubscribe = void 0;
2368
+ this.isTemporarilyUnsubscribedForFlush = false;
2369
+ if (this.sourceUnsubscribers.length === 0) {
2370
+ for (const dep of this.sourceDeps) {
2371
+ this.sourceUnsubscribers.push(
2372
+ dep.subscribe(this.onSourceInvalidate)
2373
+ );
2374
+ }
2375
+ }
2376
+ };
2377
+ this.runtime = runtime;
2378
+ this.onUpdate = opts.onUpdate;
2379
+ this.run = opts.run;
2380
+ this.deps = opts.deps ?? [];
2381
+ for (const dep of this.deps) {
2382
+ if (dep instanceof OIMEffectDependencyComputed) {
2383
+ this.computedDeps.push(dep);
2384
+ } else {
2385
+ this.sourceDeps.push(dep);
2386
+ }
2387
+ }
2388
+ this.subscribeToDeps();
2389
+ }
2390
+ destroy() {
2391
+ if (this.dequeueScheduled) {
2392
+ this.dequeueScheduled();
2393
+ this.dequeueScheduled = void 0;
2394
+ }
2395
+ this.unsubscribeFromAllDeps();
2396
+ if (this.dequeueResubscribe) {
2397
+ this.dequeueResubscribe();
2398
+ this.dequeueResubscribe = void 0;
2399
+ }
2400
+ this.isResubscribeEnqueued = false;
2401
+ this.isTemporarilyUnsubscribedForFlush = false;
2402
+ }
2403
+ runOnce() {
2404
+ this.run();
2405
+ }
2406
+ subscribeToDeps() {
2407
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2408
+ return;
2409
+ for (const dep of this.sourceDeps) {
2410
+ this.sourceUnsubscribers.push(
2411
+ dep.subscribe(this.onSourceInvalidate)
2412
+ );
2413
+ }
2414
+ for (const dep of this.computedDeps) {
2415
+ this.computedUnsubscribers.push(
2416
+ dep.subscribe(this.onComputedInvalidate)
2417
+ );
2418
+ }
2419
+ }
2420
+ unsubscribeFromSourceDeps() {
2421
+ if (this.sourceUnsubscribers.length === 0) return;
2422
+ for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
2423
+ this.sourceUnsubscribers = [];
2424
+ }
2425
+ unsubscribeFromAllDeps() {
2426
+ this.unsubscribeFromSourceDeps();
2427
+ if (this.computedUnsubscribers.length === 0) return;
2428
+ for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
2429
+ this.computedUnsubscribers = [];
2430
+ }
2431
+ ensureResubscribeAfterFlush() {
2432
+ if (this.isResubscribeEnqueued) return;
2433
+ this.isResubscribeEnqueued = true;
2434
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2435
+ this.onAfterFlush
2436
+ );
2437
+ }
2438
+ };
2439
+
2440
+ // src/modules/computed/enum/EOIMComputedEventType.ts
2441
+ var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
2442
+ EOIMComputedEventType2["UPDATE"] = "update";
2443
+ return EOIMComputedEventType2;
2444
+ })(EOIMComputedEventType || {});
2445
+
2446
+ // src/modules/computed/core/OIMComputed.ts
2447
+ var OIMComputed = class {
2448
+ constructor(runtime, opts) {
2449
+ this.emitter = new OIMEventEmitter();
2450
+ this.hasValue = false;
2451
+ this.isDirty = true;
2452
+ this.runtime = runtime;
2453
+ this.compute = opts.compute;
2454
+ this.compare = opts.compare ?? Object.is;
2455
+ this.deps = opts.deps ?? [];
2456
+ this.updateEventEmitter = new OIMUpdateEventEmitter(
2457
+ this.runtime.queue,
2458
+ "immediate"
2459
+ );
2460
+ this.effect = new OIMEffect(this.runtime, {
2461
+ deps: this.deps,
2462
+ onUpdate: () => {
2463
+ this.isDirty = true;
2464
+ },
2465
+ run: () => {
2466
+ this.recomputeAndEmitIfChanged();
2467
+ }
2468
+ });
2469
+ }
2470
+ get() {
2471
+ this.recomputeAndEmitIfChanged();
2472
+ return this.value;
2473
+ }
2474
+ getIfReady() {
2475
+ return this.hasValue ? this.value : void 0;
2476
+ }
2477
+ destroy() {
2478
+ this.effect.destroy();
2479
+ this.updateEventEmitter.destroy();
2480
+ this.emitter.offAll();
2481
+ }
2482
+ recomputeAndEmitIfChanged() {
2483
+ if (!this.isDirty) return;
2484
+ const prev = this.value;
2485
+ const hadPrev = this.hasValue;
2486
+ const next = this.compute();
2487
+ this.value = next;
2488
+ this.hasValue = true;
2489
+ this.isDirty = false;
2490
+ const changed = !hadPrev || !this.compare(prev, next);
2491
+ if (!changed) return;
2492
+ this.emitter.emit("update" /* UPDATE */, {
2493
+ keys: ["value"]
2494
+ });
2495
+ this.updateEventEmitter.markUpdatedKey("value");
2496
+ }
2497
+ };
2498
+
2499
+ // src/modules/effect/core/OIMEffectDependencyKeyed.ts
2500
+ function isReadonlyArray(value) {
2501
+ return Array.isArray(value);
2502
+ }
2503
+ var OIMEffectDependencyKeyed = class {
2504
+ constructor(subscription, keyOrKeys) {
2505
+ this.subscription = subscription;
2506
+ if (isReadonlyArray(keyOrKeys)) {
2507
+ this.keys = keyOrKeys;
2508
+ } else {
2509
+ this.key = keyOrKeys;
2510
+ }
2511
+ }
2512
+ subscribe(onUpdate) {
2513
+ if (this.keys !== void 0) {
2514
+ return this.subscription.subscribeOnKeys(this.keys, onUpdate);
2515
+ }
2516
+ if (this.key === void 0) return () => {
2517
+ };
2518
+ return this.subscription.subscribeOnKey(this.key, onUpdate);
2519
+ }
2520
+ };
2521
+
2522
+ // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
2523
+ var OIMEffectDependencyKeyedObject = class {
2524
+ constructor(object, keyOrKeys) {
2525
+ this.dep = new OIMEffectDependencyKeyed(
2526
+ object,
2527
+ keyOrKeys
2528
+ );
2529
+ }
2530
+ subscribe(onUpdate) {
2531
+ return this.dep.subscribe(onUpdate);
2532
+ }
2533
+ };
2534
+
2535
+ // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
2536
+ var OIMEffectDependencyKeyedCollection = class {
2537
+ constructor(collection, pkOrPks) {
2538
+ this.dep = new OIMEffectDependencyKeyed(
2539
+ collection,
2540
+ pkOrPks
2541
+ );
2542
+ }
2543
+ subscribe(onUpdate) {
2544
+ return this.dep.subscribe(onUpdate);
2545
+ }
2546
+ };
2547
+
2548
+ // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
2549
+ var OIMEffectDependencyKeyedIndex = class {
2550
+ constructor(index, keyOrKeys) {
2551
+ this.dep = new OIMEffectDependencyKeyed(
2552
+ index,
2553
+ keyOrKeys
2554
+ );
2555
+ }
2556
+ subscribe(onUpdate) {
2557
+ return this.dep.subscribe(onUpdate);
2558
+ }
2559
+ };
2560
+
2561
+ // src/modules/selector/core/OIMSelector.ts
2562
+ var OIMSelector = class {
2563
+ constructor(runtime, sourceDeps, computedDeps = []) {
2564
+ this.runtime = runtime;
2565
+ this.sourceDeps = sourceDeps;
2566
+ this.computedDeps = computedDeps;
2567
+ this.listeners = /* @__PURE__ */ new Set();
2568
+ this.sourceUnsubscribers = [];
2569
+ this.computedUnsubscribers = [];
2570
+ this.isResubscribeEnqueued = false;
2571
+ this.isTemporarilyUnsubscribedForFlush = false;
2572
+ this.isDeliveryEnqueued = false;
2573
+ this.hasLastValue = false;
2574
+ this.onSourceInvalidate = () => {
2575
+ if (this.listeners.size === 0) return;
2576
+ if (!this.isTemporarilyUnsubscribedForFlush) {
2577
+ this.isTemporarilyUnsubscribedForFlush = true;
2578
+ this.unsubscribeFromSourceDeps();
2579
+ this.ensureResubscribeAfterFlush();
2580
+ }
2581
+ if (this.isDeliveryEnqueued) return;
2582
+ this.isDeliveryEnqueued = true;
2583
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2584
+ };
2585
+ this.onComputedInvalidate = () => {
2586
+ if (this.listeners.size === 0) return;
2587
+ if (this.isDeliveryEnqueued) return;
2588
+ this.isDeliveryEnqueued = true;
2589
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2590
+ };
2591
+ this.deliver = () => {
2592
+ this.isDeliveryEnqueued = false;
2593
+ this.dequeueDelivery = void 0;
2594
+ if (this.listeners.size === 0) return;
2595
+ const next = this.getValue();
2596
+ if (this.hasLastValue && this.areEqual(this.lastValue, next)) return;
2597
+ this.setLastValue(next);
2598
+ const snapshot = Array.from(this.listeners);
2599
+ for (let i = 0; i < snapshot.length; i++) {
2600
+ const listener = snapshot[i];
2601
+ if (this.listeners.has(listener)) listener(next);
2602
+ }
2603
+ };
2604
+ this.onAfterFlush = () => {
2605
+ if (!this.isResubscribeEnqueued) return;
2606
+ this.isResubscribeEnqueued = false;
2607
+ this.dequeueResubscribe = void 0;
2608
+ if (this.listeners.size === 0) return;
2609
+ this.isTemporarilyUnsubscribedForFlush = false;
2610
+ if (this.sourceUnsubscribers.length === 0) {
2611
+ for (const dep of this.sourceDeps) {
2612
+ this.sourceUnsubscribers.push(
2613
+ dep.subscribe(this.onSourceInvalidate)
2614
+ );
2615
+ }
2616
+ }
2617
+ };
2618
+ }
2619
+ get queue() {
2620
+ return this.runtime.queue;
2621
+ }
2622
+ /**
2623
+ * Start watching this selector. The callback is called immediately with the current value,
2624
+ * then coalesced and delivered on `queue.flush()` when the selector invalidates.
2625
+ */
2626
+ watch(onValue) {
2627
+ this.listeners.add(onValue);
2628
+ if (this.listeners.size === 1) {
2629
+ this.subscribeToDeps();
2630
+ }
2631
+ const current = this.getValue();
2632
+ this.setLastValue(current);
2633
+ onValue(current);
2634
+ return () => {
2635
+ this.listeners.delete(onValue);
2636
+ if (this.listeners.size > 0) return;
2637
+ this.unsubscribeFromAllDeps();
2638
+ if (this.dequeueResubscribe) {
2639
+ this.dequeueResubscribe();
2640
+ this.dequeueResubscribe = void 0;
2641
+ }
2642
+ this.isResubscribeEnqueued = false;
2643
+ this.isTemporarilyUnsubscribedForFlush = false;
2644
+ if (this.dequeueDelivery) {
2645
+ this.dequeueDelivery();
2646
+ this.dequeueDelivery = void 0;
2647
+ }
2648
+ this.isDeliveryEnqueued = false;
2649
+ this.hasLastValue = false;
2650
+ };
2651
+ }
2652
+ areEqual(prev, next) {
2653
+ return Object.is(prev, next);
2654
+ }
2655
+ setLastValue(value) {
2656
+ this.lastValue = value;
2657
+ this.hasLastValue = true;
2658
+ }
2659
+ subscribeToDeps() {
2660
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2661
+ return;
2662
+ for (const dep of this.sourceDeps) {
2663
+ this.sourceUnsubscribers.push(
2664
+ dep.subscribe(this.onSourceInvalidate)
2665
+ );
2666
+ }
2667
+ for (const dep of this.computedDeps) {
2668
+ this.computedUnsubscribers.push(
2669
+ dep.subscribe(this.onComputedInvalidate)
2670
+ );
2671
+ }
2672
+ }
2673
+ unsubscribeFromSourceDeps() {
2674
+ if (this.sourceUnsubscribers.length === 0) return;
2675
+ for (const unsub of this.sourceUnsubscribers) unsub();
2676
+ this.sourceUnsubscribers = [];
2677
+ }
2678
+ unsubscribeFromAllDeps() {
2679
+ this.unsubscribeFromSourceDeps();
2680
+ if (this.computedUnsubscribers.length === 0) return;
2681
+ for (const unsub of this.computedUnsubscribers) unsub();
2682
+ this.computedUnsubscribers = [];
2683
+ }
2684
+ ensureResubscribeAfterFlush() {
2685
+ if (this.isResubscribeEnqueued) return;
2686
+ this.isResubscribeEnqueued = true;
2687
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2688
+ this.onAfterFlush
2689
+ );
2690
+ }
2691
+ };
2692
+
2693
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKey.ts
2694
+ var OIMSelectorSourceDependencyUpdateEventEmitterKey = class {
2695
+ constructor(subscription, key) {
2696
+ this.subscription = subscription;
2697
+ this.key = key;
2698
+ }
2699
+ subscribe(onUpdate) {
2700
+ return this.subscription.subscribeOnKey(this.key, onUpdate);
2701
+ }
2702
+ };
2703
+
2704
+ // src/modules/selector/core/OIMCollectionByPkSelector.ts
2705
+ var OIMCollectionByPkSelector = class extends OIMSelector {
2706
+ constructor(runtime, collection, pk) {
2707
+ super(runtime, [
2708
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2709
+ collection,
2710
+ pk
2711
+ )
2712
+ ]);
2713
+ this.collection = collection;
2714
+ this.pk = pk;
2715
+ }
2716
+ getValue() {
2717
+ return this.collection.getOneByPk(this.pk);
2718
+ }
2719
+ };
2720
+
2721
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKeys.ts
2722
+ var OIMSelectorSourceDependencyUpdateEventEmitterKeys = class {
2723
+ constructor(subscription, keys) {
2724
+ this.subscription = subscription;
2725
+ this.keys = keys;
2726
+ }
2727
+ subscribe(onUpdate) {
2728
+ return this.subscription.subscribeOnKeys(this.keys, onUpdate);
2729
+ }
2730
+ };
2731
+
2732
+ // src/modules/selector/core/OIMCollectionByPksSelector.ts
2733
+ var OIMCollectionByPksSelector = class extends OIMSelector {
2734
+ constructor(runtime, collection, pks) {
2735
+ super(runtime, [
2736
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2737
+ collection,
2738
+ pks
2739
+ )
2740
+ ]);
2741
+ this.collection = collection;
2742
+ this.pks = pks;
2743
+ }
2744
+ getValue() {
2745
+ return this.pks.map((pk) => this.collection.getOneByPk(pk));
2746
+ }
2747
+ areEqual(prev, next) {
2748
+ if (prev === next) return true;
2749
+ if (prev.length !== next.length) return false;
2750
+ for (let i = 0; i < prev.length; i++) {
2751
+ if (prev[i] !== next[i]) return false;
2752
+ }
2753
+ return true;
2754
+ }
2755
+ };
2756
+
2757
+ // src/modules/selector/core/OIMObjectValueByKeySelector.ts
2758
+ var OIMObjectValueByKeySelector = class extends OIMSelector {
2759
+ constructor(runtime, obj, key) {
2760
+ super(runtime, [
2761
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2762
+ obj,
2763
+ key
2764
+ )
2765
+ ]);
2766
+ this.obj = obj;
2767
+ this.key = key;
2768
+ }
2769
+ getValue() {
2770
+ return this.obj.get(this.key);
2771
+ }
2772
+ };
2773
+
2774
+ // src/modules/selector/core/OIMObjectValuesByKeysSelector.ts
2775
+ var OIMObjectValuesByKeysSelector = class extends OIMSelector {
2776
+ constructor(runtime, obj, keys) {
2777
+ super(runtime, [
2778
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2779
+ obj,
2780
+ keys
2781
+ )
2782
+ ]);
2783
+ this.obj = obj;
2784
+ this.keys = keys;
2785
+ }
2786
+ getValue() {
2787
+ return this.keys.map((k) => this.obj.get(k));
2788
+ }
2789
+ areEqual(prev, next) {
2790
+ if (prev === next) return true;
2791
+ if (prev.length !== next.length) return false;
2792
+ for (let i = 0; i < prev.length; i++) {
2793
+ if (prev[i] !== next[i]) return false;
2794
+ }
2795
+ return true;
2796
+ }
2797
+ };
2798
+
2799
+ // src/modules/selector/core/OIMIndexPksByKeySetBasedSelector.ts
2800
+ var OIMIndexPksByKeySetBasedSelector = class extends OIMSelector {
2801
+ constructor(runtime, reactiveIndex, key) {
2802
+ super(runtime, [
2803
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2804
+ reactiveIndex,
2805
+ key
2806
+ )
2807
+ ]);
2808
+ this.reactiveIndex = reactiveIndex;
2809
+ this.key = key;
2810
+ }
2811
+ getValue() {
2812
+ return this.reactiveIndex.getPksByKey(this.key);
2813
+ }
2814
+ };
2815
+
2816
+ // src/modules/selector/core/OIMIndexPksByKeyArrayBasedSelector.ts
2817
+ var OIMIndexPksByKeyArrayBasedSelector = class extends OIMSelector {
2818
+ constructor(runtime, reactiveIndex, key) {
2819
+ super(runtime, [
2820
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2821
+ reactiveIndex,
2822
+ key
2823
+ )
2824
+ ]);
2825
+ this.reactiveIndex = reactiveIndex;
2826
+ this.key = key;
2827
+ }
2828
+ getValue() {
2829
+ return this.reactiveIndex.getPksByKey(this.key);
2830
+ }
2831
+ areEqual(prev, next) {
2832
+ if (prev === next) return true;
2833
+ if (prev.length !== next.length) return false;
2834
+ for (let i = 0; i < prev.length; i++)
2835
+ if (prev[i] !== next[i]) return false;
2836
+ return true;
2837
+ }
2838
+ };
2839
+
2840
+ // src/modules/selector/core/OIMIndexPksByKeysSetBasedSelector.ts
2841
+ var OIMIndexPksByKeysSetBasedSelector = class extends OIMSelector {
2842
+ constructor(runtime, reactiveIndex, keys) {
2843
+ super(runtime, [
2844
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2845
+ reactiveIndex,
2846
+ keys
2847
+ )
2848
+ ]);
2849
+ this.reactiveIndex = reactiveIndex;
2850
+ this.keys = keys;
2851
+ }
2852
+ getValue() {
2853
+ return this.keys.map((key) => Array.from(this.reactiveIndex.getPksByKey(key))).flat();
2854
+ }
2855
+ areEqual(prev, next) {
2856
+ if (prev === next) return true;
2857
+ if (prev.length !== next.length) return false;
2858
+ for (let i = 0; i < prev.length; i++)
2859
+ if (prev[i] !== next[i]) return false;
2860
+ return true;
2861
+ }
2862
+ };
2863
+
2864
+ // src/modules/selector/core/OIMIndexPksByKeysArrayBasedSelector.ts
2865
+ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
2866
+ constructor(runtime, reactiveIndex, keys) {
2867
+ super(runtime, [
2868
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2869
+ reactiveIndex,
2870
+ keys
2871
+ )
2872
+ ]);
2873
+ this.reactiveIndex = reactiveIndex;
2874
+ this.keys = keys;
2875
+ }
2876
+ getValue() {
2877
+ return this.keys.map((key) => this.reactiveIndex.getPksByKey(key)).flat();
2878
+ }
2879
+ areEqual(prev, next) {
2880
+ if (prev === next) return true;
2881
+ if (prev.length !== next.length) return false;
2882
+ for (let i = 0; i < prev.length; i++)
2883
+ if (prev[i] !== next[i]) return false;
2884
+ return true;
2885
+ }
2886
+ };
2887
+
2888
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeySetBased.ts
2889
+ var OIMSelectorSourceDependencyEntitiesByIndexKeySetBased = class {
2890
+ constructor(collection, reactiveIndex, key) {
2891
+ this.collection = collection;
2892
+ this.reactiveIndex = reactiveIndex;
2893
+ this.key = key;
2894
+ this.currentPks = [];
2895
+ }
2896
+ subscribe(onUpdate) {
2897
+ const resubscribeCollection = () => {
2898
+ const nextPks = Array.from(
2899
+ this.reactiveIndex.getPksByKey(this.key)
2900
+ );
2901
+ if (this.sameArray(this.currentPks, nextPks)) return;
2902
+ this.unsubscribeFromCollectionKeys?.();
2903
+ this.unsubscribeFromCollectionKeys = void 0;
2904
+ this.currentPks = nextPks;
2905
+ if (this.currentPks.length === 0) return;
2906
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2907
+ };
2908
+ const onIndexUpdate = () => {
2909
+ resubscribeCollection();
2910
+ onUpdate();
2911
+ };
2912
+ resubscribeCollection();
2913
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2914
+ this.key,
2915
+ onIndexUpdate
2916
+ );
2917
+ return () => {
2918
+ unsubscribeFromIndex();
2919
+ this.unsubscribeFromCollectionKeys?.();
2920
+ this.unsubscribeFromCollectionKeys = void 0;
2921
+ this.currentPks = [];
2922
+ };
2923
+ }
2924
+ sameArray(a, b) {
2925
+ if (a === b) return true;
2926
+ if (a.length !== b.length) return false;
2927
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2928
+ return true;
2929
+ }
2930
+ };
2931
+
2932
+ // src/modules/selector/core/OIMEntitiesByIndexKeySetBasedSelector.ts
2933
+ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
2934
+ constructor(runtime, collection, reactiveIndex, key) {
2935
+ super(runtime, [
2936
+ new OIMSelectorSourceDependencyEntitiesByIndexKeySetBased(collection, reactiveIndex, key)
2937
+ ]);
2938
+ this.collection = collection;
2939
+ this.reactiveIndex = reactiveIndex;
2940
+ this.key = key;
2941
+ }
2942
+ getValue() {
2943
+ const pks = Array.from(this.reactiveIndex.getPksByKey(this.key));
2944
+ return pks.map((pk) => this.collection.getOneByPk(pk));
2945
+ }
2946
+ areEqual(prev, next) {
2947
+ if (prev === next) return true;
2948
+ if (prev.length !== next.length) return false;
2949
+ for (let i = 0; i < prev.length; i++)
2950
+ if (prev[i] !== next[i]) return false;
2951
+ return true;
2952
+ }
2953
+ };
2954
+
2955
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased.ts
2956
+ var OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased = class {
2957
+ constructor(collection, reactiveIndex, key) {
2958
+ this.collection = collection;
2959
+ this.reactiveIndex = reactiveIndex;
2960
+ this.key = key;
2961
+ this.currentPks = [];
2962
+ }
2963
+ subscribe(onUpdate) {
2964
+ const resubscribeCollection = () => {
2965
+ const nextPks = this.reactiveIndex.getPksByKey(this.key);
2966
+ if (this.sameArray(this.currentPks, nextPks)) return;
2967
+ this.unsubscribeFromCollectionKeys?.();
2968
+ this.unsubscribeFromCollectionKeys = void 0;
2969
+ this.currentPks = nextPks;
2970
+ if (this.currentPks.length === 0) return;
2971
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2972
+ };
2973
+ const onIndexUpdate = () => {
2974
+ resubscribeCollection();
2975
+ onUpdate();
2976
+ };
2977
+ resubscribeCollection();
2978
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2979
+ this.key,
2980
+ onIndexUpdate
2981
+ );
2982
+ return () => {
2983
+ unsubscribeFromIndex();
2984
+ this.unsubscribeFromCollectionKeys?.();
2985
+ this.unsubscribeFromCollectionKeys = void 0;
2986
+ this.currentPks = [];
2987
+ };
2988
+ }
2989
+ sameArray(a, b) {
2990
+ if (a === b) return true;
2991
+ if (a.length !== b.length) return false;
2992
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2993
+ return true;
2994
+ }
2995
+ };
2996
+
2997
+ // src/modules/selector/core/OIMEntitiesByIndexKeyArrayBasedSelector.ts
2998
+ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
2999
+ constructor(runtime, collection, reactiveIndex, key) {
3000
+ super(runtime, [
3001
+ new OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased(collection, reactiveIndex, key)
3002
+ ]);
3003
+ this.collection = collection;
3004
+ this.reactiveIndex = reactiveIndex;
3005
+ this.key = key;
3006
+ }
3007
+ getValue() {
3008
+ const pks = this.reactiveIndex.getPksByKey(this.key);
3009
+ return pks.map((pk) => this.collection.getOneByPk(pk));
3010
+ }
3011
+ areEqual(prev, next) {
3012
+ if (prev === next) return true;
3013
+ if (prev.length !== next.length) return false;
3014
+ for (let i = 0; i < prev.length; i++)
3015
+ if (prev[i] !== next[i]) return false;
3016
+ return true;
3017
+ }
3018
+ };
3019
+
3020
+ // src/modules/computative/core/OIMComputativeRuntime.ts
3021
+ var OIMComputativeRuntime = class {
3022
+ constructor(queue) {
3023
+ this.queue = queue;
3024
+ this.isFlushScheduled = false;
3025
+ this.isFlushing = false;
3026
+ this.minLevel = Infinity;
3027
+ this.maxLevel = -Infinity;
3028
+ this.dirtyByLevel = /* @__PURE__ */ new Map();
3029
+ this.afterFlushTasks = /* @__PURE__ */ new Set();
3030
+ this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
3031
+ if (!this.isFlushScheduled) return;
3032
+ this.flush();
3033
+ });
3034
+ }
3035
+ /**
3036
+ * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
3037
+ * ordered by `level` ascending.
3038
+ */
3039
+ schedule(task, level = 0) {
3040
+ let isActive = true;
3041
+ const wrapped = () => {
3042
+ if (!isActive) return;
3043
+ task();
3044
+ };
3045
+ let bucket = this.dirtyByLevel.get(level);
3046
+ if (!bucket) {
3047
+ bucket = /* @__PURE__ */ new Set();
3048
+ this.dirtyByLevel.set(level, bucket);
3049
+ }
3050
+ bucket.add(wrapped);
3051
+ if (level < this.minLevel) this.minLevel = level;
3052
+ if (level > this.maxLevel) this.maxLevel = level;
3053
+ if (!this.isFlushing) this.isFlushScheduled = true;
3054
+ return () => {
3055
+ if (!isActive) return;
3056
+ isActive = false;
3057
+ bucket.delete(wrapped);
3058
+ };
3059
+ }
3060
+ /**
3061
+ * Run once at the start of the next runtime flush (i.e. after queue.flush()).
3062
+ * Useful for resubscribing dependencies exactly once per source flush.
3063
+ */
3064
+ scheduleAfterFlush(task) {
3065
+ let isActive = true;
3066
+ const wrapped = () => {
3067
+ if (!isActive) return;
3068
+ task();
3069
+ };
3070
+ this.afterFlushTasks.add(wrapped);
3071
+ if (!this.isFlushing) this.isFlushScheduled = true;
3072
+ return () => {
3073
+ if (!isActive) return;
3074
+ isActive = false;
3075
+ this.afterFlushTasks.delete(wrapped);
3076
+ };
3077
+ }
3078
+ flush() {
3079
+ this.isFlushScheduled = false;
3080
+ if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
3081
+ return;
3082
+ this.isFlushing = true;
3083
+ try {
3084
+ if (this.afterFlushTasks.size > 0) {
3085
+ const snapshot = Array.from(this.afterFlushTasks);
3086
+ this.afterFlushTasks.clear();
3087
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3088
+ }
3089
+ if (this.dirtyByLevel.size === 0) return;
3090
+ let level = this.minLevel;
3091
+ let max = this.maxLevel;
3092
+ while (level <= max) {
3093
+ for (; ; ) {
3094
+ const bucket = this.dirtyByLevel.get(level);
3095
+ if (!bucket || bucket.size === 0) break;
3096
+ this.dirtyByLevel.delete(level);
3097
+ const snapshot = Array.from(bucket);
3098
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3099
+ }
3100
+ if (this.maxLevel > max) max = this.maxLevel;
3101
+ level++;
3102
+ }
3103
+ } finally {
3104
+ this.isFlushing = false;
3105
+ if (this.dirtyByLevel.size === 0) {
3106
+ this.minLevel = Infinity;
3107
+ this.maxLevel = -Infinity;
3108
+ } else {
3109
+ let nextMin = Infinity;
3110
+ let nextMax = -Infinity;
3111
+ this.dirtyByLevel.forEach((_bucket, lvl) => {
3112
+ if (lvl < nextMin) nextMin = lvl;
3113
+ if (lvl > nextMax) nextMax = lvl;
3114
+ });
3115
+ this.minLevel = nextMin;
3116
+ this.maxLevel = nextMax;
3117
+ this.isFlushScheduled = true;
3118
+ }
3119
+ }
3120
+ }
3121
+ };
1508
3122
  export {
1509
3123
  EOIMCollectionEventType,
3124
+ EOIMComputedEventType,
1510
3125
  EOIMEventQueueEventType,
1511
3126
  EOIMEventQueueSchedulerEventType,
1512
3127
  EOIMIndexEventType,
1513
- EOIMUpdateEventCoalescerEventType,
3128
+ EOIMObjectEventType,
1514
3129
  OIMCollection,
3130
+ OIMCollectionByPkSelector,
3131
+ OIMCollectionByPksSelector,
3132
+ OIMCollectionChangedFieldsWrapper,
1515
3133
  OIMCollectionStore,
1516
3134
  OIMCollectionStoreMapDriven,
1517
3135
  OIMComparatorFactory,
3136
+ OIMComputativeRuntime,
3137
+ OIMComputed,
1518
3138
  OIMDBSettings,
3139
+ OIMEffect,
3140
+ OIMEffectDependencyComputed,
3141
+ OIMEffectDependencyKeyed,
3142
+ OIMEffectDependencyKeyedCollection,
3143
+ OIMEffectDependencyKeyedIndex,
3144
+ OIMEffectDependencyKeyedObject,
3145
+ OIMEntitiesByIndexKeyArrayBasedSelector,
3146
+ OIMEntitiesByIndexKeySetBasedSelector,
1519
3147
  OIMEntityUpdaterFactory,
1520
3148
  OIMEventEmitter,
1521
3149
  OIMEventQueue,
@@ -1528,13 +3156,25 @@ export {
1528
3156
  OIMIndexArrayBased,
1529
3157
  OIMIndexComparatorFactory,
1530
3158
  OIMIndexManualArrayBased,
3159
+ OIMIndexManualOrderedArrayBased,
1531
3160
  OIMIndexManualSetBased,
3161
+ OIMIndexPksByKeyArrayBasedSelector,
3162
+ OIMIndexPksByKeySetBasedSelector,
3163
+ OIMIndexPksByKeysArrayBasedSelector,
3164
+ OIMIndexPksByKeysSetBasedSelector,
1532
3165
  OIMIndexSetBased,
1533
3166
  OIMIndexStoreArrayBased,
1534
3167
  OIMIndexStoreMapDrivenArrayBased,
1535
3168
  OIMIndexStoreMapDrivenSetBased,
1536
3169
  OIMIndexStoreSetBased,
1537
3170
  OIMMap2Keys,
3171
+ OIMObject,
3172
+ OIMObjectStore,
3173
+ OIMObjectStoreMapDriven,
3174
+ OIMObjectStoreRecordDriven,
3175
+ OIMObjectValueByKeySelector,
3176
+ OIMObjectValuesByKeysSelector,
3177
+ OIMOrderedListIndexCommandStreamWrapper,
1538
3178
  OIMPkSelectorFactory,
1539
3179
  OIMRICollection,
1540
3180
  OIMReactiveCollection,
@@ -1542,8 +3182,6 @@ export {
1542
3182
  OIMReactiveIndexManualArrayBased,
1543
3183
  OIMReactiveIndexManualSetBased,
1544
3184
  OIMReactiveIndexSetBased,
1545
- OIMUpdateEventCoalescer,
1546
- OIMUpdateEventCoalescerCollection,
1547
- OIMUpdateEventCoalescerIndex,
1548
- OIMUpdateEventEmitter
3185
+ OIMReactiveObject,
3186
+ OIMSelector
1549
3187
  };