@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.cjs CHANGED
@@ -21,15 +21,29 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  EOIMCollectionEventType: () => EOIMCollectionEventType,
24
+ EOIMComputedEventType: () => EOIMComputedEventType,
24
25
  EOIMEventQueueEventType: () => EOIMEventQueueEventType,
25
26
  EOIMEventQueueSchedulerEventType: () => EOIMEventQueueSchedulerEventType,
26
27
  EOIMIndexEventType: () => EOIMIndexEventType,
27
- EOIMUpdateEventCoalescerEventType: () => EOIMUpdateEventCoalescerEventType,
28
+ EOIMObjectEventType: () => EOIMObjectEventType,
28
29
  OIMCollection: () => OIMCollection,
30
+ OIMCollectionByPkSelector: () => OIMCollectionByPkSelector,
31
+ OIMCollectionByPksSelector: () => OIMCollectionByPksSelector,
32
+ OIMCollectionChangedFieldsWrapper: () => OIMCollectionChangedFieldsWrapper,
29
33
  OIMCollectionStore: () => OIMCollectionStore,
30
34
  OIMCollectionStoreMapDriven: () => OIMCollectionStoreMapDriven,
31
35
  OIMComparatorFactory: () => OIMComparatorFactory,
36
+ OIMComputativeRuntime: () => OIMComputativeRuntime,
37
+ OIMComputed: () => OIMComputed,
32
38
  OIMDBSettings: () => OIMDBSettings,
39
+ OIMEffect: () => OIMEffect,
40
+ OIMEffectDependencyComputed: () => OIMEffectDependencyComputed,
41
+ OIMEffectDependencyKeyed: () => OIMEffectDependencyKeyed,
42
+ OIMEffectDependencyKeyedCollection: () => OIMEffectDependencyKeyedCollection,
43
+ OIMEffectDependencyKeyedIndex: () => OIMEffectDependencyKeyedIndex,
44
+ OIMEffectDependencyKeyedObject: () => OIMEffectDependencyKeyedObject,
45
+ OIMEntitiesByIndexKeyArrayBasedSelector: () => OIMEntitiesByIndexKeyArrayBasedSelector,
46
+ OIMEntitiesByIndexKeySetBasedSelector: () => OIMEntitiesByIndexKeySetBasedSelector,
33
47
  OIMEntityUpdaterFactory: () => OIMEntityUpdaterFactory,
34
48
  OIMEventEmitter: () => OIMEventEmitter,
35
49
  OIMEventQueue: () => OIMEventQueue,
