@oimdb/core 3.0.1 → 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/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -422,265 +422,198 @@ var OIMIndexArrayBased = class extends OIMIndex {
|
|
|
422
422
|
}
|
|
423
423
|
};
|
|
424
424
|
|
|
425
|
-
// src/
|
|
426
|
-
var
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
this.keyHandlers = /* @__PURE__ */ new Map();
|
|
425
|
+
// src/core/OIMCarrierKeyedEmitter.ts
|
|
426
|
+
var OIMCarrierKeyedEmitter = class {
|
|
427
|
+
constructor(queue, resolver) {
|
|
428
|
+
// Carriers with at least one subscriber — for markAllUpdated / metrics.
|
|
429
|
+
this.subscribedCarriers = /* @__PURE__ */ new Set();
|
|
430
|
+
// Pending flush batch. A plain array + a `carrier.dirty` flag for dedup —
|
|
431
|
+
// marking is the hottest write-path op, and pushing to an array with a
|
|
432
|
+
// boolean check is ~4x cheaper than a `Set` identity-hash add. The flag
|
|
433
|
+
// doubles as membership: a carrier is in this array iff `carrier.dirty`.
|
|
434
|
+
this.dirtyCarriers = [];
|
|
436
435
|
this.isFlushEnqueued = false;
|
|
437
|
-
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
438
|
-
this.isAfterFlushDeliveryScheduled = false;
|
|
439
|
-
// Stable handler reference to avoid allocating a new closure on every HAS_CHANGES.
|
|
440
436
|
this.onFlush = () => {
|
|
441
437
|
this.isFlushEnqueued = false;
|
|
442
438
|
this.dequeueFlush = void 0;
|
|
443
|
-
if (this.
|
|
444
|
-
|
|
445
|
-
|
|
439
|
+
if (this.dirtyCarriers.length === 0) return;
|
|
440
|
+
const flushing = this.dirtyCarriers;
|
|
441
|
+
this.dirtyCarriers = [];
|
|
442
|
+
let i = 0;
|
|
443
|
+
try {
|
|
444
|
+
for (; i < flushing.length; i++) {
|
|
445
|
+
flushing[i].dirty = false;
|
|
446
|
+
this.notify(flushing[i]);
|
|
447
|
+
}
|
|
448
|
+
} finally {
|
|
449
|
+
for (; i < flushing.length; i++) flushing[i].dirty = false;
|
|
446
450
|
}
|
|
447
|
-
this.runSinglePass();
|
|
448
|
-
};
|
|
449
|
-
this.onAfterFlush = () => {
|
|
450
|
-
if (!this.isAfterFlushDeliveryScheduled) return;
|
|
451
|
-
this.isAfterFlushDeliveryScheduled = false;
|
|
452
|
-
this.deliverImmediate();
|
|
453
451
|
};
|
|
454
452
|
this.queue = queue;
|
|
455
|
-
this.
|
|
456
|
-
if (this.deliveryMode === "after_flush") {
|
|
457
|
-
this.unsubscribeAfterFlushDelivery = this.queue.emitter.on(
|
|
458
|
-
0 /* AFTER_FLUSH */,
|
|
459
|
-
this.onAfterFlush
|
|
460
|
-
);
|
|
461
|
-
}
|
|
453
|
+
this.resolver = resolver;
|
|
462
454
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
);
|
|
455
|
+
subscribeOnKey(key, handler) {
|
|
456
|
+
const carrier = this.resolver.getOrReserveCarrier(key);
|
|
457
|
+
this.addHandler(carrier, handler);
|
|
458
|
+
return () => this.removeHandler(carrier, handler);
|
|
459
|
+
}
|
|
460
|
+
subscribeOnKeys(keys, handler) {
|
|
461
|
+
const carriers = [];
|
|
462
|
+
for (let i = 0; i < keys.length; i++) {
|
|
463
|
+
const carrier = this.resolver.getOrReserveCarrier(keys[i]);
|
|
464
|
+
this.addHandler(carrier, handler);
|
|
465
|
+
carriers.push(carrier);
|
|
474
466
|
}
|
|
475
|
-
|
|
476
|
-
|
|
467
|
+
return () => {
|
|
468
|
+
for (let i = 0; i < carriers.length; i++) {
|
|
469
|
+
this.removeHandler(carriers[i], handler);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
unsubscribeFromKey(key, handler) {
|
|
474
|
+
const carrier = this.resolver.findCarrier(key);
|
|
475
|
+
if (carrier) this.removeHandler(carrier, handler);
|
|
476
|
+
}
|
|
477
|
+
unsubscribeFromKeys(keys, handler) {
|
|
477
478
|
for (let i = 0; i < keys.length; i++) {
|
|
478
|
-
|
|
479
|
-
const handlers = this.keyHandlers.get(key);
|
|
480
|
-
if (!handlers || handlers.size === 0) continue;
|
|
481
|
-
this.updatedKeys.add(key);
|
|
482
|
-
didAddAny = true;
|
|
479
|
+
this.unsubscribeFromKey(keys[i], handler);
|
|
483
480
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
481
|
+
}
|
|
482
|
+
addHandler(carrier, handler) {
|
|
483
|
+
let subscribers = carrier.subscribers;
|
|
484
|
+
if (!subscribers) {
|
|
485
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
486
|
+
carrier.subscribers = subscribers;
|
|
488
487
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
488
|
+
subscribers.add(handler);
|
|
489
|
+
this.subscribedCarriers.add(carrier);
|
|
490
|
+
}
|
|
491
|
+
removeHandler(carrier, handler) {
|
|
492
|
+
const subscribers = carrier.subscribers;
|
|
493
|
+
if (!subscribers) return;
|
|
494
|
+
subscribers.delete(handler);
|
|
495
|
+
if (subscribers.size === 0) {
|
|
496
|
+
this.subscribedCarriers.delete(carrier);
|
|
497
|
+
this.resolver.onCarrierEmptied?.(carrier);
|
|
492
498
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
499
|
+
}
|
|
500
|
+
/** Fast path: the writer already holds the carrier. */
|
|
501
|
+
markUpdatedCarrier(carrier) {
|
|
502
|
+
this.assertNotInFlush();
|
|
503
|
+
const subscribers = carrier.subscribers;
|
|
504
|
+
if (!subscribers || subscribers.size === 0) return;
|
|
505
|
+
if (carrier.dirty) return;
|
|
506
|
+
carrier.dirty = true;
|
|
507
|
+
this.dirtyCarriers.push(carrier);
|
|
508
|
+
this.scheduleFlush();
|
|
496
509
|
}
|
|
497
510
|
markUpdatedKey(key) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const handlers = this.keyHandlers.get(key);
|
|
505
|
-
if (!handlers || handlers.size === 0) return;
|
|
506
|
-
this.updatedKeys.add(key);
|
|
507
|
-
if (this.deliveryMode === "immediate") {
|
|
508
|
-
this.deliverImmediate();
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
if (this.deliveryMode === "after_flush") {
|
|
512
|
-
this.isAfterFlushDeliveryScheduled = true;
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
if (this.isFlushEnqueued) return;
|
|
516
|
-
this.isFlushEnqueued = true;
|
|
517
|
-
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
511
|
+
const carrier = this.resolver.findCarrier(key);
|
|
512
|
+
if (carrier) this.markUpdatedCarrier(carrier);
|
|
513
|
+
else this.assertNotInFlush();
|
|
514
|
+
}
|
|
515
|
+
markUpdatedKeys(keys) {
|
|
516
|
+
for (let i = 0; i < keys.length; i++) this.markUpdatedKey(keys[i]);
|
|
518
517
|
}
|
|
519
518
|
markAllUpdated() {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
);
|
|
519
|
+
this.assertNotInFlush();
|
|
520
|
+
if (this.subscribedCarriers.size === 0) return;
|
|
521
|
+
this.subscribedCarriers.forEach((carrier) => {
|
|
522
|
+
if (carrier.dirty) return;
|
|
523
|
+
carrier.dirty = true;
|
|
524
|
+
this.dirtyCarriers.push(carrier);
|
|
525
|
+
});
|
|
526
|
+
this.scheduleFlush();
|
|
527
|
+
}
|
|
528
|
+
hasSubscriptions() {
|
|
529
|
+
return this.subscribedCarriers.size > 0;
|
|
530
|
+
}
|
|
531
|
+
getHandlerCount(key) {
|
|
532
|
+
return this.resolver.findCarrier(key)?.subscribers?.size ?? 0;
|
|
533
|
+
}
|
|
534
|
+
getMetrics() {
|
|
535
|
+
let totalHandlers = 0;
|
|
536
|
+
this.subscribedCarriers.forEach((carrier) => {
|
|
537
|
+
totalHandlers += carrier.subscribers?.size ?? 0;
|
|
538
|
+
});
|
|
539
|
+
const totalKeys = this.subscribedCarriers.size;
|
|
540
|
+
return {
|
|
541
|
+
totalKeys,
|
|
542
|
+
totalHandlers,
|
|
543
|
+
averageHandlersPerKey: totalKeys > 0 ? totalHandlers / totalKeys : 0,
|
|
544
|
+
queueLength: this.dirtyCarriers.length
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
destroy() {
|
|
548
|
+
if (this.dequeueFlush) {
|
|
549
|
+
this.dequeueFlush();
|
|
550
|
+
this.dequeueFlush = void 0;
|
|
524
551
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
552
|
+
this.subscribedCarriers.forEach((carrier) => {
|
|
553
|
+
carrier.subscribers?.clear();
|
|
554
|
+
carrier.subscribers = void 0;
|
|
528
555
|
});
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
556
|
+
this.subscribedCarriers.clear();
|
|
557
|
+
for (let i = 0; i < this.dirtyCarriers.length; i++) {
|
|
558
|
+
this.dirtyCarriers[i].dirty = false;
|
|
532
559
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
560
|
+
this.dirtyCarriers = [];
|
|
561
|
+
this.isFlushEnqueued = false;
|
|
562
|
+
}
|
|
563
|
+
assertNotInFlush() {
|
|
564
|
+
if (this.queue.isInFlush) {
|
|
565
|
+
throw new Error(
|
|
566
|
+
"OIMCarrierKeyedEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
567
|
+
);
|
|
536
568
|
}
|
|
569
|
+
}
|
|
570
|
+
scheduleFlush() {
|
|
537
571
|
if (this.isFlushEnqueued) return;
|
|
538
572
|
this.isFlushEnqueued = true;
|
|
539
573
|
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
540
574
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
* single subscriber per key (one component per pk), handled without any
|
|
547
|
-
* allocation. For multiple subscribers a snapshot is taken so a handler may
|
|
548
|
-
* (un)subscribe during iteration without corrupting the walk.
|
|
549
|
-
*/
|
|
550
|
-
notifyHandlers(handlers) {
|
|
551
|
-
if (handlers.size === 1) {
|
|
552
|
-
const only = handlers.values().next().value;
|
|
575
|
+
notify(carrier) {
|
|
576
|
+
const subscribers = carrier.subscribers;
|
|
577
|
+
if (!subscribers || subscribers.size === 0) return;
|
|
578
|
+
if (subscribers.size === 1) {
|
|
579
|
+
const only = subscribers.values().next().value;
|
|
553
580
|
if (only) only();
|
|
554
581
|
return;
|
|
555
582
|
}
|
|
556
583
|
const snapshot = [];
|
|
557
|
-
|
|
584
|
+
subscribers.forEach((h) => snapshot.push(h));
|
|
558
585
|
for (let i = 0; i < snapshot.length; i++) {
|
|
559
586
|
const handler = snapshot[i];
|
|
560
|
-
if (
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Deliver updated keys and notify handlers once.
|
|
565
|
-
* Re-entrant updates during delivery are forbidden and will throw in markUpdatedKeys().
|
|
566
|
-
*/
|
|
567
|
-
runSinglePass() {
|
|
568
|
-
if (this.updatedKeys.size === 0) return;
|
|
569
|
-
const flushingKeys = this.updatedKeys;
|
|
570
|
-
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
571
|
-
if (this.keyHandlers.size === 0 || flushingKeys.size === 0) return;
|
|
572
|
-
if (flushingKeys.size * 2 < this.keyHandlers.size) {
|
|
573
|
-
flushingKeys.forEach((key) => {
|
|
574
|
-
const handlers = this.keyHandlers.get(key);
|
|
575
|
-
if (!handlers || handlers.size === 0) return;
|
|
576
|
-
this.notifyHandlers(handlers);
|
|
577
|
-
});
|
|
578
|
-
} else {
|
|
579
|
-
this.keyHandlers.forEach((handlers, key) => {
|
|
580
|
-
if (!flushingKeys.has(key)) return;
|
|
581
|
-
if (!handlers || handlers.size === 0) return;
|
|
582
|
-
this.notifyHandlers(handlers);
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
destroy() {
|
|
587
|
-
if (this.dequeueFlush) {
|
|
588
|
-
this.dequeueFlush();
|
|
589
|
-
this.dequeueFlush = void 0;
|
|
590
|
-
}
|
|
591
|
-
if (this.unsubscribeAfterFlushDelivery) {
|
|
592
|
-
this.unsubscribeAfterFlushDelivery();
|
|
593
|
-
this.unsubscribeAfterFlushDelivery = void 0;
|
|
587
|
+
if (subscribers.has(handler)) handler();
|
|
594
588
|
}
|
|
595
|
-
this.keyHandlers.forEach((handlers) => handlers.clear());
|
|
596
|
-
this.keyHandlers.clear();
|
|
597
|
-
this.updatedKeys.clear();
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* Get performance metrics for monitoring and debugging
|
|
601
|
-
*/
|
|
602
|
-
getMetrics() {
|
|
603
|
-
let totalHandlers = 0;
|
|
604
|
-
this.keyHandlers.forEach((handlers) => {
|
|
605
|
-
totalHandlers += handlers.size;
|
|
606
|
-
});
|
|
607
|
-
return {
|
|
608
|
-
totalKeys: this.keyHandlers.size,
|
|
609
|
-
totalHandlers,
|
|
610
|
-
averageHandlersPerKey: this.keyHandlers.size > 0 ? totalHandlers / this.keyHandlers.size : 0,
|
|
611
|
-
queueLength: this.queue.length
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* Check if there are any active subscriptions
|
|
616
|
-
*/
|
|
617
|
-
hasSubscriptions() {
|
|
618
|
-
return this.keyHandlers.size > 0;
|
|
619
589
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
subscribeOnKey(key, handler) {
|
|
627
|
-
let handlers = this.keyHandlers.get(key);
|
|
628
|
-
if (!handlers) {
|
|
629
|
-
handlers = /* @__PURE__ */ new Set();
|
|
630
|
-
this.keyHandlers.set(key, handlers);
|
|
631
|
-
}
|
|
632
|
-
handlers.add(handler);
|
|
633
|
-
return () => {
|
|
634
|
-
this.unsubscribeFromKey(key, handler);
|
|
635
|
-
};
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// src/core/OIMKeyedCarrierResolver.ts
|
|
593
|
+
var OIMKeyedCarrierResolver = class {
|
|
594
|
+
constructor() {
|
|
595
|
+
this.carriers = /* @__PURE__ */ new Map();
|
|
636
596
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (!
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
this.keyHandlers.delete(key);
|
|
643
|
-
this.updatedKeys.delete(key);
|
|
644
|
-
}
|
|
645
|
-
if (this.keyHandlers.size === 0) {
|
|
646
|
-
this.updatedKeys.clear();
|
|
647
|
-
if (this.dequeueFlush) {
|
|
648
|
-
this.dequeueFlush();
|
|
649
|
-
this.dequeueFlush = void 0;
|
|
650
|
-
}
|
|
651
|
-
this.isFlushEnqueued = false;
|
|
597
|
+
getOrReserveCarrier(key) {
|
|
598
|
+
let carrier = this.carriers.get(key);
|
|
599
|
+
if (!carrier) {
|
|
600
|
+
carrier = { key };
|
|
601
|
+
this.carriers.set(key, carrier);
|
|
652
602
|
}
|
|
603
|
+
return carrier;
|
|
653
604
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
for (const key of keys) {
|
|
657
|
-
let handlers = this.keyHandlers.get(key);
|
|
658
|
-
if (!handlers) {
|
|
659
|
-
handlers = /* @__PURE__ */ new Set();
|
|
660
|
-
this.keyHandlers.set(key, handlers);
|
|
661
|
-
}
|
|
662
|
-
if (!handlers.has(handler)) {
|
|
663
|
-
handlers.add(handler);
|
|
664
|
-
subscribedKeys.push(key);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
return () => {
|
|
668
|
-
for (const key of subscribedKeys) {
|
|
669
|
-
this.unsubscribeFromKey(key, handler);
|
|
670
|
-
}
|
|
671
|
-
};
|
|
605
|
+
findCarrier(key) {
|
|
606
|
+
return this.carriers.get(key);
|
|
672
607
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
this.unsubscribeFromKey(key, handler);
|
|
676
|
-
}
|
|
608
|
+
onCarrierEmptied(carrier) {
|
|
609
|
+
this.carriers.delete(carrier.key);
|
|
677
610
|
}
|
|
678
611
|
};
|
|
679
612
|
|
|
680
613
|
// src/abstract/OIMReactiveIndex.ts
|
|
681
614
|
var OIMReactiveIndex = class {
|
|
682
615
|
constructor(queue, createIndex) {
|
|
683
|
-
this.updateEmitter = new
|
|
616
|
+
this.updateEmitter = new OIMCarrierKeyedEmitter(queue, new OIMKeyedCarrierResolver());
|
|
684
617
|
this.index = createIndex(this.updateEmitter);
|
|
685
618
|
}
|
|
686
619
|
getEntitiesByKey(key) {
|
|
@@ -1048,154 +981,7 @@ var OIMCollection = class {
|
|
|
1048
981
|
}
|
|
1049
982
|
destroy() {
|
|
1050
983
|
this.store.destroy();
|
|
1051
|
-
this.emitter.offAll();
|
|
1052
|
-
}
|
|
1053
|
-
};
|
|
1054
|
-
|
|
1055
|
-
// src/core/OIMCarrierKeyedEmitter.ts
|
|
1056
|
-
var OIMCarrierKeyedEmitter = class {
|
|
1057
|
-
constructor(queue, resolver) {
|
|
1058
|
-
// Carriers with at least one subscriber — for markAllUpdated / metrics.
|
|
1059
|
-
this.subscribedCarriers = /* @__PURE__ */ new Set();
|
|
1060
|
-
this.dirtyCarriers = /* @__PURE__ */ new Set();
|
|
1061
|
-
this.isFlushEnqueued = false;
|
|
1062
|
-
this.onFlush = () => {
|
|
1063
|
-
this.isFlushEnqueued = false;
|
|
1064
|
-
this.dequeueFlush = void 0;
|
|
1065
|
-
if (this.dirtyCarriers.size === 0) return;
|
|
1066
|
-
const flushing = this.dirtyCarriers;
|
|
1067
|
-
this.dirtyCarriers = /* @__PURE__ */ new Set();
|
|
1068
|
-
flushing.forEach((carrier) => this.notify(carrier));
|
|
1069
|
-
};
|
|
1070
|
-
this.queue = queue;
|
|
1071
|
-
this.resolver = resolver;
|
|
1072
|
-
}
|
|
1073
|
-
subscribeOnKey(key, handler) {
|
|
1074
|
-
const carrier = this.resolver.getOrReserveCarrier(key);
|
|
1075
|
-
this.addHandler(carrier, handler);
|
|
1076
|
-
return () => this.removeHandler(carrier, handler);
|
|
1077
|
-
}
|
|
1078
|
-
subscribeOnKeys(keys, handler) {
|
|
1079
|
-
const carriers = [];
|
|
1080
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1081
|
-
const carrier = this.resolver.getOrReserveCarrier(keys[i]);
|
|
1082
|
-
this.addHandler(carrier, handler);
|
|
1083
|
-
carriers.push(carrier);
|
|
1084
|
-
}
|
|
1085
|
-
return () => {
|
|
1086
|
-
for (let i = 0; i < carriers.length; i++) {
|
|
1087
|
-
this.removeHandler(carriers[i], handler);
|
|
1088
|
-
}
|
|
1089
|
-
};
|
|
1090
|
-
}
|
|
1091
|
-
unsubscribeFromKey(key, handler) {
|
|
1092
|
-
const carrier = this.resolver.findCarrier(key);
|
|
1093
|
-
if (carrier) this.removeHandler(carrier, handler);
|
|
1094
|
-
}
|
|
1095
|
-
unsubscribeFromKeys(keys, handler) {
|
|
1096
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1097
|
-
this.unsubscribeFromKey(keys[i], handler);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
addHandler(carrier, handler) {
|
|
1101
|
-
let subscribers = carrier.subscribers;
|
|
1102
|
-
if (!subscribers) {
|
|
1103
|
-
subscribers = /* @__PURE__ */ new Set();
|
|
1104
|
-
carrier.subscribers = subscribers;
|
|
1105
|
-
}
|
|
1106
|
-
subscribers.add(handler);
|
|
1107
|
-
this.subscribedCarriers.add(carrier);
|
|
1108
|
-
}
|
|
1109
|
-
removeHandler(carrier, handler) {
|
|
1110
|
-
const subscribers = carrier.subscribers;
|
|
1111
|
-
if (!subscribers) return;
|
|
1112
|
-
subscribers.delete(handler);
|
|
1113
|
-
if (subscribers.size === 0) {
|
|
1114
|
-
this.subscribedCarriers.delete(carrier);
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
/** Fast path: the writer already holds the carrier. */
|
|
1118
|
-
markUpdatedCarrier(carrier) {
|
|
1119
|
-
this.assertNotInFlush();
|
|
1120
|
-
const subscribers = carrier.subscribers;
|
|
1121
|
-
if (!subscribers || subscribers.size === 0) return;
|
|
1122
|
-
this.dirtyCarriers.add(carrier);
|
|
1123
|
-
this.scheduleFlush();
|
|
1124
|
-
}
|
|
1125
|
-
markUpdatedKey(key) {
|
|
1126
|
-
const carrier = this.resolver.findCarrier(key);
|
|
1127
|
-
if (carrier) this.markUpdatedCarrier(carrier);
|
|
1128
|
-
else this.assertNotInFlush();
|
|
1129
|
-
}
|
|
1130
|
-
markUpdatedKeys(keys) {
|
|
1131
|
-
for (let i = 0; i < keys.length; i++) this.markUpdatedKey(keys[i]);
|
|
1132
|
-
}
|
|
1133
|
-
markAllUpdated() {
|
|
1134
|
-
this.assertNotInFlush();
|
|
1135
|
-
if (this.subscribedCarriers.size === 0) return;
|
|
1136
|
-
this.subscribedCarriers.forEach(
|
|
1137
|
-
(carrier) => this.dirtyCarriers.add(carrier)
|
|
1138
|
-
);
|
|
1139
|
-
this.scheduleFlush();
|
|
1140
|
-
}
|
|
1141
|
-
hasSubscriptions() {
|
|
1142
|
-
return this.subscribedCarriers.size > 0;
|
|
1143
|
-
}
|
|
1144
|
-
getHandlerCount(key) {
|
|
1145
|
-
return this.resolver.findCarrier(key)?.subscribers?.size ?? 0;
|
|
1146
|
-
}
|
|
1147
|
-
getMetrics() {
|
|
1148
|
-
let totalHandlers = 0;
|
|
1149
|
-
this.subscribedCarriers.forEach((carrier) => {
|
|
1150
|
-
totalHandlers += carrier.subscribers?.size ?? 0;
|
|
1151
|
-
});
|
|
1152
|
-
const totalKeys = this.subscribedCarriers.size;
|
|
1153
|
-
return {
|
|
1154
|
-
totalKeys,
|
|
1155
|
-
totalHandlers,
|
|
1156
|
-
averageHandlersPerKey: totalKeys > 0 ? totalHandlers / totalKeys : 0,
|
|
1157
|
-
queueLength: this.dirtyCarriers.size
|
|
1158
|
-
};
|
|
1159
|
-
}
|
|
1160
|
-
destroy() {
|
|
1161
|
-
if (this.dequeueFlush) {
|
|
1162
|
-
this.dequeueFlush();
|
|
1163
|
-
this.dequeueFlush = void 0;
|
|
1164
|
-
}
|
|
1165
|
-
this.subscribedCarriers.forEach((carrier) => {
|
|
1166
|
-
carrier.subscribers?.clear();
|
|
1167
|
-
carrier.subscribers = void 0;
|
|
1168
|
-
});
|
|
1169
|
-
this.subscribedCarriers.clear();
|
|
1170
|
-
this.dirtyCarriers.clear();
|
|
1171
|
-
this.isFlushEnqueued = false;
|
|
1172
|
-
}
|
|
1173
|
-
assertNotInFlush() {
|
|
1174
|
-
if (this.queue.isInFlush) {
|
|
1175
|
-
throw new Error(
|
|
1176
|
-
"OIMCarrierKeyedEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
1177
|
-
);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
scheduleFlush() {
|
|
1181
|
-
if (this.isFlushEnqueued) return;
|
|
1182
|
-
this.isFlushEnqueued = true;
|
|
1183
|
-
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
1184
|
-
}
|
|
1185
|
-
notify(carrier) {
|
|
1186
|
-
const subscribers = carrier.subscribers;
|
|
1187
|
-
if (!subscribers || subscribers.size === 0) return;
|
|
1188
|
-
if (subscribers.size === 1) {
|
|
1189
|
-
const only = subscribers.values().next().value;
|
|
1190
|
-
if (only) only();
|
|
1191
|
-
return;
|
|
1192
|
-
}
|
|
1193
|
-
const snapshot = [];
|
|
1194
|
-
subscribers.forEach((h) => snapshot.push(h));
|
|
1195
|
-
for (let i = 0; i < snapshot.length; i++) {
|
|
1196
|
-
const handler = snapshot[i];
|
|
1197
|
-
if (subscribers.has(handler)) handler();
|
|
1198
|
-
}
|
|
984
|
+
this.emitter.offAll();
|
|
1199
985
|
}
|
|
1200
986
|
};
|
|
1201
987
|
|
|
@@ -1481,6 +1267,261 @@ var OIMCollectionIndexManualOrderedArrayBased = class extends OIMIndexManualOrde
|
|
|
1481
1267
|
}
|
|
1482
1268
|
};
|
|
1483
1269
|
|
|
1270
|
+
// src/enums/EOIMEventQueueEventType.ts
|
|
1271
|
+
var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
|
|
1272
|
+
EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
|
|
1273
|
+
EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
|
|
1274
|
+
return EOIMEventQueueEventType2;
|
|
1275
|
+
})(EOIMEventQueueEventType || {});
|
|
1276
|
+
|
|
1277
|
+
// src/core/OIMUpdateEventEmitter.ts
|
|
1278
|
+
var OIMUpdateEventEmitter = class {
|
|
1279
|
+
constructor(queue, deliveryMode = "queue") {
|
|
1280
|
+
this.keyHandlers = /* @__PURE__ */ new Map();
|
|
1281
|
+
this.isFlushEnqueued = false;
|
|
1282
|
+
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
1283
|
+
this.isAfterFlushDeliveryScheduled = false;
|
|
1284
|
+
// Stable handler reference to avoid allocating a new closure on every HAS_CHANGES.
|
|
1285
|
+
this.onFlush = () => {
|
|
1286
|
+
this.isFlushEnqueued = false;
|
|
1287
|
+
this.dequeueFlush = void 0;
|
|
1288
|
+
if (this.keyHandlers.size === 0) {
|
|
1289
|
+
this.updatedKeys.clear();
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
this.runSinglePass();
|
|
1293
|
+
};
|
|
1294
|
+
this.onAfterFlush = () => {
|
|
1295
|
+
if (!this.isAfterFlushDeliveryScheduled) return;
|
|
1296
|
+
this.isAfterFlushDeliveryScheduled = false;
|
|
1297
|
+
this.deliverImmediate();
|
|
1298
|
+
};
|
|
1299
|
+
this.queue = queue;
|
|
1300
|
+
this.deliveryMode = deliveryMode;
|
|
1301
|
+
if (this.deliveryMode === "after_flush") {
|
|
1302
|
+
this.unsubscribeAfterFlushDelivery = this.queue.emitter.on(
|
|
1303
|
+
0 /* AFTER_FLUSH */,
|
|
1304
|
+
this.onAfterFlush
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Mark keys as updated and schedule delivery.
|
|
1310
|
+
*
|
|
1311
|
+
* If there are no subscriptions, this is a no-op and does not allocate or retain keys.
|
|
1312
|
+
*/
|
|
1313
|
+
markUpdatedKeys(keys) {
|
|
1314
|
+
if (keys.length === 0) return;
|
|
1315
|
+
if (this.deliveryMode === "queue" && this.queue.isInFlush) {
|
|
1316
|
+
throw new Error(
|
|
1317
|
+
"OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
if (this.keyHandlers.size === 0) return;
|
|
1321
|
+
let didAddAny = false;
|
|
1322
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1323
|
+
const key = keys[i];
|
|
1324
|
+
const handlers = this.keyHandlers.get(key);
|
|
1325
|
+
if (!handlers || handlers.size === 0) continue;
|
|
1326
|
+
this.updatedKeys.add(key);
|
|
1327
|
+
didAddAny = true;
|
|
1328
|
+
}
|
|
1329
|
+
if (!didAddAny) return;
|
|
1330
|
+
if (this.deliveryMode === "immediate") {
|
|
1331
|
+
this.deliverImmediate();
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
if (this.deliveryMode === "after_flush") {
|
|
1335
|
+
this.isAfterFlushDeliveryScheduled = true;
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
if (this.isFlushEnqueued) return;
|
|
1339
|
+
this.isFlushEnqueued = true;
|
|
1340
|
+
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
1341
|
+
}
|
|
1342
|
+
markUpdatedKey(key) {
|
|
1343
|
+
if (this.deliveryMode === "queue" && this.queue.isInFlush) {
|
|
1344
|
+
throw new Error(
|
|
1345
|
+
"OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
if (this.keyHandlers.size === 0) return;
|
|
1349
|
+
const handlers = this.keyHandlers.get(key);
|
|
1350
|
+
if (!handlers || handlers.size === 0) return;
|
|
1351
|
+
this.updatedKeys.add(key);
|
|
1352
|
+
if (this.deliveryMode === "immediate") {
|
|
1353
|
+
this.deliverImmediate();
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
if (this.deliveryMode === "after_flush") {
|
|
1357
|
+
this.isAfterFlushDeliveryScheduled = true;
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
if (this.isFlushEnqueued) return;
|
|
1361
|
+
this.isFlushEnqueued = true;
|
|
1362
|
+
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
1363
|
+
}
|
|
1364
|
+
markAllUpdated() {
|
|
1365
|
+
if (this.deliveryMode === "queue" && this.queue.isInFlush) {
|
|
1366
|
+
throw new Error(
|
|
1367
|
+
"OIMUpdateEventEmitter: updates during queue.flush() are not allowed. Finish all writes before calling queue.flush()."
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
if (this.keyHandlers.size === 0) return;
|
|
1371
|
+
this.keyHandlers.forEach((_handlers, key) => {
|
|
1372
|
+
this.updatedKeys.add(key);
|
|
1373
|
+
});
|
|
1374
|
+
if (this.deliveryMode === "immediate") {
|
|
1375
|
+
this.deliverImmediate();
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
if (this.deliveryMode === "after_flush") {
|
|
1379
|
+
this.isAfterFlushDeliveryScheduled = true;
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
if (this.isFlushEnqueued) return;
|
|
1383
|
+
this.isFlushEnqueued = true;
|
|
1384
|
+
this.dequeueFlush = this.queue.enqueue(this.onFlush);
|
|
1385
|
+
}
|
|
1386
|
+
deliverImmediate() {
|
|
1387
|
+
this.runSinglePass();
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Notifies all handlers for a key. The overwhelmingly common case is a
|
|
1391
|
+
* single subscriber per key (one component per pk), handled without any
|
|
1392
|
+
* allocation. For multiple subscribers a snapshot is taken so a handler may
|
|
1393
|
+
* (un)subscribe during iteration without corrupting the walk.
|
|
1394
|
+
*/
|
|
1395
|
+
notifyHandlers(handlers) {
|
|
1396
|
+
if (handlers.size === 1) {
|
|
1397
|
+
const only = handlers.values().next().value;
|
|
1398
|
+
if (only) only();
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
const snapshot = [];
|
|
1402
|
+
handlers.forEach((h) => snapshot.push(h));
|
|
1403
|
+
for (let i = 0; i < snapshot.length; i++) {
|
|
1404
|
+
const handler = snapshot[i];
|
|
1405
|
+
if (handlers.has(handler)) handler();
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Deliver updated keys and notify handlers once.
|
|
1410
|
+
* Re-entrant updates during delivery are forbidden and will throw in markUpdatedKeys().
|
|
1411
|
+
*/
|
|
1412
|
+
runSinglePass() {
|
|
1413
|
+
if (this.updatedKeys.size === 0) return;
|
|
1414
|
+
const flushingKeys = this.updatedKeys;
|
|
1415
|
+
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
1416
|
+
if (this.keyHandlers.size === 0 || flushingKeys.size === 0) return;
|
|
1417
|
+
if (flushingKeys.size * 2 < this.keyHandlers.size) {
|
|
1418
|
+
flushingKeys.forEach((key) => {
|
|
1419
|
+
const handlers = this.keyHandlers.get(key);
|
|
1420
|
+
if (!handlers || handlers.size === 0) return;
|
|
1421
|
+
this.notifyHandlers(handlers);
|
|
1422
|
+
});
|
|
1423
|
+
} else {
|
|
1424
|
+
this.keyHandlers.forEach((handlers, key) => {
|
|
1425
|
+
if (!flushingKeys.has(key)) return;
|
|
1426
|
+
if (!handlers || handlers.size === 0) return;
|
|
1427
|
+
this.notifyHandlers(handlers);
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
destroy() {
|
|
1432
|
+
if (this.dequeueFlush) {
|
|
1433
|
+
this.dequeueFlush();
|
|
1434
|
+
this.dequeueFlush = void 0;
|
|
1435
|
+
}
|
|
1436
|
+
if (this.unsubscribeAfterFlushDelivery) {
|
|
1437
|
+
this.unsubscribeAfterFlushDelivery();
|
|
1438
|
+
this.unsubscribeAfterFlushDelivery = void 0;
|
|
1439
|
+
}
|
|
1440
|
+
this.keyHandlers.forEach((handlers) => handlers.clear());
|
|
1441
|
+
this.keyHandlers.clear();
|
|
1442
|
+
this.updatedKeys.clear();
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Get performance metrics for monitoring and debugging
|
|
1446
|
+
*/
|
|
1447
|
+
getMetrics() {
|
|
1448
|
+
let totalHandlers = 0;
|
|
1449
|
+
this.keyHandlers.forEach((handlers) => {
|
|
1450
|
+
totalHandlers += handlers.size;
|
|
1451
|
+
});
|
|
1452
|
+
return {
|
|
1453
|
+
totalKeys: this.keyHandlers.size,
|
|
1454
|
+
totalHandlers,
|
|
1455
|
+
averageHandlersPerKey: this.keyHandlers.size > 0 ? totalHandlers / this.keyHandlers.size : 0,
|
|
1456
|
+
queueLength: this.queue.length
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Check if there are any active subscriptions
|
|
1461
|
+
*/
|
|
1462
|
+
hasSubscriptions() {
|
|
1463
|
+
return this.keyHandlers.size > 0;
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Get the number of handlers for a specific key
|
|
1467
|
+
*/
|
|
1468
|
+
getHandlerCount(key) {
|
|
1469
|
+
return this.keyHandlers.get(key)?.size ?? 0;
|
|
1470
|
+
}
|
|
1471
|
+
subscribeOnKey(key, handler) {
|
|
1472
|
+
let handlers = this.keyHandlers.get(key);
|
|
1473
|
+
if (!handlers) {
|
|
1474
|
+
handlers = /* @__PURE__ */ new Set();
|
|
1475
|
+
this.keyHandlers.set(key, handlers);
|
|
1476
|
+
}
|
|
1477
|
+
handlers.add(handler);
|
|
1478
|
+
return () => {
|
|
1479
|
+
this.unsubscribeFromKey(key, handler);
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
unsubscribeFromKey(key, handler) {
|
|
1483
|
+
const handlers = this.keyHandlers.get(key);
|
|
1484
|
+
if (!handlers) return;
|
|
1485
|
+
handlers.delete(handler);
|
|
1486
|
+
if (handlers.size === 0) {
|
|
1487
|
+
this.keyHandlers.delete(key);
|
|
1488
|
+
this.updatedKeys.delete(key);
|
|
1489
|
+
}
|
|
1490
|
+
if (this.keyHandlers.size === 0) {
|
|
1491
|
+
this.updatedKeys.clear();
|
|
1492
|
+
if (this.dequeueFlush) {
|
|
1493
|
+
this.dequeueFlush();
|
|
1494
|
+
this.dequeueFlush = void 0;
|
|
1495
|
+
}
|
|
1496
|
+
this.isFlushEnqueued = false;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
subscribeOnKeys(keys, handler) {
|
|
1500
|
+
const subscribedKeys = [];
|
|
1501
|
+
for (const key of keys) {
|
|
1502
|
+
let handlers = this.keyHandlers.get(key);
|
|
1503
|
+
if (!handlers) {
|
|
1504
|
+
handlers = /* @__PURE__ */ new Set();
|
|
1505
|
+
this.keyHandlers.set(key, handlers);
|
|
1506
|
+
}
|
|
1507
|
+
if (!handlers.has(handler)) {
|
|
1508
|
+
handlers.add(handler);
|
|
1509
|
+
subscribedKeys.push(key);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return () => {
|
|
1513
|
+
for (const key of subscribedKeys) {
|
|
1514
|
+
this.unsubscribeFromKey(key, handler);
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
unsubscribeFromKeys(keys, handler) {
|
|
1519
|
+
for (const key of keys) {
|
|
1520
|
+
this.unsubscribeFromKey(key, handler);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
|
|
1484
1525
|
// src/modules/wrapper/index/OIMOrderedListCommandStream.ts
|
|
1485
1526
|
var OIMOrderedListCommandStream = class {
|
|
1486
1527
|
constructor(queue, index) {
|