@oimdb/core 3.7.0 → 3.8.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/README.md +20 -0
- package/dist/index.cjs +1276 -268
- package/dist/index.d.cts +915 -231
- package/dist/index.d.ts +915 -231
- package/dist/index.js +1260 -268
- package/llms.txt +43 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
EOIMEventQueueSchedulerEventType: () => EOIMEventQueueSchedulerEventType,
|
|
27
27
|
EOIMIndexEventType: () => EOIMIndexEventType,
|
|
28
28
|
EOIMObjectEventType: () => EOIMObjectEventType,
|
|
29
|
+
OIMBucketCarrierResolver: () => OIMBucketCarrierResolver,
|
|
29
30
|
OIMCarrierKeyedEmitter: () => OIMCarrierKeyedEmitter,
|
|
30
31
|
OIMCarrierSingleEmitter: () => OIMCarrierSingleEmitter,
|
|
31
32
|
OIMCollection: () => OIMCollection,
|
|
@@ -38,6 +39,7 @@ __export(index_exports, {
|
|
|
38
39
|
OIMCollectionSelectors: () => OIMCollectionSelectors,
|
|
39
40
|
OIMCollectionStore: () => OIMCollectionStore,
|
|
40
41
|
OIMCollectionStoreMapDriven: () => OIMCollectionStoreMapDriven,
|
|
42
|
+
OIMCollectionStoreTrieDriven: () => OIMCollectionStoreTrieDriven,
|
|
41
43
|
OIMComparatorFactory: () => OIMComparatorFactory,
|
|
42
44
|
OIMComputeRuntime: () => OIMComputeRuntime,
|
|
43
45
|
OIMComputed: () => OIMComputed,
|
|
@@ -46,6 +48,7 @@ __export(index_exports, {
|
|
|
46
48
|
OIMDerivedCollectionGlobalIndexSetBased: () => OIMDerivedCollectionGlobalIndexSetBased,
|
|
47
49
|
OIMDerivedCollectionIndexArrayBased: () => OIMDerivedCollectionIndexArrayBased,
|
|
48
50
|
OIMDerivedCollectionIndexSetBased: () => OIMDerivedCollectionIndexSetBased,
|
|
51
|
+
OIMDisposeScope: () => OIMDisposeScope,
|
|
49
52
|
OIMEffect: () => OIMEffect,
|
|
50
53
|
OIMEffectDependencyComputed: () => OIMEffectDependencyComputed,
|
|
51
54
|
OIMEffectDependencyKeyed: () => OIMEffectDependencyKeyed,
|
|
@@ -88,6 +91,13 @@ __export(index_exports, {
|
|
|
88
91
|
OIMIndexStoreMapDrivenArrayBased: () => OIMIndexStoreMapDrivenArrayBased,
|
|
89
92
|
OIMIndexStoreMapDrivenSetBased: () => OIMIndexStoreMapDrivenSetBased,
|
|
90
93
|
OIMIndexStoreSetBased: () => OIMIndexStoreSetBased,
|
|
94
|
+
OIMIndexStoreTrieDrivenArrayBased: () => OIMIndexStoreTrieDrivenArrayBased,
|
|
95
|
+
OIMIndexStoreTrieDrivenSetBased: () => OIMIndexStoreTrieDrivenSetBased,
|
|
96
|
+
OIMKeyDomainNative: () => OIMKeyDomainNative,
|
|
97
|
+
OIMKeyDomainTrie: () => OIMKeyDomainTrie,
|
|
98
|
+
OIMKeyPathCarrierResolver: () => OIMKeyPathCarrierResolver,
|
|
99
|
+
OIMKeyedBucketArrayBased: () => OIMKeyedBucketArrayBased,
|
|
100
|
+
OIMKeyedBucketSetBased: () => OIMKeyedBucketSetBased,
|
|
91
101
|
OIMMap2Keys: () => OIMMap2Keys,
|
|
92
102
|
OIMObject: () => OIMObject,
|
|
93
103
|
OIMObjectStore: () => OIMObjectStore,
|
|
@@ -99,10 +109,13 @@ __export(index_exports, {
|
|
|
99
109
|
OIMOrderedListCommandStream: () => OIMOrderedListCommandStream,
|
|
100
110
|
OIMOrderedListCommandStreamDiffDriven: () => OIMOrderedListCommandStreamDiffDriven,
|
|
101
111
|
OIMOrderedListMappedCommandStream: () => OIMOrderedListMappedCommandStream,
|
|
112
|
+
OIMPkCodecKeyPath: () => OIMPkCodecKeyPath,
|
|
102
113
|
OIMPkSelectorFactory: () => OIMPkSelectorFactory,
|
|
103
114
|
OIMReactiveCollection: () => OIMReactiveCollection,
|
|
104
115
|
OIMReactiveCollectionGlobalIndexManualArrayBased: () => OIMReactiveCollectionGlobalIndexManualArrayBased,
|
|
105
116
|
OIMReactiveCollectionGlobalIndexManualSetBased: () => OIMReactiveCollectionGlobalIndexManualSetBased,
|
|
117
|
+
OIMReactiveCollectionIndexCompositeTrieArrayBased: () => OIMReactiveCollectionIndexCompositeTrieArrayBased,
|
|
118
|
+
OIMReactiveCollectionIndexCompositeTrieSetBased: () => OIMReactiveCollectionIndexCompositeTrieSetBased,
|
|
106
119
|
OIMReactiveCollectionIndexManualArrayBased: () => OIMReactiveCollectionIndexManualArrayBased,
|
|
107
120
|
OIMReactiveCollectionIndexManualSetBased: () => OIMReactiveCollectionIndexManualSetBased,
|
|
108
121
|
OIMReactiveGlobalIndex: () => OIMReactiveGlobalIndex,
|
|
@@ -117,10 +130,13 @@ __export(index_exports, {
|
|
|
117
130
|
OIMReactiveIndexSetBased: () => OIMReactiveIndexSetBased,
|
|
118
131
|
OIMReactiveObject: () => OIMReactiveObject,
|
|
119
132
|
OIMSelector: () => OIMSelector,
|
|
133
|
+
OIMTrieMap: () => OIMTrieMap,
|
|
134
|
+
OIMTrieSet: () => OIMTrieSet,
|
|
120
135
|
createInPlaceEntityUpdater: () => createInPlaceEntityUpdater,
|
|
121
136
|
createMergeEntityUpdater: () => createMergeEntityUpdater,
|
|
122
137
|
createOIMCollectionIndexFactory: () => createOIMCollectionIndexFactory,
|
|
123
138
|
createOIMCollectionKit: () => createOIMCollectionKit,
|
|
139
|
+
createOIMDisposeScope: () => createOIMDisposeScope,
|
|
124
140
|
createOIMGlobalIndexSlotMap: () => createOIMGlobalIndexSlotMap,
|
|
125
141
|
createOIMIndexSlotMap: () => createOIMIndexSlotMap,
|
|
126
142
|
createOIMOrderedListCommandStreamDiffDriven: () => createOIMOrderedListCommandStreamDiffDriven,
|
|
@@ -358,21 +374,82 @@ var OIMEventQueueScheduler = class {
|
|
|
358
374
|
}
|
|
359
375
|
};
|
|
360
376
|
|
|
377
|
+
// src/core/OIMKeyedBucketSetBased.ts
|
|
378
|
+
var OIMKeyedBucketSetBased = class extends Set {
|
|
379
|
+
constructor(key) {
|
|
380
|
+
super();
|
|
381
|
+
this.key = key;
|
|
382
|
+
}
|
|
383
|
+
hasSubscribers() {
|
|
384
|
+
return this.subscribers !== void 0 && this.subscribers.size > 0;
|
|
385
|
+
}
|
|
386
|
+
/** Clearing the slot set also clears the pk membership — they stay in sync. */
|
|
387
|
+
clear() {
|
|
388
|
+
super.clear();
|
|
389
|
+
this.membership?.clear();
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
361
393
|
// src/core/OIMIndexStoreMapDrivenSetBased.ts
|
|
362
394
|
var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
363
395
|
constructor() {
|
|
364
396
|
super(...arguments);
|
|
397
|
+
// Live buckets (non-empty, or freshly promoted). Enumeration reads these only.
|
|
365
398
|
this.slots = /* @__PURE__ */ new Map();
|
|
399
|
+
// Empty buckets kept alive solely by their subscribers, so a subscription
|
|
400
|
+
// survives a key going empty → re-filled. Invisible to reads/enumeration.
|
|
401
|
+
this.reservedBuckets = /* @__PURE__ */ new Map();
|
|
402
|
+
}
|
|
403
|
+
getOrReserveBucket(key) {
|
|
404
|
+
const live = this.slots.get(key);
|
|
405
|
+
if (live) return live;
|
|
406
|
+
let reserved = this.reservedBuckets.get(key);
|
|
407
|
+
if (!reserved) {
|
|
408
|
+
reserved = new OIMKeyedBucketSetBased(key);
|
|
409
|
+
this.reservedBuckets.set(key, reserved);
|
|
410
|
+
}
|
|
411
|
+
return reserved;
|
|
412
|
+
}
|
|
413
|
+
findBucket(key) {
|
|
414
|
+
return this.slots.get(key) ?? this.reservedBuckets.get(key);
|
|
415
|
+
}
|
|
416
|
+
retainBucket(bucket) {
|
|
417
|
+
if (bucket.size === 0) return;
|
|
418
|
+
this.reservedBuckets.delete(bucket.key);
|
|
419
|
+
if (!this.slots.has(bucket.key)) this.slots.set(bucket.key, bucket);
|
|
420
|
+
}
|
|
421
|
+
releaseBucket(bucket) {
|
|
422
|
+
if (bucket.size > 0) return;
|
|
423
|
+
this.slots.delete(bucket.key);
|
|
424
|
+
if (bucket.hasSubscribers()) {
|
|
425
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
426
|
+
} else {
|
|
427
|
+
this.reservedBuckets.delete(bucket.key);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
dropIfReserved(key) {
|
|
431
|
+
const reserved = this.reservedBuckets.get(key);
|
|
432
|
+
if (reserved && reserved.size === 0 && !reserved.hasSubscribers()) {
|
|
433
|
+
this.reservedBuckets.delete(key);
|
|
434
|
+
}
|
|
366
435
|
}
|
|
367
436
|
setOneByKey(key, slots) {
|
|
368
|
-
this.
|
|
437
|
+
const bucket = this.getOrReserveBucket(key);
|
|
438
|
+
if (bucket !== slots) {
|
|
439
|
+
bucket.clear();
|
|
440
|
+
for (const slot of slots) bucket.add(slot);
|
|
441
|
+
}
|
|
442
|
+
if (bucket.size > 0) this.retainBucket(bucket);
|
|
443
|
+
else this.releaseBucket(bucket);
|
|
369
444
|
}
|
|
370
445
|
removeOneByKey(key) {
|
|
371
446
|
this.slots.delete(key);
|
|
447
|
+
this.reservedBuckets.delete(key);
|
|
372
448
|
}
|
|
373
449
|
removeManyByKeys(keys) {
|
|
374
450
|
for (const key of keys) {
|
|
375
451
|
this.slots.delete(key);
|
|
452
|
+
this.reservedBuckets.delete(key);
|
|
376
453
|
}
|
|
377
454
|
}
|
|
378
455
|
getOneByKey(key) {
|
|
@@ -381,10 +458,8 @@ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
|
381
458
|
getManyByKeys(keys) {
|
|
382
459
|
const result = /* @__PURE__ */ new Map();
|
|
383
460
|
for (const key of keys) {
|
|
384
|
-
const slots = this.
|
|
385
|
-
if (slots !== void 0)
|
|
386
|
-
result.set(key, slots);
|
|
387
|
-
}
|
|
461
|
+
const slots = this.slots.get(key);
|
|
462
|
+
if (slots !== void 0) result.set(key, slots);
|
|
388
463
|
}
|
|
389
464
|
return result;
|
|
390
465
|
}
|
|
@@ -398,6 +473,12 @@ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
|
398
473
|
return this.slots.size;
|
|
399
474
|
}
|
|
400
475
|
clear() {
|
|
476
|
+
for (const bucket of this.slots.values()) {
|
|
477
|
+
bucket.clear();
|
|
478
|
+
if (bucket.hasSubscribers()) {
|
|
479
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
401
482
|
this.slots.clear();
|
|
402
483
|
}
|
|
403
484
|
};
|
|
@@ -405,10 +486,9 @@ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
|
405
486
|
// src/abstract/OIMIndexSetBased.ts
|
|
406
487
|
var OIMIndexSetBased = class extends OIMIndex {
|
|
407
488
|
constructor(options = {}) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
);
|
|
489
|
+
const store = options.store ?? new OIMIndexStoreMapDrivenSetBased();
|
|
490
|
+
super(store, options.comparePks);
|
|
491
|
+
this.setStore = store;
|
|
412
492
|
}
|
|
413
493
|
getBucketSize(bucket) {
|
|
414
494
|
return bucket.size;
|
|
@@ -436,22 +516,36 @@ var OIMIndexSetBased = class extends OIMIndex {
|
|
|
436
516
|
for (const slot of slots) pks.add(slot.pk);
|
|
437
517
|
return pks;
|
|
438
518
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
519
|
+
/**
|
|
520
|
+
* True if a comparator is set and considers `bucket`'s current pks equal to
|
|
521
|
+
* `newSlots` — i.e. a `setSlots` would be a no-op and should not emit.
|
|
522
|
+
*/
|
|
523
|
+
bucketMatchesComparator(bucket, newSlots) {
|
|
524
|
+
if (!this.comparePks) return false;
|
|
525
|
+
if (bucket.size !== newSlots.size) return false;
|
|
526
|
+
if (bucket.size === 0) return true;
|
|
527
|
+
return this.comparePks(
|
|
528
|
+
Array.from(this.slotsToPks(bucket)),
|
|
529
|
+
Array.from(this.slotsToPks(newSlots))
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// src/core/OIMKeyedBucketArrayBased.ts
|
|
535
|
+
var OIMKeyedBucketArrayBased = class extends Array {
|
|
536
|
+
constructor(key) {
|
|
537
|
+
super();
|
|
538
|
+
this.key = key;
|
|
539
|
+
}
|
|
540
|
+
static get [Symbol.species]() {
|
|
541
|
+
return Array;
|
|
542
|
+
}
|
|
543
|
+
hasSubscribers() {
|
|
544
|
+
return this.subscribers !== void 0 && this.subscribers.size > 0;
|
|
545
|
+
}
|
|
546
|
+
/** Empty the bucket in place (Array has no `clear`). */
|
|
547
|
+
clear() {
|
|
548
|
+
this.length = 0;
|
|
455
549
|
}
|
|
456
550
|
};
|
|
457
551
|
|
|
@@ -459,17 +553,61 @@ var OIMIndexSetBased = class extends OIMIndex {
|
|
|
459
553
|
var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
460
554
|
constructor() {
|
|
461
555
|
super(...arguments);
|
|
556
|
+
// Live buckets (non-empty). Enumeration reads these only.
|
|
462
557
|
this.slots = /* @__PURE__ */ new Map();
|
|
558
|
+
// Empty buckets kept alive by their subscribers, invisible to reads.
|
|
559
|
+
this.reservedBuckets = /* @__PURE__ */ new Map();
|
|
560
|
+
}
|
|
561
|
+
getOrReserveBucket(key) {
|
|
562
|
+
const live = this.slots.get(key);
|
|
563
|
+
if (live) return live;
|
|
564
|
+
let reserved = this.reservedBuckets.get(key);
|
|
565
|
+
if (!reserved) {
|
|
566
|
+
reserved = new OIMKeyedBucketArrayBased(key);
|
|
567
|
+
this.reservedBuckets.set(key, reserved);
|
|
568
|
+
}
|
|
569
|
+
return reserved;
|
|
570
|
+
}
|
|
571
|
+
findBucket(key) {
|
|
572
|
+
return this.slots.get(key) ?? this.reservedBuckets.get(key);
|
|
573
|
+
}
|
|
574
|
+
retainBucket(bucket) {
|
|
575
|
+
if (bucket.length === 0) return;
|
|
576
|
+
this.reservedBuckets.delete(bucket.key);
|
|
577
|
+
if (!this.slots.has(bucket.key)) this.slots.set(bucket.key, bucket);
|
|
578
|
+
}
|
|
579
|
+
releaseBucket(bucket) {
|
|
580
|
+
if (bucket.length > 0) return;
|
|
581
|
+
this.slots.delete(bucket.key);
|
|
582
|
+
if (bucket.hasSubscribers()) {
|
|
583
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
584
|
+
} else {
|
|
585
|
+
this.reservedBuckets.delete(bucket.key);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
dropIfReserved(key) {
|
|
589
|
+
const reserved = this.reservedBuckets.get(key);
|
|
590
|
+
if (reserved && reserved.length === 0 && !reserved.hasSubscribers()) {
|
|
591
|
+
this.reservedBuckets.delete(key);
|
|
592
|
+
}
|
|
463
593
|
}
|
|
464
594
|
setOneByKey(key, slots) {
|
|
465
|
-
this.
|
|
595
|
+
const bucket = this.getOrReserveBucket(key);
|
|
596
|
+
if (bucket !== slots) {
|
|
597
|
+
bucket.length = 0;
|
|
598
|
+
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
599
|
+
}
|
|
600
|
+
if (bucket.length > 0) this.retainBucket(bucket);
|
|
601
|
+
else this.releaseBucket(bucket);
|
|
466
602
|
}
|
|
467
603
|
removeOneByKey(key) {
|
|
468
604
|
this.slots.delete(key);
|
|
605
|
+
this.reservedBuckets.delete(key);
|
|
469
606
|
}
|
|
470
607
|
removeManyByKeys(keys) {
|
|
471
608
|
for (const key of keys) {
|
|
472
609
|
this.slots.delete(key);
|
|
610
|
+
this.reservedBuckets.delete(key);
|
|
473
611
|
}
|
|
474
612
|
}
|
|
475
613
|
getOneByKey(key) {
|
|
@@ -478,10 +616,8 @@ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
|
478
616
|
getManyByKeys(keys) {
|
|
479
617
|
const result = /* @__PURE__ */ new Map();
|
|
480
618
|
for (const key of keys) {
|
|
481
|
-
const slots = this.
|
|
482
|
-
if (slots !== void 0)
|
|
483
|
-
result.set(key, slots);
|
|
484
|
-
}
|
|
619
|
+
const slots = this.slots.get(key);
|
|
620
|
+
if (slots !== void 0) result.set(key, slots);
|
|
485
621
|
}
|
|
486
622
|
return result;
|
|
487
623
|
}
|
|
@@ -495,6 +631,12 @@ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
|
495
631
|
return this.slots.size;
|
|
496
632
|
}
|
|
497
633
|
clear() {
|
|
634
|
+
for (const bucket of this.slots.values()) {
|
|
635
|
+
bucket.length = 0;
|
|
636
|
+
if (bucket.hasSubscribers()) {
|
|
637
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
498
640
|
this.slots.clear();
|
|
499
641
|
}
|
|
500
642
|
};
|
|
@@ -502,10 +644,9 @@ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
|
502
644
|
// src/abstract/OIMIndexArrayBased.ts
|
|
503
645
|
var OIMIndexArrayBased = class extends OIMIndex {
|
|
504
646
|
constructor(options = {}) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
);
|
|
647
|
+
const store = options.store ?? new OIMIndexStoreMapDrivenArrayBased();
|
|
648
|
+
super(store, options.comparePks);
|
|
649
|
+
this.arrayStore = store;
|
|
509
650
|
}
|
|
510
651
|
getBucketSize(bucket) {
|
|
511
652
|
return bucket.length;
|
|
@@ -523,10 +664,15 @@ var OIMIndexArrayBased = class extends OIMIndex {
|
|
|
523
664
|
return result;
|
|
524
665
|
}
|
|
525
666
|
getSlotsByKey(key) {
|
|
526
|
-
|
|
667
|
+
const bucket = this.store.getOneByKey(key);
|
|
668
|
+
return bucket ? bucket.slice() : [];
|
|
527
669
|
}
|
|
528
670
|
getSlotsByKeys(keys) {
|
|
529
|
-
|
|
671
|
+
const result = /* @__PURE__ */ new Map();
|
|
672
|
+
for (const [key, bucket] of this.store.getManyByKeys(keys)) {
|
|
673
|
+
result.set(key, bucket.slice());
|
|
674
|
+
}
|
|
675
|
+
return result;
|
|
530
676
|
}
|
|
531
677
|
slotsToPks(slots) {
|
|
532
678
|
const pks = [];
|
|
@@ -534,22 +680,18 @@ var OIMIndexArrayBased = class extends OIMIndex {
|
|
|
534
680
|
for (let i = 0; i < slots.length; i++) pks[i] = slots[i].pk;
|
|
535
681
|
return pks;
|
|
536
682
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
this.store.setOneByKey(key, newSlots);
|
|
552
|
-
return true;
|
|
683
|
+
/**
|
|
684
|
+
* True if a comparator is set and considers `bucket`'s pks equal to
|
|
685
|
+
* `newSlots` — i.e. a `setSlots` would be a no-op and should not emit.
|
|
686
|
+
*/
|
|
687
|
+
bucketMatchesComparator(bucket, newSlots) {
|
|
688
|
+
if (!this.comparePks) return false;
|
|
689
|
+
if (bucket.length !== newSlots.length) return false;
|
|
690
|
+
if (bucket.length === 0) return true;
|
|
691
|
+
return this.comparePks(
|
|
692
|
+
this.slotsToPks(bucket),
|
|
693
|
+
this.slotsToPks(newSlots)
|
|
694
|
+
);
|
|
553
695
|
}
|
|
554
696
|
};
|
|
555
697
|
|
|
@@ -638,11 +780,19 @@ var OIMCarrierKeyedEmitter = class {
|
|
|
638
780
|
this.scheduleFlush();
|
|
639
781
|
}
|
|
640
782
|
markUpdatedKey(key) {
|
|
783
|
+
if (this.subscribedCarriers.size === 0) {
|
|
784
|
+
this.assertNotInFlush();
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
641
787
|
const carrier = this.resolver.findCarrier(key);
|
|
642
788
|
if (carrier) this.markUpdatedCarrier(carrier);
|
|
643
789
|
else this.assertNotInFlush();
|
|
644
790
|
}
|
|
645
791
|
markUpdatedKeys(keys) {
|
|
792
|
+
if (this.subscribedCarriers.size === 0) {
|
|
793
|
+
this.assertNotInFlush();
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
646
796
|
for (let i = 0; i < keys.length; i++) this.markUpdatedKey(keys[i]);
|
|
647
797
|
}
|
|
648
798
|
markAllUpdated() {
|
|
@@ -742,8 +892,8 @@ var OIMKeyedCarrierResolver = class {
|
|
|
742
892
|
|
|
743
893
|
// src/abstract/OIMReactiveIndex.ts
|
|
744
894
|
var OIMReactiveIndex = class {
|
|
745
|
-
constructor(queue, createIndex) {
|
|
746
|
-
this.updateEmitter = new OIMCarrierKeyedEmitter(queue,
|
|
895
|
+
constructor(queue, createIndex, createResolver = () => new OIMKeyedCarrierResolver()) {
|
|
896
|
+
this.updateEmitter = new OIMCarrierKeyedEmitter(queue, createResolver());
|
|
747
897
|
this.index = createIndex(this.updateEmitter);
|
|
748
898
|
}
|
|
749
899
|
getEntitiesByKey(key) {
|
|
@@ -799,8 +949,8 @@ var OIMReactiveIndex = class {
|
|
|
799
949
|
|
|
800
950
|
// src/abstract/OIMReactiveIndexSetBased.ts
|
|
801
951
|
var OIMReactiveIndexSetBased = class extends OIMReactiveIndex {
|
|
802
|
-
constructor(queue, createIndex) {
|
|
803
|
-
super(queue, createIndex);
|
|
952
|
+
constructor(queue, createIndex, createResolver) {
|
|
953
|
+
super(queue, createIndex, createResolver);
|
|
804
954
|
}
|
|
805
955
|
getPksByKey(key) {
|
|
806
956
|
return this.index.getPksByKey(key);
|
|
@@ -818,8 +968,8 @@ var OIMReactiveIndexSetBased = class extends OIMReactiveIndex {
|
|
|
818
968
|
|
|
819
969
|
// src/abstract/OIMReactiveIndexArrayBased.ts
|
|
820
970
|
var OIMReactiveIndexArrayBased = class extends OIMReactiveIndex {
|
|
821
|
-
constructor(queue, createIndex) {
|
|
822
|
-
super(queue, createIndex);
|
|
971
|
+
constructor(queue, createIndex, createResolver) {
|
|
972
|
+
super(queue, createIndex, createResolver);
|
|
823
973
|
}
|
|
824
974
|
getPksByKey(key) {
|
|
825
975
|
return this.index.getPksByKey(key);
|
|
@@ -1094,10 +1244,24 @@ var OIMPkSelectorFactory = class {
|
|
|
1094
1244
|
}
|
|
1095
1245
|
};
|
|
1096
1246
|
|
|
1247
|
+
// src/core/OIMKeyDomainNative.ts
|
|
1248
|
+
var OIMKeyDomainNative = class {
|
|
1249
|
+
createMap() {
|
|
1250
|
+
return /* @__PURE__ */ new Map();
|
|
1251
|
+
}
|
|
1252
|
+
createSet() {
|
|
1253
|
+
return /* @__PURE__ */ new Set();
|
|
1254
|
+
}
|
|
1255
|
+
canonicalize(key) {
|
|
1256
|
+
return key;
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1097
1260
|
// src/core/OIMCollectionStoreMapDriven.ts
|
|
1098
1261
|
var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
|
|
1099
1262
|
constructor() {
|
|
1100
1263
|
super(...arguments);
|
|
1264
|
+
this.keyDomain = new OIMKeyDomainNative();
|
|
1101
1265
|
this.slots = /* @__PURE__ */ new Map();
|
|
1102
1266
|
// Slots reserved by indexes for PKs whose entity has not arrived yet.
|
|
1103
1267
|
// They carry `item: undefined` and live apart from `slots` so they do not
|
|
@@ -1270,6 +1434,10 @@ var OIMCollection = class {
|
|
|
1270
1434
|
this.store = opts?.store ?? new OIMCollectionStoreMapDriven();
|
|
1271
1435
|
this.updateEntity = opts?.updateEntity ?? new OIMEntityUpdaterFactory().createMergeEntityUpdater();
|
|
1272
1436
|
}
|
|
1437
|
+
/** The collection's PK keying strategy — read by indexes and wrappers. */
|
|
1438
|
+
get keyDomain() {
|
|
1439
|
+
return this.store.keyDomain;
|
|
1440
|
+
}
|
|
1273
1441
|
getOneByPk(pk) {
|
|
1274
1442
|
return this.store.getOneByPk(pk);
|
|
1275
1443
|
}
|
|
@@ -1350,15 +1518,12 @@ var OIMCollection = class {
|
|
|
1350
1518
|
`[OIMCollection]: PK is required to upsert an entity ${JSON.stringify(entity)}`
|
|
1351
1519
|
);
|
|
1352
1520
|
}
|
|
1353
|
-
const
|
|
1354
|
-
if (
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
this.updateEntity(entity, existingEntity)
|
|
1358
|
-
);
|
|
1359
|
-
} else {
|
|
1360
|
-
return this.store.setOneByPk(pk, entity);
|
|
1521
|
+
const slot = this.store.getSlotByPk(pk);
|
|
1522
|
+
if (slot !== void 0 && slot.item !== void 0) {
|
|
1523
|
+
slot.item = this.updateEntity(entity, slot.item);
|
|
1524
|
+
return slot;
|
|
1361
1525
|
}
|
|
1526
|
+
return this.store.setOneByPk(pk, entity);
|
|
1362
1527
|
}
|
|
1363
1528
|
destroy() {
|
|
1364
1529
|
this.store.destroy();
|
|
@@ -1371,13 +1536,13 @@ var OIMReactiveCollection = class extends OIMCollection {
|
|
|
1371
1536
|
constructor(queue, opts) {
|
|
1372
1537
|
super(opts);
|
|
1373
1538
|
this.anyUpdateHandlers = /* @__PURE__ */ new Set();
|
|
1374
|
-
this.pendingAnyUpdatePks =
|
|
1539
|
+
this.pendingAnyUpdatePks = this.store.keyDomain.createSet();
|
|
1375
1540
|
this.isAnyUpdateClearPending = false;
|
|
1376
1541
|
this.isAnyUpdateScheduled = false;
|
|
1377
1542
|
this.runAnyUpdate = () => {
|
|
1378
1543
|
if (!this.isAnyUpdateScheduled) return;
|
|
1379
1544
|
this.isAnyUpdateScheduled = false;
|
|
1380
|
-
const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks);
|
|
1545
|
+
const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks.values());
|
|
1381
1546
|
this.pendingAnyUpdatePks.clear();
|
|
1382
1547
|
this.isAnyUpdateClearPending = false;
|
|
1383
1548
|
const snapshot = Array.from(this.anyUpdateHandlers);
|
|
@@ -1536,38 +1701,40 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1536
1701
|
if (key === void 0) {
|
|
1537
1702
|
const keys = this.getKeys();
|
|
1538
1703
|
if (keys.length > 0) {
|
|
1539
|
-
this.
|
|
1704
|
+
this.arrayStore.clear();
|
|
1540
1705
|
this.emitUpdate(Array.from(keys));
|
|
1541
1706
|
}
|
|
1542
1707
|
return;
|
|
1543
1708
|
}
|
|
1544
|
-
|
|
1545
|
-
|
|
1709
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1710
|
+
if (bucket && bucket.length > 0) {
|
|
1711
|
+
bucket.length = 0;
|
|
1712
|
+
this.arrayStore.releaseBucket(bucket);
|
|
1546
1713
|
this.emitUpdate([key]);
|
|
1547
1714
|
}
|
|
1548
1715
|
}
|
|
1549
1716
|
pushSlot(key, slot) {
|
|
1550
|
-
const
|
|
1551
|
-
const index =
|
|
1552
|
-
|
|
1717
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
1718
|
+
const index = bucket.length;
|
|
1719
|
+
bucket.push(slot);
|
|
1720
|
+
this.arrayStore.retainBucket(bucket);
|
|
1553
1721
|
this.emitUpdate([key]);
|
|
1554
1722
|
return index;
|
|
1555
1723
|
}
|
|
1556
1724
|
insertSlotAt(key, index, slot) {
|
|
1557
|
-
const
|
|
1558
|
-
const safeIndex = Math.max(0, Math.min(index,
|
|
1559
|
-
|
|
1725
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
1726
|
+
const safeIndex = Math.max(0, Math.min(index, bucket.length));
|
|
1727
|
+
bucket.splice(safeIndex, 0, slot);
|
|
1728
|
+
this.arrayStore.retainBucket(bucket);
|
|
1560
1729
|
this.emitUpdate([key]);
|
|
1561
1730
|
return safeIndex;
|
|
1562
1731
|
}
|
|
1563
1732
|
removeAt(key, index) {
|
|
1564
|
-
const
|
|
1565
|
-
if (!
|
|
1566
|
-
if (index < 0 || index >=
|
|
1567
|
-
const [removed] =
|
|
1568
|
-
if (
|
|
1569
|
-
this.store.removeOneByKey(key);
|
|
1570
|
-
}
|
|
1733
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1734
|
+
if (!bucket) return void 0;
|
|
1735
|
+
if (index < 0 || index >= bucket.length) return void 0;
|
|
1736
|
+
const [removed] = bucket.splice(index, 1);
|
|
1737
|
+
if (bucket.length === 0) this.arrayStore.releaseBucket(bucket);
|
|
1571
1738
|
this.emitUpdate([key]);
|
|
1572
1739
|
return removed;
|
|
1573
1740
|
}
|
|
@@ -1576,35 +1743,33 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1576
1743
|
* Returns the number actually removed (clamped to bounds; 0 if nothing).
|
|
1577
1744
|
*/
|
|
1578
1745
|
removeRange(key, index, count) {
|
|
1579
|
-
const
|
|
1580
|
-
if (!
|
|
1581
|
-
if (index < 0 || index >=
|
|
1582
|
-
const actual = Math.min(count,
|
|
1583
|
-
|
|
1584
|
-
if (
|
|
1585
|
-
this.store.removeOneByKey(key);
|
|
1586
|
-
}
|
|
1746
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1747
|
+
if (!bucket) return 0;
|
|
1748
|
+
if (index < 0 || index >= bucket.length || count <= 0) return 0;
|
|
1749
|
+
const actual = Math.min(count, bucket.length - index);
|
|
1750
|
+
bucket.splice(index, actual);
|
|
1751
|
+
if (bucket.length === 0) this.arrayStore.releaseBucket(bucket);
|
|
1587
1752
|
this.emitUpdate([key]);
|
|
1588
1753
|
return actual;
|
|
1589
1754
|
}
|
|
1590
1755
|
move(key, fromIndex, toIndex) {
|
|
1591
|
-
const
|
|
1592
|
-
if (!
|
|
1593
|
-
if (fromIndex < 0 || fromIndex >=
|
|
1594
|
-
if (toIndex < 0 || toIndex >=
|
|
1595
|
-
if (fromIndex === toIndex) return
|
|
1596
|
-
const [slot] =
|
|
1756
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1757
|
+
if (!bucket) return void 0;
|
|
1758
|
+
if (fromIndex < 0 || fromIndex >= bucket.length) return void 0;
|
|
1759
|
+
if (toIndex < 0 || toIndex >= bucket.length) return void 0;
|
|
1760
|
+
if (fromIndex === toIndex) return bucket[fromIndex];
|
|
1761
|
+
const [slot] = bucket.splice(fromIndex, 1);
|
|
1597
1762
|
if (slot === void 0) return void 0;
|
|
1598
|
-
|
|
1763
|
+
bucket.splice(toIndex, 0, slot);
|
|
1599
1764
|
this.emitUpdate([key]);
|
|
1600
1765
|
return slot;
|
|
1601
1766
|
}
|
|
1602
1767
|
/** Replace the slot at `index` in place. Returns the safe index, or -1. */
|
|
1603
1768
|
setSlotAt(key, index, slot) {
|
|
1604
|
-
const
|
|
1605
|
-
if (!
|
|
1606
|
-
if (index < 0 || index >=
|
|
1607
|
-
|
|
1769
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1770
|
+
if (!bucket) return -1;
|
|
1771
|
+
if (index < 0 || index >= bucket.length) return -1;
|
|
1772
|
+
bucket[index] = slot;
|
|
1608
1773
|
this.emitUpdate([key]);
|
|
1609
1774
|
return index;
|
|
1610
1775
|
}
|
|
@@ -1614,36 +1779,33 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1614
1779
|
* Returns the number actually moved (0 if a no-op / out of bounds).
|
|
1615
1780
|
*/
|
|
1616
1781
|
moveRange(key, from, to, count) {
|
|
1617
|
-
const
|
|
1618
|
-
if (!
|
|
1619
|
-
if (from < 0 || from >=
|
|
1620
|
-
const actual = Math.min(count,
|
|
1621
|
-
const insertAt = Math.max(0, Math.min(to,
|
|
1782
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1783
|
+
if (!bucket) return 0;
|
|
1784
|
+
if (from < 0 || from >= bucket.length || count <= 0) return 0;
|
|
1785
|
+
const actual = Math.min(count, bucket.length - from);
|
|
1786
|
+
const insertAt = Math.max(0, Math.min(to, bucket.length - actual));
|
|
1622
1787
|
if (insertAt === from) return 0;
|
|
1623
|
-
const block =
|
|
1624
|
-
|
|
1788
|
+
const block = bucket.splice(from, actual);
|
|
1789
|
+
bucket.splice(insertAt, 0, ...block);
|
|
1625
1790
|
this.emitUpdate([key]);
|
|
1626
1791
|
return actual;
|
|
1627
1792
|
}
|
|
1628
1793
|
resetSlots(key, slots) {
|
|
1629
1794
|
if (slots.length === 0) {
|
|
1630
|
-
|
|
1631
|
-
|
|
1795
|
+
const bucket2 = this.arrayStore.findBucket(key);
|
|
1796
|
+
if (bucket2 && bucket2.length > 0) {
|
|
1797
|
+
bucket2.length = 0;
|
|
1798
|
+
this.arrayStore.releaseBucket(bucket2);
|
|
1632
1799
|
this.emitUpdate([key]);
|
|
1633
1800
|
}
|
|
1634
1801
|
return;
|
|
1635
1802
|
}
|
|
1636
|
-
this.
|
|
1803
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
1804
|
+
bucket.length = 0;
|
|
1805
|
+
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
1806
|
+
this.arrayStore.retainBucket(bucket);
|
|
1637
1807
|
this.emitUpdate([key]);
|
|
1638
1808
|
}
|
|
1639
|
-
getOrCreateList(key) {
|
|
1640
|
-
let list = this.store.getOneByKey(key);
|
|
1641
|
-
if (!list) {
|
|
1642
|
-
list = [];
|
|
1643
|
-
this.store.setOneByKey(key, list);
|
|
1644
|
-
}
|
|
1645
|
-
return list;
|
|
1646
|
-
}
|
|
1647
1809
|
};
|
|
1648
1810
|
|
|
1649
1811
|
// src/core/OIMCollectionIndexManualOrderedArrayBased.ts
|
|
@@ -2161,43 +2323,68 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
|
|
|
2161
2323
|
super(options);
|
|
2162
2324
|
}
|
|
2163
2325
|
setSlots(key, slots) {
|
|
2164
|
-
const
|
|
2165
|
-
if (
|
|
2166
|
-
|
|
2167
|
-
|
|
2326
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
2327
|
+
if (this.bucketMatchesComparator(bucket, slots)) return;
|
|
2328
|
+
bucket.length = 0;
|
|
2329
|
+
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
2330
|
+
if (bucket.length > 0) this.arrayStore.retainBucket(bucket);
|
|
2331
|
+
else this.arrayStore.releaseBucket(bucket);
|
|
2332
|
+
this.emitBucketChanged(bucket);
|
|
2168
2333
|
}
|
|
2169
2334
|
/**
|
|
2170
|
-
* Appends pre-deduplicated slots to a key's bucket IN PLACE
|
|
2171
|
-
*
|
|
2172
|
-
* not O(bucket)); used by collection-bound indexes for fast `addPks`.
|
|
2335
|
+
* Appends pre-deduplicated slots to a key's bucket IN PLACE and emits once.
|
|
2336
|
+
* O(added); callers own dedup.
|
|
2173
2337
|
*/
|
|
2174
2338
|
appendSlots(key, slots) {
|
|
2175
2339
|
if (slots.length === 0) return;
|
|
2176
|
-
|
|
2177
|
-
if (!bucket) {
|
|
2178
|
-
bucket = [];
|
|
2179
|
-
this.store.setOneByKey(key, bucket);
|
|
2180
|
-
}
|
|
2340
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
2181
2341
|
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
2182
|
-
this.
|
|
2342
|
+
this.arrayStore.retainBucket(bucket);
|
|
2343
|
+
this.emitBucketChanged(bucket);
|
|
2183
2344
|
}
|
|
2184
2345
|
/**
|
|
2185
|
-
* Clear all primary keys for a specific index key, or all keys if
|
|
2346
|
+
* Clear all primary keys for a specific index key, or all keys if none given.
|
|
2186
2347
|
*/
|
|
2187
2348
|
clear(key) {
|
|
2188
2349
|
if (key === void 0) {
|
|
2189
|
-
const allKeys = this.
|
|
2350
|
+
const allKeys = this.arrayStore.getAllKeys();
|
|
2190
2351
|
if (allKeys.length > 0) {
|
|
2191
|
-
this.
|
|
2352
|
+
this.arrayStore.clear();
|
|
2192
2353
|
this.emitUpdate(allKeys);
|
|
2193
2354
|
}
|
|
2194
2355
|
} else {
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2356
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
2357
|
+
if (bucket && bucket.length > 0) {
|
|
2358
|
+
bucket.length = 0;
|
|
2359
|
+
this.arrayStore.releaseBucket(bucket);
|
|
2360
|
+
this.emitBucketChanged(bucket);
|
|
2198
2361
|
}
|
|
2199
2362
|
}
|
|
2200
2363
|
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Emit that a bucket changed. Base (non-reactive) path emits by key on the
|
|
2366
|
+
* plain event emitter; the reactive subclass overrides this to mark the
|
|
2367
|
+
* bucket carrier directly (O(1), no key→carrier lookup).
|
|
2368
|
+
*/
|
|
2369
|
+
emitBucketChanged(bucket) {
|
|
2370
|
+
this.emitUpdateOne(bucket.key);
|
|
2371
|
+
}
|
|
2372
|
+
};
|
|
2373
|
+
|
|
2374
|
+
// src/core/OIMBucketCarrierResolver.ts
|
|
2375
|
+
var OIMBucketCarrierResolver = class {
|
|
2376
|
+
constructor(store) {
|
|
2377
|
+
this.store = store;
|
|
2378
|
+
}
|
|
2379
|
+
getOrReserveCarrier(key) {
|
|
2380
|
+
return this.store.getOrReserveBucket(key);
|
|
2381
|
+
}
|
|
2382
|
+
findCarrier(key) {
|
|
2383
|
+
return this.store.findBucket(key);
|
|
2384
|
+
}
|
|
2385
|
+
onCarrierEmptied(carrier) {
|
|
2386
|
+
this.store.dropIfReserved(carrier.key);
|
|
2387
|
+
}
|
|
2201
2388
|
};
|
|
2202
2389
|
|
|
2203
2390
|
// src/core/OIMReactiveIndexManualArrayBased.ts
|
|
@@ -2206,6 +2393,10 @@ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
|
|
|
2206
2393
|
super(opts);
|
|
2207
2394
|
this.updateEmitter = updateEmitter;
|
|
2208
2395
|
}
|
|
2396
|
+
// Deliver straight off the bucket the write just touched — O(1).
|
|
2397
|
+
emitBucketChanged(bucket) {
|
|
2398
|
+
this.updateEmitter.markUpdatedCarrier(bucket);
|
|
2399
|
+
}
|
|
2209
2400
|
emitUpdate(keys) {
|
|
2210
2401
|
this.updateEmitter.markUpdatedKeys(keys);
|
|
2211
2402
|
}
|
|
@@ -2215,12 +2406,15 @@ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
|
|
|
2215
2406
|
};
|
|
2216
2407
|
var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
|
|
2217
2408
|
constructor(queue, opts) {
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2409
|
+
const store = opts?.indexOptions?.store ?? new OIMIndexStoreMapDrivenArrayBased();
|
|
2410
|
+
super(
|
|
2411
|
+
queue,
|
|
2412
|
+
(updateEmitter) => new OIMIndexManualArrayBasedReactive(updateEmitter, {
|
|
2413
|
+
comparePks: opts?.indexOptions?.comparePks,
|
|
2414
|
+
store
|
|
2415
|
+
}),
|
|
2416
|
+
() => new OIMBucketCarrierResolver(store)
|
|
2417
|
+
);
|
|
2224
2418
|
}
|
|
2225
2419
|
setSlots(key, slots) {
|
|
2226
2420
|
this.index.setSlots(key, slots);
|
|
@@ -2237,6 +2431,7 @@ var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased
|
|
|
2237
2431
|
var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexManualArrayBased {
|
|
2238
2432
|
constructor(queue, opts) {
|
|
2239
2433
|
const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
|
|
2434
|
+
const pkDomain = opts.collection?.keyDomain ?? new OIMKeyDomainNative();
|
|
2240
2435
|
super(queue, {
|
|
2241
2436
|
indexOptions: opts.indexOptions
|
|
2242
2437
|
});
|
|
@@ -2244,9 +2439,10 @@ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexM
|
|
|
2244
2439
|
// a Set from the whole bucket on every call). Kept in sync by setSlots/clear.
|
|
2245
2440
|
this.pksByKey = /* @__PURE__ */ new Map();
|
|
2246
2441
|
this.resolveSlot = resolveSlot;
|
|
2442
|
+
this.pkDomain = pkDomain;
|
|
2247
2443
|
}
|
|
2248
2444
|
setPks(key, pks) {
|
|
2249
|
-
const pkSet =
|
|
2445
|
+
const pkSet = this.pkDomain.createSet();
|
|
2250
2446
|
for (let i = 0; i < pks.length; i++) pkSet.add(pks[i]);
|
|
2251
2447
|
this.pksByKey.set(key, pkSet);
|
|
2252
2448
|
super.setSlots(key, this.resolveSlots(pks));
|
|
@@ -2289,7 +2485,7 @@ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexM
|
|
|
2289
2485
|
membershipOf(key) {
|
|
2290
2486
|
let pkSet = this.pksByKey.get(key);
|
|
2291
2487
|
if (!pkSet) {
|
|
2292
|
-
pkSet =
|
|
2488
|
+
pkSet = this.pkDomain.createSet();
|
|
2293
2489
|
const existing = this.index.getSlotsByKey(key);
|
|
2294
2490
|
for (let i = 0; i < existing.length; i++) {
|
|
2295
2491
|
pkSet.add(existing[i].pk);
|
|
@@ -2323,24 +2519,98 @@ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexM
|
|
|
2323
2519
|
var OIMIndexManualSetBased = class extends OIMIndexSetBased {
|
|
2324
2520
|
constructor(options = {}) {
|
|
2325
2521
|
super(options);
|
|
2522
|
+
this.pkDomain = options.pkDomain ?? new OIMKeyDomainNative();
|
|
2326
2523
|
}
|
|
2327
2524
|
setSlots(key, slots) {
|
|
2328
|
-
const
|
|
2329
|
-
|
|
2330
|
-
|
|
2525
|
+
const bucket = this.setStore.getOrReserveBucket(key);
|
|
2526
|
+
this.replaceBucketContents(bucket, slots);
|
|
2527
|
+
}
|
|
2528
|
+
// --- pk-oriented writes: resolve the bucket ONCE and use its own membership
|
|
2529
|
+
// (pk → slot) for dedup/removal, so a composite (trie) key is walked a single
|
|
2530
|
+
// time instead of once for the bucket and once for a separate membership map.
|
|
2531
|
+
setPks(key, pks, resolveSlot) {
|
|
2532
|
+
const bucket = this.setStore.getOrReserveBucket(key);
|
|
2533
|
+
const slots = [];
|
|
2534
|
+
for (let i = 0; i < pks.length; i++) slots.push(resolveSlot(pks[i]));
|
|
2535
|
+
this.replaceBucketContents(bucket, slots);
|
|
2536
|
+
const membership = bucket.membership ?? (bucket.membership = this.pkDomain.createMap());
|
|
2537
|
+
membership.clear();
|
|
2538
|
+
for (let i = 0; i < pks.length; i++) {
|
|
2539
|
+
if (!membership.has(pks[i])) membership.set(pks[i], slots[i]);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
addPks(key, pks, resolveSlot) {
|
|
2543
|
+
if (pks.length === 0) return;
|
|
2544
|
+
const bucket = this.setStore.getOrReserveBucket(key);
|
|
2545
|
+
const membership = this.membershipOf(bucket);
|
|
2546
|
+
let changed = false;
|
|
2547
|
+
for (let i = 0; i < pks.length; i++) {
|
|
2548
|
+
const pk = pks[i];
|
|
2549
|
+
if (membership.has(pk)) continue;
|
|
2550
|
+
const slot = resolveSlot(pk);
|
|
2551
|
+
membership.set(pk, slot);
|
|
2552
|
+
if (!bucket.has(slot)) {
|
|
2553
|
+
bucket.add(slot);
|
|
2554
|
+
changed = true;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
if (!changed) return;
|
|
2558
|
+
this.setStore.retainBucket(bucket);
|
|
2559
|
+
this.emitBucketChanged(bucket);
|
|
2560
|
+
}
|
|
2561
|
+
removePks(key, pks) {
|
|
2562
|
+
if (pks.length === 0) return;
|
|
2563
|
+
const bucket = this.setStore.findBucket(key);
|
|
2564
|
+
if (!bucket || bucket.size === 0) return;
|
|
2565
|
+
const membership = this.membershipOf(bucket);
|
|
2566
|
+
let changed = false;
|
|
2567
|
+
for (let i = 0; i < pks.length; i++) {
|
|
2568
|
+
const slot = membership.get(pks[i]);
|
|
2569
|
+
if (slot) {
|
|
2570
|
+
membership.delete(pks[i]);
|
|
2571
|
+
if (bucket.delete(slot)) changed = true;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
if (!changed) return;
|
|
2575
|
+
if (bucket.size === 0) this.setStore.releaseBucket(bucket);
|
|
2576
|
+
this.emitBucketChanged(bucket);
|
|
2577
|
+
}
|
|
2578
|
+
/**
|
|
2579
|
+
* The bucket's pk → slot membership, lazily created and seeded from the
|
|
2580
|
+
* bucket's current slots (so it stays correct even if slots were written
|
|
2581
|
+
* through the lower-level `setSlots` path).
|
|
2582
|
+
*/
|
|
2583
|
+
membershipOf(bucket) {
|
|
2584
|
+
let membership = bucket.membership;
|
|
2585
|
+
if (!membership) {
|
|
2586
|
+
membership = this.pkDomain.createMap();
|
|
2587
|
+
bucket.membership = membership;
|
|
2588
|
+
}
|
|
2589
|
+
if (membership.size === 0 && bucket.size > 0) {
|
|
2590
|
+
for (const slot of bucket) membership.set(slot.pk, slot);
|
|
2591
|
+
}
|
|
2592
|
+
return membership;
|
|
2593
|
+
}
|
|
2594
|
+
replaceBucketContents(bucket, slots) {
|
|
2595
|
+
if (this.comparePks) {
|
|
2596
|
+
const newSet = slots instanceof Set ? slots : new Set(slots);
|
|
2597
|
+
if (this.bucketMatchesComparator(bucket, newSet)) return;
|
|
2598
|
+
bucket.clear();
|
|
2599
|
+
for (const slot of newSet) bucket.add(slot);
|
|
2600
|
+
} else {
|
|
2601
|
+
bucket.clear();
|
|
2602
|
+
for (const slot of slots) bucket.add(slot);
|
|
2331
2603
|
}
|
|
2604
|
+
if (bucket.size > 0) this.setStore.retainBucket(bucket);
|
|
2605
|
+
else this.setStore.releaseBucket(bucket);
|
|
2606
|
+
this.emitBucketChanged(bucket);
|
|
2332
2607
|
}
|
|
2333
2608
|
/**
|
|
2334
|
-
* Adds slots to a key's bucket IN PLACE
|
|
2335
|
-
* emits once. O(added). Used by collection-bound indexes for fast `addPks`.
|
|
2609
|
+
* Adds slots to a key's bucket IN PLACE and emits once. O(added).
|
|
2336
2610
|
*/
|
|
2337
2611
|
addSlots(key, slots) {
|
|
2338
2612
|
if (slots.length === 0) return;
|
|
2339
|
-
|
|
2340
|
-
if (!bucket) {
|
|
2341
|
-
bucket = /* @__PURE__ */ new Set();
|
|
2342
|
-
this.store.setOneByKey(key, bucket);
|
|
2343
|
-
}
|
|
2613
|
+
const bucket = this.setStore.getOrReserveBucket(key);
|
|
2344
2614
|
let changed = false;
|
|
2345
2615
|
for (let i = 0; i < slots.length; i++) {
|
|
2346
2616
|
if (!bucket.has(slots[i])) {
|
|
@@ -2348,25 +2618,27 @@ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
|
|
|
2348
2618
|
changed = true;
|
|
2349
2619
|
}
|
|
2350
2620
|
}
|
|
2351
|
-
if (changed)
|
|
2621
|
+
if (!changed) return;
|
|
2622
|
+
this.setStore.retainBucket(bucket);
|
|
2623
|
+
this.emitBucketChanged(bucket);
|
|
2352
2624
|
}
|
|
2353
2625
|
/**
|
|
2354
2626
|
* Removes slots from a key's bucket IN PLACE (O(removed)) and emits once.
|
|
2355
2627
|
*/
|
|
2356
2628
|
removeSlots(key, slots) {
|
|
2357
2629
|
if (slots.length === 0) return;
|
|
2358
|
-
const bucket = this.
|
|
2630
|
+
const bucket = this.setStore.findBucket(key);
|
|
2359
2631
|
if (!bucket) return;
|
|
2360
2632
|
let changed = false;
|
|
2361
2633
|
for (let i = 0; i < slots.length; i++) {
|
|
2362
2634
|
if (bucket.delete(slots[i])) changed = true;
|
|
2363
2635
|
}
|
|
2364
2636
|
if (!changed) return;
|
|
2365
|
-
if (bucket.size === 0) this.
|
|
2366
|
-
this.
|
|
2637
|
+
if (bucket.size === 0) this.setStore.releaseBucket(bucket);
|
|
2638
|
+
this.emitBucketChanged(bucket);
|
|
2367
2639
|
}
|
|
2368
2640
|
/**
|
|
2369
|
-
* Clear all primary keys for a specific index key, or all keys if
|
|
2641
|
+
* Clear all primary keys for a specific index key, or all keys if none given.
|
|
2370
2642
|
*/
|
|
2371
2643
|
clear(key) {
|
|
2372
2644
|
if (key === void 0) {
|
|
@@ -2376,12 +2648,22 @@ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
|
|
|
2376
2648
|
this.emitUpdate(allKeys);
|
|
2377
2649
|
}
|
|
2378
2650
|
} else {
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2651
|
+
const bucket = this.setStore.findBucket(key);
|
|
2652
|
+
if (bucket && bucket.size > 0) {
|
|
2653
|
+
bucket.clear();
|
|
2654
|
+
this.setStore.releaseBucket(bucket);
|
|
2655
|
+
this.emitBucketChanged(bucket);
|
|
2382
2656
|
}
|
|
2383
2657
|
}
|
|
2384
2658
|
}
|
|
2659
|
+
/**
|
|
2660
|
+
* Emit that a bucket changed. Base (non-reactive) path emits by key on the
|
|
2661
|
+
* plain event emitter; the reactive subclass overrides this to mark the
|
|
2662
|
+
* bucket carrier directly (O(1), no key→carrier lookup).
|
|
2663
|
+
*/
|
|
2664
|
+
emitBucketChanged(bucket) {
|
|
2665
|
+
this.emitUpdateOne(bucket.key);
|
|
2666
|
+
}
|
|
2385
2667
|
};
|
|
2386
2668
|
|
|
2387
2669
|
// src/core/OIMReactiveIndexManualSetBased.ts
|
|
@@ -2390,6 +2672,12 @@ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
|
|
|
2390
2672
|
super(opts);
|
|
2391
2673
|
this.updateEmitter = updateEmitter;
|
|
2392
2674
|
}
|
|
2675
|
+
// Deliver straight off the bucket the write just touched — O(1), no
|
|
2676
|
+
// key→carrier lookup (the whole point of bucket-as-carrier).
|
|
2677
|
+
emitBucketChanged(bucket) {
|
|
2678
|
+
this.updateEmitter.markUpdatedCarrier(bucket);
|
|
2679
|
+
}
|
|
2680
|
+
// clear(all) still emits by key set (rare path).
|
|
2393
2681
|
emitUpdate(keys) {
|
|
2394
2682
|
this.updateEmitter.markUpdatedKeys(keys);
|
|
2395
2683
|
}
|
|
@@ -2399,12 +2687,16 @@ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
|
|
|
2399
2687
|
};
|
|
2400
2688
|
var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
|
|
2401
2689
|
constructor(queue, opts) {
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2690
|
+
const store = opts?.indexOptions?.store ?? new OIMIndexStoreMapDrivenSetBased();
|
|
2691
|
+
super(
|
|
2692
|
+
queue,
|
|
2693
|
+
(updateEmitter) => new OIMIndexManualSetBasedReactive(updateEmitter, {
|
|
2694
|
+
comparePks: opts?.indexOptions?.comparePks,
|
|
2695
|
+
store,
|
|
2696
|
+
pkDomain: opts?.pkDomain
|
|
2697
|
+
}),
|
|
2698
|
+
() => new OIMBucketCarrierResolver(store)
|
|
2699
|
+
);
|
|
2408
2700
|
}
|
|
2409
2701
|
setSlots(key, slots) {
|
|
2410
2702
|
this.index.setSlots(key, slots);
|
|
@@ -2425,71 +2717,26 @@ var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexMan
|
|
|
2425
2717
|
constructor(queue, opts) {
|
|
2426
2718
|
const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
|
|
2427
2719
|
super(queue, {
|
|
2428
|
-
indexOptions: opts.indexOptions
|
|
2720
|
+
indexOptions: opts.indexOptions,
|
|
2721
|
+
pkDomain: opts.collection?.keyDomain
|
|
2429
2722
|
});
|
|
2430
|
-
//
|
|
2431
|
-
//
|
|
2432
|
-
this.
|
|
2723
|
+
// Bound once so the hot pk-write path passes a stable function (no per-call
|
|
2724
|
+
// closure allocation).
|
|
2725
|
+
this.resolveRequiredSlot = (pk) => {
|
|
2726
|
+
const slot = this.resolveSlot(pk);
|
|
2727
|
+
if (!slot) return { pk, item: void 0 };
|
|
2728
|
+
return slot;
|
|
2729
|
+
};
|
|
2433
2730
|
this.resolveSlot = resolveSlot;
|
|
2434
2731
|
}
|
|
2435
2732
|
setPks(key, pks) {
|
|
2436
|
-
|
|
2437
|
-
const slots = [];
|
|
2438
|
-
for (let i = 0; i < pks.length; i++) {
|
|
2439
|
-
const pk = pks[i];
|
|
2440
|
-
const slot = this.resolveRequiredSlot(pk);
|
|
2441
|
-
if (!map.has(pk)) map.set(pk, slot);
|
|
2442
|
-
slots.push(slot);
|
|
2443
|
-
}
|
|
2444
|
-
this.slotByPk.set(key, map);
|
|
2445
|
-
super.setSlots(key, slots);
|
|
2733
|
+
this.index.setPks(key, pks, this.resolveRequiredSlot);
|
|
2446
2734
|
}
|
|
2447
2735
|
addPks(key, pks) {
|
|
2448
|
-
|
|
2449
|
-
const map = this.membershipOf(key);
|
|
2450
|
-
const newSlots = [];
|
|
2451
|
-
for (const pk of pks) {
|
|
2452
|
-
if (map.has(pk)) continue;
|
|
2453
|
-
const slot = this.resolveRequiredSlot(pk);
|
|
2454
|
-
map.set(pk, slot);
|
|
2455
|
-
newSlots.push(slot);
|
|
2456
|
-
}
|
|
2457
|
-
if (newSlots.length > 0) this.addSlots(key, newSlots);
|
|
2736
|
+
this.index.addPks(key, pks, this.resolveRequiredSlot);
|
|
2458
2737
|
}
|
|
2459
2738
|
removePks(key, pks) {
|
|
2460
|
-
|
|
2461
|
-
const map = this.membershipOf(key);
|
|
2462
|
-
if (map.size === 0) return;
|
|
2463
|
-
const removed = [];
|
|
2464
|
-
for (const pk of pks) {
|
|
2465
|
-
const slot = map.get(pk);
|
|
2466
|
-
if (slot) {
|
|
2467
|
-
map.delete(pk);
|
|
2468
|
-
removed.push(slot);
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2471
|
-
if (removed.length === 0) return;
|
|
2472
|
-
if (map.size === 0) this.slotByPk.delete(key);
|
|
2473
|
-
this.removeSlots(key, removed);
|
|
2474
|
-
}
|
|
2475
|
-
clear(key) {
|
|
2476
|
-
if (key === void 0) this.slotByPk.clear();
|
|
2477
|
-
else this.slotByPk.delete(key);
|
|
2478
|
-
super.clear(key);
|
|
2479
|
-
}
|
|
2480
|
-
/**
|
|
2481
|
-
* The pk→slot map for a key, lazily seeded from the current bucket so it
|
|
2482
|
-
* stays correct even if slots were set through a lower-level path.
|
|
2483
|
-
*/
|
|
2484
|
-
membershipOf(key) {
|
|
2485
|
-
let map = this.slotByPk.get(key);
|
|
2486
|
-
if (!map) {
|
|
2487
|
-
map = /* @__PURE__ */ new Map();
|
|
2488
|
-
const existing = this.index.getSlotsByKey(key);
|
|
2489
|
-
for (const slot of existing) map.set(slot.pk, slot);
|
|
2490
|
-
this.slotByPk.set(key, map);
|
|
2491
|
-
}
|
|
2492
|
-
return map;
|
|
2739
|
+
this.index.removePks(key, pks);
|
|
2493
2740
|
}
|
|
2494
2741
|
getEntitiesByKey(key) {
|
|
2495
2742
|
return super.getEntitiesByKey(key);
|
|
@@ -2497,50 +2744,459 @@ var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexMan
|
|
|
2497
2744
|
getEntitiesByKeys(keys) {
|
|
2498
2745
|
return super.getEntitiesByKeys(keys);
|
|
2499
2746
|
}
|
|
2500
|
-
resolveRequiredSlot(pk) {
|
|
2501
|
-
const slot = this.resolveSlot(pk);
|
|
2502
|
-
if (!slot) return { pk, item: void 0 };
|
|
2503
|
-
return slot;
|
|
2504
|
-
}
|
|
2505
2747
|
};
|
|
2506
2748
|
|
|
2507
|
-
// src/core/
|
|
2508
|
-
var
|
|
2509
|
-
constructor(
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
this.
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2749
|
+
// src/core/OIMTrieMap.ts
|
|
2750
|
+
var OIMTrieMap = class {
|
|
2751
|
+
constructor() {
|
|
2752
|
+
this.root = { hasValue: false };
|
|
2753
|
+
this._size = 0;
|
|
2754
|
+
}
|
|
2755
|
+
/** Number of stored paths (terminal nodes), not the number of trie nodes. */
|
|
2756
|
+
get size() {
|
|
2757
|
+
return this._size;
|
|
2758
|
+
}
|
|
2759
|
+
set(path, value) {
|
|
2760
|
+
let node = this.root;
|
|
2761
|
+
for (let i = 0; i < path.length; i++) {
|
|
2762
|
+
let children = node.children;
|
|
2763
|
+
if (!children) {
|
|
2764
|
+
children = /* @__PURE__ */ new Map();
|
|
2765
|
+
node.children = children;
|
|
2519
2766
|
}
|
|
2520
|
-
|
|
2521
|
-
|
|
2767
|
+
let next = children.get(path[i]);
|
|
2768
|
+
if (!next) {
|
|
2769
|
+
next = { hasValue: false };
|
|
2770
|
+
children.set(path[i], next);
|
|
2522
2771
|
}
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2772
|
+
node = next;
|
|
2773
|
+
}
|
|
2774
|
+
if (!node.hasValue) {
|
|
2775
|
+
node.hasValue = true;
|
|
2776
|
+
this._size++;
|
|
2777
|
+
}
|
|
2778
|
+
node.value = value;
|
|
2779
|
+
}
|
|
2780
|
+
get(path) {
|
|
2781
|
+
const node = this.findNode(path);
|
|
2782
|
+
return node && node.hasValue ? node.value : void 0;
|
|
2783
|
+
}
|
|
2784
|
+
has(path) {
|
|
2785
|
+
const node = this.findNode(path);
|
|
2786
|
+
return !!node && node.hasValue;
|
|
2787
|
+
}
|
|
2788
|
+
delete(path) {
|
|
2789
|
+
const parents = [];
|
|
2790
|
+
let node = this.root;
|
|
2791
|
+
for (let i = 0; i < path.length; i++) {
|
|
2792
|
+
const next = node.children?.get(path[i]);
|
|
2793
|
+
if (!next) return false;
|
|
2794
|
+
parents.push(node);
|
|
2795
|
+
node = next;
|
|
2796
|
+
}
|
|
2797
|
+
if (!node.hasValue) return false;
|
|
2798
|
+
node.hasValue = false;
|
|
2799
|
+
node.value = void 0;
|
|
2800
|
+
this._size--;
|
|
2801
|
+
let child = node;
|
|
2802
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
2803
|
+
if (child.hasValue || child.children && child.children.size > 0) {
|
|
2804
|
+
break;
|
|
2805
|
+
}
|
|
2806
|
+
const parent = parents[i];
|
|
2807
|
+
parent.children.delete(path[i]);
|
|
2808
|
+
if (parent.children.size === 0) parent.children = void 0;
|
|
2809
|
+
child = parent;
|
|
2532
2810
|
}
|
|
2811
|
+
return true;
|
|
2533
2812
|
}
|
|
2534
|
-
|
|
2535
|
-
this.
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2813
|
+
clear() {
|
|
2814
|
+
this.root.children = void 0;
|
|
2815
|
+
this.root.value = void 0;
|
|
2816
|
+
this.root.hasValue = false;
|
|
2817
|
+
this._size = 0;
|
|
2818
|
+
}
|
|
2819
|
+
/** Depth-first iteration over `[path, value]` for every stored path. */
|
|
2820
|
+
*entries() {
|
|
2821
|
+
const stack = [{ node: this.root, path: [] }];
|
|
2822
|
+
while (stack.length > 0) {
|
|
2823
|
+
const frame = stack.pop();
|
|
2824
|
+
const { node, path } = frame;
|
|
2825
|
+
if (node.hasValue) {
|
|
2826
|
+
yield [path, node.value];
|
|
2827
|
+
}
|
|
2828
|
+
if (node.children) {
|
|
2829
|
+
for (const [segment, child] of node.children) {
|
|
2830
|
+
stack.push({ node: child, path: [...path, segment] });
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
*keys() {
|
|
2836
|
+
for (const [path] of this.entries()) yield path;
|
|
2837
|
+
}
|
|
2838
|
+
*values() {
|
|
2839
|
+
for (const [, value] of this.entries()) yield value;
|
|
2840
|
+
}
|
|
2841
|
+
findNode(path) {
|
|
2842
|
+
let node = this.root;
|
|
2843
|
+
for (let i = 0; i < path.length; i++) {
|
|
2844
|
+
node = node.children?.get(path[i]);
|
|
2845
|
+
if (!node) return void 0;
|
|
2846
|
+
}
|
|
2847
|
+
return node;
|
|
2848
|
+
}
|
|
2849
|
+
};
|
|
2850
|
+
|
|
2851
|
+
// src/core/OIMIndexStoreTrieDrivenSetBased.ts
|
|
2852
|
+
var OIMIndexStoreTrieDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
2853
|
+
constructor() {
|
|
2854
|
+
super(...arguments);
|
|
2855
|
+
this.slots = new OIMTrieMap();
|
|
2856
|
+
this.reservedBuckets = new OIMTrieMap();
|
|
2857
|
+
}
|
|
2858
|
+
getOrReserveBucket(key) {
|
|
2859
|
+
const live = this.slots.get(key);
|
|
2860
|
+
if (live) return live;
|
|
2861
|
+
let reserved = this.reservedBuckets.get(key);
|
|
2862
|
+
if (!reserved) {
|
|
2863
|
+
reserved = new OIMKeyedBucketSetBased(key);
|
|
2864
|
+
this.reservedBuckets.set(key, reserved);
|
|
2865
|
+
}
|
|
2866
|
+
return reserved;
|
|
2867
|
+
}
|
|
2868
|
+
findBucket(key) {
|
|
2869
|
+
return this.slots.get(key) ?? this.reservedBuckets.get(key);
|
|
2870
|
+
}
|
|
2871
|
+
retainBucket(bucket) {
|
|
2872
|
+
if (bucket.size === 0) return;
|
|
2873
|
+
this.reservedBuckets.delete(bucket.key);
|
|
2874
|
+
if (!this.slots.has(bucket.key)) this.slots.set(bucket.key, bucket);
|
|
2875
|
+
}
|
|
2876
|
+
releaseBucket(bucket) {
|
|
2877
|
+
if (bucket.size > 0) return;
|
|
2878
|
+
this.slots.delete(bucket.key);
|
|
2879
|
+
if (bucket.hasSubscribers()) {
|
|
2880
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
2881
|
+
} else {
|
|
2882
|
+
this.reservedBuckets.delete(bucket.key);
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
dropIfReserved(key) {
|
|
2886
|
+
const reserved = this.reservedBuckets.get(key);
|
|
2887
|
+
if (reserved && reserved.size === 0 && !reserved.hasSubscribers()) {
|
|
2888
|
+
this.reservedBuckets.delete(key);
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
setOneByKey(key, slots) {
|
|
2892
|
+
const bucket = this.getOrReserveBucket(key);
|
|
2893
|
+
if (bucket !== slots) {
|
|
2894
|
+
bucket.clear();
|
|
2895
|
+
for (const slot of slots) bucket.add(slot);
|
|
2896
|
+
}
|
|
2897
|
+
if (bucket.size > 0) this.retainBucket(bucket);
|
|
2898
|
+
else this.releaseBucket(bucket);
|
|
2899
|
+
}
|
|
2900
|
+
removeOneByKey(key) {
|
|
2901
|
+
this.slots.delete(key);
|
|
2902
|
+
this.reservedBuckets.delete(key);
|
|
2903
|
+
}
|
|
2904
|
+
removeManyByKeys(keys) {
|
|
2905
|
+
for (let i = 0; i < keys.length; i++) {
|
|
2906
|
+
this.slots.delete(keys[i]);
|
|
2907
|
+
this.reservedBuckets.delete(keys[i]);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
getOneByKey(key) {
|
|
2911
|
+
return this.slots.get(key);
|
|
2912
|
+
}
|
|
2913
|
+
getManyByKeys(keys) {
|
|
2914
|
+
const result = /* @__PURE__ */ new Map();
|
|
2915
|
+
for (let i = 0; i < keys.length; i++) {
|
|
2916
|
+
const slots = this.slots.get(keys[i]);
|
|
2917
|
+
if (slots !== void 0) result.set(keys[i], slots);
|
|
2918
|
+
}
|
|
2919
|
+
return result;
|
|
2920
|
+
}
|
|
2921
|
+
getAllKeys() {
|
|
2922
|
+
return Array.from(this.slots.keys());
|
|
2923
|
+
}
|
|
2924
|
+
getAll() {
|
|
2925
|
+
const result = /* @__PURE__ */ new Map();
|
|
2926
|
+
for (const [path, bucket] of this.slots.entries()) {
|
|
2927
|
+
result.set(path, bucket);
|
|
2928
|
+
}
|
|
2929
|
+
return result;
|
|
2930
|
+
}
|
|
2931
|
+
countAll() {
|
|
2932
|
+
return this.slots.size;
|
|
2933
|
+
}
|
|
2934
|
+
clear() {
|
|
2935
|
+
for (const bucket of this.slots.values()) {
|
|
2936
|
+
bucket.clear();
|
|
2937
|
+
if (bucket.hasSubscribers()) {
|
|
2938
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
this.slots.clear();
|
|
2942
|
+
}
|
|
2943
|
+
};
|
|
2944
|
+
|
|
2945
|
+
// src/core/OIMReactiveCollectionIndexCompositeTrieSetBased.ts
|
|
2946
|
+
var OIMReactiveCollectionIndexCompositeTrieSetBased = class extends OIMReactiveIndexManualSetBased {
|
|
2947
|
+
constructor(queue, opts) {
|
|
2948
|
+
const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
|
|
2949
|
+
super(queue, {
|
|
2950
|
+
indexOptions: {
|
|
2951
|
+
comparePks: opts.indexOptions?.comparePks,
|
|
2952
|
+
store: opts.indexOptions?.store ?? new OIMIndexStoreTrieDrivenSetBased()
|
|
2953
|
+
},
|
|
2954
|
+
pkDomain: opts.collection?.keyDomain
|
|
2955
|
+
});
|
|
2956
|
+
this.resolveRequiredSlot = (pk) => {
|
|
2957
|
+
const slot = this.resolveSlot(pk);
|
|
2958
|
+
if (!slot) return { pk, item: void 0 };
|
|
2959
|
+
return slot;
|
|
2960
|
+
};
|
|
2961
|
+
this.resolveSlot = resolveSlot;
|
|
2962
|
+
}
|
|
2963
|
+
setPks(key, pks) {
|
|
2964
|
+
this.index.setPks(key, pks, this.resolveRequiredSlot);
|
|
2965
|
+
}
|
|
2966
|
+
addPks(key, pks) {
|
|
2967
|
+
this.index.addPks(key, pks, this.resolveRequiredSlot);
|
|
2968
|
+
}
|
|
2969
|
+
removePks(key, pks) {
|
|
2970
|
+
this.index.removePks(key, pks);
|
|
2971
|
+
}
|
|
2972
|
+
getEntitiesByKey(key) {
|
|
2973
|
+
return super.getEntitiesByKey(key);
|
|
2974
|
+
}
|
|
2975
|
+
getEntitiesByKeys(keys) {
|
|
2976
|
+
return super.getEntitiesByKeys(keys);
|
|
2977
|
+
}
|
|
2978
|
+
};
|
|
2979
|
+
|
|
2980
|
+
// src/core/OIMIndexStoreTrieDrivenArrayBased.ts
|
|
2981
|
+
var OIMIndexStoreTrieDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
2982
|
+
constructor() {
|
|
2983
|
+
super(...arguments);
|
|
2984
|
+
this.slots = new OIMTrieMap();
|
|
2985
|
+
this.reservedBuckets = new OIMTrieMap();
|
|
2986
|
+
}
|
|
2987
|
+
getOrReserveBucket(key) {
|
|
2988
|
+
const live = this.slots.get(key);
|
|
2989
|
+
if (live) return live;
|
|
2990
|
+
let reserved = this.reservedBuckets.get(key);
|
|
2991
|
+
if (!reserved) {
|
|
2992
|
+
reserved = new OIMKeyedBucketArrayBased(key);
|
|
2993
|
+
this.reservedBuckets.set(key, reserved);
|
|
2994
|
+
}
|
|
2995
|
+
return reserved;
|
|
2996
|
+
}
|
|
2997
|
+
findBucket(key) {
|
|
2998
|
+
return this.slots.get(key) ?? this.reservedBuckets.get(key);
|
|
2999
|
+
}
|
|
3000
|
+
retainBucket(bucket) {
|
|
3001
|
+
if (bucket.length === 0) return;
|
|
3002
|
+
this.reservedBuckets.delete(bucket.key);
|
|
3003
|
+
if (!this.slots.has(bucket.key)) this.slots.set(bucket.key, bucket);
|
|
3004
|
+
}
|
|
3005
|
+
releaseBucket(bucket) {
|
|
3006
|
+
if (bucket.length > 0) return;
|
|
3007
|
+
this.slots.delete(bucket.key);
|
|
3008
|
+
if (bucket.hasSubscribers()) {
|
|
3009
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
3010
|
+
} else {
|
|
3011
|
+
this.reservedBuckets.delete(bucket.key);
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
dropIfReserved(key) {
|
|
3015
|
+
const reserved = this.reservedBuckets.get(key);
|
|
3016
|
+
if (reserved && reserved.length === 0 && !reserved.hasSubscribers()) {
|
|
3017
|
+
this.reservedBuckets.delete(key);
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
setOneByKey(key, slots) {
|
|
3021
|
+
const bucket = this.getOrReserveBucket(key);
|
|
3022
|
+
if (bucket !== slots) {
|
|
3023
|
+
bucket.length = 0;
|
|
3024
|
+
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
3025
|
+
}
|
|
3026
|
+
if (bucket.length > 0) this.retainBucket(bucket);
|
|
3027
|
+
else this.releaseBucket(bucket);
|
|
3028
|
+
}
|
|
3029
|
+
removeOneByKey(key) {
|
|
3030
|
+
this.slots.delete(key);
|
|
3031
|
+
this.reservedBuckets.delete(key);
|
|
3032
|
+
}
|
|
3033
|
+
removeManyByKeys(keys) {
|
|
3034
|
+
for (let i = 0; i < keys.length; i++) {
|
|
3035
|
+
this.slots.delete(keys[i]);
|
|
3036
|
+
this.reservedBuckets.delete(keys[i]);
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
getOneByKey(key) {
|
|
3040
|
+
return this.slots.get(key);
|
|
3041
|
+
}
|
|
3042
|
+
getManyByKeys(keys) {
|
|
3043
|
+
const result = /* @__PURE__ */ new Map();
|
|
3044
|
+
for (let i = 0; i < keys.length; i++) {
|
|
3045
|
+
const slots = this.slots.get(keys[i]);
|
|
3046
|
+
if (slots !== void 0) result.set(keys[i], slots);
|
|
3047
|
+
}
|
|
3048
|
+
return result;
|
|
3049
|
+
}
|
|
3050
|
+
getAllKeys() {
|
|
3051
|
+
return Array.from(this.slots.keys());
|
|
3052
|
+
}
|
|
3053
|
+
getAll() {
|
|
3054
|
+
const result = /* @__PURE__ */ new Map();
|
|
3055
|
+
for (const [path, bucket] of this.slots.entries()) {
|
|
3056
|
+
result.set(path, bucket);
|
|
3057
|
+
}
|
|
3058
|
+
return result;
|
|
3059
|
+
}
|
|
3060
|
+
countAll() {
|
|
3061
|
+
return this.slots.size;
|
|
3062
|
+
}
|
|
3063
|
+
clear() {
|
|
3064
|
+
for (const bucket of this.slots.values()) {
|
|
3065
|
+
bucket.length = 0;
|
|
3066
|
+
if (bucket.hasSubscribers()) {
|
|
3067
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
this.slots.clear();
|
|
3071
|
+
}
|
|
3072
|
+
};
|
|
3073
|
+
|
|
3074
|
+
// src/core/OIMReactiveCollectionIndexCompositeTrieArrayBased.ts
|
|
3075
|
+
var OIMReactiveCollectionIndexCompositeTrieArrayBased = class extends OIMReactiveIndexManualArrayBased {
|
|
3076
|
+
constructor(queue, opts) {
|
|
3077
|
+
const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
|
|
3078
|
+
super(queue, {
|
|
3079
|
+
indexOptions: {
|
|
3080
|
+
comparePks: opts.indexOptions?.comparePks,
|
|
3081
|
+
store: opts.indexOptions?.store ?? new OIMIndexStoreTrieDrivenArrayBased()
|
|
3082
|
+
}
|
|
3083
|
+
});
|
|
3084
|
+
// Persistent per-key-path pk membership for O(1) `addPks` dedup. Trie-keyed
|
|
3085
|
+
// so a rebuilt key path matches by content.
|
|
3086
|
+
this.pksByKey = new OIMTrieMap();
|
|
3087
|
+
this.resolveSlot = resolveSlot;
|
|
3088
|
+
}
|
|
3089
|
+
setPks(key, pks) {
|
|
3090
|
+
const pkSet = /* @__PURE__ */ new Set();
|
|
3091
|
+
for (let i = 0; i < pks.length; i++) pkSet.add(pks[i]);
|
|
3092
|
+
this.pksByKey.set(key, pkSet);
|
|
3093
|
+
super.setSlots(key, this.resolveSlots(pks));
|
|
3094
|
+
}
|
|
3095
|
+
addPks(key, pks) {
|
|
3096
|
+
if (pks.length === 0) return;
|
|
3097
|
+
const pkSet = this.membershipOf(key);
|
|
3098
|
+
const newSlots = [];
|
|
3099
|
+
for (const pk of pks) {
|
|
3100
|
+
if (pkSet.has(pk)) continue;
|
|
3101
|
+
pkSet.add(pk);
|
|
3102
|
+
newSlots.push(this.resolveRequiredSlot(pk));
|
|
3103
|
+
}
|
|
3104
|
+
if (newSlots.length > 0) this.appendSlots(key, newSlots);
|
|
3105
|
+
}
|
|
3106
|
+
removePks(key, pks) {
|
|
3107
|
+
if (pks.length === 0) return;
|
|
3108
|
+
const pksToRemove = new Set(pks);
|
|
3109
|
+
const existingSlots = this.index.getSlotsByKey(key);
|
|
3110
|
+
const nextSlots = existingSlots.filter(
|
|
3111
|
+
(slot) => !pksToRemove.has(slot.pk)
|
|
3112
|
+
);
|
|
3113
|
+
if (nextSlots.length === existingSlots.length) return;
|
|
3114
|
+
const pkSet = this.pksByKey.get(key);
|
|
3115
|
+
if (pkSet) {
|
|
3116
|
+
for (let i = 0; i < pks.length; i++) pkSet.delete(pks[i]);
|
|
3117
|
+
}
|
|
3118
|
+
if (nextSlots.length === 0) this.clear(key);
|
|
3119
|
+
else super.setSlots(key, nextSlots);
|
|
3120
|
+
}
|
|
3121
|
+
clear(key) {
|
|
3122
|
+
if (key === void 0) this.pksByKey.clear();
|
|
3123
|
+
else this.pksByKey.delete(key);
|
|
3124
|
+
super.clear(key);
|
|
3125
|
+
}
|
|
3126
|
+
/**
|
|
3127
|
+
* The membership set for a key path, lazily seeded from the current bucket so
|
|
3128
|
+
* it stays correct even if slots were set through a lower-level path.
|
|
3129
|
+
*/
|
|
3130
|
+
membershipOf(key) {
|
|
3131
|
+
let pkSet = this.pksByKey.get(key);
|
|
3132
|
+
if (!pkSet) {
|
|
3133
|
+
pkSet = /* @__PURE__ */ new Set();
|
|
3134
|
+
const existing = this.index.getSlotsByKey(key);
|
|
3135
|
+
for (let i = 0; i < existing.length; i++) {
|
|
3136
|
+
pkSet.add(existing[i].pk);
|
|
3137
|
+
}
|
|
3138
|
+
this.pksByKey.set(key, pkSet);
|
|
3139
|
+
}
|
|
3140
|
+
return pkSet;
|
|
3141
|
+
}
|
|
3142
|
+
getEntitiesByKey(key) {
|
|
3143
|
+
return super.getEntitiesByKey(key);
|
|
3144
|
+
}
|
|
3145
|
+
getEntitiesByKeys(keys) {
|
|
3146
|
+
return super.getEntitiesByKeys(keys);
|
|
3147
|
+
}
|
|
3148
|
+
resolveSlots(pks) {
|
|
3149
|
+
const slots = [];
|
|
3150
|
+
slots.length = pks.length;
|
|
3151
|
+
for (let i = 0; i < pks.length; i++) {
|
|
3152
|
+
slots[i] = this.resolveRequiredSlot(pks[i]);
|
|
3153
|
+
}
|
|
3154
|
+
return slots;
|
|
3155
|
+
}
|
|
3156
|
+
resolveRequiredSlot(pk) {
|
|
3157
|
+
const slot = this.resolveSlot(pk);
|
|
3158
|
+
if (!slot) return { pk, item: void 0 };
|
|
3159
|
+
return slot;
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
3162
|
+
|
|
3163
|
+
// src/core/OIMDerivedCollectionIndexSetBased.ts
|
|
3164
|
+
var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndexManualSetBased {
|
|
3165
|
+
constructor(queue, collection, opts) {
|
|
3166
|
+
super(queue, {
|
|
3167
|
+
collection,
|
|
3168
|
+
indexOptions: opts.indexOptions
|
|
3169
|
+
});
|
|
3170
|
+
this.keysByPk = /* @__PURE__ */ new Map();
|
|
3171
|
+
this.onCollectionUpdate = (pks) => {
|
|
3172
|
+
if (pks.length === 0) {
|
|
3173
|
+
this.rebuildFromCollection();
|
|
3174
|
+
return;
|
|
3175
|
+
}
|
|
3176
|
+
for (const pk of pks) {
|
|
3177
|
+
this.syncPk(pk);
|
|
3178
|
+
}
|
|
3179
|
+
};
|
|
3180
|
+
this.collection = collection;
|
|
3181
|
+
this.selectIndexKeys = opts.selectIndexKeys;
|
|
3182
|
+
this.unsubscribeFromCollection = collection.emitter.on(
|
|
3183
|
+
0 /* UPDATE */,
|
|
3184
|
+
(payload) => this.onCollectionUpdate(payload.pks)
|
|
3185
|
+
);
|
|
3186
|
+
if (opts.buildInitial !== false) {
|
|
3187
|
+
this.rebuildFromCollection();
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
rebuildFromCollection() {
|
|
3191
|
+
this.keysByPk.clear();
|
|
3192
|
+
super.clear();
|
|
3193
|
+
const slotsByKey = /* @__PURE__ */ new Map();
|
|
3194
|
+
const slots = this.collection.getAllSlots();
|
|
3195
|
+
for (const slot of slots) {
|
|
3196
|
+
if (slot.item === void 0) continue;
|
|
3197
|
+
const keys = this.normalizeKeys(this.selectIndexKeys(slot.item));
|
|
3198
|
+
if (keys.length === 0) continue;
|
|
3199
|
+
this.keysByPk.set(slot.pk, new Set(keys));
|
|
2544
3200
|
for (const key of keys) {
|
|
2545
3201
|
let slotsForKey = slotsByKey.get(key);
|
|
2546
3202
|
if (!slotsForKey) {
|
|
@@ -3202,6 +3858,30 @@ var OIMCollectionIndexFactory = class {
|
|
|
3202
3858
|
indexOptions: opts.indexOptions
|
|
3203
3859
|
});
|
|
3204
3860
|
}
|
|
3861
|
+
/**
|
|
3862
|
+
* Composite (trie-backed) manual Set-based index keyed by an
|
|
3863
|
+
* arbitrary-length key path, e.g. `setPks([userId, projectId, role], pks)`.
|
|
3864
|
+
* Key paths are matched by content in O(arity) — no string concatenation,
|
|
3865
|
+
* no separator collisions, each segment keeps its type. Primitive-keyed
|
|
3866
|
+
* indexes are unaffected and keep their native-`Map` fast path.
|
|
3867
|
+
*/
|
|
3868
|
+
compositeSetIndex(opts = {}) {
|
|
3869
|
+
return new OIMReactiveCollectionIndexCompositeTrieSetBased(this.queue, {
|
|
3870
|
+
collection: this.collection,
|
|
3871
|
+
indexOptions: opts.indexOptions
|
|
3872
|
+
});
|
|
3873
|
+
}
|
|
3874
|
+
/**
|
|
3875
|
+
* Composite (trie-backed) manual Array-based (ordered) index keyed by an
|
|
3876
|
+
* arbitrary-length key path, e.g. `setPks([channelId, threadId], pks)`.
|
|
3877
|
+
* Ordered counterpart of `compositeSetIndex`.
|
|
3878
|
+
*/
|
|
3879
|
+
compositeArrayIndex(opts = {}) {
|
|
3880
|
+
return new OIMReactiveCollectionIndexCompositeTrieArrayBased(this.queue, {
|
|
3881
|
+
collection: this.collection,
|
|
3882
|
+
indexOptions: opts.indexOptions
|
|
3883
|
+
});
|
|
3884
|
+
}
|
|
3205
3885
|
derivedSetIndex(selectIndexKeys, opts = {}) {
|
|
3206
3886
|
return new OIMDerivedCollectionIndexSetBased(
|
|
3207
3887
|
this.queue,
|
|
@@ -3450,6 +4130,195 @@ var OIMReactiveObject = class extends OIMObject {
|
|
|
3450
4130
|
}
|
|
3451
4131
|
};
|
|
3452
4132
|
|
|
4133
|
+
// src/core/OIMTrieSet.ts
|
|
4134
|
+
var OIMTrieSet = class {
|
|
4135
|
+
constructor() {
|
|
4136
|
+
this.map = new OIMTrieMap();
|
|
4137
|
+
}
|
|
4138
|
+
get size() {
|
|
4139
|
+
return this.map.size;
|
|
4140
|
+
}
|
|
4141
|
+
add(key) {
|
|
4142
|
+
this.map.set(key, true);
|
|
4143
|
+
}
|
|
4144
|
+
has(key) {
|
|
4145
|
+
return this.map.has(key);
|
|
4146
|
+
}
|
|
4147
|
+
delete(key) {
|
|
4148
|
+
return this.map.delete(key);
|
|
4149
|
+
}
|
|
4150
|
+
*values() {
|
|
4151
|
+
yield* this.map.keys();
|
|
4152
|
+
}
|
|
4153
|
+
clear() {
|
|
4154
|
+
this.map.clear();
|
|
4155
|
+
}
|
|
4156
|
+
};
|
|
4157
|
+
|
|
4158
|
+
// src/core/OIMKeyDomainTrie.ts
|
|
4159
|
+
var OIMKeyDomainTrie = class {
|
|
4160
|
+
constructor() {
|
|
4161
|
+
// Interning pool: logical key (by content) → the one canonical reference.
|
|
4162
|
+
this.pool = new OIMTrieMap();
|
|
4163
|
+
}
|
|
4164
|
+
createMap() {
|
|
4165
|
+
return new OIMTrieMap();
|
|
4166
|
+
}
|
|
4167
|
+
createSet() {
|
|
4168
|
+
return new OIMTrieSet();
|
|
4169
|
+
}
|
|
4170
|
+
canonicalize(key) {
|
|
4171
|
+
const existing = this.pool.get(key);
|
|
4172
|
+
if (existing !== void 0) return existing;
|
|
4173
|
+
const canonical = Object.freeze(key.slice());
|
|
4174
|
+
this.pool.set(canonical, canonical);
|
|
4175
|
+
return canonical;
|
|
4176
|
+
}
|
|
4177
|
+
};
|
|
4178
|
+
|
|
4179
|
+
// src/core/OIMCollectionStoreTrieDriven.ts
|
|
4180
|
+
var OIMCollectionStoreTrieDriven = class extends OIMCollectionStore {
|
|
4181
|
+
constructor(keyDomain = new OIMKeyDomainTrie()) {
|
|
4182
|
+
super();
|
|
4183
|
+
this.keyDomain = keyDomain;
|
|
4184
|
+
this.slots = new OIMTrieMap();
|
|
4185
|
+
this.reservedSlots = new OIMTrieMap();
|
|
4186
|
+
}
|
|
4187
|
+
setOneByPk(pk, entity) {
|
|
4188
|
+
const slot = this.slots.get(pk);
|
|
4189
|
+
if (slot) {
|
|
4190
|
+
slot.item = entity;
|
|
4191
|
+
return slot;
|
|
4192
|
+
}
|
|
4193
|
+
const reserved = this.reservedSlots.get(pk);
|
|
4194
|
+
if (reserved) {
|
|
4195
|
+
reserved.item = entity;
|
|
4196
|
+
this.reservedSlots.delete(pk);
|
|
4197
|
+
this.slots.set(pk, reserved);
|
|
4198
|
+
return reserved;
|
|
4199
|
+
}
|
|
4200
|
+
const nextSlot = {
|
|
4201
|
+
pk: this.keyDomain.canonicalize(pk),
|
|
4202
|
+
item: entity
|
|
4203
|
+
};
|
|
4204
|
+
this.slots.set(pk, nextSlot);
|
|
4205
|
+
return nextSlot;
|
|
4206
|
+
}
|
|
4207
|
+
getOrReserveSlotByPk(pk) {
|
|
4208
|
+
const slot = this.slots.get(pk);
|
|
4209
|
+
if (slot) return slot;
|
|
4210
|
+
let reserved = this.reservedSlots.get(pk);
|
|
4211
|
+
if (!reserved) {
|
|
4212
|
+
reserved = { pk: this.keyDomain.canonicalize(pk), item: void 0 };
|
|
4213
|
+
this.reservedSlots.set(pk, reserved);
|
|
4214
|
+
}
|
|
4215
|
+
return reserved;
|
|
4216
|
+
}
|
|
4217
|
+
setManyByPks(pks, entities) {
|
|
4218
|
+
const slots = [];
|
|
4219
|
+
slots.length = pks.length;
|
|
4220
|
+
for (let i = 0; i < pks.length; i++) {
|
|
4221
|
+
slots[i] = this.setOneByPk(pks[i], entities[i]);
|
|
4222
|
+
}
|
|
4223
|
+
return slots;
|
|
4224
|
+
}
|
|
4225
|
+
getSlotByPk(pk) {
|
|
4226
|
+
return this.slots.get(pk);
|
|
4227
|
+
}
|
|
4228
|
+
findSlotByPk(pk) {
|
|
4229
|
+
return this.slots.get(pk) ?? this.reservedSlots.get(pk);
|
|
4230
|
+
}
|
|
4231
|
+
hasSubscribers(slot) {
|
|
4232
|
+
return slot.subscribers !== void 0 && slot.subscribers.size > 0;
|
|
4233
|
+
}
|
|
4234
|
+
getSlotsByPks(pks) {
|
|
4235
|
+
const result = [];
|
|
4236
|
+
result.length = pks.length;
|
|
4237
|
+
let writeIndex = 0;
|
|
4238
|
+
for (let i = 0; i < pks.length; i++) {
|
|
4239
|
+
const slot = this.slots.get(pks[i]);
|
|
4240
|
+
if (slot !== void 0) result[writeIndex++] = slot;
|
|
4241
|
+
}
|
|
4242
|
+
result.length = writeIndex;
|
|
4243
|
+
return result;
|
|
4244
|
+
}
|
|
4245
|
+
getAllSlots() {
|
|
4246
|
+
return Array.from(this.slots.values());
|
|
4247
|
+
}
|
|
4248
|
+
removeOneByPk(pk) {
|
|
4249
|
+
const slot = this.slots.get(pk);
|
|
4250
|
+
if (slot) {
|
|
4251
|
+
slot.item = void 0;
|
|
4252
|
+
this.slots.delete(pk);
|
|
4253
|
+
if (this.hasSubscribers(slot)) {
|
|
4254
|
+
this.reservedSlots.set(pk, slot);
|
|
4255
|
+
}
|
|
4256
|
+
return;
|
|
4257
|
+
}
|
|
4258
|
+
const reserved = this.reservedSlots.get(pk);
|
|
4259
|
+
if (reserved) {
|
|
4260
|
+
reserved.item = void 0;
|
|
4261
|
+
if (!this.hasSubscribers(reserved)) {
|
|
4262
|
+
this.reservedSlots.delete(pk);
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
removeManyByPks(pks) {
|
|
4267
|
+
for (const pk of pks) this.removeOneByPk(pk);
|
|
4268
|
+
}
|
|
4269
|
+
getOneByPk(pk) {
|
|
4270
|
+
return this.slots.get(pk)?.item;
|
|
4271
|
+
}
|
|
4272
|
+
getManyByPks(pks) {
|
|
4273
|
+
const result = [];
|
|
4274
|
+
result.length = pks.length;
|
|
4275
|
+
let writeIndex = 0;
|
|
4276
|
+
for (let i = 0; i < pks.length; i++) {
|
|
4277
|
+
const entity = this.getOneByPk(pks[i]);
|
|
4278
|
+
if (entity !== void 0) result[writeIndex++] = entity;
|
|
4279
|
+
}
|
|
4280
|
+
result.length = writeIndex;
|
|
4281
|
+
return result;
|
|
4282
|
+
}
|
|
4283
|
+
getAll() {
|
|
4284
|
+
const result = [];
|
|
4285
|
+
for (const slot of this.slots.values()) {
|
|
4286
|
+
if (slot.item !== void 0) result.push(slot.item);
|
|
4287
|
+
}
|
|
4288
|
+
return result;
|
|
4289
|
+
}
|
|
4290
|
+
countAll() {
|
|
4291
|
+
return this.slots.size;
|
|
4292
|
+
}
|
|
4293
|
+
clear() {
|
|
4294
|
+
const retained = [];
|
|
4295
|
+
for (const slot of this.slots.values()) {
|
|
4296
|
+
slot.item = void 0;
|
|
4297
|
+
if (this.hasSubscribers(slot)) retained.push(slot);
|
|
4298
|
+
}
|
|
4299
|
+
this.slots.clear();
|
|
4300
|
+
for (const slot of this.reservedSlots.values()) {
|
|
4301
|
+
slot.item = void 0;
|
|
4302
|
+
if (this.hasSubscribers(slot)) retained.push(slot);
|
|
4303
|
+
}
|
|
4304
|
+
this.reservedSlots.clear();
|
|
4305
|
+
for (const slot of retained) {
|
|
4306
|
+
this.reservedSlots.set(slot.pk, slot);
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
getAllPks() {
|
|
4310
|
+
return Array.from(this.slots.keys());
|
|
4311
|
+
}
|
|
4312
|
+
destroy() {
|
|
4313
|
+
this.clear();
|
|
4314
|
+
for (const slot of this.reservedSlots.values()) {
|
|
4315
|
+
slot.subscribers?.clear();
|
|
4316
|
+
slot.subscribers = void 0;
|
|
4317
|
+
}
|
|
4318
|
+
this.reservedSlots.clear();
|
|
4319
|
+
}
|
|
4320
|
+
};
|
|
4321
|
+
|
|
3453
4322
|
// src/core/OIMObjectStoreMapDriven.ts
|
|
3454
4323
|
var OIMObjectStoreMapDriven = class extends OIMObjectStore {
|
|
3455
4324
|
constructor() {
|
|
@@ -3497,6 +4366,27 @@ var OIMObjectStoreMapDriven = class extends OIMObjectStore {
|
|
|
3497
4366
|
}
|
|
3498
4367
|
};
|
|
3499
4368
|
|
|
4369
|
+
// src/core/OIMKeyPathCarrierResolver.ts
|
|
4370
|
+
var OIMKeyPathCarrierResolver = class {
|
|
4371
|
+
constructor() {
|
|
4372
|
+
this.carriers = new OIMTrieMap();
|
|
4373
|
+
}
|
|
4374
|
+
getOrReserveCarrier(key) {
|
|
4375
|
+
let carrier = this.carriers.get(key);
|
|
4376
|
+
if (!carrier) {
|
|
4377
|
+
carrier = { key };
|
|
4378
|
+
this.carriers.set(key, carrier);
|
|
4379
|
+
}
|
|
4380
|
+
return carrier;
|
|
4381
|
+
}
|
|
4382
|
+
findCarrier(key) {
|
|
4383
|
+
return this.carriers.get(key);
|
|
4384
|
+
}
|
|
4385
|
+
onCarrierEmptied(carrier) {
|
|
4386
|
+
this.carriers.delete(carrier.key);
|
|
4387
|
+
}
|
|
4388
|
+
};
|
|
4389
|
+
|
|
3500
4390
|
// src/core/OIMComparatorFactory.ts
|
|
3501
4391
|
var OIMComparatorFactory = class {
|
|
3502
4392
|
createShallowComparator() {
|
|
@@ -3715,6 +4605,78 @@ var OIMMap2Keys = class {
|
|
|
3715
4605
|
}
|
|
3716
4606
|
};
|
|
3717
4607
|
|
|
4608
|
+
// src/core/OIMDisposeScope.ts
|
|
4609
|
+
var OIMDisposeScope = class _OIMDisposeScope {
|
|
4610
|
+
constructor() {
|
|
4611
|
+
this.disposables = [];
|
|
4612
|
+
this.disposed = false;
|
|
4613
|
+
}
|
|
4614
|
+
/** Number of not-yet-disposed items currently held. */
|
|
4615
|
+
get size() {
|
|
4616
|
+
return this.disposables.length;
|
|
4617
|
+
}
|
|
4618
|
+
get isDisposed() {
|
|
4619
|
+
return this.disposed;
|
|
4620
|
+
}
|
|
4621
|
+
/**
|
|
4622
|
+
* Register a disposable (object with `destroy()` or an unsubscribe function)
|
|
4623
|
+
* and return it unchanged, so it can be captured inline. If the scope is
|
|
4624
|
+
* already disposed the item is disposed immediately, so nothing leaks.
|
|
4625
|
+
*/
|
|
4626
|
+
add(disposable) {
|
|
4627
|
+
if (this.disposed) {
|
|
4628
|
+
this.disposeOne(disposable);
|
|
4629
|
+
return disposable;
|
|
4630
|
+
}
|
|
4631
|
+
this.disposables.push(disposable);
|
|
4632
|
+
return disposable;
|
|
4633
|
+
}
|
|
4634
|
+
/** A nested scope, itself registered here — dispose it early or with the parent. */
|
|
4635
|
+
child() {
|
|
4636
|
+
return this.add(new _OIMDisposeScope());
|
|
4637
|
+
}
|
|
4638
|
+
/**
|
|
4639
|
+
* Dispose everything in reverse registration order, then clear. Idempotent.
|
|
4640
|
+
* If any `destroy()`/unsubscribe throws, the rest are still disposed and the
|
|
4641
|
+
* first error is rethrown once teardown completes.
|
|
4642
|
+
*/
|
|
4643
|
+
destroy() {
|
|
4644
|
+
if (this.disposed) return;
|
|
4645
|
+
this.disposed = true;
|
|
4646
|
+
let firstError;
|
|
4647
|
+
let hasError = false;
|
|
4648
|
+
for (let i = this.disposables.length - 1; i >= 0; i--) {
|
|
4649
|
+
try {
|
|
4650
|
+
this.disposeOne(this.disposables[i]);
|
|
4651
|
+
} catch (error) {
|
|
4652
|
+
if (!hasError) {
|
|
4653
|
+
hasError = true;
|
|
4654
|
+
firstError = error;
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
this.disposables.length = 0;
|
|
4659
|
+
if (hasError) throw firstError;
|
|
4660
|
+
}
|
|
4661
|
+
disposeOne(disposable) {
|
|
4662
|
+
if (typeof disposable === "function") disposable();
|
|
4663
|
+
else disposable.destroy();
|
|
4664
|
+
}
|
|
4665
|
+
};
|
|
4666
|
+
function createOIMDisposeScope() {
|
|
4667
|
+
return new OIMDisposeScope();
|
|
4668
|
+
}
|
|
4669
|
+
|
|
4670
|
+
// src/core/OIMPkCodecKeyPath.ts
|
|
4671
|
+
var OIMPkCodecKeyPath = class {
|
|
4672
|
+
encode(pk) {
|
|
4673
|
+
return JSON.stringify(pk);
|
|
4674
|
+
}
|
|
4675
|
+
decode(encoded) {
|
|
4676
|
+
return JSON.parse(encoded);
|
|
4677
|
+
}
|
|
4678
|
+
};
|
|
4679
|
+
|
|
3718
4680
|
// src/modules/compute/core/OIMComputeRuntime.ts
|
|
3719
4681
|
var OIMComputeRuntime = class {
|
|
3720
4682
|
constructor(queue) {
|
|
@@ -4318,6 +5280,27 @@ var OIMCollectionSelectors = class {
|
|
|
4318
5280
|
key
|
|
4319
5281
|
);
|
|
4320
5282
|
}
|
|
5283
|
+
/**
|
|
5284
|
+
* Entities of a composite (key-path) set-based index for one key path, e.g.
|
|
5285
|
+
* `entitiesByCompositeSetIndexKey(index, [projectId, role])`.
|
|
5286
|
+
*/
|
|
5287
|
+
entitiesByCompositeSetIndexKey(index, key) {
|
|
5288
|
+
return new OIMEntitiesByIndexKeySetBasedSelector(
|
|
5289
|
+
this.runtime,
|
|
5290
|
+
this.collection,
|
|
5291
|
+
index,
|
|
5292
|
+
key
|
|
5293
|
+
);
|
|
5294
|
+
}
|
|
5295
|
+
/** Entities of a composite (key-path) array-based index for one key path. */
|
|
5296
|
+
entitiesByCompositeArrayIndexKey(index, key) {
|
|
5297
|
+
return new OIMEntitiesByIndexKeyArrayBasedSelector(
|
|
5298
|
+
this.runtime,
|
|
5299
|
+
this.collection,
|
|
5300
|
+
index,
|
|
5301
|
+
key
|
|
5302
|
+
);
|
|
5303
|
+
}
|
|
4321
5304
|
/** Entities of a keyless (whole-collection) array-based index, ordered. */
|
|
4322
5305
|
entitiesByArrayGlobalIndex(index) {
|
|
4323
5306
|
return new OIMEntitiesByGlobalIndexArrayBasedSelector(
|
|
@@ -4341,7 +5324,16 @@ function createOIMCollectionKit(queue, opts) {
|
|
|
4341
5324
|
const collection = new OIMReactiveCollection(queue, opts);
|
|
4342
5325
|
const indexFactory = new OIMCollectionIndexFactory(queue, collection);
|
|
4343
5326
|
const select = new OIMCollectionSelectors(queue, collection);
|
|
4344
|
-
|
|
5327
|
+
const scope = new OIMDisposeScope();
|
|
5328
|
+
scope.add(collection);
|
|
5329
|
+
return {
|
|
5330
|
+
queue,
|
|
5331
|
+
collection,
|
|
5332
|
+
indexFactory,
|
|
5333
|
+
select,
|
|
5334
|
+
scope,
|
|
5335
|
+
destroy: () => scope.destroy()
|
|
5336
|
+
};
|
|
4345
5337
|
}
|
|
4346
5338
|
|
|
4347
5339
|
// src/modules/wrapper/collection/OIMCollectionChangedFields.ts
|
|
@@ -5530,6 +6522,7 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
5530
6522
|
EOIMEventQueueSchedulerEventType,
|
|
5531
6523
|
EOIMIndexEventType,
|
|
5532
6524
|
EOIMObjectEventType,
|
|
6525
|
+
OIMBucketCarrierResolver,
|
|
5533
6526
|
OIMCarrierKeyedEmitter,
|
|
5534
6527
|
OIMCarrierSingleEmitter,
|
|
5535
6528
|
OIMCollection,
|
|
@@ -5542,6 +6535,7 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
5542
6535
|
OIMCollectionSelectors,
|
|
5543
6536
|
OIMCollectionStore,
|
|
5544
6537
|
OIMCollectionStoreMapDriven,
|
|
6538
|
+
OIMCollectionStoreTrieDriven,
|
|
5545
6539
|
OIMComparatorFactory,
|
|
5546
6540
|
OIMComputeRuntime,
|
|
5547
6541
|
OIMComputed,
|
|
@@ -5550,6 +6544,7 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
5550
6544
|
OIMDerivedCollectionGlobalIndexSetBased,
|
|
5551
6545
|
OIMDerivedCollectionIndexArrayBased,
|
|
5552
6546
|
OIMDerivedCollectionIndexSetBased,
|
|
6547
|
+
OIMDisposeScope,
|
|
5553
6548
|
OIMEffect,
|
|
5554
6549
|
OIMEffectDependencyComputed,
|
|
5555
6550
|
OIMEffectDependencyKeyed,
|
|
@@ -5592,6 +6587,13 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
5592
6587
|
OIMIndexStoreMapDrivenArrayBased,
|
|
5593
6588
|
OIMIndexStoreMapDrivenSetBased,
|
|
5594
6589
|
OIMIndexStoreSetBased,
|
|
6590
|
+
OIMIndexStoreTrieDrivenArrayBased,
|
|
6591
|
+
OIMIndexStoreTrieDrivenSetBased,
|
|
6592
|
+
OIMKeyDomainNative,
|
|
6593
|
+
OIMKeyDomainTrie,
|
|
6594
|
+
OIMKeyPathCarrierResolver,
|
|
6595
|
+
OIMKeyedBucketArrayBased,
|
|
6596
|
+
OIMKeyedBucketSetBased,
|
|
5595
6597
|
OIMMap2Keys,
|
|
5596
6598
|
OIMObject,
|
|
5597
6599
|
OIMObjectStore,
|
|
@@ -5603,10 +6605,13 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
5603
6605
|
OIMOrderedListCommandStream,
|
|
5604
6606
|
OIMOrderedListCommandStreamDiffDriven,
|
|
5605
6607
|
OIMOrderedListMappedCommandStream,
|
|
6608
|
+
OIMPkCodecKeyPath,
|
|
5606
6609
|
OIMPkSelectorFactory,
|
|
5607
6610
|
OIMReactiveCollection,
|
|
5608
6611
|
OIMReactiveCollectionGlobalIndexManualArrayBased,
|
|
5609
6612
|
OIMReactiveCollectionGlobalIndexManualSetBased,
|
|
6613
|
+
OIMReactiveCollectionIndexCompositeTrieArrayBased,
|
|
6614
|
+
OIMReactiveCollectionIndexCompositeTrieSetBased,
|
|
5610
6615
|
OIMReactiveCollectionIndexManualArrayBased,
|
|
5611
6616
|
OIMReactiveCollectionIndexManualSetBased,
|
|
5612
6617
|
OIMReactiveGlobalIndex,
|
|
@@ -5621,10 +6626,13 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
5621
6626
|
OIMReactiveIndexSetBased,
|
|
5622
6627
|
OIMReactiveObject,
|
|
5623
6628
|
OIMSelector,
|
|
6629
|
+
OIMTrieMap,
|
|
6630
|
+
OIMTrieSet,
|
|
5624
6631
|
createInPlaceEntityUpdater,
|
|
5625
6632
|
createMergeEntityUpdater,
|
|
5626
6633
|
createOIMCollectionIndexFactory,
|
|
5627
6634
|
createOIMCollectionKit,
|
|
6635
|
+
createOIMDisposeScope,
|
|
5628
6636
|
createOIMGlobalIndexSlotMap,
|
|
5629
6637
|
createOIMIndexSlotMap,
|
|
5630
6638
|
createOIMOrderedListCommandStreamDiffDriven,
|