@oimdb/core 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +405 -364
- package/dist/index.d.cts +110 -68
- package/dist/index.d.ts +110 -68
- package/dist/index.js +405 -364
- package/llms.txt +30 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -524,265 +524,198 @@ var OIMIndexArrayBased = class extends OIMIndex {
|
|
|
524
524
|
}
|
|
525
525
|
};
|
|
526
526
|
|
|
527
|
-
// src/
|
|
528
|
-
var
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
//
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
this.keyHandlers = /* @__PURE__ */ new Map();
|
|
527
|
+
// src/core/OIMCarrierKeyedEmitter.ts
|
|
528
|
+
var OIMCarrierKeyedEmitter = class {
|
|
529
|
+
constructor(queue, resolver) {
|
|
530
|
+
// Carriers with at least one subscriber — for markAllUpdated / metrics.
|
|
531
|
+
this.subscribedCarriers = /* @__PURE__ */ new Set();
|
|
532
|
+
// Pending flush batch. A plain array + a `carrier.dirty` flag for dedup —
|
|
533
|
+
// marking is the hottest write-path op, and pushing to an array with a
|
|
534
|
+
// boolean check is ~4x cheaper than a `Set` identity-hash add. The flag
|
|
535
|
+
// doubles as membership: a carrier is in this array iff `carrier.dirty`.
|
|
536
|
+
this.dirtyCarriers = [];
|
|
538
537
|
this.isFlushEnqueued = false;
|
|
539
|
-
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
540
|
-
this.isAfterFlushDeliveryScheduled = false;
|
|
541
|
-
// Stable handler reference to avoid allocating a new closure on every HAS_CHANGES.
|
|
542
538
|
this.onFlush = () => {
|
|
543
539
|
this.isFlushEnqueued = false;
|
|
544
540
|
this.dequeueFlush = void 0;
|
|
545
|
-
if (this.
|
|
546
|
-
|
|
547
|
-
|
|
541
|
+
if (this.dirtyCarriers.length === 0) return;
|
|
542
|
+
const flushing = this.dirtyCarriers;
|
|
543
|
+
this.dirtyCarriers = [];
|
|
544
|
+
let i = 0;
|
|
545
|
+
try {
|
|
546
|
+
for (; i < flushing.length; i++) {
|
|
547
|
+
flushing[i].dirty = false;
|
|
548
|
+
this.notify(flushing[i]);
|
|
549
|
+
}
|
|
550
|
+
} finally {
|
|
551
|
+
for (; i < flushing.length; i++) flushing[i].dirty = false;
|
|
548
552
|
}
|
|
549
|
-
this.runSinglePass();
|
|
550
|
-
};
|
|
551
|
-
this.onAfterFlush = () => {
|
|
552
|
-
if (!this.isAfterFlushDeliveryScheduled) return;
|
|
553
|
-
this.isAfterFlushDeliveryScheduled = false;
|
|
554
|
-
this.deliverImmediate();
|
|
555
553
|
};
|
|
556
554
|
this.queue = queue;
|
|
557
|
-
this.
|
|
558
|
-
if (this.deliveryMode === "after_flush") {
|
|
559
|
-
this.unsubscribeAfterFlushDelivery = this.queue.emitter.on(
|
|
560
|
-
0 /* AFTER_FLUSH */,
|
|
561
|
-
this.onAfterFlush
|
|
562
|
-
);
|
|
563
|
-
}
|
|
555
|
+
this.resolver = resolver;
|
|
564
556
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
);
|
|
557
|
+
subscribeOnKey(key, handler) {
|
|
558
|
+
const carrier = this.resolver.getOrReserveCarrier(key);
|
|
559
|
+
this.addHandler(carrier, handler);
|
|
560
|
+
return () => this.removeHandler(carrier, handler);
|
|
561
|
+
}
|
|
562
|
+
subscribeOnKeys(keys, handler) {
|
|
563
|
+
const carriers = [];
|
|
564
|
+
for (let i = 0; i < keys.length; i++) {
|
|
565
|
+
const carrier = this.resolver.getOrReserveCarrier(keys[i]);
|
|
566
|
+
this.addHandler(carrier, handler);
|
|
567
|
+
carriers.push(carrier);
|
|
576
568
|
}
|
|
577
|
-
|
|
578
|
-
|
|
569
|
+
return () => {
|
|
570
|
+
for (let i = 0; i < carriers.length; i++) {
|
|
571
|
+
this.removeHandler(carriers[i], handler);
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
unsubscribeFromKey(key, handler) {
|
|
576
|
+
const carrier = this.resolver.findCarrier(key);
|
|
577
|
+
if (carrier) this.removeHandler(carrier, handler);
|
|
578
|
+
}
|
|
579
|
+
unsubscribeFromKeys(keys, handler) {
|
|
579
580
|
for (let i = 0; i < keys.length; i++) {
|
|
580
|
-
|
|
581
|
-
const handlers = this.keyHandlers.get(key);
|
|
582
|
-
if (!handlers || handlers.size === 0) continue;
|
|
583
|
-
this.updatedKeys.add(key);
|
|
584
|
-
didAddAny = true;
|
|
581
|
+
this.unsubscribeFromKey(keys[i], handler);
|
|
585
582
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
583
|
+
}
|
|
584
|
+
addHandler(carrier, handler) {
|
|
585
|
+
let subscribers = carrier.subscribers;
|
|
586
|
+
if (!subscribers) {
|
|
587
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
588
|
+
carrier.subscribers = subscribers;
|
|
590
589
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
590
|
+
subscribers.add(handler);
|
|
591
|
+
this.subscribedCarriers.add(carrier);
|
|
592
|
+
}
|
|
593
|
+
removeHandler(carrier, handler) {
|
|
594
|
+
const subscribers = carrier.subscribers;
|
|
595
|
+
if (!subscribers) return;
|
|
596
|
+
subscribers.delete(handler);
|
|
597
|
+
if (subscribers.size === 0) {
|
|
598
|
+
this.subscribedCarriers.delete(carrier);
|
|
599
|
+
this.resolver.onCarrierEmptied?.(carrier);
|
|
594
600
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
601
|
+
}
|
|
602
|
+
/** Fast path: the writer already holds the carrier. */
|
|
603
|
+
markUpdatedCarrier(carrier) {
|
|
604
|
+
this.assertNotInFlush();
|
|
605
|
+
const subscribers = carrier.subscribers;
|
|
606
|
+
if (!subscribers || subscribers.size === 0) return;
|
|
607
|
+
if (carrier.dirty) return;
|
|
608
|
+
carrier.dirty = true;
|
|
609
|
+
this.dirtyCarriers.push(carrier);
|
|
610
|
+
this.scheduleFlush();
|
|
598
611
|
}
|
|
599
612
|
markUpdatedKey(key) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const handlers = this.keyHandlers.get(key);
|
|
607
|
-
if (!handlers || handlers.size === 0) return;
|
|
608
|
-
this.updatedKeys.add(key);
|
|
609
|
-
if (this.deliveryMode === "immediate") {
|
|
610
|
-
this.deliverImmediate();
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
if (this.deliveryMode === "after_flush") {
|
|
614
|
-
this.isAfterFlushDeliveryScheduled = true;
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
if (this.isFlushEnqueued) return;
|
|
618
|
-
this.isFlushEnqueued = true;
|
|
619
|
-
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
613
|
+
const carrier = this.resolver.findCarrier(key);
|
|
614
|
+
if (carrier) this.markUpdatedCarrier(carrier);
|
|
615
|
+
else this.assertNotInFlush();
|
|
616
|
+
}
|
|
617
|
+
markUpdatedKeys(keys) {
|
|
618
|
+
for (let i = 0; i < keys.length; i++) this.markUpdatedKey(keys[i]);
|
|
620
619
|
}
|
|
621
620
|
markAllUpdated() {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
);
|
|
621
|
+
this.assertNotInFlush();
|
|
622
|
+
if (this.subscribedCarriers.size === 0) return;
|
|
623
|
+
this.subscribedCarriers.forEach((carrier) => {
|
|
624
|
+
if (carrier.dirty) return;
|
|
625
|
+
carrier.dirty = true;
|
|
626
|
+
this.dirtyCarriers.push(carrier);
|
|
627
|
+
});
|
|
628
|
+
this.scheduleFlush();
|
|
629
|
+
}
|
|
630
|
+
hasSubscriptions() {
|
|
631
|
+
return this.subscribedCarriers.size > 0;
|
|
632
|
+
}
|
|
633
|
+
getHandlerCount(key) {
|
|
634
|
+
return this.resolver.findCarrier(key)?.subscribers?.size ?? 0;
|
|
635
|
+
}
|
|
636
|
+
getMetrics() {
|
|
637
|
+
let totalHandlers = 0;
|
|
638
|
+
this.subscribedCarriers.forEach((carrier) => {
|
|
639
|
+
totalHandlers += carrier.subscribers?.size ?? 0;
|
|
640
|
+
});
|
|
641
|
+
const totalKeys = this.subscribedCarriers.size;
|
|
642
|
+
return {
|
|
643
|
+
totalKeys,
|
|
644
|
+
totalHandlers,
|
|
645
|
+
averageHandlersPerKey: totalKeys > 0 ? totalHandlers / totalKeys : 0,
|
|
646
|
+
queueLength: this.dirtyCarriers.length
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
destroy() {
|
|
650
|
+
if (this.dequeueFlush) {
|
|
651
|
+
this.dequeueFlush();
|
|
652
|
+
this.dequeueFlush = void 0;
|
|
626
653
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
654
|
+
this.subscribedCarriers.forEach((carrier) => {
|
|
655
|
+
carrier.subscribers?.clear();
|
|
656
|
+
carrier.subscribers = void 0;
|
|
630
657
|
});
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
658
|
+
this.subscribedCarriers.clear();
|
|
659
|
+
for (let i = 0; i < this.dirtyCarriers.length; i++) {
|
|
660
|
+
this.dirtyCarriers[i].dirty = false;
|
|
634
661
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
662
|
+
this.dirtyCarriers = [];
|
|
663
|
+
this.isFlushEnqueued = false;
|
|
664
|
+
}
|
|
665
|
+
assertNotInFlush() {
|
|
666
|
+
if (this.queue.isInFlush) {
|
|
667
|
+
throw new Error(
|
|
668
|
+
"OIMCarrierKeyedEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
669
|
+
);
|
|
638
670
|
}
|
|
671
|
+
}
|
|
672
|
+
scheduleFlush() {
|
|
639
673
|
if (this.isFlushEnqueued) return;
|
|
640
674
|
this.isFlushEnqueued = true;
|
|
641
675
|
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
642
676
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
* single subscriber per key (one component per pk), handled without any
|
|
649
|
-
* allocation. For multiple subscribers a snapshot is taken so a handler may
|
|
650
|
-
* (un)subscribe during iteration without corrupting the walk.
|
|
651
|
-
*/
|
|
652
|
-
notifyHandlers(handlers) {
|
|
653
|
-
if (handlers.size === 1) {
|
|
654
|
-
const only = handlers.values().next().value;
|
|
677
|
+
notify(carrier) {
|
|
678
|
+
const subscribers = carrier.subscribers;
|
|
679
|
+
if (!subscribers || subscribers.size === 0) return;
|
|
680
|
+
if (subscribers.size === 1) {
|
|
681
|
+
const only = subscribers.values().next().value;
|
|
655
682
|
if (only) only();
|
|
656
683
|
return;
|
|
657
684
|
}
|
|
658
685
|
const snapshot = [];
|
|
659
|
-
|
|
686
|
+
subscribers.forEach((h) => snapshot.push(h));
|
|
660
687
|
for (let i = 0; i < snapshot.length; i++) {
|
|
661
688
|
const handler = snapshot[i];
|
|
662
|
-
if (
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Deliver updated keys and notify handlers once.
|
|
667
|
-
* Re-entrant updates during delivery are forbidden and will throw in markUpdatedKeys().
|
|
668
|
-
*/
|
|
669
|
-
runSinglePass() {
|
|
670
|
-
if (this.updatedKeys.size === 0) return;
|
|
671
|
-
const flushingKeys = this.updatedKeys;
|
|
672
|
-
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
673
|
-
if (this.keyHandlers.size === 0 || flushingKeys.size === 0) return;
|
|
674
|
-
if (flushingKeys.size * 2 < this.keyHandlers.size) {
|
|
675
|
-
flushingKeys.forEach((key) => {
|
|
676
|
-
const handlers = this.keyHandlers.get(key);
|
|
677
|
-
if (!handlers || handlers.size === 0) return;
|
|
678
|
-
this.notifyHandlers(handlers);
|
|
679
|
-
});
|
|
680
|
-
} else {
|
|
681
|
-
this.keyHandlers.forEach((handlers, key) => {
|
|
682
|
-
if (!flushingKeys.has(key)) return;
|
|
683
|
-
if (!handlers || handlers.size === 0) return;
|
|
684
|
-
this.notifyHandlers(handlers);
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
destroy() {
|
|
689
|
-
if (this.dequeueFlush) {
|
|
690
|
-
this.dequeueFlush();
|
|
691
|
-
this.dequeueFlush = void 0;
|
|
692
|
-
}
|
|
693
|
-
if (this.unsubscribeAfterFlushDelivery) {
|
|
694
|
-
this.unsubscribeAfterFlushDelivery();
|
|
695
|
-
this.unsubscribeAfterFlushDelivery = void 0;
|
|
689
|
+
if (subscribers.has(handler)) handler();
|
|
696
690
|
}
|
|
697
|
-
this.keyHandlers.forEach((handlers) => handlers.clear());
|
|
698
|
-
this.keyHandlers.clear();
|
|
699
|
-
this.updatedKeys.clear();
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Get performance metrics for monitoring and debugging
|
|
703
|
-
*/
|
|
704
|
-
getMetrics() {
|
|
705
|
-
let totalHandlers = 0;
|
|
706
|
-
this.keyHandlers.forEach((handlers) => {
|
|
707
|
-
totalHandlers += handlers.size;
|
|
708
|
-
});
|
|
709
|
-
return {
|
|
710
|
-
totalKeys: this.keyHandlers.size,
|
|
711
|
-
totalHandlers,
|
|
712
|
-
averageHandlersPerKey: this.keyHandlers.size > 0 ? totalHandlers / this.keyHandlers.size : 0,
|
|
713
|
-
queueLength: this.queue.length
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
/**
|
|
717
|
-
* Check if there are any active subscriptions
|
|
718
|
-
*/
|
|
719
|
-
hasSubscriptions() {
|
|
720
|
-
return this.keyHandlers.size > 0;
|
|
721
691
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
subscribeOnKey(key, handler) {
|
|
729
|
-
let handlers = this.keyHandlers.get(key);
|
|
730
|
-
if (!handlers) {
|
|
731
|
-
handlers = /* @__PURE__ */ new Set();
|
|
732
|
-
this.keyHandlers.set(key, handlers);
|
|
733
|
-
}
|
|
734
|
-
handlers.add(handler);
|
|
735
|
-
return () => {
|
|
736
|
-
this.unsubscribeFromKey(key, handler);
|
|
737
|
-
};
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
// src/core/OIMKeyedCarrierResolver.ts
|
|
695
|
+
var OIMKeyedCarrierResolver = class {
|
|
696
|
+
constructor() {
|
|
697
|
+
this.carriers = /* @__PURE__ */ new Map();
|
|
738
698
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
if (!
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
this.keyHandlers.delete(key);
|
|
745
|
-
this.updatedKeys.delete(key);
|
|
746
|
-
}
|
|
747
|
-
if (this.keyHandlers.size === 0) {
|
|
748
|
-
this.updatedKeys.clear();
|
|
749
|
-
if (this.dequeueFlush) {
|
|
750
|
-
this.dequeueFlush();
|
|
751
|
-
this.dequeueFlush = void 0;
|
|
752
|
-
}
|
|
753
|
-
this.isFlushEnqueued = false;
|
|
699
|
+
getOrReserveCarrier(key) {
|
|
700
|
+
let carrier = this.carriers.get(key);
|
|
701
|
+
if (!carrier) {
|
|
702
|
+
carrier = { key };
|
|
703
|
+
this.carriers.set(key, carrier);
|
|
754
704
|
}
|
|
705
|
+
return carrier;
|
|
755
706
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
for (const key of keys) {
|
|
759
|
-
let handlers = this.keyHandlers.get(key);
|
|
760
|
-
if (!handlers) {
|
|
761
|
-
handlers = /* @__PURE__ */ new Set();
|
|
762
|
-
this.keyHandlers.set(key, handlers);
|
|
763
|
-
}
|
|
764
|
-
if (!handlers.has(handler)) {
|
|
765
|
-
handlers.add(handler);
|
|
766
|
-
subscribedKeys.push(key);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
return () => {
|
|
770
|
-
for (const key of subscribedKeys) {
|
|
771
|
-
this.unsubscribeFromKey(key, handler);
|
|
772
|
-
}
|
|
773
|
-
};
|
|
707
|
+
findCarrier(key) {
|
|
708
|
+
return this.carriers.get(key);
|
|
774
709
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
this.unsubscribeFromKey(key, handler);
|
|
778
|
-
}
|
|
710
|
+
onCarrierEmptied(carrier) {
|
|
711
|
+
this.carriers.delete(carrier.key);
|
|
779
712
|
}
|
|
780
713
|
};
|
|
781
714
|
|
|
782
715
|
// src/abstract/OIMReactiveIndex.ts
|
|
783
716
|
var OIMReactiveIndex = class {
|
|
784
717
|
constructor(queue, createIndex) {
|
|
785
|
-
this.updateEmitter = new
|
|
718
|
+
this.updateEmitter = new OIMCarrierKeyedEmitter(queue, new OIMKeyedCarrierResolver());
|
|
786
719
|
this.index = createIndex(this.updateEmitter);
|
|
787
720
|
}
|
|
788
721
|
getEntitiesByKey(key) {
|
|
@@ -1150,154 +1083,7 @@ var OIMCollection = class {
|
|
|
1150
1083
|
}
|
|
1151
1084
|
destroy() {
|
|
1152
1085
|
this.store.destroy();
|
|
1153
|
-
this.emitter.offAll();
|
|
1154
|
-
}
|
|
1155
|
-
};
|
|
1156
|
-
|
|
1157
|
-
// src/core/OIMCarrierKeyedEmitter.ts
|
|
1158
|
-
var OIMCarrierKeyedEmitter = class {
|
|
1159
|
-
constructor(queue, resolver) {
|
|
1160
|
-
// Carriers with at least one subscriber — for markAllUpdated / metrics.
|
|
1161
|
-
this.subscribedCarriers = /* @__PURE__ */ new Set();
|
|
1162
|
-
this.dirtyCarriers = /* @__PURE__ */ new Set();
|
|
1163
|
-
this.isFlushEnqueued = false;
|
|
1164
|
-
this.onFlush = () => {
|
|
1165
|
-
this.isFlushEnqueued = false;
|
|
1166
|
-
this.dequeueFlush = void 0;
|
|
1167
|
-
if (this.dirtyCarriers.size === 0) return;
|
|
1168
|
-
const flushing = this.dirtyCarriers;
|
|
1169
|
-
this.dirtyCarriers = /* @__PURE__ */ new Set();
|
|
1170
|
-
flushing.forEach((carrier) => this.notify(carrier));
|
|
1171
|
-
};
|
|
1172
|
-
this.queue = queue;
|
|
1173
|
-
this.resolver = resolver;
|
|
1174
|
-
}
|
|
1175
|
-
subscribeOnKey(key, handler) {
|
|
1176
|
-
const carrier = this.resolver.getOrReserveCarrier(key);
|
|
1177
|
-
this.addHandler(carrier, handler);
|
|
1178
|
-
return () => this.removeHandler(carrier, handler);
|
|
1179
|
-
}
|
|
1180
|
-
subscribeOnKeys(keys, handler) {
|
|
1181
|
-
const carriers = [];
|
|
1182
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1183
|
-
const carrier = this.resolver.getOrReserveCarrier(keys[i]);
|
|
1184
|
-
this.addHandler(carrier, handler);
|
|
1185
|
-
carriers.push(carrier);
|
|
1186
|
-
}
|
|
1187
|
-
return () => {
|
|
1188
|
-
for (let i = 0; i < carriers.length; i++) {
|
|
1189
|
-
this.removeHandler(carriers[i], handler);
|
|
1190
|
-
}
|
|
1191
|
-
};
|
|
1192
|
-
}
|
|
1193
|
-
unsubscribeFromKey(key, handler) {
|
|
1194
|
-
const carrier = this.resolver.findCarrier(key);
|
|
1195
|
-
if (carrier) this.removeHandler(carrier, handler);
|
|
1196
|
-
}
|
|
1197
|
-
unsubscribeFromKeys(keys, handler) {
|
|
1198
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1199
|
-
this.unsubscribeFromKey(keys[i], handler);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
addHandler(carrier, handler) {
|
|
1203
|
-
let subscribers = carrier.subscribers;
|
|
1204
|
-
if (!subscribers) {
|
|
1205
|
-
subscribers = /* @__PURE__ */ new Set();
|
|
1206
|
-
carrier.subscribers = subscribers;
|
|
1207
|
-
}
|
|
1208
|
-
subscribers.add(handler);
|
|
1209
|
-
this.subscribedCarriers.add(carrier);
|
|
1210
|
-
}
|
|
1211
|
-
removeHandler(carrier, handler) {
|
|
1212
|
-
const subscribers = carrier.subscribers;
|
|
1213
|
-
if (!subscribers) return;
|
|
1214
|
-
subscribers.delete(handler);
|
|
1215
|
-
if (subscribers.size === 0) {
|
|
1216
|
-
this.subscribedCarriers.delete(carrier);
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
/** Fast path: the writer already holds the carrier. */
|
|
1220
|
-
markUpdatedCarrier(carrier) {
|
|
1221
|
-
this.assertNotInFlush();
|
|
1222
|
-
const subscribers = carrier.subscribers;
|
|
1223
|
-
if (!subscribers || subscribers.size === 0) return;
|
|
1224
|
-
this.dirtyCarriers.add(carrier);
|
|
1225
|
-
this.scheduleFlush();
|
|
1226
|
-
}
|
|
1227
|
-
markUpdatedKey(key) {
|
|
1228
|
-
const carrier = this.resolver.findCarrier(key);
|
|
1229
|
-
if (carrier) this.markUpdatedCarrier(carrier);
|
|
1230
|
-
else this.assertNotInFlush();
|
|
1231
|
-
}
|
|
1232
|
-
markUpdatedKeys(keys) {
|
|
1233
|
-
for (let i = 0; i < keys.length; i++) this.markUpdatedKey(keys[i]);
|
|
1234
|
-
}
|
|
1235
|
-
markAllUpdated() {
|
|
1236
|
-
this.assertNotInFlush();
|
|
1237
|
-
if (this.subscribedCarriers.size === 0) return;
|
|
1238
|
-
this.subscribedCarriers.forEach(
|
|
1239
|
-
(carrier) => this.dirtyCarriers.add(carrier)
|
|
1240
|
-
);
|
|
1241
|
-
this.scheduleFlush();
|
|
1242
|
-
}
|
|
1243
|
-
hasSubscriptions() {
|
|
1244
|
-
return this.subscribedCarriers.size > 0;
|
|
1245
|
-
}
|
|
1246
|
-
getHandlerCount(key) {
|
|
1247
|
-
return this.resolver.findCarrier(key)?.subscribers?.size ?? 0;
|
|
1248
|
-
}
|
|
1249
|
-
getMetrics() {
|
|
1250
|
-
let totalHandlers = 0;
|
|
1251
|
-
this.subscribedCarriers.forEach((carrier) => {
|
|
1252
|
-
totalHandlers += carrier.subscribers?.size ?? 0;
|
|
1253
|
-
});
|
|
1254
|
-
const totalKeys = this.subscribedCarriers.size;
|
|
1255
|
-
return {
|
|
1256
|
-
totalKeys,
|
|
1257
|
-
totalHandlers,
|
|
1258
|
-
averageHandlersPerKey: totalKeys > 0 ? totalHandlers / totalKeys : 0,
|
|
1259
|
-
queueLength: this.dirtyCarriers.size
|
|
1260
|
-
};
|
|
1261
|
-
}
|
|
1262
|
-
destroy() {
|
|
1263
|
-
if (this.dequeueFlush) {
|
|
1264
|
-
this.dequeueFlush();
|
|
1265
|
-
this.dequeueFlush = void 0;
|
|
1266
|
-
}
|
|
1267
|
-
this.subscribedCarriers.forEach((carrier) => {
|
|
1268
|
-
carrier.subscribers?.clear();
|
|
1269
|
-
carrier.subscribers = void 0;
|
|
1270
|
-
});
|
|
1271
|
-
this.subscribedCarriers.clear();
|
|
1272
|
-
this.dirtyCarriers.clear();
|
|
1273
|
-
this.isFlushEnqueued = false;
|
|
1274
|
-
}
|
|
1275
|
-
assertNotInFlush() {
|
|
1276
|
-
if (this.queue.isInFlush) {
|
|
1277
|
-
throw new Error(
|
|
1278
|
-
"OIMCarrierKeyedEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
1279
|
-
);
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
scheduleFlush() {
|
|
1283
|
-
if (this.isFlushEnqueued) return;
|
|
1284
|
-
this.isFlushEnqueued = true;
|
|
1285
|
-
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
1286
|
-
}
|
|
1287
|
-
notify(carrier) {
|
|
1288
|
-
const subscribers = carrier.subscribers;
|
|
1289
|
-
if (!subscribers || subscribers.size === 0) return;
|
|
1290
|
-
if (subscribers.size === 1) {
|
|
1291
|
-
const only = subscribers.values().next().value;
|
|
1292
|
-
if (only) only();
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
1295
|
-
const snapshot = [];
|
|
1296
|
-
subscribers.forEach((h) => snapshot.push(h));
|
|
1297
|
-
for (let i = 0; i < snapshot.length; i++) {
|
|
1298
|
-
const handler = snapshot[i];
|
|
1299
|
-
if (subscribers.has(handler)) handler();
|
|
1300
|
-
}
|
|
1086
|
+
this.emitter.offAll();
|
|
1301
1087
|
}
|
|
1302
1088
|
};
|
|
1303
1089
|
|
|
@@ -1583,6 +1369,261 @@ var OIMCollectionIndexManualOrderedArrayBased = class extends OIMIndexManualOrde
|
|
|
1583
1369
|
}
|
|
1584
1370
|
};
|
|
1585
1371
|
|
|
1372
|
+
// src/enums/EOIMEventQueueEventType.ts
|
|
1373
|
+
var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
|
|
1374
|
+
EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
|
|
1375
|
+
EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
|
|
1376
|
+
return EOIMEventQueueEventType2;
|
|
1377
|
+
})(EOIMEventQueueEventType || {});
|
|
1378
|
+
|
|
1379
|
+
// src/core/OIMUpdateEventEmitter.ts
|
|
1380
|
+
var OIMUpdateEventEmitter = class {
|
|
1381
|
+
constructor(queue, deliveryMode = "queue") {
|
|
1382
|
+
this.keyHandlers = /* @__PURE__ */ new Map();
|
|
1383
|
+
this.isFlushEnqueued = false;
|
|
1384
|
+
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
1385
|
+
this.isAfterFlushDeliveryScheduled = false;
|
|
1386
|
+
// Stable handler reference to avoid allocating a new closure on every HAS_CHANGES.
|
|
1387
|
+
this.onFlush = () => {
|
|
1388
|
+
this.isFlushEnqueued = false;
|
|
1389
|
+
this.dequeueFlush = void 0;
|
|
1390
|
+
if (this.keyHandlers.size === 0) {
|
|
1391
|
+
this.updatedKeys.clear();
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
this.runSinglePass();
|
|
1395
|
+
};
|
|
1396
|
+
this.onAfterFlush = () => {
|
|
1397
|
+
if (!this.isAfterFlushDeliveryScheduled) return;
|
|
1398
|
+
this.isAfterFlushDeliveryScheduled = false;
|
|
1399
|
+
this.deliverImmediate();
|
|
1400
|
+
};
|
|
1401
|
+
this.queue = queue;
|
|
1402
|
+
this.deliveryMode = deliveryMode;
|
|
1403
|
+
if (this.deliveryMode === "after_flush") {
|
|
1404
|
+
this.unsubscribeAfterFlushDelivery = this.queue.emitter.on(
|
|
1405
|
+
0 /* AFTER_FLUSH */,
|
|
1406
|
+
this.onAfterFlush
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Mark keys as updated and schedule delivery.
|
|
1412
|
+
*
|
|
1413
|
+
* If there are no subscriptions, this is a no-op and does not allocate or retain keys.
|
|
1414
|
+
*/
|
|
1415
|
+
markUpdatedKeys(keys) {
|
|
1416
|
+
if (keys.length === 0) return;
|
|
1417
|
+
if (this.deliveryMode === "queue" && this.queue.isInFlush) {
|
|
1418
|
+
throw new Error(
|
|
1419
|
+
"OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
if (this.keyHandlers.size === 0) return;
|
|
1423
|
+
let didAddAny = false;
|
|
1424
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1425
|
+
const key = keys[i];
|
|
1426
|
+
const handlers = this.keyHandlers.get(key);
|
|
1427
|
+
if (!handlers || handlers.size === 0) continue;
|
|
1428
|
+
this.updatedKeys.add(key);
|
|
1429
|
+
didAddAny = true;
|
|
1430
|
+
}
|
|
1431
|
+
if (!didAddAny) return;
|
|
1432
|
+
if (this.deliveryMode === "immediate") {
|
|
1433
|
+
this.deliverImmediate();
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
if (this.deliveryMode === "after_flush") {
|
|
1437
|
+
this.isAfterFlushDeliveryScheduled = true;
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
if (this.isFlushEnqueued) return;
|
|
1441
|
+
this.isFlushEnqueued = true;
|
|
1442
|
+
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
1443
|
+
}
|
|
1444
|
+
markUpdatedKey(key) {
|
|
1445
|
+
if (this.deliveryMode === "queue" && this.queue.isInFlush) {
|
|
1446
|
+
throw new Error(
|
|
1447
|
+
"OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
if (this.keyHandlers.size === 0) return;
|
|
1451
|
+
const handlers = this.keyHandlers.get(key);
|
|
1452
|
+
if (!handlers || handlers.size === 0) return;
|
|
1453
|
+
this.updatedKeys.add(key);
|
|
1454
|
+
if (this.deliveryMode === "immediate") {
|
|
1455
|
+
this.deliverImmediate();
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
if (this.deliveryMode === "after_flush") {
|
|
1459
|
+
this.isAfterFlushDeliveryScheduled = true;
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
if (this.isFlushEnqueued) return;
|
|
1463
|
+
this.isFlushEnqueued = true;
|
|
1464
|
+
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
1465
|
+
}
|
|
1466
|
+
markAllUpdated() {
|
|
1467
|
+
if (this.deliveryMode === "queue" && this.queue.isInFlush) {
|
|
1468
|
+
throw new Error(
|
|
1469
|
+
"OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
if (this.keyHandlers.size === 0) return;
|
|
1473
|
+
this.keyHandlers.forEach((_handlers, key) => {
|
|
1474
|
+
this.updatedKeys.add(key);
|
|
1475
|
+
});
|
|
1476
|
+
if (this.deliveryMode === "immediate") {
|
|
1477
|
+
this.deliverImmediate();
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
if (this.deliveryMode === "after_flush") {
|
|
1481
|
+
this.isAfterFlushDeliveryScheduled = true;
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
if (this.isFlushEnqueued) return;
|
|
1485
|
+
this.isFlushEnqueued = true;
|
|
1486
|
+
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
1487
|
+
}
|
|
1488
|
+
deliverImmediate() {
|
|
1489
|
+
this.runSinglePass();
|
|
1490
|
+
}
|
|
1491
|
+
/**
|
|
1492
|
+
* Notifies all handlers for a key. The overwhelmingly common case is a
|
|
1493
|
+
* single subscriber per key (one component per pk), handled without any
|
|
1494
|
+
* allocation. For multiple subscribers a snapshot is taken so a handler may
|
|
1495
|
+
* (un)subscribe during iteration without corrupting the walk.
|
|
1496
|
+
*/
|
|
1497
|
+
notifyHandlers(handlers) {
|
|
1498
|
+
if (handlers.size === 1) {
|
|
1499
|
+
const only = handlers.values().next().value;
|
|
1500
|
+
if (only) only();
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
const snapshot = [];
|
|
1504
|
+
handlers.forEach((h) => snapshot.push(h));
|
|
1505
|
+
for (let i = 0; i < snapshot.length; i++) {
|
|
1506
|
+
const handler = snapshot[i];
|
|
1507
|
+
if (handlers.has(handler)) handler();
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Deliver updated keys and notify handlers once.
|
|
1512
|
+
* Re-entrant updates during delivery are forbidden and will throw in markUpdatedKeys().
|
|
1513
|
+
*/
|
|
1514
|
+
runSinglePass() {
|
|
1515
|
+
if (this.updatedKeys.size === 0) return;
|
|
1516
|
+
const flushingKeys = this.updatedKeys;
|
|
1517
|
+
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
1518
|
+
if (this.keyHandlers.size === 0 || flushingKeys.size === 0) return;
|
|
1519
|
+
if (flushingKeys.size * 2 < this.keyHandlers.size) {
|
|
1520
|
+
flushingKeys.forEach((key) => {
|
|
1521
|
+
const handlers = this.keyHandlers.get(key);
|
|
1522
|
+
if (!handlers || handlers.size === 0) return;
|
|
1523
|
+
this.notifyHandlers(handlers);
|
|
1524
|
+
});
|
|
1525
|
+
} else {
|
|
1526
|
+
this.keyHandlers.forEach((handlers, key) => {
|
|
1527
|
+
if (!flushingKeys.has(key)) return;
|
|
1528
|
+
if (!handlers || handlers.size === 0) return;
|
|
1529
|
+
this.notifyHandlers(handlers);
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
destroy() {
|
|
1534
|
+
if (this.dequeueFlush) {
|
|
1535
|
+
this.dequeueFlush();
|
|
1536
|
+
this.dequeueFlush = void 0;
|
|
1537
|
+
}
|
|
1538
|
+
if (this.unsubscribeAfterFlushDelivery) {
|
|
1539
|
+
this.unsubscribeAfterFlushDelivery();
|
|
1540
|
+
this.unsubscribeAfterFlushDelivery = void 0;
|
|
1541
|
+
}
|
|
1542
|
+
this.keyHandlers.forEach((handlers) => handlers.clear());
|
|
1543
|
+
this.keyHandlers.clear();
|
|
1544
|
+
this.updatedKeys.clear();
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Get performance metrics for monitoring and debugging
|
|
1548
|
+
*/
|
|
1549
|
+
getMetrics() {
|
|
1550
|
+
let totalHandlers = 0;
|
|
1551
|
+
this.keyHandlers.forEach((handlers) => {
|
|
1552
|
+
totalHandlers += handlers.size;
|
|
1553
|
+
});
|
|
1554
|
+
return {
|
|
1555
|
+
totalKeys: this.keyHandlers.size,
|
|
1556
|
+
totalHandlers,
|
|
1557
|
+
averageHandlersPerKey: this.keyHandlers.size > 0 ? totalHandlers / this.keyHandlers.size : 0,
|
|
1558
|
+
queueLength: this.queue.length
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Check if there are any active subscriptions
|
|
1563
|
+
*/
|
|
1564
|
+
hasSubscriptions() {
|
|
1565
|
+
return this.keyHandlers.size > 0;
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Get the number of handlers for a specific key
|
|
1569
|
+
*/
|
|
1570
|
+
getHandlerCount(key) {
|
|
1571
|
+
return this.keyHandlers.get(key)?.size ?? 0;
|
|
1572
|
+
}
|
|
1573
|
+
subscribeOnKey(key, handler) {
|
|
1574
|
+
let handlers = this.keyHandlers.get(key);
|
|
1575
|
+
if (!handlers) {
|
|
1576
|
+
handlers = /* @__PURE__ */ new Set();
|
|
1577
|
+
this.keyHandlers.set(key, handlers);
|
|
1578
|
+
}
|
|
1579
|
+
handlers.add(handler);
|
|
1580
|
+
return () => {
|
|
1581
|
+
this.unsubscribeFromKey(key, handler);
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
unsubscribeFromKey(key, handler) {
|
|
1585
|
+
const handlers = this.keyHandlers.get(key);
|
|
1586
|
+
if (!handlers) return;
|
|
1587
|
+
handlers.delete(handler);
|
|
1588
|
+
if (handlers.size === 0) {
|
|
1589
|
+
this.keyHandlers.delete(key);
|
|
1590
|
+
this.updatedKeys.delete(key);
|
|
1591
|
+
}
|
|
1592
|
+
if (this.keyHandlers.size === 0) {
|
|
1593
|
+
this.updatedKeys.clear();
|
|
1594
|
+
if (this.dequeueFlush) {
|
|
1595
|
+
this.dequeueFlush();
|
|
1596
|
+
this.dequeueFlush = void 0;
|
|
1597
|
+
}
|
|
1598
|
+
this.isFlushEnqueued = false;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
subscribeOnKeys(keys, handler) {
|
|
1602
|
+
const subscribedKeys = [];
|
|
1603
|
+
for (const key of keys) {
|
|
1604
|
+
let handlers = this.keyHandlers.get(key);
|
|
1605
|
+
if (!handlers) {
|
|
1606
|
+
handlers = /* @__PURE__ */ new Set();
|
|
1607
|
+
this.keyHandlers.set(key, handlers);
|
|
1608
|
+
}
|
|
1609
|
+
if (!handlers.has(handler)) {
|
|
1610
|
+
handlers.add(handler);
|
|
1611
|
+
subscribedKeys.push(key);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
return () => {
|
|
1615
|
+
for (const key of subscribedKeys) {
|
|
1616
|
+
this.unsubscribeFromKey(key, handler);
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
unsubscribeFromKeys(keys, handler) {
|
|
1621
|
+
for (const key of keys) {
|
|
1622
|
+
this.unsubscribeFromKey(key, handler);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
};
|
|
1626
|
+
|
|
1586
1627
|
// src/modules/wrapper/index/OIMOrderedListCommandStream.ts
|
|
1587
1628
|
var OIMOrderedListCommandStream = class {
|
|
1588
1629
|
constructor(queue, index) {
|