@@ -42,13 +56,25 @@ __export(index_exports, {
42
56
  OIMIndexArrayBased: () => OIMIndexArrayBased,
43
57
  OIMIndexComparatorFactory: () => OIMIndexComparatorFactory,
44
58
  OIMIndexManualArrayBased: () => OIMIndexManualArrayBased,
59
+ OIMIndexManualOrderedArrayBased: () => OIMIndexManualOrderedArrayBased,
45
60
  OIMIndexManualSetBased: () => OIMIndexManualSetBased,
61
+ OIMIndexPksByKeyArrayBasedSelector: () => OIMIndexPksByKeyArrayBasedSelector,
62
+ OIMIndexPksByKeySetBasedSelector: () => OIMIndexPksByKeySetBasedSelector,
63
+ OIMIndexPksByKeysArrayBasedSelector: () => OIMIndexPksByKeysArrayBasedSelector,
64
+ OIMIndexPksByKeysSetBasedSelector: () => OIMIndexPksByKeysSetBasedSelector,
46
65
  OIMIndexSetBased: () => OIMIndexSetBased,
47
66
  OIMIndexStoreArrayBased: () => OIMIndexStoreArrayBased,
48
67
  OIMIndexStoreMapDrivenArrayBased: () => OIMIndexStoreMapDrivenArrayBased,
49
68
  OIMIndexStoreMapDrivenSetBased: () => OIMIndexStoreMapDrivenSetBased,
50
69
  OIMIndexStoreSetBased: () => OIMIndexStoreSetBased,
51
70
  OIMMap2Keys: () => OIMMap2Keys,
71
+ OIMObject: () => OIMObject,
72
+ OIMObjectStore: () => OIMObjectStore,
73
+ OIMObjectStoreMapDriven: () => OIMObjectStoreMapDriven,
74
+ OIMObjectStoreRecordDriven: () => OIMObjectStoreRecordDriven,
75
+ OIMObjectValueByKeySelector: () => OIMObjectValueByKeySelector,
76
+ OIMObjectValuesByKeysSelector: () => OIMObjectValuesByKeysSelector,
77
+ OIMOrderedListIndexCommandStreamWrapper: () => OIMOrderedListIndexCommandStreamWrapper,
52
78
  OIMPkSelectorFactory: () => OIMPkSelectorFactory,
53
79
  OIMRICollection: () => OIMRICollection,
54
80
  OIMReactiveCollection: () => OIMReactiveCollection,
@@ -56,10 +82,8 @@ __export(index_exports, {
56
82
  OIMReactiveIndexManualArrayBased: () => OIMReactiveIndexManualArrayBased,
57
83
  OIMReactiveIndexManualSetBased: () => OIMReactiveIndexManualSetBased,
58
84
  OIMReactiveIndexSetBased: () => OIMReactiveIndexSetBased,
59
- OIMUpdateEventCoalescer: () => OIMUpdateEventCoalescer,
60
- OIMUpdateEventCoalescerCollection: () => OIMUpdateEventCoalescerCollection,
61
- OIMUpdateEventCoalescerIndex: () => OIMUpdateEventCoalescerIndex,
62
- OIMUpdateEventEmitter: () => OIMUpdateEventEmitter
85
+ OIMReactiveObject: () => OIMReactiveObject,
86
+ OIMSelector: () => OIMSelector
63
87
  });
64
88
  module.exports = __toCommonJS(index_exports);
65
89
 
@@ -80,6 +104,11 @@ var OIMEventEmitter = class {
80
104
  constructor() {
81
105
  this.buckets = {};
82
106
  }
107
+ hasHandlers(event) {
108
+ const bucket = this.buckets[event];
109
+ if (!bucket) return false;
110
+ return bucket.indexByHandler.size > 0;
111
+ }
83
112
  getOrCreateBucket(event) {
84
113
  let bucket = this.buckets[event];
85
114
  if (!bucket) {
@@ -93,39 +122,40 @@ var OIMEventEmitter = class {
93
122
  }
94
123
  return bucket;
95
124
  }
125
+ /**
126
+ * Compacts the handlers array by removing null entries (tombstones) left by handler removal.
127
+ * Rebuilds the indexByHandler map with updated indices and resets the tombstone count.
128
+ * This reduces array fragmentation and improves iteration performance.
129
+ */
96
130
  compactBucket(bucket) {
97
131
  const handlers = bucket.handlers;
98
132
  const length = handlers.length;
99
133
  const newHandlers = [];
100
134
  bucket.indexByHandler.clear();
101
- if (bucket.tombstoneCount < length >> 2) {
102
- newHandlers.length = length - bucket.tombstoneCount;
103
- }
104
- let writeIndex = 0;
105
135
  for (let i = 0; i < length; i++) {
106
136
  const handler = handlers[i];
107
137
  if (handler) {
108
- newHandlers[writeIndex] = handler;
138
+ const writeIndex = newHandlers.length;
139
+ newHandlers.push(handler);
109
140
  bucket.indexByHandler.set(handler, writeIndex);
110
- writeIndex++;
111
141
  }
112
142
  }
113
- if (newHandlers.length !== writeIndex) {
114
- newHandlers.length = writeIndex;
115
- }
116
143
  bucket.handlers = newHandlers;
117
144
  bucket.tombstoneCount = 0;
118
145
  }
119
146
  on(event, handler) {
120
147
  const bucket = this.getOrCreateBucket(event);
121
- if (bucket.indexByHandler.has(handler))
122
- return;
148
+ if (bucket.indexByHandler.has(handler)) {
149
+ return () => {
150
+ this.off(event, handler);
151
+ };
152
+ }
123
153
  const index = bucket.handlers.length;
124
154
  bucket.handlers.push(handler);
125
- bucket.indexByHandler.set(
126
- handler,
127
- index
128
- );
155
+ bucket.indexByHandler.set(handler, index);
156
+ return () => {
157
+ this.off(event, handler);
158
+ };
129
159
  }
130
160
  emit(event, payload) {
131
161
  const bucket = this.buckets[event];
@@ -140,10 +170,11 @@ var OIMEventEmitter = class {
140
170
  }
141
171
  bucket.isEmitting--;
142
172
  if (bucket.isEmitting === 0 && bucket.tombstoneCount > 0) {
143
- if (bucket.tombstoneCount >= length >> 1) {
144
- this.compactBucket(bucket);
145
- } else if (length === bucket.tombstoneCount) {
173
+ const currentLength = bucket.handlers.length;
174
+ if (bucket.tombstoneCount === currentLength) {
146
175
  delete this.buckets[event];
176
+ } else if (bucket.tombstoneCount >= currentLength >> 1) {
177
+ this.compactBucket(bucket);
147
178
  }
148
179
  }
149
180
  }
@@ -157,10 +188,10 @@ var OIMEventEmitter = class {
157
188
  bucket.indexByHandler.delete(handler);
158
189
  if (bucket.isEmitting === 0) {
159
190
  const length = bucket.handlers.length;
160
- if (bucket.tombstoneCount >= length >> 1) {
161
- this.compactBucket(bucket);
162
- } else if (length === bucket.tombstoneCount) {
191
+ if (bucket.tombstoneCount === length) {
163
192
  delete this.buckets[event];
193
+ } else if (bucket.tombstoneCount >= length >> 1) {
194
+ this.compactBucket(bucket);
164
195
  }
165
196
  }
166
197
  }
@@ -360,6 +391,12 @@ var OIMIndexSetBased = class {
360
391
  emitUpdate(keys) {
361
392
  this.emitter.emit(0 /* UPDATE */, { keys });
362
393
  }
394
+ /**
395
+ * Emit update event for a single key (fast-path override point).
396
+ */
397
+ emitUpdateOne(key) {
398
+ this.emitUpdate([key]);
399
+ }
363
400
  };
364
401
 
365
402
  // src/core/OIMIndexStoreMapDrivenArrayBased.ts
@@ -509,70 +546,195 @@ var OIMIndexArrayBased = class {
509
546
  emitUpdate(keys) {
510
547
  this.emitter.emit(0 /* UPDATE */, { keys });
511
548
  }
549
+ /**
550
+ * Emit update event for a single key (fast-path override point).
551
+ */
552
+ emitUpdateOne(key) {
553
+ this.emitUpdate([key]);
554
+ }
512
555
  };
513
556
 
514
- // src/enum/EOIMUpdateEventCoalescerEventType.ts
515
- var EOIMUpdateEventCoalescerEventType = /* @__PURE__ */ ((EOIMUpdateEventCoalescerEventType2) => {
516
- EOIMUpdateEventCoalescerEventType2[EOIMUpdateEventCoalescerEventType2["HAS_CHANGES"] = 0] = "HAS_CHANGES";
517
- EOIMUpdateEventCoalescerEventType2[EOIMUpdateEventCoalescerEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
518
- EOIMUpdateEventCoalescerEventType2[EOIMUpdateEventCoalescerEventType2["AFTER_FLUSH"] = 2] = "AFTER_FLUSH";
519
- return EOIMUpdateEventCoalescerEventType2;
520
- })(EOIMUpdateEventCoalescerEventType || {});
557
+ // src/enum/EOIMEventQueueEventType.ts
558
+ var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
559
+ EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
560
+ EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
561
+ return EOIMEventQueueEventType2;
562
+ })(EOIMEventQueueEventType || {});
521
563
 
522
564
  // src/core/OIMUpdateEventEmitter.ts
523
565
  var OIMUpdateEventEmitter = class {
524
- constructor(opts) {
566
+ constructor(queue, deliveryMode = "queue") {
525
567
  this.keyHandlers = /* @__PURE__ */ new Map();
526
- this.handleHasChanges = () => {
527
- this.queue.enqueue(() => {
528
- const updatedKeys = this.coalescer.getUpdatedKeys();
529
- if (this.keyHandlers.size === 0) {
530
- this.coalescer.clearUpdatedKeys();
531
- return;
568
+ this.isFlushEnqueued = false;
569
+ this.updatedKeys = /* @__PURE__ */ new Set();
570
+ this.isAfterFlushDeliveryScheduled = false;
571
+ // Stable handler reference to avoid allocating a new closure on every HAS_CHANGES.
572
+ this.onFlush = () => {
573
+ this.isFlushEnqueued = false;
574
+ this.dequeueFlush = void 0;
575
+ if (this.keyHandlers.size === 0) {
576
+ this.updatedKeys.clear();
577
+ return;
578
+ }
579
+ const makeHandlerSnapshot = (handlers) => {
580
+ const snapshot = [];
581
+ handlers.forEach((h) => snapshot.push(h));
582
+ return snapshot;
583
+ };
584
+ this.runSinglePass(makeHandlerSnapshot);
585
+ };
586
+ this.onAfterFlush = () => {
587
+ if (!this.isAfterFlushDeliveryScheduled) return;
588
+ this.isAfterFlushDeliveryScheduled = false;
589
+ this.deliverImmediate();
590
+ };
591
+ this.queue = queue;
592
+ this.deliveryMode = deliveryMode;
593
+ if (this.deliveryMode === "after_flush") {
594
+ this.unsubscribeAfterFlushDelivery = this.queue.emitter.on(
595
+ 0 /* AFTER_FLUSH */,
596
+ this.onAfterFlush
597
+ );
598
+ }
599
+ }
600
+ /**
601
+ * Mark keys as updated and schedule delivery.
602
+ *
603
+ * If there are no subscriptions, this is a no-op and does not allocate or retain keys.
604
+ */
605
+ markUpdatedKeys(keys) {
606
+ if (keys.length === 0) return;
607
+ if (this.deliveryMode === "queue" && this.queue.isInFlush) {
608
+ throw new Error(
609
+ "OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
610
+ );
611
+ }
612
+ if (this.keyHandlers.size === 0) return;
613
+ let didAddAny = false;
614
+ for (let i = 0; i < keys.length; i++) {
615
+ const key = keys[i];
616
+ const handlers = this.keyHandlers.get(key);
617
+ if (!handlers || handlers.size === 0) continue;
618
+ this.updatedKeys.add(key);
619
+ didAddAny = true;
620
+ }
621
+ if (!didAddAny) return;
622
+ if (this.deliveryMode === "immediate") {
623
+ this.deliverImmediate();
624
+ return;
625
+ }
626
+ if (this.deliveryMode === "after_flush") {
627
+ this.isAfterFlushDeliveryScheduled = true;
628
+ return;
629
+ }
630
+ if (this.isFlushEnqueued) return;
631
+ this.isFlushEnqueued = true;
632
+ this.dequeueFlush = this.queue.enqueue(this.onFlush);
633
+ }
634
+ markUpdatedKey(key) {
635
+ if (this.deliveryMode === "queue" && this.queue.isInFlush) {
636
+ throw new Error(
637
+ "OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
638
+ );
639
+ }
640
+ if (this.keyHandlers.size === 0) return;
641
+ const handlers = this.keyHandlers.get(key);
642
+ if (!handlers || handlers.size === 0) return;
643
+ this.updatedKeys.add(key);
644
+ if (this.deliveryMode === "immediate") {
645
+ this.deliverImmediate();
646
+ return;
647
+ }
648
+ if (this.deliveryMode === "after_flush") {
649
+ this.isAfterFlushDeliveryScheduled = true;
650
+ return;
651
+ }
652
+ if (this.isFlushEnqueued) return;
653
+ this.isFlushEnqueued = true;
654
+ this.dequeueFlush = this.queue.enqueue(this.onFlush);
655
+ }
656
+ markAllUpdated() {
657
+ if (this.deliveryMode === "queue" && this.queue.isInFlush) {
658
+ throw new Error(
659
+ "OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
660
+ );
661
+ }
662
+ if (this.keyHandlers.size === 0) return;
663
+ this.keyHandlers.forEach((_handlers, key) => {
664
+ this.updatedKeys.add(key);
665
+ });
666
+ if (this.deliveryMode === "immediate") {
667
+ this.deliverImmediate();
668
+ return;
669
+ }
670
+ if (this.deliveryMode === "after_flush") {
671
+ this.isAfterFlushDeliveryScheduled = true;
672
+ return;
673
+ }
674
+ if (this.isFlushEnqueued) return;
675
+ this.isFlushEnqueued = true;
676
+ this.dequeueFlush = this.queue.enqueue(this.onFlush);
677
+ }
678
+ deliverImmediate() {
679
+ const makeHandlerSnapshot = (handlers) => {
680
+ const snapshot = [];
681
+ handlers.forEach((h) => snapshot.push(h));
682
+ return snapshot;
683
+ };
684
+ this.runSinglePass(makeHandlerSnapshot);
685
+ }
686
+ /**
687
+ * Deliver updated keys and notify handlers once.
688
+ * Re-entrant updates during delivery are forbidden and will throw in markUpdatedKeys().
689
+ */
690
+ runSinglePass(makeHandlerSnapshot) {
691
+ if (this.updatedKeys.size === 0) return;
692
+ const flushingKeys = this.updatedKeys;
693
+ this.updatedKeys = /* @__PURE__ */ new Set();
694
+ if (this.keyHandlers.size === 0 || flushingKeys.size === 0) return;
695
+ if (flushingKeys.size * 2 < this.keyHandlers.size) {
696
+ flushingKeys.forEach((key) => {
697
+ const handlers = this.keyHandlers.get(key);
698
+ if (!handlers || handlers.size === 0) return;
699
+ const handlerSnapshot = makeHandlerSnapshot(handlers);
700
+ for (let i = 0; i < handlerSnapshot.length; i++) {
701
+ const handler = handlerSnapshot[i];
702
+ if (handlers.has(handler)) handler();
532
703
  }
533
- if (updatedKeys.size * 2 < this.keyHandlers.size) {
534
- for (const key of updatedKeys.values()) {
535
- const handlers = this.keyHandlers.get(key);
536
- if (handlers) {
537
- for (const handler of handlers) {
538
- handler();
539
- }
540
- }
541
- }
542
- } else {
543
- for (const [key, handlers] of this.keyHandlers) {
544
- if (updatedKeys.has(key)) {
545
- for (const handler of handlers) {
546
- handler();
547
- }
548
- }
549
- }
704
+ });
705
+ } else {
706
+ this.keyHandlers.forEach((handlers, key) => {
707
+ if (!flushingKeys.has(key)) return;
708
+ if (!handlers || handlers.size === 0) return;
709
+ const handlerSnapshot = makeHandlerSnapshot(handlers);
710
+ for (let i = 0; i < handlerSnapshot.length; i++) {
711
+ const handler = handlerSnapshot[i];
712
+ if (handlers.has(handler)) handler();
550
713
  }
551
- this.coalescer.clearUpdatedKeys();
552
714
  });
553
- };
554
- this.coalescer = opts.coalescer;
555
- this.coalescer.emitter.on(
556
- 0 /* HAS_CHANGES */,
557
- this.handleHasChanges
558
- );
559
- this.queue = opts.queue;
715
+ }
560
716
  }
561
717
  destroy() {
562
- this.coalescer.emitter.off(
563
- 0 /* HAS_CHANGES */,
564
- this.handleHasChanges
565
- );
718
+ if (this.dequeueFlush) {
719
+ this.dequeueFlush();
720
+ this.dequeueFlush = void 0;
721
+ }
722
+ if (this.unsubscribeAfterFlushDelivery) {
723
+ this.unsubscribeAfterFlushDelivery();
724
+ this.unsubscribeAfterFlushDelivery = void 0;
725
+ }
726
+ this.keyHandlers.forEach((handlers) => handlers.clear());
566
727
  this.keyHandlers.clear();
728
+ this.updatedKeys.clear();
567
729
  }
568
730
  /**
569
731
  * Get performance metrics for monitoring and debugging
570
732
  */
571
733
  getMetrics() {
572
734
  let totalHandlers = 0;
573
- for (const handlers of this.keyHandlers.values()) {
735
+ this.keyHandlers.forEach((handlers) => {
574
736
  totalHandlers += handlers.size;
575
- }
737
+ });
576
738
  return {
577
739
  totalKeys: this.keyHandlers.size,
578
740
  totalHandlers,
@@ -609,6 +771,15 @@ var OIMUpdateEventEmitter = class {
609
771
  handlers.delete(handler);
610
772
  if (handlers.size === 0) {
611
773
  this.keyHandlers.delete(key);
774
+ this.updatedKeys.delete(key);
775
+ }
776
+ if (this.keyHandlers.size === 0) {
777
+ this.updatedKeys.clear();
778
+ if (this.dequeueFlush) {
779
+ this.dequeueFlush();
780
+ this.dequeueFlush = void 0;
781
+ }
782
+ this.isFlushEnqueued = false;
612
783
  }
613
784
  }
614
785
  subscribeOnKeys(keys, handler) {
@@ -637,181 +808,11 @@ var OIMUpdateEventEmitter = class {
637
808
  }
638
809
  };
639
810
 
640
- // src/core/OIMIndexManualSetBased.ts
641
- var OIMIndexManualSetBased = class extends OIMIndexSetBased {
642
- constructor(options = {}) {
643
- super(options);
644
- }
645
- /**
646
- * Set primary keys for a specific index key, replacing any existing values.
647
- * Uses optional comparator to skip updates if PKs haven't actually changed.
648
- */
649
- setPks(key, pks) {
650
- const hasChanges = this.setPksWithComparison(key, new Set(pks));
651
- if (hasChanges) {
652
- this.emitUpdate([key]);
653
- }
654
- }
655
- /**
656
- * Add primary keys to a specific index key
657
- */
658
- addPks(key, pks) {
659
- if (pks.length === 0) return;
660
- let pksSet = this.store.getOneByKey(key);
661
- if (!pksSet) {
662
- pksSet = /* @__PURE__ */ new Set();
663
- this.store.setOneByKey(key, pksSet);
664
- }
665
- let hasChanges = false;
666
- for (const pk of pks) {
667
- if (!pksSet.has(pk)) {
668
- pksSet.add(pk);
669
- hasChanges = true;
670
- }
671
- }
672
- if (hasChanges) {
673
- this.store.setOneByKey(key, pksSet);
674
- this.emitUpdate([key]);
675
- }
676
- }
677
- /**
678
- * Remove primary keys from a specific index key
679
- */
680
- removePks(key, pks) {
681
- if (pks.length === 0) return;
682
- const pksSet = this.store.getOneByKey(key);
683
- if (!pksSet) return;
684
- let hasChanges = false;
685
- for (const pk of pks) {
686
- if (pksSet.delete(pk)) {
687
- hasChanges = true;
688
- }
689
- }
690
- if (pksSet.size === 0) {
691
- this.store.removeOneByKey(key);
692
- hasChanges = true;
693
- } else if (hasChanges) {
694
- this.store.setOneByKey(key, pksSet);
695
- }
696
- if (hasChanges) {
697
- this.emitUpdate([key]);
698
- }
699
- }
700
- /**
701
- * Clear all primary keys for a specific index key, or all keys if no key specified
702
- */
703
- clear(key) {
704
- if (key === void 0) {
705
- const allKeys = this.store.getAllKeys();
706
- if (allKeys.length > 0) {
707
- this.store.clear();
708
- this.emitUpdate(allKeys);
709
- }
710
- } else {
711
- if (this.store.getOneByKey(key) !== void 0) {
712
- this.store.removeOneByKey(key);
713
- this.emitUpdate([key]);
714
- }
715
- }
716
- }
717
- };
718
-
719
- // src/const/OIMDBSettings.ts
720
- var OIMDBSettings = class {
721
- };
722
- /**
723
- * Fixed key used in the manual index to store updated keys set in coalescers.
724
- * This constant is exported so users can create their own indexes and pass them to coalescers.
725
- */
726
- OIMDBSettings.UPDATED_KEYS_INDEX_KEY = "__updatedKeys__";
727
-
728
- // src/core/OIMUpdateEventCoalescer.ts
729
- var OIMUpdateEventCoalescer = class {
730
- constructor(index) {
731
- this.emitter = new OIMEventEmitter();
732
- this.hasEmittedChanges = false;
733
- this.updatedKeysIndex = index ?? new OIMIndexManualSetBased({
734
- store: new OIMIndexStoreMapDrivenSetBased()
735
- });
736
- if (!this.updatedKeysIndex.hasKey(OIMDBSettings.UPDATED_KEYS_INDEX_KEY)) {
737
- this.updatedKeysIndex.setPks(
738
- OIMDBSettings.UPDATED_KEYS_INDEX_KEY,
739
- []
740
- );
741
- }
742
- }
743
- /**
744
- * Get the set of keys that have been updated since last clear
745
- */
746
- getUpdatedKeys() {
747
- return this.updatedKeysIndex.getPksByKey(
748
- OIMDBSettings.UPDATED_KEYS_INDEX_KEY
749
- );
750
- }
751
- /**
752
- * Clear all updated keys and reset the changed state
753
- */
754
- clearUpdatedKeys() {
755
- this.emitter.emit(
756
- 1 /* BEFORE_FLUSH */,
757
- void 0
758
- );
759
- this.updatedKeysIndex.setPks(OIMDBSettings.UPDATED_KEYS_INDEX_KEY, []);
760
- this.hasEmittedChanges = false;
761
- this.emitter.emit(
762
- 2 /* AFTER_FLUSH */,
763
- void 0
764
- );
765
- }
766
- /**
767
- * Add keys to the updated set and emit HAS_CHANGES event if needed
768
- */
769
- addUpdatedKeys(keys) {
770
- this.updatedKeysIndex.addPks(
771
- OIMDBSettings.UPDATED_KEYS_INDEX_KEY,
772
- keys
773
- );
774
- if (!this.hasEmittedChanges) {
775
- this.hasEmittedChanges = true;
776
- this.emitter.emit(
777
- 0 /* HAS_CHANGES */,
778
- void 0
779
- );
780
- }
781
- }
782
- };
783
-
784
- // src/core/OIMUpdateEventCoalescerIndex.ts
785
- var OIMUpdateEventCoalescerIndex = class extends OIMUpdateEventCoalescer {
786
- constructor(indexEmitter) {
787
- super();
788
- this.indexEmitter = indexEmitter;
789
- /**
790
- * When the index is updated, we keep track of the updated keys.
791
- */
792
- this.handleUpdate = (payload) => {
793
- this.addUpdatedKeys(payload.keys);
794
- };
795
- this.indexEmitter.on(0 /* UPDATE */, this.handleUpdate);
796
- }
797
- destroy() {
798
- this.indexEmitter.off(0 /* UPDATE */, this.handleUpdate);
799
- this.emitter.offAll();
800
- this.clearUpdatedKeys();
801
- }
802
- };
803
-
804
811
  // src/abstract/OIMReactiveIndexSetBased.ts
805
812
  var OIMReactiveIndexSetBased = class {
806
- constructor(queue, opts) {
807
- this.index = opts?.index ?? this.createDefaultIndex();
808
- this.coalescer = new OIMUpdateEventCoalescerIndex(
809
- this.index.emitter
810
- );
811
- this.updateEventEmitter = new OIMUpdateEventEmitter({
812
- coalescer: this.coalescer,
813
- queue
814
- });
813
+ constructor(queue, createIndex) {
814
+ this.updateEmitter = new OIMUpdateEventEmitter(queue);
815
+ this.index = createIndex(this.updateEmitter);
815
816
  }
816
817
  getPksByKey(key) {
817
818
  return this.index.getPksByKey(key);
@@ -837,22 +838,38 @@ var OIMReactiveIndexSetBased = class {
837
838
  getMetrics() {
838
839
  return this.index.getMetrics();
839
840
  }
841
+ subscribeOnKey(key, handler) {
842
+ return this.updateEmitter.subscribeOnKey(key, handler);
843
+ }
844
+ subscribeOnKeys(keys, handler) {
845
+ return this.updateEmitter.subscribeOnKeys(keys, handler);
846
+ }
847
+ unsubscribeFromKey(key, handler) {
848
+ this.updateEmitter.unsubscribeFromKey(key, handler);
849
+ }
850
+ unsubscribeFromKeys(keys, handler) {
851
+ this.updateEmitter.unsubscribeFromKeys(keys, handler);
852
+ }
853
+ hasSubscriptions() {
854
+ return this.updateEmitter.hasSubscriptions();
855
+ }
856
+ getHandlerCount(key) {
857
+ return this.updateEmitter.getHandlerCount(key);
858
+ }
859
+ getSubscriptionMetrics() {
860
+ return this.updateEmitter.getMetrics();
861
+ }
840
862
  destroy() {
863
+ this.updateEmitter.destroy();
841
864
  this.index.destroy();
842
865
  }
843
866
  };
844
867
 
845
868
  // src/abstract/OIMReactiveIndexArrayBased.ts
846
869
  var OIMReactiveIndexArrayBased = class {
847
- constructor(queue, opts) {
848
- this.index = opts?.index ?? this.createDefaultIndex();
849
- this.coalescer = new OIMUpdateEventCoalescerIndex(
850
- this.index.emitter
851
- );
852
- this.updateEventEmitter = new OIMUpdateEventEmitter({
853
- coalescer: this.coalescer,
854
- queue
855
- });
870
+ constructor(queue, createIndex) {
871
+ this.updateEmitter = new OIMUpdateEventEmitter(queue);
872
+ this.index = createIndex(this.updateEmitter);
856
873
  }
857
874
  getPksByKey(key) {
858
875
  return this.index.getPksByKey(key);
@@ -878,11 +895,37 @@ var OIMReactiveIndexArrayBased = class {
878
895
  getMetrics() {
879
896
  return this.index.getMetrics();
880
897
  }
898
+ subscribeOnKey(key, handler) {
899
+ return this.updateEmitter.subscribeOnKey(key, handler);
900
+ }
901
+ subscribeOnKeys(keys, handler) {
902
+ return this.updateEmitter.subscribeOnKeys(keys, handler);
903
+ }
904
+ unsubscribeFromKey(key, handler) {
905
+ this.updateEmitter.unsubscribeFromKey(key, handler);
906
+ }
907
+ unsubscribeFromKeys(keys, handler) {
908
+ this.updateEmitter.unsubscribeFromKeys(keys, handler);
909
+ }
910
+ hasSubscriptions() {
911
+ return this.updateEmitter.hasSubscriptions();
912
+ }
913
+ getHandlerCount(key) {
914
+ return this.updateEmitter.getHandlerCount(key);
915
+ }
916
+ getSubscriptionMetrics() {
917
+ return this.updateEmitter.getMetrics();
918
+ }
881
919
  destroy() {
920
+ this.updateEmitter.destroy();
882
921
  this.index.destroy();
883
922
  }
884
923
  };
885
924
 
925
+ // src/abstract/OIMObjectStore.ts
926
+ var OIMObjectStore = class {
927
+ };
928
+
886
929
  // src/core/OIMPkSelectorFactory.ts
887
930
  var OIMPkSelectorFactory = class {
888
931
  createIdSelector() {
@@ -940,6 +983,9 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
940
983
  getAllPks() {
941
984
  return Array.from(this.entities.keys());
942
985
  }
986
+ destroy() {
987
+ this.clear();
988
+ }
943
989
  };
944
990
 
945
991
  // src/core/OIMEntityUpdaterFactory.ts
@@ -1037,31 +1083,9 @@ var OIMCollection = class {
1037
1083
  this.store.setOneByPk(pk, entity);
1038
1084
  }
1039
1085
  }
1040
- };
1041
-
1042
- // src/core/OIMUpdateEventCoalescerCollection.ts
1043
- var OIMUpdateEventCoalescerCollection = class extends OIMUpdateEventCoalescer {
1044
- constructor(collectionEmitter) {
1045
- super();
1046
- this.collectionEmitter = collectionEmitter;
1047
- /**
1048
- * When the collection is updated, we keep track of the updated pks.
1049
- */
1050
- this.handleUpdate = (payload) => {
1051
- this.addUpdatedKeys(payload.pks);
1052
- };
1053
- this.collectionEmitter.on(
1054
- 0 /* UPDATE */,
1055
- this.handleUpdate
1056
- );
1057
- }
1058
1086
  destroy() {
1059
- this.collectionEmitter.off(
1060
- 0 /* UPDATE */,
1061
- this.handleUpdate
1062
- );
1087
+ this.store.destroy();
1063
1088
  this.emitter.offAll();
1064
- this.clearUpdatedKeys();
1065
1089
  }
1066
1090
  };
1067
1091
 
@@ -1069,28 +1093,367 @@ var OIMUpdateEventCoalescerCollection = class extends OIMUpdateEventCoalescer {
1069
1093
  var OIMReactiveCollection = class extends OIMCollection {
1070
1094
  constructor(queue, opts) {
1071
1095
  super(opts);
1072
- this.coalescer = new OIMUpdateEventCoalescerCollection(
1073
- this.emitter
1074
- );
1075
- this.updateEventEmitter = new OIMUpdateEventEmitter({
1076
- coalescer: this.coalescer,
1077
- queue
1078
- });
1096
+ this.anyUpdateHandlers = /* @__PURE__ */ new Set();
1097
+ this.pendingAnyUpdatePks = /* @__PURE__ */ new Set();
1098
+ this.isAnyUpdateClearPending = false;
1099
+ this.isAnyUpdateScheduled = false;
1100
+ this.runAnyUpdate = () => {
1101
+ if (!this.isAnyUpdateScheduled) return;
1102
+ this.isAnyUpdateScheduled = false;
1103
+ this.dequeueAnyUpdate = void 0;
1104
+ const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks);
1105
+ this.pendingAnyUpdatePks.clear();
1106
+ this.isAnyUpdateClearPending = false;
1107
+ const snapshot = Array.from(this.anyUpdateHandlers);
1108
+ for (let i = 0; i < snapshot.length; i++) snapshot[i](pks);
1109
+ };
1110
+ this.queue = queue;
1111
+ this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
1079
1112
  }
1080
- };
1081
-
1082
- // src/core/OIMReactiveIndexManualSetBased.ts
1083
- var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1084
- constructor(queue, opts) {
1085
- super(queue, opts);
1113
+ subscribeOnKey(pk, handler) {
1114
+ return this.updateEmitter.subscribeOnKey(pk, handler);
1086
1115
  }
1087
- createDefaultIndex() {
1088
- return new OIMIndexManualSetBased();
1116
+ subscribeOnKeys(pks, handler) {
1117
+ return this.updateEmitter.subscribeOnKeys(pks, handler);
1089
1118
  }
1090
- setPks(key, pks) {
1091
- this.index.setPks(key, pks);
1119
+ unsubscribeFromKey(pk, handler) {
1120
+ this.updateEmitter.unsubscribeFromKey(pk, handler);
1092
1121
  }
1093
- addPks(key, pks) {
1122
+ unsubscribeFromKeys(pks, handler) {
1123
+ this.updateEmitter.unsubscribeFromKeys(pks, handler);
1124
+ }
1125
+ destroySubscriptions() {
1126
+ this.updateEmitter.destroy();
1127
+ }
1128
+ hasSubscriptions() {
1129
+ return this.updateEmitter.hasSubscriptions();
1130
+ }
1131
+ getHandlerCount(pk) {
1132
+ return this.updateEmitter.getHandlerCount(pk);
1133
+ }
1134
+ getMetrics() {
1135
+ return this.updateEmitter.getMetrics();
1136
+ }
1137
+ subscribeOnAnyUpdate(handler) {
1138
+ this.anyUpdateHandlers.add(handler);
1139
+ return () => {
1140
+ this.anyUpdateHandlers.delete(handler);
1141
+ };
1142
+ }
1143
+ upsertOneByPk(pk, entity) {
1144
+ this.upsertOneWithoutNotificationsByPk(pk, entity);
1145
+ this.updateEmitter.markUpdatedKey(pk);
1146
+ this.trackAnyUpdatePk(pk);
1147
+ }
1148
+ upsertOne(entity) {
1149
+ const pk = this.upsertOneWithoutNotifications(entity);
1150
+ this.updateEmitter.markUpdatedKey(pk);
1151
+ this.trackAnyUpdatePk(pk);
1152
+ }
1153
+ upsertMany(entities) {
1154
+ const pks = entities.map(
1155
+ (entity) => this.upsertOneWithoutNotifications(entity)
1156
+ );
1157
+ this.updateEmitter.markUpdatedKeys(pks);
1158
+ this.trackAnyUpdatePks(pks);
1159
+ }
1160
+ removeOne(entity) {
1161
+ const pk = this.selectPk(entity);
1162
+ this.store.removeOneByPk(pk);
1163
+ this.updateEmitter.markUpdatedKey(pk);
1164
+ this.trackAnyUpdatePk(pk);
1165
+ }
1166
+ removeMany(entities) {
1167
+ const pks = entities.map(this.selectPk);
1168
+ this.store.removeManyByPks(pks);
1169
+ this.updateEmitter.markUpdatedKeys(pks);
1170
+ this.trackAnyUpdatePks(pks);
1171
+ }
1172
+ removeOneByPk(pk) {
1173
+ this.store.removeOneByPk(pk);
1174
+ this.updateEmitter.markUpdatedKey(pk);
1175
+ this.trackAnyUpdatePk(pk);
1176
+ }
1177
+ removeManyByPks(pks) {
1178
+ this.store.removeManyByPks(pks);
1179
+ this.updateEmitter.markUpdatedKeys(pks);
1180
+ this.trackAnyUpdatePks(pks);
1181
+ }
1182
+ clear() {
1183
+ this.store.clear();
1184
+ this.updateEmitter.markAllUpdated();
1185
+ this.trackAnyUpdateClear();
1186
+ }
1187
+ trackAnyUpdatePk(pk) {
1188
+ if (this.anyUpdateHandlers.size === 0) return;
1189
+ this.pendingAnyUpdatePks.add(pk);
1190
+ this.ensureAnyUpdateEnqueued();
1191
+ }
1192
+ trackAnyUpdatePks(pks) {
1193
+ if (this.anyUpdateHandlers.size === 0) return;
1194
+ for (let i = 0; i < pks.length; i++)
1195
+ this.pendingAnyUpdatePks.add(pks[i]);
1196
+ this.ensureAnyUpdateEnqueued();
1197
+ }
1198
+ trackAnyUpdateClear() {
1199
+ if (this.anyUpdateHandlers.size === 0) return;
1200
+ this.isAnyUpdateClearPending = true;
1201
+ this.pendingAnyUpdatePks.clear();
1202
+ this.ensureAnyUpdateEnqueued();
1203
+ }
1204
+ ensureAnyUpdateEnqueued() {
1205
+ if (this.isAnyUpdateScheduled) return;
1206
+ this.isAnyUpdateScheduled = true;
1207
+ this.dequeueAnyUpdate = this.queue.enqueue(this.runAnyUpdate);
1208
+ }
1209
+ destroy() {
1210
+ this.updateEmitter.destroy();
1211
+ if (this.dequeueAnyUpdate) {
1212
+ this.dequeueAnyUpdate();
1213
+ this.dequeueAnyUpdate = void 0;
1214
+ }
1215
+ this.anyUpdateHandlers.clear();
1216
+ this.pendingAnyUpdatePks.clear();
1217
+ super.destroy();
1218
+ }
1219
+ };
1220
+
1221
+ // src/core/OIMObjectStoreRecordDriven.ts
1222
+ var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
1223
+ constructor(initial) {
1224
+ super();
1225
+ this.properties = Object.assign(/* @__PURE__ */ Object.create(null), initial ?? {});
1226
+ }
1227
+ setProperty(key, value) {
1228
+ this.properties[key] = value;
1229
+ }
1230
+ merge(draft) {
1231
+ Object.assign(this.properties, draft);
1232
+ }
1233
+ delete(key) {
1234
+ delete this.properties[key];
1235
+ }
1236
+ get(key) {
1237
+ return this.properties[key];
1238
+ }
1239
+ getAll() {
1240
+ return { ...this.properties };
1241
+ }
1242
+ clear() {
1243
+ this.properties = /* @__PURE__ */ Object.create(null);
1244
+ }
1245
+ count() {
1246
+ return Object.keys(this.properties).length;
1247
+ }
1248
+ keys() {
1249
+ return Object.keys(this.properties);
1250
+ }
1251
+ values() {
1252
+ return Object.values(this.properties);
1253
+ }
1254
+ entries() {
1255
+ return Object.entries(this.properties);
1256
+ }
1257
+ destroy() {
1258
+ this.clear();
1259
+ }
1260
+ };
1261
+
1262
+ // src/enum/EOIMObjectEventType.ts
1263
+ var EOIMObjectEventType = /* @__PURE__ */ ((EOIMObjectEventType2) => {
1264
+ EOIMObjectEventType2[EOIMObjectEventType2["UPDATE"] = 0] = "UPDATE";
1265
+ return EOIMObjectEventType2;
1266
+ })(EOIMObjectEventType || {});
1267
+
1268
+ // src/core/OIMObject.ts
1269
+ var OIMObject = class {
1270
+ constructor(opts) {
1271
+ this.emitter = new OIMEventEmitter();
1272
+ this.store = opts?.store ?? new OIMObjectStoreRecordDriven();
1273
+ }
1274
+ setProperty(key, value) {
1275
+ this.store.setProperty(key, value);
1276
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1277
+ }
1278
+ merge(draft) {
1279
+ this.store.merge(draft);
1280
+ this.emitter.emit(0 /* UPDATE */, {
1281
+ keys: Object.keys(draft)
1282
+ });
1283
+ }
1284
+ delete(key) {
1285
+ this.store.delete(key);
1286
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1287
+ }
1288
+ get(key) {
1289
+ return this.store.get(key);
1290
+ }
1291
+ getAll() {
1292
+ return this.store.getAll();
1293
+ }
1294
+ clear() {
1295
+ this.store.clear();
1296
+ this.emitter.emit(0 /* UPDATE */, { keys: [] });
1297
+ }
1298
+ count() {
1299
+ return this.store.count();
1300
+ }
1301
+ keys() {
1302
+ return this.store.keys();
1303
+ }
1304
+ values() {
1305
+ return this.store.values();
1306
+ }
1307
+ entries() {
1308
+ return this.store.entries();
1309
+ }
1310
+ destroy() {
1311
+ this.store.destroy();
1312
+ this.emitter.offAll();
1313
+ }
1314
+ };
1315
+
1316
+ // src/core/OIMReactiveObject.ts
1317
+ var OIMReactiveObject = class extends OIMObject {
1318
+ constructor(queue, opts) {
1319
+ super(opts);
1320
+ this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
1321
+ this.emitter.on(0 /* UPDATE */, (payload) => {
1322
+ this.updateEmitter.markUpdatedKeys(payload.keys);
1323
+ });
1324
+ }
1325
+ subscribeOnKey(key, handler) {
1326
+ return this.updateEmitter.subscribeOnKey(key, handler);
1327
+ }
1328
+ subscribeOnKeys(keys, handler) {
1329
+ return this.updateEmitter.subscribeOnKeys(keys, handler);
1330
+ }
1331
+ unsubscribeFromKey(key, handler) {
1332
+ this.updateEmitter.unsubscribeFromKey(key, handler);
1333
+ }
1334
+ unsubscribeFromKeys(keys, handler) {
1335
+ this.updateEmitter.unsubscribeFromKeys(keys, handler);
1336
+ }
1337
+ hasSubscriptions() {
1338
+ return this.updateEmitter.hasSubscriptions();
1339
+ }
1340
+ getHandlerCount(key) {
1341
+ return this.updateEmitter.getHandlerCount(key);
1342
+ }
1343
+ getMetrics() {
1344
+ return this.updateEmitter.getMetrics();
1345
+ }
1346
+ destroy() {
1347
+ this.updateEmitter.destroy();
1348
+ super.destroy();
1349
+ }
1350
+ };
1351
+
1352
+ // src/core/OIMIndexManualSetBased.ts
1353
+ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1354
+ constructor(options = {}) {
1355
+ super(options);
1356
+ }
1357
+ /**
1358
+ * Set primary keys for a specific index key, replacing any existing values.
1359
+ * Uses optional comparator to skip updates if PKs haven't actually changed.
1360
+ */
1361
+ setPks(key, pks) {
1362
+ const hasChanges = this.setPksWithComparison(key, new Set(pks));
1363
+ if (hasChanges) {
1364
+ this.emitUpdateOne(key);
1365
+ }
1366
+ }
1367
+ /**
1368
+ * Add primary keys to a specific index key
1369
+ */
1370
+ addPks(key, pks) {
1371
+ if (pks.length === 0) return;
1372
+ let pksSet = this.store.getOneByKey(key);
1373
+ if (!pksSet) {
1374
+ pksSet = /* @__PURE__ */ new Set();
1375
+ this.store.setOneByKey(key, pksSet);
1376
+ }
1377
+ let hasChanges = false;
1378
+ for (const pk of pks) {
1379
+ if (!pksSet.has(pk)) {
1380
+ pksSet.add(pk);
1381
+ hasChanges = true;
1382
+ }
1383
+ }
1384
+ if (hasChanges) {
1385
+ this.store.setOneByKey(key, pksSet);
1386
+ this.emitUpdateOne(key);
1387
+ }
1388
+ }
1389
+ /**
1390
+ * Remove primary keys from a specific index key
1391
+ */
1392
+ removePks(key, pks) {
1393
+ if (pks.length === 0) return;
1394
+ const pksSet = this.store.getOneByKey(key);
1395
+ if (!pksSet) return;
1396
+ let hasChanges = false;
1397
+ for (const pk of pks) {
1398
+ if (pksSet.delete(pk)) {
1399
+ hasChanges = true;
1400
+ }
1401
+ }
1402
+ if (pksSet.size === 0) {
1403
+ this.store.removeOneByKey(key);
1404
+ hasChanges = true;
1405
+ } else if (hasChanges) {
1406
+ this.store.setOneByKey(key, pksSet);
1407
+ }
1408
+ if (hasChanges) {
1409
+ this.emitUpdateOne(key);
1410
+ }
1411
+ }
1412
+ /**
1413
+ * Clear all primary keys for a specific index key, or all keys if no key specified
1414
+ */
1415
+ clear(key) {
1416
+ if (key === void 0) {
1417
+ const allKeys = this.store.getAllKeys();
1418
+ if (allKeys.length > 0) {
1419
+ this.store.clear();
1420
+ this.emitUpdate(allKeys);
1421
+ }
1422
+ } else {
1423
+ if (this.store.getOneByKey(key) !== void 0) {
1424
+ this.store.removeOneByKey(key);
1425
+ this.emitUpdateOne(key);
1426
+ }
1427
+ }
1428
+ }
1429
+ };
1430
+
1431
+ // src/core/OIMReactiveIndexManualSetBased.ts
1432
+ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1433
+ constructor(updateEmitter, opts) {
1434
+ super(opts);
1435
+ this.updateEmitter = updateEmitter;
1436
+ }
1437
+ emitUpdate(keys) {
1438
+ this.updateEmitter.markUpdatedKeys(keys);
1439
+ }
1440
+ emitUpdateOne(key) {
1441
+ this.updateEmitter.markUpdatedKey(key);
1442
+ }
1443
+ };
1444
+ var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1445
+ constructor(queue, opts) {
1446
+ super(queue, (updateEmitter) => {
1447
+ return new OIMIndexManualSetBasedReactive(
1448
+ updateEmitter,
1449
+ opts?.indexOptions
1450
+ );
1451
+ });
1452
+ }
1453
+ setPks(key, pks) {
1454
+ this.index.setPks(key, pks);
1455
+ }
1456
+ addPks(key, pks) {
1094
1457
  this.index.addPks(key, pks);
1095
1458
  }
1096
1459
  removePks(key, pks) {
@@ -1113,7 +1476,7 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1113
1476
  setPks(key, pks) {
1114
1477
  const hasChanges = this.setPksWithComparison(key, pks);
1115
1478
  if (hasChanges) {
1116
- this.emitUpdate([key]);
1479
+ this.emitUpdateOne(key);
1117
1480
  }
1118
1481
  }
1119
1482
  /**
@@ -1136,7 +1499,7 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1136
1499
  }
1137
1500
  if (hasChanges) {
1138
1501
  this.store.setOneByKey(key, Array.from(pksSet));
1139
- this.emitUpdate([key]);
1502
+ this.emitUpdateOne(key);
1140
1503
  }
1141
1504
  }
1142
1505
  /**
@@ -1155,7 +1518,7 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1155
1518
  } else {
1156
1519
  this.store.setOneByKey(key, filtered);
1157
1520
  }
1158
- this.emitUpdate([key]);
1521
+ this.emitUpdateOne(key);
1159
1522
  }
1160
1523
  }
1161
1524
  /**
@@ -1171,19 +1534,33 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1171
1534
  } else {
1172
1535
  if (this.store.getOneByKey(key) !== void 0) {
1173
1536
  this.store.removeOneByKey(key);
1174
- this.emitUpdate([key]);
1537
+ this.emitUpdateOne(key);
1175
1538
  }
1176
1539
  }
1177
1540
  }
1178
1541
  };
1179
1542
 
1180
1543
  // src/core/OIMReactiveIndexManualArrayBased.ts
1544
+ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1545
+ constructor(updateEmitter, opts) {
1546
+ super(opts);
1547
+ this.updateEmitter = updateEmitter;
1548
+ }
1549
+ emitUpdate(keys) {
1550
+ this.updateEmitter.markUpdatedKeys(keys);
1551
+ }
1552
+ emitUpdateOne(key) {
1553
+ this.updateEmitter.markUpdatedKey(key);
1554
+ }
1555
+ };
1181
1556
  var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
1182
1557
  constructor(queue, opts) {
1183
- super(queue, opts);
1184
- }
1185
- createDefaultIndex() {
1186
- return new OIMIndexManualArrayBased();
1558
+ super(queue, (updateEmitter) => {
1559
+ return new OIMIndexManualArrayBasedReactive(
1560
+ updateEmitter,
1561
+ opts?.indexOptions
1562
+ );
1563
+ });
1187
1564
  }
1188
1565
  setPks(key, pks) {
1189
1566
  this.index.setPks(key, pks);
@@ -1207,6 +1584,53 @@ var OIMRICollection = class extends OIMReactiveCollection {
1207
1584
  }
1208
1585
  };
1209
1586
 
1587
+ // src/core/OIMObjectStoreMapDriven.ts
1588
+ var OIMObjectStoreMapDriven = class extends OIMObjectStore {
1589
+ constructor() {
1590
+ super(...arguments);
1591
+ this.properties = /* @__PURE__ */ new Map();
1592
+ }
1593
+ setProperty(key, value) {
1594
+ this.properties.set(key, value);
1595
+ }
1596
+ merge(draft) {
1597
+ for (const key in draft) {
1598
+ if (!Object.prototype.hasOwnProperty.call(draft, key)) continue;
1599
+ this.properties.set(
1600
+ key,
1601
+ draft[key]
1602
+ );
1603
+ }
1604
+ }
1605
+ delete(key) {
1606
+ this.properties.delete(key);
1607
+ }
1608
+ get(key) {
1609
+ return this.properties.get(key);
1610
+ }
1611
+ getAll() {
1612
+ return Object.fromEntries(this.properties.entries());
1613
+ }
1614
+ clear() {
1615
+ this.properties.clear();
1616
+ }
1617
+ count() {
1618
+ return this.properties.size;
1619
+ }
1620
+ keys() {
1621
+ return Array.from(this.properties.keys());
1622
+ }
1623
+ values() {
1624
+ return Array.from(this.properties.values());
1625
+ }
1626
+ entries() {
1627
+ return Array.from(this.properties.entries());
1628
+ }
1629
+ destroy() {
1630
+ this.properties.clear();
1631
+ }
1632
+ };
1633
+
1210
1634
  // src/core/OIMComparatorFactory.ts
1211
1635
  var OIMComparatorFactory = class {
1212
1636
  createShallowComparator() {
@@ -1225,18 +1649,12 @@ var OIMComparatorFactory = class {
1225
1649
  }
1226
1650
  };
1227
1651
 
1228
- // src/enum/EOIMEventQueueEventType.ts
1229
- var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
1230
- EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
1231
- EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
1232
- return EOIMEventQueueEventType2;
1233
- })(EOIMEventQueueEventType || {});
1234
-
1235
1652
  // src/core/OIMEventQueue.ts
1236
1653
  var OIMEventQueue = class {
1237
1654
  constructor(options = {}) {
1238
1655
  this.emitter = new OIMEventEmitter();
1239
- this.queue = [];
1656
+ this.tasks = /* @__PURE__ */ new Set();
1657
+ this.isFlushing = false;
1240
1658
  this.scheduler = options.scheduler;
1241
1659
  if (this.scheduler) {
1242
1660
  this.flushBound = () => this.flush();
@@ -1247,48 +1665,76 @@ var OIMEventQueue = class {
1247
1665
  }
1248
1666
  }
1249
1667
  /**
1250
- * Add a function to the queue. If scheduler is configured and autoSchedule is enabled,
1251
- * automatically schedules a flush operation.
1668
+ * Enqueue a one-shot task to be executed on the next flush.
1669
+ * Returns a dequeue function that can be called to cancel the task before it runs.
1252
1670
  */
1253
1671
  enqueue(fn) {
1254
- this.queue.push(fn);
1255
- if (this.scheduler && this.queue.length === 1) {
1256
- this.scheduler.schedule();
1257
- }
1672
+ let isActive = true;
1673
+ const task = () => {
1674
+ if (!isActive) return;
1675
+ fn();
1676
+ };
1677
+ this.tasks.add(task);
1678
+ this.ensureScheduled();
1679
+ return () => {
1680
+ if (!isActive) return;
1681
+ isActive = false;
1682
+ this.tasks.delete(task);
1683
+ };
1684
+ }
1685
+ get isInFlush() {
1686
+ return this.isFlushing;
1687
+ }
1688
+ /**
1689
+ * Dequeue (cancel) a previously enqueued task by its dequeue function.
1690
+ */
1691
+ dequeue(dequeueFn) {
1692
+ dequeueFn();
1258
1693
  }
1259
1694
  /**
1260
- * Execute all queued functions and clear the queue.
1261
- * This method is safe to call multiple times and handles reentrancy.
1695
+ * Execute all pending tasks.
1696
+ *
1697
+ * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
1698
+ * executed within the same flush.
1262
1699
  */
1263
1700
  flush() {
1264
- if (this.queue.length === 0) return;
1701
+ this.scheduler?.cancel();
1702
+ this.isFlushing = true;
1265
1703
  this.emitter.emit(1 /* BEFORE_FLUSH */, void 0);
1266
- const currentQueue = this.queue.slice();
1267
- this.queue.length = 0;
1268
- for (let i = 0; i < currentQueue.length; i++) {
1269
- currentQueue[i]();
1704
+ const snapshot = Array.from(this.tasks);
1705
+ this.tasks.clear();
1706
+ for (let i = 0; i < snapshot.length; i++) {
1707
+ snapshot[i]();
1270
1708
  }
1709
+ this.isFlushing = false;
1271
1710
  this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
1272
1711
  }
1273
1712
  /**
1274
1713
  * Get the current number of queued functions.
1275
1714
  */
1276
1715
  get length() {
1277
- return this.queue.length;
1716
+ return this.tasks.size;
1278
1717
  }
1279
1718
  /**
1280
1719
  * Check if the queue is empty.
1281
1720
  */
1282
1721
  get isEmpty() {
1283
- return this.queue.length === 0;
1722
+ return this.tasks.size === 0;
1284
1723
  }
1285
1724
  /**
1286
1725
  * Clear the queue without executing functions and cancel any scheduled flush.
1287
1726
  */
1288
1727
  clear() {
1289
- this.queue.length = 0;
1728
+ this.tasks.clear();
1290
1729
  this.scheduler?.cancel();
1291
1730
  }
1731
+ ensureScheduled() {
1732
+ if (!this.scheduler) return;
1733
+ if (this.isFlushing) return;
1734
+ if (this.tasks.size === 1) {
1735
+ this.scheduler.schedule();
1736
+ }
1737
+ }
1292
1738
  /**
1293
1739
  * Clean up scheduler subscription when queue is no longer needed.
1294
1740
  */
@@ -1336,7 +1782,7 @@ var OIMIndexComparatorFactory = class {
1336
1782
  if (existingSet.size !== newSet.size || existingSet.size !== existingPks.length) {
1337
1783
  return false;
1338
1784
  }
1339
- for (const pk of existingSet) {
1785
+ for (const pk of Array.from(existingSet)) {
1340
1786
  if (!newSet.has(pk)) {
1341
1787
  return false;
1342
1788
  }
@@ -1402,9 +1848,390 @@ var OIMMap2Keys = class {
1402
1848
  }
1403
1849
  };
1404
1850
 
1405
- // src/core/event-queue-scheduler/OIMEventQueueSchedulerAnimationFrame.ts
1406
- var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler {
1407
- constructor() {
1851
+ // src/modules/wrapper/collection/OIMCollectionChangedFieldsWrapper.ts
1852
+ var OIMCollectionChangedFieldsWrapper = class {
1853
+ constructor(queue, collection, opts) {
1854
+ this.isInWrite = false;
1855
+ this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
1856
+ this.changedFieldsByPk = /* @__PURE__ */ new Map();
1857
+ this.changedPksByField = /* @__PURE__ */ new Map();
1858
+ this.onAfterFlush = () => {
1859
+ this.clear();
1860
+ };
1861
+ this.onCollectionUpdate = (payload) => {
1862
+ if (this.isInWrite) return;
1863
+ if (payload.pks.length === 0) return;
1864
+ for (let i = 0; i < payload.pks.length; i++) {
1865
+ const pk = payload.pks[i];
1866
+ this.trackExternalMutationByPk(pk);
1867
+ }
1868
+ };
1869
+ this.collection = collection;
1870
+ this.selectPk = opts?.selectPk ?? (() => {
1871
+ throw new Error(
1872
+ `[OIMCollectionChangedFieldsWrapper]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
1873
+ );
1874
+ });
1875
+ this.changedPksEventEmitter = new OIMUpdateEventEmitter(
1876
+ queue,
1877
+ "after_flush"
1878
+ );
1879
+ this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
1880
+ this.unsubscribeAfterFlush = queue.emitter.on(
1881
+ 0 /* AFTER_FLUSH */,
1882
+ this.onAfterFlush
1883
+ );
1884
+ const maybeReactive = collection;
1885
+ this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
1886
+ this.onCollectionUpdate({
1887
+ pks
1888
+ });
1889
+ }) : collection.emitter.on(
1890
+ 0 /* UPDATE */,
1891
+ this.onCollectionUpdate
1892
+ );
1893
+ }
1894
+ upsertOneByPk(pk, patch) {
1895
+ this.withWrite(() => {
1896
+ this.trackPatch(pk, patch);
1897
+ this.collection.upsertOneByPk(pk, patch);
1898
+ });
1899
+ this.updateEntitySnapshot(pk);
1900
+ }
1901
+ upsertOne(entity) {
1902
+ this.withWrite(() => {
1903
+ const pk = this.selectPk(entity);
1904
+ this.trackPatch(pk, entity);
1905
+ this.collection.upsertOne(entity);
1906
+ });
1907
+ this.updateEntitySnapshot(this.selectPk(entity));
1908
+ }
1909
+ upsertMany(entities) {
1910
+ const pks = [];
1911
+ this.withWrite(() => {
1912
+ for (const entity of entities) {
1913
+ const pk = this.selectPk(entity);
1914
+ pks.push(pk);
1915
+ this.trackPatch(pk, entity);
1916
+ }
1917
+ this.collection.upsertMany(entities);
1918
+ });
1919
+ for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
1920
+ }
1921
+ getChangedFieldsByPk(pk) {
1922
+ return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
1923
+ }
1924
+ consumeChangedFieldsByPk(pk) {
1925
+ const existing = this.changedFieldsByPk.get(pk);
1926
+ if (!existing) return /* @__PURE__ */ new Set();
1927
+ return new Set(existing);
1928
+ }
1929
+ getChangedPksByField(field) {
1930
+ return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
1931
+ }
1932
+ consumeChangedPksByField(field) {
1933
+ const existing = this.changedPksByField.get(field);
1934
+ if (!existing) return /* @__PURE__ */ new Set();
1935
+ return new Set(existing);
1936
+ }
1937
+ clear() {
1938
+ this.changedFieldsByPk.clear();
1939
+ this.changedPksByField.clear();
1940
+ }
1941
+ destroy() {
1942
+ this.changedPksEventEmitter.destroy();
1943
+ this.changedFieldsEventEmitter.destroy();
1944
+ this.unsubscribeAfterFlush();
1945
+ this.unsubscribeFromCollectionEmitter();
1946
+ this.clear();
1947
+ }
1948
+ withWrite(fn) {
1949
+ if (this.isInWrite) {
1950
+ fn();
1951
+ return;
1952
+ }
1953
+ this.isInWrite = true;
1954
+ try {
1955
+ fn();
1956
+ } finally {
1957
+ this.isInWrite = false;
1958
+ }
1959
+ }
1960
+ trackExternalMutationByPk(pk) {
1961
+ const prev = this.lastEntitySnapshotByPk.get(pk);
1962
+ const next = this.collection.getOneByPk(pk);
1963
+ const changedKeys = this.diffChangedKeys(prev, next);
1964
+ this.updateEntitySnapshot(pk);
1965
+ if (changedKeys.length > 0) {
1966
+ this.trackChangedKeys(pk, changedKeys);
1967
+ } else {
1968
+ this.changedPksEventEmitter.markUpdatedKeys([pk]);
1969
+ }
1970
+ }
1971
+ diffChangedKeys(prev, next) {
1972
+ if (!prev && !next) return [];
1973
+ if (!prev && next) return Object.keys(next);
1974
+ if (prev && !next) return Object.keys(prev);
1975
+ const prevObj = prev;
1976
+ const nextObj = next;
1977
+ const keys = /* @__PURE__ */ new Set();
1978
+ Object.keys(prevObj).forEach((k) => keys.add(k));
1979
+ Object.keys(nextObj).forEach((k) => keys.add(k));
1980
+ const changed = [];
1981
+ keys.forEach((k) => {
1982
+ if (prevObj[k] !== nextObj[k])
1983
+ changed.push(k);
1984
+ });
1985
+ return changed;
1986
+ }
1987
+ updateEntitySnapshot(pk) {
1988
+ const entity = this.collection.getOneByPk(pk);
1989
+ if (!entity) {
1990
+ this.lastEntitySnapshotByPk.delete(pk);
1991
+ return;
1992
+ }
1993
+ this.lastEntitySnapshotByPk.set(pk, {
1994
+ ...entity
1995
+ });
1996
+ }
1997
+ trackChangedKeys(pk, keys) {
1998
+ let pkFields = this.changedFieldsByPk.get(pk);
1999
+ if (!pkFields) {
2000
+ pkFields = /* @__PURE__ */ new Set();
2001
+ this.changedFieldsByPk.set(pk, pkFields);
2002
+ }
2003
+ for (const key of keys) {
2004
+ pkFields.add(key);
2005
+ let fieldPks = this.changedPksByField.get(key);
2006
+ if (!fieldPks) {
2007
+ fieldPks = /* @__PURE__ */ new Set();
2008
+ this.changedPksByField.set(key, fieldPks);
2009
+ }
2010
+ fieldPks.add(pk);
2011
+ }
2012
+ this.changedPksEventEmitter.markUpdatedKeys([pk]);
2013
+ this.changedFieldsEventEmitter.markUpdatedKeys(keys);
2014
+ }
2015
+ trackPatch(pk, patch) {
2016
+ if (pk === void 0 || pk === null || pk === "") {
2017
+ throw new Error(
2018
+ `[OIMCollectionChangedFieldsWrapper]: PK is required to track changes.`
2019
+ );
2020
+ }
2021
+ const keys = Object.keys(patch);
2022
+ if (keys.length === 0) return;
2023
+ this.trackChangedKeys(pk, keys);
2024
+ }
2025
+ };
2026
+
2027
+ // src/modules/wrapper/index/OIMIndexManualOrderedArrayBased.ts
2028
+ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
2029
+ clear(key) {
2030
+ if (key === void 0) {
2031
+ const keys = this.getKeys();
2032
+ if (keys.length > 0) {
2033
+ this.store.clear();
2034
+ this.emitUpdate(Array.from(keys));
2035
+ }
2036
+ return;
2037
+ }
2038
+ if (this.store.getOneByKey(key) !== void 0) {
2039
+ this.store.removeOneByKey(key);
2040
+ this.emitUpdate([key]);
2041
+ }
2042
+ }
2043
+ push(key, itemKey) {
2044
+ const list = this.getOrCreateList(key);
2045
+ const index = list.length;
2046
+ list.push(itemKey);
2047
+ this.emitUpdate([key]);
2048
+ return index;
2049
+ }
2050
+ insertAt(key, index, itemKey) {
2051
+ const list = this.getOrCreateList(key);
2052
+ const safeIndex = Math.max(0, Math.min(index, list.length));
2053
+ list.splice(safeIndex, 0, itemKey);
2054
+ this.emitUpdate([key]);
2055
+ }
2056
+ removeAt(key, index) {
2057
+ const list = this.store.getOneByKey(key);
2058
+ if (!list) return void 0;
2059
+ if (index < 0 || index >= list.length) return void 0;
2060
+ const [removed] = list.splice(index, 1);
2061
+ if (list.length === 0) {
2062
+ this.store.removeOneByKey(key);
2063
+ }
2064
+ this.emitUpdate([key]);
2065
+ return removed;
2066
+ }
2067
+ move(key, fromIndex, toIndex) {
2068
+ const list = this.store.getOneByKey(key);
2069
+ if (!list) return void 0;
2070
+ if (fromIndex < 0 || fromIndex >= list.length) return void 0;
2071
+ if (toIndex < 0 || toIndex >= list.length) return void 0;
2072
+ if (fromIndex === toIndex) return list[fromIndex];
2073
+ const [itemKey] = list.splice(fromIndex, 1);
2074
+ if (itemKey === void 0) return void 0;
2075
+ list.splice(toIndex, 0, itemKey);
2076
+ this.emitUpdate([key]);
2077
+ return itemKey;
2078
+ }
2079
+ reset(key, itemKeys) {
2080
+ if (itemKeys.length === 0) {
2081
+ if (this.store.getOneByKey(key) !== void 0) {
2082
+ this.store.removeOneByKey(key);
2083
+ this.emitUpdate([key]);
2084
+ }
2085
+ return;
2086
+ }
2087
+ this.store.setOneByKey(key, Array.from(itemKeys));
2088
+ this.emitUpdate([key]);
2089
+ }
2090
+ getOrCreateList(key) {
2091
+ let list = this.store.getOneByKey(key);
2092
+ if (!list) {
2093
+ list = [];
2094
+ this.store.setOneByKey(key, list);
2095
+ }
2096
+ return list;
2097
+ }
2098
+ };
2099
+
2100
+ // src/modules/wrapper/index/OIMOrderedListIndexCommandStreamWrapper.ts
2101
+ var OIMOrderedListIndexCommandStreamWrapper = class {
2102
+ constructor(queue, index) {
2103
+ this.isInWrite = false;
2104
+ this.commandsByKey = /* @__PURE__ */ new Map();
2105
+ this.onAfterFlush = () => {
2106
+ this.commandsByKey.clear();
2107
+ };
2108
+ this.onIndexUpdate = (payload) => {
2109
+ if (this.isInWrite) return;
2110
+ if (payload.keys.length === 0) return;
2111
+ for (let i = 0; i < payload.keys.length; i++) {
2112
+ const key = payload.keys[i];
2113
+ const list = this.index.getPksByKey(key);
2114
+ this.appendCommand(key, { type: "set", keys: Array.from(list) });
2115
+ }
2116
+ };
2117
+ this.commandsEventEmitter = new OIMUpdateEventEmitter(
2118
+ queue,
2119
+ "after_flush"
2120
+ );
2121
+ this.index = index ?? new OIMIndexManualOrderedArrayBased();
2122
+ this.unsubscribeAfterFlush = queue.emitter.on(
2123
+ 0 /* AFTER_FLUSH */,
2124
+ this.onAfterFlush
2125
+ );
2126
+ this.unsubscribeFromIndexEmitter = this.index.emitter.on(
2127
+ 0 /* UPDATE */,
2128
+ this.onIndexUpdate
2129
+ );
2130
+ }
2131
+ getPksByKey(key) {
2132
+ return this.index.getPksByKey(key);
2133
+ }
2134
+ hasKey(key) {
2135
+ return this.index.hasKey(key);
2136
+ }
2137
+ getKeys() {
2138
+ return this.index.getKeys();
2139
+ }
2140
+ push(key, itemKey) {
2141
+ this.withWrite(() => {
2142
+ const index = this.index.push(key, itemKey);
2143
+ this.appendCommand(key, { type: "add", key: itemKey, index });
2144
+ });
2145
+ }
2146
+ removeAt(key, index) {
2147
+ this.withWrite(() => {
2148
+ const itemKey = this.index.removeAt(key, index);
2149
+ if (itemKey === void 0) return;
2150
+ this.appendCommand(key, { type: "remove", key: itemKey, index });
2151
+ });
2152
+ }
2153
+ move(key, fromIndex, toIndex) {
2154
+ this.withWrite(() => {
2155
+ const itemKey = this.index.move(key, fromIndex, toIndex);
2156
+ if (itemKey === void 0) return;
2157
+ this.appendCommand(key, {
2158
+ type: "move",
2159
+ key: itemKey,
2160
+ fromIndex,
2161
+ toIndex
2162
+ });
2163
+ });
2164
+ }
2165
+ set(key, keys) {
2166
+ this.withWrite(() => {
2167
+ this.index.reset(key, keys);
2168
+ this.appendCommand(key, { type: "set", keys });
2169
+ });
2170
+ }
2171
+ /**
2172
+ * Read-only peek at buffered commands without clearing them.
2173
+ */
2174
+ getBufferedCommands(key) {
2175
+ return this.commandsByKey.get(key) ?? [];
2176
+ }
2177
+ /**
2178
+ * Drain buffered commands for a key, preserving order.
2179
+ * Intended usage: call inside a `commandsEventEmitter.subscribeOnKey(key, ...)` handler.
2180
+ */
2181
+ consumeCommands(key) {
2182
+ const cmds = this.commandsByKey.get(key);
2183
+ if (!cmds || cmds.length === 0) return [];
2184
+ return cmds.slice();
2185
+ }
2186
+ clear(key) {
2187
+ if (key === void 0) {
2188
+ this.index.destroy();
2189
+ this.commandsByKey.clear();
2190
+ return;
2191
+ }
2192
+ this.index.clear(key);
2193
+ this.commandsByKey.delete(key);
2194
+ }
2195
+ destroy() {
2196
+ this.commandsEventEmitter.destroy();
2197
+ this.unsubscribeAfterFlush();
2198
+ this.unsubscribeFromIndexEmitter();
2199
+ this.clear();
2200
+ }
2201
+ withWrite(fn) {
2202
+ if (this.isInWrite) {
2203
+ fn();
2204
+ return;
2205
+ }
2206
+ this.isInWrite = true;
2207
+ try {
2208
+ fn();
2209
+ } finally {
2210
+ this.isInWrite = false;
2211
+ }
2212
+ }
2213
+ appendCommand(key, cmd) {
2214
+ let cmds = this.commandsByKey.get(key);
2215
+ if (!cmds) {
2216
+ cmds = [];
2217
+ this.commandsByKey.set(key, cmds);
2218
+ }
2219
+ if (cmd.type === "set") {
2220
+ cmds.length = 0;
2221
+ cmds.push(cmd);
2222
+ } else if (cmds.length > 0 && cmds[0].type === "set") {
2223
+ const setCmd = cmds[0];
2224
+ setCmd.keys = Array.from(this.index.getPksByKey(key));
2225
+ } else {
2226
+ cmds.push(cmd);
2227
+ }
2228
+ this.commandsEventEmitter.markUpdatedKeys([key]);
2229
+ }
2230
+ };
2231
+
2232
+ // src/core/event-queue-scheduler/OIMEventQueueSchedulerAnimationFrame.ts
2233
+ var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler {
2234
+ constructor() {
1408
2235
  super();
1409
2236
  this.useRequestAnimationFrame = typeof requestAnimationFrame !== "undefined";
1410
2237
  }
@@ -1570,18 +2397,843 @@ var OIMEventQueueSchedulerFactory = class {
1570
2397
  return new OIMEventQueueSchedulerImmediate();
1571
2398
  }
1572
2399
  };
2400
+
2401
+ // src/const/OIMDBSettings.ts
2402
+ var OIMDBSettings = class {
2403
+ };
2404
+ /**
2405
+ * Fixed key used in the manual index to store updated keys set in coalescers.
2406
+ * This constant is exported so users can create their own indexes and pass them to coalescers.
2407
+ */
2408
+ OIMDBSettings.UPDATED_KEYS_INDEX_KEY = "__updatedKeys__";
2409
+ OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
2410
+
2411
+ // src/modules/effect/core/OIMEffectDependencyComputed.ts
2412
+ var OIMEffectDependencyComputed = class {
2413
+ constructor(computed) {
2414
+ this.computed = computed;
2415
+ }
2416
+ subscribe(onUpdate) {
2417
+ return this.computed.emitter.on("update", onUpdate);
2418
+ }
2419
+ };
2420
+
2421
+ // src/modules/effect/core/OIMEffect.ts
2422
+ var OIMEffect = class {
2423
+ constructor(runtime, opts) {
2424
+ this.sourceDeps = [];
2425
+ this.computedDeps = [];
2426
+ this.sourceUnsubscribers = [];
2427
+ this.computedUnsubscribers = [];
2428
+ this.isResubscribeEnqueued = false;
2429
+ this.isTemporarilyUnsubscribedForFlush = false;
2430
+ this.isScheduled = false;
2431
+ this.runTask = () => {
2432
+ this.isScheduled = false;
2433
+ this.dequeueScheduled = void 0;
2434
+ this.runOnce();
2435
+ };
2436
+ this.onSourceInvalidate = () => {
2437
+ this.onUpdate?.();
2438
+ if (!this.isTemporarilyUnsubscribedForFlush) {
2439
+ this.isTemporarilyUnsubscribedForFlush = true;
2440
+ this.unsubscribeFromSourceDeps();
2441
+ this.ensureResubscribeAfterFlush();
2442
+ }
2443
+ if (this.isScheduled) return;
2444
+ this.isScheduled = true;
2445
+ this.dequeueScheduled = this.runtime.schedule(this.runTask);
2446
+ };
2447
+ this.onComputedInvalidate = () => {
2448
+ this.onUpdate?.();
2449
+ if (this.isScheduled) return;
2450
+ this.isScheduled = true;
2451
+ this.dequeueScheduled = this.runtime.schedule(this.runTask);
2452
+ };
2453
+ this.onAfterFlush = () => {
2454
+ if (!this.isResubscribeEnqueued) return;
2455
+ this.isResubscribeEnqueued = false;
2456
+ this.dequeueResubscribe = void 0;
2457
+ this.isTemporarilyUnsubscribedForFlush = false;
2458
+ if (this.sourceUnsubscribers.length === 0) {
2459
+ for (const dep of this.sourceDeps) {
2460
+ this.sourceUnsubscribers.push(
2461
+ dep.subscribe(this.onSourceInvalidate)
2462
+ );
2463
+ }
2464
+ }
2465
+ };
2466
+ this.runtime = runtime;
2467
+ this.onUpdate = opts.onUpdate;
2468
+ this.run = opts.run;
2469
+ this.deps = opts.deps ?? [];
2470
+ for (const dep of this.deps) {
2471
+ if (dep instanceof OIMEffectDependencyComputed) {
2472
+ this.computedDeps.push(dep);
2473
+ } else {
2474
+ this.sourceDeps.push(dep);
2475
+ }
2476
+ }
2477
+ this.subscribeToDeps();
2478
+ }
2479
+ destroy() {
2480
+ if (this.dequeueScheduled) {
2481
+ this.dequeueScheduled();
2482
+ this.dequeueScheduled = void 0;
2483
+ }
2484
+ this.unsubscribeFromAllDeps();
2485
+ if (this.dequeueResubscribe) {
2486
+ this.dequeueResubscribe();
2487
+ this.dequeueResubscribe = void 0;
2488
+ }
2489
+ this.isResubscribeEnqueued = false;
2490
+ this.isTemporarilyUnsubscribedForFlush = false;
2491
+ }
2492
+ runOnce() {
2493
+ this.run();
2494
+ }
2495
+ subscribeToDeps() {
2496
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2497
+ return;
2498
+ for (const dep of this.sourceDeps) {
2499
+ this.sourceUnsubscribers.push(
2500
+ dep.subscribe(this.onSourceInvalidate)
2501
+ );
2502
+ }
2503
+ for (const dep of this.computedDeps) {
2504
+ this.computedUnsubscribers.push(
2505
+ dep.subscribe(this.onComputedInvalidate)
2506
+ );
2507
+ }
2508
+ }
2509
+ unsubscribeFromSourceDeps() {
2510
+ if (this.sourceUnsubscribers.length === 0) return;
2511
+ for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
2512
+ this.sourceUnsubscribers = [];
2513
+ }
2514
+ unsubscribeFromAllDeps() {
2515
+ this.unsubscribeFromSourceDeps();
2516
+ if (this.computedUnsubscribers.length === 0) return;
2517
+ for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
2518
+ this.computedUnsubscribers = [];
2519
+ }
2520
+ ensureResubscribeAfterFlush() {
2521
+ if (this.isResubscribeEnqueued) return;
2522
+ this.isResubscribeEnqueued = true;
2523
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2524
+ this.onAfterFlush
2525
+ );
2526
+ }
2527
+ };
2528
+
2529
+ // src/modules/computed/enum/EOIMComputedEventType.ts
2530
+ var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
2531
+ EOIMComputedEventType2["UPDATE"] = "update";
2532
+ return EOIMComputedEventType2;
2533
+ })(EOIMComputedEventType || {});
2534
+
2535
+ // src/modules/computed/core/OIMComputed.ts
2536
+ var OIMComputed = class {
2537
+ constructor(runtime, opts) {
2538
+ this.emitter = new OIMEventEmitter();
2539
+ this.hasValue = false;
2540
+ this.isDirty = true;
2541
+ this.runtime = runtime;
2542
+ this.compute = opts.compute;
2543
+ this.compare = opts.compare ?? Object.is;
2544
+ this.deps = opts.deps ?? [];
2545
+ this.updateEventEmitter = new OIMUpdateEventEmitter(
2546
+ this.runtime.queue,
2547
+ "immediate"
2548
+ );
2549
+ this.effect = new OIMEffect(this.runtime, {
2550
+ deps: this.deps,
2551
+ onUpdate: () => {
2552
+ this.isDirty = true;
2553
+ },
2554
+ run: () => {
2555
+ this.recomputeAndEmitIfChanged();
2556
+ }
2557
+ });
2558
+ }
2559
+ get() {
2560
+ this.recomputeAndEmitIfChanged();
2561
+ return this.value;
2562
+ }
2563
+ getIfReady() {
2564
+ return this.hasValue ? this.value : void 0;
2565
+ }
2566
+ destroy() {
2567
+ this.effect.destroy();
2568
+ this.updateEventEmitter.destroy();
2569
+ this.emitter.offAll();
2570
+ }
2571
+ recomputeAndEmitIfChanged() {
2572
+ if (!this.isDirty) return;
2573
+ const prev = this.value;
2574
+ const hadPrev = this.hasValue;
2575
+ const next = this.compute();
2576
+ this.value = next;
2577
+ this.hasValue = true;
2578
+ this.isDirty = false;
2579
+ const changed = !hadPrev || !this.compare(prev, next);
2580
+ if (!changed) return;
2581
+ this.emitter.emit("update" /* UPDATE */, {
2582
+ keys: ["value"]
2583
+ });
2584
+ this.updateEventEmitter.markUpdatedKey("value");
2585
+ }
2586
+ };
2587
+
2588
+ // src/modules/effect/core/OIMEffectDependencyKeyed.ts
2589
+ function isReadonlyArray(value) {
2590
+ return Array.isArray(value);
2591
+ }
2592
+ var OIMEffectDependencyKeyed = class {
2593
+ constructor(subscription, keyOrKeys) {
2594
+ this.subscription = subscription;
2595
+ if (isReadonlyArray(keyOrKeys)) {
2596
+ this.keys = keyOrKeys;
2597
+ } else {
2598
+ this.key = keyOrKeys;
2599
+ }
2600
+ }
2601
+ subscribe(onUpdate) {
2602
+ if (this.keys !== void 0) {
2603
+ return this.subscription.subscribeOnKeys(this.keys, onUpdate);
2604
+ }
2605
+ if (this.key === void 0) return () => {
2606
+ };
2607
+ return this.subscription.subscribeOnKey(this.key, onUpdate);
2608
+ }
2609
+ };
2610
+
2611
+ // src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
2612
+ var OIMEffectDependencyKeyedObject = class {
2613
+ constructor(object, keyOrKeys) {
2614
+ this.dep = new OIMEffectDependencyKeyed(
2615
+ object,
2616
+ keyOrKeys
2617
+ );
2618
+ }
2619
+ subscribe(onUpdate) {
2620
+ return this.dep.subscribe(onUpdate);
2621
+ }
2622
+ };
2623
+
2624
+ // src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
2625
+ var OIMEffectDependencyKeyedCollection = class {
2626
+ constructor(collection, pkOrPks) {
2627
+ this.dep = new OIMEffectDependencyKeyed(
2628
+ collection,
2629
+ pkOrPks
2630
+ );
2631
+ }
2632
+ subscribe(onUpdate) {
2633
+ return this.dep.subscribe(onUpdate);
2634
+ }
2635
+ };
2636
+
2637
+ // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
2638
+ var OIMEffectDependencyKeyedIndex = class {
2639
+ constructor(index, keyOrKeys) {
2640
+ this.dep = new OIMEffectDependencyKeyed(
2641
+ index,
2642
+ keyOrKeys
2643
+ );
2644
+ }
2645
+ subscribe(onUpdate) {
2646
+ return this.dep.subscribe(onUpdate);
2647
+ }
2648
+ };
2649
+
2650
+ // src/modules/selector/core/OIMSelector.ts
2651
+ var OIMSelector = class {
2652
+ constructor(runtime, sourceDeps, computedDeps = []) {
2653
+ this.runtime = runtime;
2654
+ this.sourceDeps = sourceDeps;
2655
+ this.computedDeps = computedDeps;
2656
+ this.listeners = /* @__PURE__ */ new Set();
2657
+ this.sourceUnsubscribers = [];
2658
+ this.computedUnsubscribers = [];
2659
+ this.isResubscribeEnqueued = false;
2660
+ this.isTemporarilyUnsubscribedForFlush = false;
2661
+ this.isDeliveryEnqueued = false;
2662
+ this.hasLastValue = false;
2663
+ this.onSourceInvalidate = () => {
2664
+ if (this.listeners.size === 0) return;
2665
+ if (!this.isTemporarilyUnsubscribedForFlush) {
2666
+ this.isTemporarilyUnsubscribedForFlush = true;
2667
+ this.unsubscribeFromSourceDeps();
2668
+ this.ensureResubscribeAfterFlush();
2669
+ }
2670
+ if (this.isDeliveryEnqueued) return;
2671
+ this.isDeliveryEnqueued = true;
2672
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2673
+ };
2674
+ this.onComputedInvalidate = () => {
2675
+ if (this.listeners.size === 0) return;
2676
+ if (this.isDeliveryEnqueued) return;
2677
+ this.isDeliveryEnqueued = true;
2678
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2679
+ };
2680
+ this.deliver = () => {
2681
+ this.isDeliveryEnqueued = false;
2682
+ this.dequeueDelivery = void 0;
2683
+ if (this.listeners.size === 0) return;
2684
+ const next = this.getValue();
2685
+ if (this.hasLastValue && this.areEqual(this.lastValue, next)) return;
2686
+ this.setLastValue(next);
2687
+ const snapshot = Array.from(this.listeners);
2688
+ for (let i = 0; i < snapshot.length; i++) {
2689
+ const listener = snapshot[i];
2690
+ if (this.listeners.has(listener)) listener(next);
2691
+ }
2692
+ };
2693
+ this.onAfterFlush = () => {
2694
+ if (!this.isResubscribeEnqueued) return;
2695
+ this.isResubscribeEnqueued = false;
2696
+ this.dequeueResubscribe = void 0;
2697
+ if (this.listeners.size === 0) return;
2698
+ this.isTemporarilyUnsubscribedForFlush = false;
2699
+ if (this.sourceUnsubscribers.length === 0) {
2700
+ for (const dep of this.sourceDeps) {
2701
+ this.sourceUnsubscribers.push(
2702
+ dep.subscribe(this.onSourceInvalidate)
2703
+ );
2704
+ }
2705
+ }
2706
+ };
2707
+ }
2708
+ get queue() {
2709
+ return this.runtime.queue;
2710
+ }
2711
+ /**
2712
+ * Start watching this selector. The callback is called immediately with the current value,
2713
+ * then coalesced and delivered on `queue.flush()` when the selector invalidates.
2714
+ */
2715
+ watch(onValue) {
2716
+ this.listeners.add(onValue);
2717
+ if (this.listeners.size === 1) {
2718
+ this.subscribeToDeps();
2719
+ }
2720
+ const current = this.getValue();
2721
+ this.setLastValue(current);
2722
+ onValue(current);
2723
+ return () => {
2724
+ this.listeners.delete(onValue);
2725
+ if (this.listeners.size > 0) return;
2726
+ this.unsubscribeFromAllDeps();
2727
+ if (this.dequeueResubscribe) {
2728
+ this.dequeueResubscribe();
2729
+ this.dequeueResubscribe = void 0;
2730
+ }
2731
+ this.isResubscribeEnqueued = false;
2732
+ this.isTemporarilyUnsubscribedForFlush = false;
2733
+ if (this.dequeueDelivery) {
2734
+ this.dequeueDelivery();
2735
+ this.dequeueDelivery = void 0;
2736
+ }
2737
+ this.isDeliveryEnqueued = false;
2738
+ this.hasLastValue = false;
2739
+ };
2740
+ }
2741
+ areEqual(prev, next) {
2742
+ return Object.is(prev, next);
2743
+ }
2744
+ setLastValue(value) {
2745
+ this.lastValue = value;
2746
+ this.hasLastValue = true;
2747
+ }
2748
+ subscribeToDeps() {
2749
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2750
+ return;
2751
+ for (const dep of this.sourceDeps) {
2752
+ this.sourceUnsubscribers.push(
2753
+ dep.subscribe(this.onSourceInvalidate)
2754
+ );
2755
+ }
2756
+ for (const dep of this.computedDeps) {
2757
+ this.computedUnsubscribers.push(
2758
+ dep.subscribe(this.onComputedInvalidate)
2759
+ );
2760
+ }
2761
+ }
2762
+ unsubscribeFromSourceDeps() {
2763
+ if (this.sourceUnsubscribers.length === 0) return;
2764
+ for (const unsub of this.sourceUnsubscribers) unsub();
2765
+ this.sourceUnsubscribers = [];
2766
+ }
2767
+ unsubscribeFromAllDeps() {
2768
+ this.unsubscribeFromSourceDeps();
2769
+ if (this.computedUnsubscribers.length === 0) return;
2770
+ for (const unsub of this.computedUnsubscribers) unsub();
2771
+ this.computedUnsubscribers = [];
2772
+ }
2773
+ ensureResubscribeAfterFlush() {
2774
+ if (this.isResubscribeEnqueued) return;
2775
+ this.isResubscribeEnqueued = true;
2776
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2777
+ this.onAfterFlush
2778
+ );
2779
+ }
2780
+ };
2781
+
2782
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKey.ts
2783
+ var OIMSelectorSourceDependencyUpdateEventEmitterKey = class {
2784
+ constructor(subscription, key) {
2785
+ this.subscription = subscription;
2786
+ this.key = key;
2787
+ }
2788
+ subscribe(onUpdate) {
2789
+ return this.subscription.subscribeOnKey(this.key, onUpdate);
2790
+ }
2791
+ };
2792
+
2793
+ // src/modules/selector/core/OIMCollectionByPkSelector.ts
2794
+ var OIMCollectionByPkSelector = class extends OIMSelector {
2795
+ constructor(runtime, collection, pk) {
2796
+ super(runtime, [
2797
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2798
+ collection,
2799
+ pk
2800
+ )
2801
+ ]);
2802
+ this.collection = collection;
2803
+ this.pk = pk;
2804
+ }
2805
+ getValue() {
2806
+ return this.collection.getOneByPk(this.pk);
2807
+ }
2808
+ };
2809
+
2810
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKeys.ts
2811
+ var OIMSelectorSourceDependencyUpdateEventEmitterKeys = class {
2812
+ constructor(subscription, keys) {
2813
+ this.subscription = subscription;
2814
+ this.keys = keys;
2815
+ }
2816
+ subscribe(onUpdate) {
2817
+ return this.subscription.subscribeOnKeys(this.keys, onUpdate);
2818
+ }
2819
+ };
2820
+
2821
+ // src/modules/selector/core/OIMCollectionByPksSelector.ts
2822
+ var OIMCollectionByPksSelector = class extends OIMSelector {
2823
+ constructor(runtime, collection, pks) {
2824
+ super(runtime, [
2825
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2826
+ collection,
2827
+ pks
2828
+ )
2829
+ ]);
2830
+ this.collection = collection;
2831
+ this.pks = pks;
2832
+ }
2833
+ getValue() {
2834
+ return this.pks.map((pk) => this.collection.getOneByPk(pk));
2835
+ }
2836
+ areEqual(prev, next) {
2837
+ if (prev === next) return true;
2838
+ if (prev.length !== next.length) return false;
2839
+ for (let i = 0; i < prev.length; i++) {
2840
+ if (prev[i] !== next[i]) return false;
2841
+ }
2842
+ return true;
2843
+ }
2844
+ };
2845
+
2846
+ // src/modules/selector/core/OIMObjectValueByKeySelector.ts
2847
+ var OIMObjectValueByKeySelector = class extends OIMSelector {
2848
+ constructor(runtime, obj, key) {
2849
+ super(runtime, [
2850
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2851
+ obj,
2852
+ key
2853
+ )
2854
+ ]);
2855
+ this.obj = obj;
2856
+ this.key = key;
2857
+ }
2858
+ getValue() {
2859
+ return this.obj.get(this.key);
2860
+ }
2861
+ };
2862
+
2863
+ // src/modules/selector/core/OIMObjectValuesByKeysSelector.ts
2864
+ var OIMObjectValuesByKeysSelector = class extends OIMSelector {
2865
+ constructor(runtime, obj, keys) {
2866
+ super(runtime, [
2867
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2868
+ obj,
2869
+ keys
2870
+ )
2871
+ ]);
2872
+ this.obj = obj;
2873
+ this.keys = keys;
2874
+ }
2875
+ getValue() {
2876
+ return this.keys.map((k) => this.obj.get(k));
2877
+ }
2878
+ areEqual(prev, next) {
2879
+ if (prev === next) return true;
2880
+ if (prev.length !== next.length) return false;
2881
+ for (let i = 0; i < prev.length; i++) {
2882
+ if (prev[i] !== next[i]) return false;
2883
+ }
2884
+ return true;
2885
+ }
2886
+ };
2887
+
2888
+ // src/modules/selector/core/OIMIndexPksByKeySetBasedSelector.ts
2889
+ var OIMIndexPksByKeySetBasedSelector = class extends OIMSelector {
2890
+ constructor(runtime, reactiveIndex, key) {
2891
+ super(runtime, [
2892
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2893
+ reactiveIndex,
2894
+ key
2895
+ )
2896
+ ]);
2897
+ this.reactiveIndex = reactiveIndex;
2898
+ this.key = key;
2899
+ }
2900
+ getValue() {
2901
+ return this.reactiveIndex.getPksByKey(this.key);
2902
+ }
2903
+ };
2904
+
2905
+ // src/modules/selector/core/OIMIndexPksByKeyArrayBasedSelector.ts
2906
+ var OIMIndexPksByKeyArrayBasedSelector = class extends OIMSelector {
2907
+ constructor(runtime, reactiveIndex, key) {
2908
+ super(runtime, [
2909
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2910
+ reactiveIndex,
2911
+ key
2912
+ )
2913
+ ]);
2914
+ this.reactiveIndex = reactiveIndex;
2915
+ this.key = key;
2916
+ }
2917
+ getValue() {
2918
+ return this.reactiveIndex.getPksByKey(this.key);
2919
+ }
2920
+ areEqual(prev, next) {
2921
+ if (prev === next) return true;
2922
+ if (prev.length !== next.length) return false;
2923
+ for (let i = 0; i < prev.length; i++)
2924
+ if (prev[i] !== next[i]) return false;
2925
+ return true;
2926
+ }
2927
+ };
2928
+
2929
+ // src/modules/selector/core/OIMIndexPksByKeysSetBasedSelector.ts
2930
+ var OIMIndexPksByKeysSetBasedSelector = class extends OIMSelector {
2931
+ constructor(runtime, reactiveIndex, keys) {
2932
+ super(runtime, [
2933
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2934
+ reactiveIndex,
2935
+ keys
2936
+ )
2937
+ ]);
2938
+ this.reactiveIndex = reactiveIndex;
2939
+ this.keys = keys;
2940
+ }
2941
+ getValue() {
2942
+ return this.keys.map((key) => Array.from(this.reactiveIndex.getPksByKey(key))).flat();
2943
+ }
2944
+ areEqual(prev, next) {
2945
+ if (prev === next) return true;
2946
+ if (prev.length !== next.length) return false;
2947
+ for (let i = 0; i < prev.length; i++)
2948
+ if (prev[i] !== next[i]) return false;
2949
+ return true;
2950
+ }
2951
+ };
2952
+
2953
+ // src/modules/selector/core/OIMIndexPksByKeysArrayBasedSelector.ts
2954
+ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
2955
+ constructor(runtime, reactiveIndex, keys) {
2956
+ super(runtime, [
2957
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2958
+ reactiveIndex,
2959
+ keys
2960
+ )
2961
+ ]);
2962
+ this.reactiveIndex = reactiveIndex;
2963
+ this.keys = keys;
2964
+ }
2965
+ getValue() {
2966
+ return this.keys.map((key) => this.reactiveIndex.getPksByKey(key)).flat();
2967
+ }
2968
+ areEqual(prev, next) {
2969
+ if (prev === next) return true;
2970
+ if (prev.length !== next.length) return false;
2971
+ for (let i = 0; i < prev.length; i++)
2972
+ if (prev[i] !== next[i]) return false;
2973
+ return true;
2974
+ }
2975
+ };
2976
+
2977
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeySetBased.ts
2978
+ var OIMSelectorSourceDependencyEntitiesByIndexKeySetBased = class {
2979
+ constructor(collection, reactiveIndex, key) {
2980
+ this.collection = collection;
2981
+ this.reactiveIndex = reactiveIndex;
2982
+ this.key = key;
2983
+ this.currentPks = [];
2984
+ }
2985
+ subscribe(onUpdate) {
2986
+ const resubscribeCollection = () => {
2987
+ const nextPks = Array.from(
2988
+ this.reactiveIndex.getPksByKey(this.key)
2989
+ );
2990
+ if (this.sameArray(this.currentPks, nextPks)) return;
2991
+ this.unsubscribeFromCollectionKeys?.();
2992
+ this.unsubscribeFromCollectionKeys = void 0;
2993
+ this.currentPks = nextPks;
2994
+ if (this.currentPks.length === 0) return;
2995
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2996
+ };
2997
+ const onIndexUpdate = () => {
2998
+ resubscribeCollection();
2999
+ onUpdate();
3000
+ };
3001
+ resubscribeCollection();
3002
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
3003
+ this.key,
3004
+ onIndexUpdate
3005
+ );
3006
+ return () => {
3007
+ unsubscribeFromIndex();
3008
+ this.unsubscribeFromCollectionKeys?.();
3009
+ this.unsubscribeFromCollectionKeys = void 0;
3010
+ this.currentPks = [];
3011
+ };
3012
+ }
3013
+ sameArray(a, b) {
3014
+ if (a === b) return true;
3015
+ if (a.length !== b.length) return false;
3016
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3017
+ return true;
3018
+ }
3019
+ };
3020
+
3021
+ // src/modules/selector/core/OIMEntitiesByIndexKeySetBasedSelector.ts
3022
+ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
3023
+ constructor(runtime, collection, reactiveIndex, key) {
3024
+ super(runtime, [
3025
+ new OIMSelectorSourceDependencyEntitiesByIndexKeySetBased(collection, reactiveIndex, key)
3026
+ ]);
3027
+ this.collection = collection;
3028
+ this.reactiveIndex = reactiveIndex;
3029
+ this.key = key;
3030
+ }
3031
+ getValue() {
3032
+ const pks = Array.from(this.reactiveIndex.getPksByKey(this.key));
3033
+ return pks.map((pk) => this.collection.getOneByPk(pk));
3034
+ }
3035
+ areEqual(prev, next) {
3036
+ if (prev === next) return true;
3037
+ if (prev.length !== next.length) return false;
3038
+ for (let i = 0; i < prev.length; i++)
3039
+ if (prev[i] !== next[i]) return false;
3040
+ return true;
3041
+ }
3042
+ };
3043
+
3044
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased.ts
3045
+ var OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased = class {
3046
+ constructor(collection, reactiveIndex, key) {
3047
+ this.collection = collection;
3048
+ this.reactiveIndex = reactiveIndex;
3049
+ this.key = key;
3050
+ this.currentPks = [];
3051
+ }
3052
+ subscribe(onUpdate) {
3053
+ const resubscribeCollection = () => {
3054
+ const nextPks = this.reactiveIndex.getPksByKey(this.key);
3055
+ if (this.sameArray(this.currentPks, nextPks)) return;
3056
+ this.unsubscribeFromCollectionKeys?.();
3057
+ this.unsubscribeFromCollectionKeys = void 0;
3058
+ this.currentPks = nextPks;
3059
+ if (this.currentPks.length === 0) return;
3060
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
3061
+ };
3062
+ const onIndexUpdate = () => {
3063
+ resubscribeCollection();
3064
+ onUpdate();
3065
+ };
3066
+ resubscribeCollection();
3067
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
3068
+ this.key,
3069
+ onIndexUpdate
3070
+ );
3071
+ return () => {
3072
+ unsubscribeFromIndex();
3073
+ this.unsubscribeFromCollectionKeys?.();
3074
+ this.unsubscribeFromCollectionKeys = void 0;
3075
+ this.currentPks = [];
3076
+ };
3077
+ }
3078
+ sameArray(a, b) {
3079
+ if (a === b) return true;
3080
+ if (a.length !== b.length) return false;
3081
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3082
+ return true;
3083
+ }
3084
+ };
3085
+
3086
+ // src/modules/selector/core/OIMEntitiesByIndexKeyArrayBasedSelector.ts
3087
+ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
3088
+ constructor(runtime, collection, reactiveIndex, key) {
3089
+ super(runtime, [
3090
+ new OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased(collection, reactiveIndex, key)
3091
+ ]);
3092
+ this.collection = collection;
3093
+ this.reactiveIndex = reactiveIndex;
3094
+ this.key = key;
3095
+ }
3096
+ getValue() {
3097
+ const pks = this.reactiveIndex.getPksByKey(this.key);
3098
+ return pks.map((pk) => this.collection.getOneByPk(pk));
3099
+ }
3100
+ areEqual(prev, next) {
3101
+ if (prev === next) return true;
3102
+ if (prev.length !== next.length) return false;
3103
+ for (let i = 0; i < prev.length; i++)
3104
+ if (prev[i] !== next[i]) return false;
3105
+ return true;
3106
+ }
3107
+ };
3108
+
3109
+ // src/modules/computative/core/OIMComputativeRuntime.ts
3110
+ var OIMComputativeRuntime = class {
3111
+ constructor(queue) {
3112
+ this.queue = queue;
3113
+ this.isFlushScheduled = false;
3114
+ this.isFlushing = false;
3115
+ this.minLevel = Infinity;
3116
+ this.maxLevel = -Infinity;
3117
+ this.dirtyByLevel = /* @__PURE__ */ new Map();
3118
+ this.afterFlushTasks = /* @__PURE__ */ new Set();
3119
+ this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
3120
+ if (!this.isFlushScheduled) return;
3121
+ this.flush();
3122
+ });
3123
+ }
3124
+ /**
3125
+ * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
3126
+ * ordered by `level` ascending.
3127
+ */
3128
+ schedule(task, level = 0) {
3129
+ let isActive = true;
3130
+ const wrapped = () => {
3131
+ if (!isActive) return;
3132
+ task();
3133
+ };
3134
+ let bucket = this.dirtyByLevel.get(level);
3135
+ if (!bucket) {
3136
+ bucket = /* @__PURE__ */ new Set();
3137
+ this.dirtyByLevel.set(level, bucket);
3138
+ }
3139
+ bucket.add(wrapped);
3140
+ if (level < this.minLevel) this.minLevel = level;
3141
+ if (level > this.maxLevel) this.maxLevel = level;
3142
+ if (!this.isFlushing) this.isFlushScheduled = true;
3143
+ return () => {
3144
+ if (!isActive) return;
3145
+ isActive = false;
3146
+ bucket.delete(wrapped);
3147
+ };
3148
+ }
3149
+ /**
3150
+ * Run once at the start of the next runtime flush (i.e. after queue.flush()).
3151
+ * Useful for resubscribing dependencies exactly once per source flush.
3152
+ */
3153
+ scheduleAfterFlush(task) {
3154
+ let isActive = true;
3155
+ const wrapped = () => {
3156
+ if (!isActive) return;
3157
+ task();
3158
+ };
3159
+ this.afterFlushTasks.add(wrapped);
3160
+ if (!this.isFlushing) this.isFlushScheduled = true;
3161
+ return () => {
3162
+ if (!isActive) return;
3163
+ isActive = false;
3164
+ this.afterFlushTasks.delete(wrapped);
3165
+ };
3166
+ }
3167
+ flush() {
3168
+ this.isFlushScheduled = false;
3169
+ if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
3170
+ return;
3171
+ this.isFlushing = true;
3172
+ try {
3173
+ if (this.afterFlushTasks.size > 0) {
3174
+ const snapshot = Array.from(this.afterFlushTasks);
3175
+ this.afterFlushTasks.clear();
3176
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3177
+ }
3178
+ if (this.dirtyByLevel.size === 0) return;
3179
+ let level = this.minLevel;
3180
+ let max = this.maxLevel;
3181
+ while (level <= max) {
3182
+ for (; ; ) {
3183
+ const bucket = this.dirtyByLevel.get(level);
3184
+ if (!bucket || bucket.size === 0) break;
3185
+ this.dirtyByLevel.delete(level);
3186
+ const snapshot = Array.from(bucket);
3187
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3188
+ }
3189
+ if (this.maxLevel > max) max = this.maxLevel;
3190
+ level++;
3191
+ }
3192
+ } finally {
3193
+ this.isFlushing = false;
3194
+ if (this.dirtyByLevel.size === 0) {
3195
+ this.minLevel = Infinity;
3196
+ this.maxLevel = -Infinity;
3197
+ } else {
3198
+ let nextMin = Infinity;
3199
+ let nextMax = -Infinity;
3200
+ this.dirtyByLevel.forEach((_bucket, lvl) => {
3201
+ if (lvl < nextMin) nextMin = lvl;
3202
+ if (lvl > nextMax) nextMax = lvl;
3203
+ });
3204
+ this.minLevel = nextMin;
3205
+ this.maxLevel = nextMax;
3206
+ this.isFlushScheduled = true;
3207
+ }
3208
+ }
3209
+ }
3210
+ };
1573
3211
  // Annotate the CommonJS export names for ESM import in node:
1574
3212
  0 && (module.exports = {
1575
3213
  EOIMCollectionEventType,
3214
+ EOIMComputedEventType,
1576
3215
  EOIMEventQueueEventType,
1577
3216
  EOIMEventQueueSchedulerEventType,
1578
3217
  EOIMIndexEventType,
1579
- EOIMUpdateEventCoalescerEventType,
3218
+ EOIMObjectEventType,
1580
3219
  OIMCollection,
3220
+ OIMCollectionByPkSelector,
3221
+ OIMCollectionByPksSelector,
3222
+ OIMCollectionChangedFieldsWrapper,
1581
3223
  OIMCollectionStore,
1582
3224
  OIMCollectionStoreMapDriven,
1583
3225
  OIMComparatorFactory,
3226
+ OIMComputativeRuntime,
3227
+ OIMComputed,
1584
3228
  OIMDBSettings,
3229
+ OIMEffect,
3230
+ OIMEffectDependencyComputed,
3231
+ OIMEffectDependencyKeyed,
3232
+ OIMEffectDependencyKeyedCollection,
3233
+ OIMEffectDependencyKeyedIndex,
3234
+ OIMEffectDependencyKeyedObject,
3235
+ OIMEntitiesByIndexKeyArrayBasedSelector,
3236
+ OIMEntitiesByIndexKeySetBasedSelector,
1585
3237
  OIMEntityUpdaterFactory,
1586
3238
  OIMEventEmitter,
1587
3239
  OIMEventQueue,
@@ -1594,13 +3246,25 @@ var OIMEventQueueSchedulerFactory = class {
1594
3246
  OIMIndexArrayBased,
1595
3247
  OIMIndexComparatorFactory,
1596
3248
  OIMIndexManualArrayBased,
3249
+ OIMIndexManualOrderedArrayBased,
1597
3250
  OIMIndexManualSetBased,
3251
+ OIMIndexPksByKeyArrayBasedSelector,
3252
+ OIMIndexPksByKeySetBasedSelector,
3253
+ OIMIndexPksByKeysArrayBasedSelector,
3254
+ OIMIndexPksByKeysSetBasedSelector,
1598
3255
  OIMIndexSetBased,
1599
3256
  OIMIndexStoreArrayBased,
1600
3257
  OIMIndexStoreMapDrivenArrayBased,
1601
3258
  OIMIndexStoreMapDrivenSetBased,
1602
3259
  OIMIndexStoreSetBased,
1603
3260
  OIMMap2Keys,
3261
+ OIMObject,
3262
+ OIMObjectStore,
3263
+ OIMObjectStoreMapDriven,
3264
+ OIMObjectStoreRecordDriven,
3265
+ OIMObjectValueByKeySelector,
3266
+ OIMObjectValuesByKeysSelector,
3267
+ OIMOrderedListIndexCommandStreamWrapper,
1604
3268
  OIMPkSelectorFactory,
1605
3269
  OIMRICollection,
1606
3270
  OIMReactiveCollection,
@@ -1608,8 +3272,6 @@ var OIMEventQueueSchedulerFactory = class {
1608
3272
  OIMReactiveIndexManualArrayBased,
1609
3273
  OIMReactiveIndexManualSetBased,
1610
3274
  OIMReactiveIndexSetBased,
1611
- OIMUpdateEventCoalescer,
1612
- OIMUpdateEventCoalescerCollection,
1613
- OIMUpdateEventCoalescerIndex,
1614
- OIMUpdateEventEmitter
3275
+ OIMReactiveObject,
3276
+ OIMSelector
1615
3277
  });