@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.js
CHANGED
|
@@ -227,21 +227,82 @@ var OIMEventQueueScheduler = class {
|
|
|
227
227
|
}
|
|
228
228
|
};
|
|
229
229
|
|
|
230
|
+
// src/core/OIMKeyedBucketSetBased.ts
|
|
231
|
+
var OIMKeyedBucketSetBased = class extends Set {
|
|
232
|
+
constructor(key) {
|
|
233
|
+
super();
|
|
234
|
+
this.key = key;
|
|
235
|
+
}
|
|
236
|
+
hasSubscribers() {
|
|
237
|
+
return this.subscribers !== void 0 && this.subscribers.size > 0;
|
|
238
|
+
}
|
|
239
|
+
/** Clearing the slot set also clears the pk membership — they stay in sync. */
|
|
240
|
+
clear() {
|
|
241
|
+
super.clear();
|
|
242
|
+
this.membership?.clear();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
230
246
|
// src/core/OIMIndexStoreMapDrivenSetBased.ts
|
|
231
247
|
var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
232
248
|
constructor() {
|
|
233
249
|
super(...arguments);
|
|
250
|
+
// Live buckets (non-empty, or freshly promoted). Enumeration reads these only.
|
|
234
251
|
this.slots = /* @__PURE__ */ new Map();
|
|
252
|
+
// Empty buckets kept alive solely by their subscribers, so a subscription
|
|
253
|
+
// survives a key going empty → re-filled. Invisible to reads/enumeration.
|
|
254
|
+
this.reservedBuckets = /* @__PURE__ */ new Map();
|
|
255
|
+
}
|
|
256
|
+
getOrReserveBucket(key) {
|
|
257
|
+
const live = this.slots.get(key);
|
|
258
|
+
if (live) return live;
|
|
259
|
+
let reserved = this.reservedBuckets.get(key);
|
|
260
|
+
if (!reserved) {
|
|
261
|
+
reserved = new OIMKeyedBucketSetBased(key);
|
|
262
|
+
this.reservedBuckets.set(key, reserved);
|
|
263
|
+
}
|
|
264
|
+
return reserved;
|
|
265
|
+
}
|
|
266
|
+
findBucket(key) {
|
|
267
|
+
return this.slots.get(key) ?? this.reservedBuckets.get(key);
|
|
268
|
+
}
|
|
269
|
+
retainBucket(bucket) {
|
|
270
|
+
if (bucket.size === 0) return;
|
|
271
|
+
this.reservedBuckets.delete(bucket.key);
|
|
272
|
+
if (!this.slots.has(bucket.key)) this.slots.set(bucket.key, bucket);
|
|
273
|
+
}
|
|
274
|
+
releaseBucket(bucket) {
|
|
275
|
+
if (bucket.size > 0) return;
|
|
276
|
+
this.slots.delete(bucket.key);
|
|
277
|
+
if (bucket.hasSubscribers()) {
|
|
278
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
279
|
+
} else {
|
|
280
|
+
this.reservedBuckets.delete(bucket.key);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
dropIfReserved(key) {
|
|
284
|
+
const reserved = this.reservedBuckets.get(key);
|
|
285
|
+
if (reserved && reserved.size === 0 && !reserved.hasSubscribers()) {
|
|
286
|
+
this.reservedBuckets.delete(key);
|
|
287
|
+
}
|
|
235
288
|
}
|
|
236
289
|
setOneByKey(key, slots) {
|
|
237
|
-
this.
|
|
290
|
+
const bucket = this.getOrReserveBucket(key);
|
|
291
|
+
if (bucket !== slots) {
|
|
292
|
+
bucket.clear();
|
|
293
|
+
for (const slot of slots) bucket.add(slot);
|
|
294
|
+
}
|
|
295
|
+
if (bucket.size > 0) this.retainBucket(bucket);
|
|
296
|
+
else this.releaseBucket(bucket);
|
|
238
297
|
}
|
|
239
298
|
removeOneByKey(key) {
|
|
240
299
|
this.slots.delete(key);
|
|
300
|
+
this.reservedBuckets.delete(key);
|
|
241
301
|
}
|
|
242
302
|
removeManyByKeys(keys) {
|
|
243
303
|
for (const key of keys) {
|
|
244
304
|
this.slots.delete(key);
|
|
305
|
+
this.reservedBuckets.delete(key);
|
|
245
306
|
}
|
|
246
307
|
}
|
|
247
308
|
getOneByKey(key) {
|
|
@@ -250,10 +311,8 @@ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
|
250
311
|
getManyByKeys(keys) {
|
|
251
312
|
const result = /* @__PURE__ */ new Map();
|
|
252
313
|
for (const key of keys) {
|
|
253
|
-
const slots = this.
|
|
254
|
-
if (slots !== void 0)
|
|
255
|
-
result.set(key, slots);
|
|
256
|
-
}
|
|
314
|
+
const slots = this.slots.get(key);
|
|
315
|
+
if (slots !== void 0) result.set(key, slots);
|
|
257
316
|
}
|
|
258
317
|
return result;
|
|
259
318
|
}
|
|
@@ -267,6 +326,12 @@ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
|
267
326
|
return this.slots.size;
|
|
268
327
|
}
|
|
269
328
|
clear() {
|
|
329
|
+
for (const bucket of this.slots.values()) {
|
|
330
|
+
bucket.clear();
|
|
331
|
+
if (bucket.hasSubscribers()) {
|
|
332
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
270
335
|
this.slots.clear();
|
|
271
336
|
}
|
|
272
337
|
};
|
|
@@ -274,10 +339,9 @@ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
|
274
339
|
// src/abstract/OIMIndexSetBased.ts
|
|
275
340
|
var OIMIndexSetBased = class extends OIMIndex {
|
|
276
341
|
constructor(options = {}) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
);
|
|
342
|
+
const store = options.store ?? new OIMIndexStoreMapDrivenSetBased();
|
|
343
|
+
super(store, options.comparePks);
|
|
344
|
+
this.setStore = store;
|
|
281
345
|
}
|
|
282
346
|
getBucketSize(bucket) {
|
|
283
347
|
return bucket.size;
|
|
@@ -305,22 +369,36 @@ var OIMIndexSetBased = class extends OIMIndex {
|
|
|
305
369
|
for (const slot of slots) pks.add(slot.pk);
|
|
306
370
|
return pks;
|
|
307
371
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
372
|
+
/**
|
|
373
|
+
* True if a comparator is set and considers `bucket`'s current pks equal to
|
|
374
|
+
* `newSlots` — i.e. a `setSlots` would be a no-op and should not emit.
|
|
375
|
+
*/
|
|
376
|
+
bucketMatchesComparator(bucket, newSlots) {
|
|
377
|
+
if (!this.comparePks) return false;
|
|
378
|
+
if (bucket.size !== newSlots.size) return false;
|
|
379
|
+
if (bucket.size === 0) return true;
|
|
380
|
+
return this.comparePks(
|
|
381
|
+
Array.from(this.slotsToPks(bucket)),
|
|
382
|
+
Array.from(this.slotsToPks(newSlots))
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// src/core/OIMKeyedBucketArrayBased.ts
|
|
388
|
+
var OIMKeyedBucketArrayBased = class extends Array {
|
|
389
|
+
constructor(key) {
|
|
390
|
+
super();
|
|
391
|
+
this.key = key;
|
|
392
|
+
}
|
|
393
|
+
static get [Symbol.species]() {
|
|
394
|
+
return Array;
|
|
395
|
+
}
|
|
396
|
+
hasSubscribers() {
|
|
397
|
+
return this.subscribers !== void 0 && this.subscribers.size > 0;
|
|
398
|
+
}
|
|
399
|
+
/** Empty the bucket in place (Array has no `clear`). */
|
|
400
|
+
clear() {
|
|
401
|
+
this.length = 0;
|
|
324
402
|
}
|
|
325
403
|
};
|
|
326
404
|
|
|
@@ -328,17 +406,61 @@ var OIMIndexSetBased = class extends OIMIndex {
|
|
|
328
406
|
var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
329
407
|
constructor() {
|
|
330
408
|
super(...arguments);
|
|
409
|
+
// Live buckets (non-empty). Enumeration reads these only.
|
|
331
410
|
this.slots = /* @__PURE__ */ new Map();
|
|
411
|
+
// Empty buckets kept alive by their subscribers, invisible to reads.
|
|
412
|
+
this.reservedBuckets = /* @__PURE__ */ new Map();
|
|
413
|
+
}
|
|
414
|
+
getOrReserveBucket(key) {
|
|
415
|
+
const live = this.slots.get(key);
|
|
416
|
+
if (live) return live;
|
|
417
|
+
let reserved = this.reservedBuckets.get(key);
|
|
418
|
+
if (!reserved) {
|
|
419
|
+
reserved = new OIMKeyedBucketArrayBased(key);
|
|
420
|
+
this.reservedBuckets.set(key, reserved);
|
|
421
|
+
}
|
|
422
|
+
return reserved;
|
|
423
|
+
}
|
|
424
|
+
findBucket(key) {
|
|
425
|
+
return this.slots.get(key) ?? this.reservedBuckets.get(key);
|
|
426
|
+
}
|
|
427
|
+
retainBucket(bucket) {
|
|
428
|
+
if (bucket.length === 0) return;
|
|
429
|
+
this.reservedBuckets.delete(bucket.key);
|
|
430
|
+
if (!this.slots.has(bucket.key)) this.slots.set(bucket.key, bucket);
|
|
431
|
+
}
|
|
432
|
+
releaseBucket(bucket) {
|
|
433
|
+
if (bucket.length > 0) return;
|
|
434
|
+
this.slots.delete(bucket.key);
|
|
435
|
+
if (bucket.hasSubscribers()) {
|
|
436
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
437
|
+
} else {
|
|
438
|
+
this.reservedBuckets.delete(bucket.key);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
dropIfReserved(key) {
|
|
442
|
+
const reserved = this.reservedBuckets.get(key);
|
|
443
|
+
if (reserved && reserved.length === 0 && !reserved.hasSubscribers()) {
|
|
444
|
+
this.reservedBuckets.delete(key);
|
|
445
|
+
}
|
|
332
446
|
}
|
|
333
447
|
setOneByKey(key, slots) {
|
|
334
|
-
this.
|
|
448
|
+
const bucket = this.getOrReserveBucket(key);
|
|
449
|
+
if (bucket !== slots) {
|
|
450
|
+
bucket.length = 0;
|
|
451
|
+
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
452
|
+
}
|
|
453
|
+
if (bucket.length > 0) this.retainBucket(bucket);
|
|
454
|
+
else this.releaseBucket(bucket);
|
|
335
455
|
}
|
|
336
456
|
removeOneByKey(key) {
|
|
337
457
|
this.slots.delete(key);
|
|
458
|
+
this.reservedBuckets.delete(key);
|
|
338
459
|
}
|
|
339
460
|
removeManyByKeys(keys) {
|
|
340
461
|
for (const key of keys) {
|
|
341
462
|
this.slots.delete(key);
|
|
463
|
+
this.reservedBuckets.delete(key);
|
|
342
464
|
}
|
|
343
465
|
}
|
|
344
466
|
getOneByKey(key) {
|
|
@@ -347,10 +469,8 @@ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
|
347
469
|
getManyByKeys(keys) {
|
|
348
470
|
const result = /* @__PURE__ */ new Map();
|
|
349
471
|
for (const key of keys) {
|
|
350
|
-
const slots = this.
|
|
351
|
-
if (slots !== void 0)
|
|
352
|
-
result.set(key, slots);
|
|
353
|
-
}
|
|
472
|
+
const slots = this.slots.get(key);
|
|
473
|
+
if (slots !== void 0) result.set(key, slots);
|
|
354
474
|
}
|
|
355
475
|
return result;
|
|
356
476
|
}
|
|
@@ -364,6 +484,12 @@ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
|
364
484
|
return this.slots.size;
|
|
365
485
|
}
|
|
366
486
|
clear() {
|
|
487
|
+
for (const bucket of this.slots.values()) {
|
|
488
|
+
bucket.length = 0;
|
|
489
|
+
if (bucket.hasSubscribers()) {
|
|
490
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
367
493
|
this.slots.clear();
|
|
368
494
|
}
|
|
369
495
|
};
|
|
@@ -371,10 +497,9 @@ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
|
371
497
|
// src/abstract/OIMIndexArrayBased.ts
|
|
372
498
|
var OIMIndexArrayBased = class extends OIMIndex {
|
|
373
499
|
constructor(options = {}) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
);
|
|
500
|
+
const store = options.store ?? new OIMIndexStoreMapDrivenArrayBased();
|
|
501
|
+
super(store, options.comparePks);
|
|
502
|
+
this.arrayStore = store;
|
|
378
503
|
}
|
|
379
504
|
getBucketSize(bucket) {
|
|
380
505
|
return bucket.length;
|
|
@@ -392,10 +517,15 @@ var OIMIndexArrayBased = class extends OIMIndex {
|
|
|
392
517
|
return result;
|
|
393
518
|
}
|
|
394
519
|
getSlotsByKey(key) {
|
|
395
|
-
|
|
520
|
+
const bucket = this.store.getOneByKey(key);
|
|
521
|
+
return bucket ? bucket.slice() : [];
|
|
396
522
|
}
|
|
397
523
|
getSlotsByKeys(keys) {
|
|
398
|
-
|
|
524
|
+
const result = /* @__PURE__ */ new Map();
|
|
525
|
+
for (const [key, bucket] of this.store.getManyByKeys(keys)) {
|
|
526
|
+
result.set(key, bucket.slice());
|
|
527
|
+
}
|
|
528
|
+
return result;
|
|
399
529
|
}
|
|
400
530
|
slotsToPks(slots) {
|
|
401
531
|
const pks = [];
|
|
@@ -403,22 +533,18 @@ var OIMIndexArrayBased = class extends OIMIndex {
|
|
|
403
533
|
for (let i = 0; i < slots.length; i++) pks[i] = slots[i].pk;
|
|
404
534
|
return pks;
|
|
405
535
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
this.store.setOneByKey(key, newSlots);
|
|
421
|
-
return true;
|
|
536
|
+
/**
|
|
537
|
+
* True if a comparator is set and considers `bucket`'s pks equal to
|
|
538
|
+
* `newSlots` — i.e. a `setSlots` would be a no-op and should not emit.
|
|
539
|
+
*/
|
|
540
|
+
bucketMatchesComparator(bucket, newSlots) {
|
|
541
|
+
if (!this.comparePks) return false;
|
|
542
|
+
if (bucket.length !== newSlots.length) return false;
|
|
543
|
+
if (bucket.length === 0) return true;
|
|
544
|
+
return this.comparePks(
|
|
545
|
+
this.slotsToPks(bucket),
|
|
546
|
+
this.slotsToPks(newSlots)
|
|
547
|
+
);
|
|
422
548
|
}
|
|
423
549
|
};
|
|
424
550
|
|
|
@@ -507,11 +633,19 @@ var OIMCarrierKeyedEmitter = class {
|
|
|
507
633
|
this.scheduleFlush();
|
|
508
634
|
}
|
|
509
635
|
markUpdatedKey(key) {
|
|
636
|
+
if (this.subscribedCarriers.size === 0) {
|
|
637
|
+
this.assertNotInFlush();
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
510
640
|
const carrier = this.resolver.findCarrier(key);
|
|
511
641
|
if (carrier) this.markUpdatedCarrier(carrier);
|
|
512
642
|
else this.assertNotInFlush();
|
|
513
643
|
}
|
|
514
644
|
markUpdatedKeys(keys) {
|
|
645
|
+
if (this.subscribedCarriers.size === 0) {
|
|
646
|
+
this.assertNotInFlush();
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
515
649
|
for (let i = 0; i < keys.length; i++) this.markUpdatedKey(keys[i]);
|
|
516
650
|
}
|
|
517
651
|
markAllUpdated() {
|
|
@@ -611,8 +745,8 @@ var OIMKeyedCarrierResolver = class {
|
|
|
611
745
|
|
|
612
746
|
// src/abstract/OIMReactiveIndex.ts
|
|
613
747
|
var OIMReactiveIndex = class {
|
|
614
|
-
constructor(queue, createIndex) {
|
|
615
|
-
this.updateEmitter = new OIMCarrierKeyedEmitter(queue,
|
|
748
|
+
constructor(queue, createIndex, createResolver = () => new OIMKeyedCarrierResolver()) {
|
|
749
|
+
this.updateEmitter = new OIMCarrierKeyedEmitter(queue, createResolver());
|
|
616
750
|
this.index = createIndex(this.updateEmitter);
|
|
617
751
|
}
|
|
618
752
|
getEntitiesByKey(key) {
|
|
@@ -668,8 +802,8 @@ var OIMReactiveIndex = class {
|
|
|
668
802
|
|
|
669
803
|
// src/abstract/OIMReactiveIndexSetBased.ts
|
|
670
804
|
var OIMReactiveIndexSetBased = class extends OIMReactiveIndex {
|
|
671
|
-
constructor(queue, createIndex) {
|
|
672
|
-
super(queue, createIndex);
|
|
805
|
+
constructor(queue, createIndex, createResolver) {
|
|
806
|
+
super(queue, createIndex, createResolver);
|
|
673
807
|
}
|
|
674
808
|
getPksByKey(key) {
|
|
675
809
|
return this.index.getPksByKey(key);
|
|
@@ -687,8 +821,8 @@ var OIMReactiveIndexSetBased = class extends OIMReactiveIndex {
|
|
|
687
821
|
|
|
688
822
|
// src/abstract/OIMReactiveIndexArrayBased.ts
|
|
689
823
|
var OIMReactiveIndexArrayBased = class extends OIMReactiveIndex {
|
|
690
|
-
constructor(queue, createIndex) {
|
|
691
|
-
super(queue, createIndex);
|
|
824
|
+
constructor(queue, createIndex, createResolver) {
|
|
825
|
+
super(queue, createIndex, createResolver);
|
|
692
826
|
}
|
|
693
827
|
getPksByKey(key) {
|
|
694
828
|
return this.index.getPksByKey(key);
|
|
@@ -963,10 +1097,24 @@ var OIMPkSelectorFactory = class {
|
|
|
963
1097
|
}
|
|
964
1098
|
};
|
|
965
1099
|
|
|
1100
|
+
// src/core/OIMKeyDomainNative.ts
|
|
1101
|
+
var OIMKeyDomainNative = class {
|
|
1102
|
+
createMap() {
|
|
1103
|
+
return /* @__PURE__ */ new Map();
|
|
1104
|
+
}
|
|
1105
|
+
createSet() {
|
|
1106
|
+
return /* @__PURE__ */ new Set();
|
|
1107
|
+
}
|
|
1108
|
+
canonicalize(key) {
|
|
1109
|
+
return key;
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
|
|
966
1113
|
// src/core/OIMCollectionStoreMapDriven.ts
|
|
967
1114
|
var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
|
|
968
1115
|
constructor() {
|
|
969
1116
|
super(...arguments);
|
|
1117
|
+
this.keyDomain = new OIMKeyDomainNative();
|
|
970
1118
|
this.slots = /* @__PURE__ */ new Map();
|
|
971
1119
|
// Slots reserved by indexes for PKs whose entity has not arrived yet.
|
|
972
1120
|
// They carry `item: undefined` and live apart from `slots` so they do not
|
|
@@ -1139,6 +1287,10 @@ var OIMCollection = class {
|
|
|
1139
1287
|
this.store = opts?.store ?? new OIMCollectionStoreMapDriven();
|
|
1140
1288
|
this.updateEntity = opts?.updateEntity ?? new OIMEntityUpdaterFactory().createMergeEntityUpdater();
|
|
1141
1289
|
}
|
|
1290
|
+
/** The collection's PK keying strategy — read by indexes and wrappers. */
|
|
1291
|
+
get keyDomain() {
|
|
1292
|
+
return this.store.keyDomain;
|
|
1293
|
+
}
|
|
1142
1294
|
getOneByPk(pk) {
|
|
1143
1295
|
return this.store.getOneByPk(pk);
|
|
1144
1296
|
}
|
|
@@ -1219,15 +1371,12 @@ var OIMCollection = class {
|
|
|
1219
1371
|
`[OIMCollection]: PK is required to upsert an entity ${JSON.stringify(entity)}`
|
|
1220
1372
|
);
|
|
1221
1373
|
}
|
|
1222
|
-
const
|
|
1223
|
-
if (
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
this.updateEntity(entity, existingEntity)
|
|
1227
|
-
);
|
|
1228
|
-
} else {
|
|
1229
|
-
return this.store.setOneByPk(pk, entity);
|
|
1374
|
+
const slot = this.store.getSlotByPk(pk);
|
|
1375
|
+
if (slot !== void 0 && slot.item !== void 0) {
|
|
1376
|
+
slot.item = this.updateEntity(entity, slot.item);
|
|
1377
|
+
return slot;
|
|
1230
1378
|
}
|
|
1379
|
+
return this.store.setOneByPk(pk, entity);
|
|
1231
1380
|
}
|
|
1232
1381
|
destroy() {
|
|
1233
1382
|
this.store.destroy();
|
|
@@ -1240,13 +1389,13 @@ var OIMReactiveCollection = class extends OIMCollection {
|
|
|
1240
1389
|
constructor(queue, opts) {
|
|
1241
1390
|
super(opts);
|
|
1242
1391
|
this.anyUpdateHandlers = /* @__PURE__ */ new Set();
|
|
1243
|
-
this.pendingAnyUpdatePks =
|
|
1392
|
+
this.pendingAnyUpdatePks = this.store.keyDomain.createSet();
|
|
1244
1393
|
this.isAnyUpdateClearPending = false;
|
|
1245
1394
|
this.isAnyUpdateScheduled = false;
|
|
1246
1395
|
this.runAnyUpdate = () => {
|
|
1247
1396
|
if (!this.isAnyUpdateScheduled) return;
|
|
1248
1397
|
this.isAnyUpdateScheduled = false;
|
|
1249
|
-
const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks);
|
|
1398
|
+
const pks = this.isAnyUpdateClearPending ? [] : Array.from(this.pendingAnyUpdatePks.values());
|
|
1250
1399
|
this.pendingAnyUpdatePks.clear();
|
|
1251
1400
|
this.isAnyUpdateClearPending = false;
|
|
1252
1401
|
const snapshot = Array.from(this.anyUpdateHandlers);
|
|
@@ -1405,38 +1554,40 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1405
1554
|
if (key === void 0) {
|
|
1406
1555
|
const keys = this.getKeys();
|
|
1407
1556
|
if (keys.length > 0) {
|
|
1408
|
-
this.
|
|
1557
|
+
this.arrayStore.clear();
|
|
1409
1558
|
this.emitUpdate(Array.from(keys));
|
|
1410
1559
|
}
|
|
1411
1560
|
return;
|
|
1412
1561
|
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1562
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1563
|
+
if (bucket && bucket.length > 0) {
|
|
1564
|
+
bucket.length = 0;
|
|
1565
|
+
this.arrayStore.releaseBucket(bucket);
|
|
1415
1566
|
this.emitUpdate([key]);
|
|
1416
1567
|
}
|
|
1417
1568
|
}
|
|
1418
1569
|
pushSlot(key, slot) {
|
|
1419
|
-
const
|
|
1420
|
-
const index =
|
|
1421
|
-
|
|
1570
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
1571
|
+
const index = bucket.length;
|
|
1572
|
+
bucket.push(slot);
|
|
1573
|
+
this.arrayStore.retainBucket(bucket);
|
|
1422
1574
|
this.emitUpdate([key]);
|
|
1423
1575
|
return index;
|
|
1424
1576
|
}
|
|
1425
1577
|
insertSlotAt(key, index, slot) {
|
|
1426
|
-
const
|
|
1427
|
-
const safeIndex = Math.max(0, Math.min(index,
|
|
1428
|
-
|
|
1578
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
1579
|
+
const safeIndex = Math.max(0, Math.min(index, bucket.length));
|
|
1580
|
+
bucket.splice(safeIndex, 0, slot);
|
|
1581
|
+
this.arrayStore.retainBucket(bucket);
|
|
1429
1582
|
this.emitUpdate([key]);
|
|
1430
1583
|
return safeIndex;
|
|
1431
1584
|
}
|
|
1432
1585
|
removeAt(key, index) {
|
|
1433
|
-
const
|
|
1434
|
-
if (!
|
|
1435
|
-
if (index < 0 || index >=
|
|
1436
|
-
const [removed] =
|
|
1437
|
-
if (
|
|
1438
|
-
this.store.removeOneByKey(key);
|
|
1439
|
-
}
|
|
1586
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1587
|
+
if (!bucket) return void 0;
|
|
1588
|
+
if (index < 0 || index >= bucket.length) return void 0;
|
|
1589
|
+
const [removed] = bucket.splice(index, 1);
|
|
1590
|
+
if (bucket.length === 0) this.arrayStore.releaseBucket(bucket);
|
|
1440
1591
|
this.emitUpdate([key]);
|
|
1441
1592
|
return removed;
|
|
1442
1593
|
}
|
|
@@ -1445,35 +1596,33 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1445
1596
|
* Returns the number actually removed (clamped to bounds; 0 if nothing).
|
|
1446
1597
|
*/
|
|
1447
1598
|
removeRange(key, index, count) {
|
|
1448
|
-
const
|
|
1449
|
-
if (!
|
|
1450
|
-
if (index < 0 || index >=
|
|
1451
|
-
const actual = Math.min(count,
|
|
1452
|
-
|
|
1453
|
-
if (
|
|
1454
|
-
this.store.removeOneByKey(key);
|
|
1455
|
-
}
|
|
1599
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1600
|
+
if (!bucket) return 0;
|
|
1601
|
+
if (index < 0 || index >= bucket.length || count <= 0) return 0;
|
|
1602
|
+
const actual = Math.min(count, bucket.length - index);
|
|
1603
|
+
bucket.splice(index, actual);
|
|
1604
|
+
if (bucket.length === 0) this.arrayStore.releaseBucket(bucket);
|
|
1456
1605
|
this.emitUpdate([key]);
|
|
1457
1606
|
return actual;
|
|
1458
1607
|
}
|
|
1459
1608
|
move(key, fromIndex, toIndex) {
|
|
1460
|
-
const
|
|
1461
|
-
if (!
|
|
1462
|
-
if (fromIndex < 0 || fromIndex >=
|
|
1463
|
-
if (toIndex < 0 || toIndex >=
|
|
1464
|
-
if (fromIndex === toIndex) return
|
|
1465
|
-
const [slot] =
|
|
1609
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1610
|
+
if (!bucket) return void 0;
|
|
1611
|
+
if (fromIndex < 0 || fromIndex >= bucket.length) return void 0;
|
|
1612
|
+
if (toIndex < 0 || toIndex >= bucket.length) return void 0;
|
|
1613
|
+
if (fromIndex === toIndex) return bucket[fromIndex];
|
|
1614
|
+
const [slot] = bucket.splice(fromIndex, 1);
|
|
1466
1615
|
if (slot === void 0) return void 0;
|
|
1467
|
-
|
|
1616
|
+
bucket.splice(toIndex, 0, slot);
|
|
1468
1617
|
this.emitUpdate([key]);
|
|
1469
1618
|
return slot;
|
|
1470
1619
|
}
|
|
1471
1620
|
/** Replace the slot at `index` in place. Returns the safe index, or -1. */
|
|
1472
1621
|
setSlotAt(key, index, slot) {
|
|
1473
|
-
const
|
|
1474
|
-
if (!
|
|
1475
|
-
if (index < 0 || index >=
|
|
1476
|
-
|
|
1622
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1623
|
+
if (!bucket) return -1;
|
|
1624
|
+
if (index < 0 || index >= bucket.length) return -1;
|
|
1625
|
+
bucket[index] = slot;
|
|
1477
1626
|
this.emitUpdate([key]);
|
|
1478
1627
|
return index;
|
|
1479
1628
|
}
|
|
@@ -1483,36 +1632,33 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1483
1632
|
* Returns the number actually moved (0 if a no-op / out of bounds).
|
|
1484
1633
|
*/
|
|
1485
1634
|
moveRange(key, from, to, count) {
|
|
1486
|
-
const
|
|
1487
|
-
if (!
|
|
1488
|
-
if (from < 0 || from >=
|
|
1489
|
-
const actual = Math.min(count,
|
|
1490
|
-
const insertAt = Math.max(0, Math.min(to,
|
|
1635
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
1636
|
+
if (!bucket) return 0;
|
|
1637
|
+
if (from < 0 || from >= bucket.length || count <= 0) return 0;
|
|
1638
|
+
const actual = Math.min(count, bucket.length - from);
|
|
1639
|
+
const insertAt = Math.max(0, Math.min(to, bucket.length - actual));
|
|
1491
1640
|
if (insertAt === from) return 0;
|
|
1492
|
-
const block =
|
|
1493
|
-
|
|
1641
|
+
const block = bucket.splice(from, actual);
|
|
1642
|
+
bucket.splice(insertAt, 0, ...block);
|
|
1494
1643
|
this.emitUpdate([key]);
|
|
1495
1644
|
return actual;
|
|
1496
1645
|
}
|
|
1497
1646
|
resetSlots(key, slots) {
|
|
1498
1647
|
if (slots.length === 0) {
|
|
1499
|
-
|
|
1500
|
-
|
|
1648
|
+
const bucket2 = this.arrayStore.findBucket(key);
|
|
1649
|
+
if (bucket2 && bucket2.length > 0) {
|
|
1650
|
+
bucket2.length = 0;
|
|
1651
|
+
this.arrayStore.releaseBucket(bucket2);
|
|
1501
1652
|
this.emitUpdate([key]);
|
|
1502
1653
|
}
|
|
1503
1654
|
return;
|
|
1504
1655
|
}
|
|
1505
|
-
this.
|
|
1656
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
1657
|
+
bucket.length = 0;
|
|
1658
|
+
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
1659
|
+
this.arrayStore.retainBucket(bucket);
|
|
1506
1660
|
this.emitUpdate([key]);
|
|
1507
1661
|
}
|
|
1508
|
-
getOrCreateList(key) {
|
|
1509
|
-
let list = this.store.getOneByKey(key);
|
|
1510
|
-
if (!list) {
|
|
1511
|
-
list = [];
|
|
1512
|
-
this.store.setOneByKey(key, list);
|
|
1513
|
-
}
|
|
1514
|
-
return list;
|
|
1515
|
-
}
|
|
1516
1662
|
};
|
|
1517
1663
|
|
|
1518
1664
|
// src/core/OIMCollectionIndexManualOrderedArrayBased.ts
|
|
@@ -2030,43 +2176,68 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
|
|
|
2030
2176
|
super(options);
|
|
2031
2177
|
}
|
|
2032
2178
|
setSlots(key, slots) {
|
|
2033
|
-
const
|
|
2034
|
-
if (
|
|
2035
|
-
|
|
2036
|
-
|
|
2179
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
2180
|
+
if (this.bucketMatchesComparator(bucket, slots)) return;
|
|
2181
|
+
bucket.length = 0;
|
|
2182
|
+
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
2183
|
+
if (bucket.length > 0) this.arrayStore.retainBucket(bucket);
|
|
2184
|
+
else this.arrayStore.releaseBucket(bucket);
|
|
2185
|
+
this.emitBucketChanged(bucket);
|
|
2037
2186
|
}
|
|
2038
2187
|
/**
|
|
2039
|
-
* Appends pre-deduplicated slots to a key's bucket IN PLACE
|
|
2040
|
-
*
|
|
2041
|
-
* not O(bucket)); used by collection-bound indexes for fast `addPks`.
|
|
2188
|
+
* Appends pre-deduplicated slots to a key's bucket IN PLACE and emits once.
|
|
2189
|
+
* O(added); callers own dedup.
|
|
2042
2190
|
*/
|
|
2043
2191
|
appendSlots(key, slots) {
|
|
2044
2192
|
if (slots.length === 0) return;
|
|
2045
|
-
|
|
2046
|
-
if (!bucket) {
|
|
2047
|
-
bucket = [];
|
|
2048
|
-
this.store.setOneByKey(key, bucket);
|
|
2049
|
-
}
|
|
2193
|
+
const bucket = this.arrayStore.getOrReserveBucket(key);
|
|
2050
2194
|
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
2051
|
-
this.
|
|
2195
|
+
this.arrayStore.retainBucket(bucket);
|
|
2196
|
+
this.emitBucketChanged(bucket);
|
|
2052
2197
|
}
|
|
2053
2198
|
/**
|
|
2054
|
-
* Clear all primary keys for a specific index key, or all keys if
|
|
2199
|
+
* Clear all primary keys for a specific index key, or all keys if none given.
|
|
2055
2200
|
*/
|
|
2056
2201
|
clear(key) {
|
|
2057
2202
|
if (key === void 0) {
|
|
2058
|
-
const allKeys = this.
|
|
2203
|
+
const allKeys = this.arrayStore.getAllKeys();
|
|
2059
2204
|
if (allKeys.length > 0) {
|
|
2060
|
-
this.
|
|
2205
|
+
this.arrayStore.clear();
|
|
2061
2206
|
this.emitUpdate(allKeys);
|
|
2062
2207
|
}
|
|
2063
2208
|
} else {
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2209
|
+
const bucket = this.arrayStore.findBucket(key);
|
|
2210
|
+
if (bucket && bucket.length > 0) {
|
|
2211
|
+
bucket.length = 0;
|
|
2212
|
+
this.arrayStore.releaseBucket(bucket);
|
|
2213
|
+
this.emitBucketChanged(bucket);
|
|
2067
2214
|
}
|
|
2068
2215
|
}
|
|
2069
2216
|
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Emit that a bucket changed. Base (non-reactive) path emits by key on the
|
|
2219
|
+
* plain event emitter; the reactive subclass overrides this to mark the
|
|
2220
|
+
* bucket carrier directly (O(1), no key→carrier lookup).
|
|
2221
|
+
*/
|
|
2222
|
+
emitBucketChanged(bucket) {
|
|
2223
|
+
this.emitUpdateOne(bucket.key);
|
|
2224
|
+
}
|
|
2225
|
+
};
|
|
2226
|
+
|
|
2227
|
+
// src/core/OIMBucketCarrierResolver.ts
|
|
2228
|
+
var OIMBucketCarrierResolver = class {
|
|
2229
|
+
constructor(store) {
|
|
2230
|
+
this.store = store;
|
|
2231
|
+
}
|
|
2232
|
+
getOrReserveCarrier(key) {
|
|
2233
|
+
return this.store.getOrReserveBucket(key);
|
|
2234
|
+
}
|
|
2235
|
+
findCarrier(key) {
|
|
2236
|
+
return this.store.findBucket(key);
|
|
2237
|
+
}
|
|
2238
|
+
onCarrierEmptied(carrier) {
|
|
2239
|
+
this.store.dropIfReserved(carrier.key);
|
|
2240
|
+
}
|
|
2070
2241
|
};
|
|
2071
2242
|
|
|
2072
2243
|
// src/core/OIMReactiveIndexManualArrayBased.ts
|
|
@@ -2075,6 +2246,10 @@ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
|
|
|
2075
2246
|
super(opts);
|
|
2076
2247
|
this.updateEmitter = updateEmitter;
|
|
2077
2248
|
}
|
|
2249
|
+
// Deliver straight off the bucket the write just touched — O(1).
|
|
2250
|
+
emitBucketChanged(bucket) {
|
|
2251
|
+
this.updateEmitter.markUpdatedCarrier(bucket);
|
|
2252
|
+
}
|
|
2078
2253
|
emitUpdate(keys) {
|
|
2079
2254
|
this.updateEmitter.markUpdatedKeys(keys);
|
|
2080
2255
|
}
|
|
@@ -2084,12 +2259,15 @@ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
|
|
|
2084
2259
|
};
|
|
2085
2260
|
var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
|
|
2086
2261
|
constructor(queue, opts) {
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2262
|
+
const store = opts?.indexOptions?.store ?? new OIMIndexStoreMapDrivenArrayBased();
|
|
2263
|
+
super(
|
|
2264
|
+
queue,
|
|
2265
|
+
(updateEmitter) => new OIMIndexManualArrayBasedReactive(updateEmitter, {
|
|
2266
|
+
comparePks: opts?.indexOptions?.comparePks,
|
|
2267
|
+
store
|
|
2268
|
+
}),
|
|
2269
|
+
() => new OIMBucketCarrierResolver(store)
|
|
2270
|
+
);
|
|
2093
2271
|
}
|
|
2094
2272
|
setSlots(key, slots) {
|
|
2095
2273
|
this.index.setSlots(key, slots);
|
|
@@ -2106,6 +2284,7 @@ var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased
|
|
|
2106
2284
|
var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexManualArrayBased {
|
|
2107
2285
|
constructor(queue, opts) {
|
|
2108
2286
|
const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
|
|
2287
|
+
const pkDomain = opts.collection?.keyDomain ?? new OIMKeyDomainNative();
|
|
2109
2288
|
super(queue, {
|
|
2110
2289
|
indexOptions: opts.indexOptions
|
|
2111
2290
|
});
|
|
@@ -2113,9 +2292,10 @@ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexM
|
|
|
2113
2292
|
// a Set from the whole bucket on every call). Kept in sync by setSlots/clear.
|
|
2114
2293
|
this.pksByKey = /* @__PURE__ */ new Map();
|
|
2115
2294
|
this.resolveSlot = resolveSlot;
|
|
2295
|
+
this.pkDomain = pkDomain;
|
|
2116
2296
|
}
|
|
2117
2297
|
setPks(key, pks) {
|
|
2118
|
-
const pkSet =
|
|
2298
|
+
const pkSet = this.pkDomain.createSet();
|
|
2119
2299
|
for (let i = 0; i < pks.length; i++) pkSet.add(pks[i]);
|
|
2120
2300
|
this.pksByKey.set(key, pkSet);
|
|
2121
2301
|
super.setSlots(key, this.resolveSlots(pks));
|
|
@@ -2158,7 +2338,7 @@ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexM
|
|
|
2158
2338
|
membershipOf(key) {
|
|
2159
2339
|
let pkSet = this.pksByKey.get(key);
|
|
2160
2340
|
if (!pkSet) {
|
|
2161
|
-
pkSet =
|
|
2341
|
+
pkSet = this.pkDomain.createSet();
|
|
2162
2342
|
const existing = this.index.getSlotsByKey(key);
|
|
2163
2343
|
for (let i = 0; i < existing.length; i++) {
|
|
2164
2344
|
pkSet.add(existing[i].pk);
|
|
@@ -2192,24 +2372,98 @@ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexM
|
|
|
2192
2372
|
var OIMIndexManualSetBased = class extends OIMIndexSetBased {
|
|
2193
2373
|
constructor(options = {}) {
|
|
2194
2374
|
super(options);
|
|
2375
|
+
this.pkDomain = options.pkDomain ?? new OIMKeyDomainNative();
|
|
2195
2376
|
}
|
|
2196
2377
|
setSlots(key, slots) {
|
|
2197
|
-
const
|
|
2198
|
-
|
|
2199
|
-
|
|
2378
|
+
const bucket = this.setStore.getOrReserveBucket(key);
|
|
2379
|
+
this.replaceBucketContents(bucket, slots);
|
|
2380
|
+
}
|
|
2381
|
+
// --- pk-oriented writes: resolve the bucket ONCE and use its own membership
|
|
2382
|
+
// (pk → slot) for dedup/removal, so a composite (trie) key is walked a single
|
|
2383
|
+
// time instead of once for the bucket and once for a separate membership map.
|
|
2384
|
+
setPks(key, pks, resolveSlot) {
|
|
2385
|
+
const bucket = this.setStore.getOrReserveBucket(key);
|
|
2386
|
+
const slots = [];
|
|
2387
|
+
for (let i = 0; i < pks.length; i++) slots.push(resolveSlot(pks[i]));
|
|
2388
|
+
this.replaceBucketContents(bucket, slots);
|
|
2389
|
+
const membership = bucket.membership ?? (bucket.membership = this.pkDomain.createMap());
|
|
2390
|
+
membership.clear();
|
|
2391
|
+
for (let i = 0; i < pks.length; i++) {
|
|
2392
|
+
if (!membership.has(pks[i])) membership.set(pks[i], slots[i]);
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
addPks(key, pks, resolveSlot) {
|
|
2396
|
+
if (pks.length === 0) return;
|
|
2397
|
+
const bucket = this.setStore.getOrReserveBucket(key);
|
|
2398
|
+
const membership = this.membershipOf(bucket);
|
|
2399
|
+
let changed = false;
|
|
2400
|
+
for (let i = 0; i < pks.length; i++) {
|
|
2401
|
+
const pk = pks[i];
|
|
2402
|
+
if (membership.has(pk)) continue;
|
|
2403
|
+
const slot = resolveSlot(pk);
|
|
2404
|
+
membership.set(pk, slot);
|
|
2405
|
+
if (!bucket.has(slot)) {
|
|
2406
|
+
bucket.add(slot);
|
|
2407
|
+
changed = true;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
if (!changed) return;
|
|
2411
|
+
this.setStore.retainBucket(bucket);
|
|
2412
|
+
this.emitBucketChanged(bucket);
|
|
2413
|
+
}
|
|
2414
|
+
removePks(key, pks) {
|
|
2415
|
+
if (pks.length === 0) return;
|
|
2416
|
+
const bucket = this.setStore.findBucket(key);
|
|
2417
|
+
if (!bucket || bucket.size === 0) return;
|
|
2418
|
+
const membership = this.membershipOf(bucket);
|
|
2419
|
+
let changed = false;
|
|
2420
|
+
for (let i = 0; i < pks.length; i++) {
|
|
2421
|
+
const slot = membership.get(pks[i]);
|
|
2422
|
+
if (slot) {
|
|
2423
|
+
membership.delete(pks[i]);
|
|
2424
|
+
if (bucket.delete(slot)) changed = true;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
if (!changed) return;
|
|
2428
|
+
if (bucket.size === 0) this.setStore.releaseBucket(bucket);
|
|
2429
|
+
this.emitBucketChanged(bucket);
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* The bucket's pk → slot membership, lazily created and seeded from the
|
|
2433
|
+
* bucket's current slots (so it stays correct even if slots were written
|
|
2434
|
+
* through the lower-level `setSlots` path).
|
|
2435
|
+
*/
|
|
2436
|
+
membershipOf(bucket) {
|
|
2437
|
+
let membership = bucket.membership;
|
|
2438
|
+
if (!membership) {
|
|
2439
|
+
membership = this.pkDomain.createMap();
|
|
2440
|
+
bucket.membership = membership;
|
|
2441
|
+
}
|
|
2442
|
+
if (membership.size === 0 && bucket.size > 0) {
|
|
2443
|
+
for (const slot of bucket) membership.set(slot.pk, slot);
|
|
2444
|
+
}
|
|
2445
|
+
return membership;
|
|
2446
|
+
}
|
|
2447
|
+
replaceBucketContents(bucket, slots) {
|
|
2448
|
+
if (this.comparePks) {
|
|
2449
|
+
const newSet = slots instanceof Set ? slots : new Set(slots);
|
|
2450
|
+
if (this.bucketMatchesComparator(bucket, newSet)) return;
|
|
2451
|
+
bucket.clear();
|
|
2452
|
+
for (const slot of newSet) bucket.add(slot);
|
|
2453
|
+
} else {
|
|
2454
|
+
bucket.clear();
|
|
2455
|
+
for (const slot of slots) bucket.add(slot);
|
|
2200
2456
|
}
|
|
2457
|
+
if (bucket.size > 0) this.setStore.retainBucket(bucket);
|
|
2458
|
+
else this.setStore.releaseBucket(bucket);
|
|
2459
|
+
this.emitBucketChanged(bucket);
|
|
2201
2460
|
}
|
|
2202
2461
|
/**
|
|
2203
|
-
* Adds slots to a key's bucket IN PLACE
|
|
2204
|
-
* emits once. O(added). Used by collection-bound indexes for fast `addPks`.
|
|
2462
|
+
* Adds slots to a key's bucket IN PLACE and emits once. O(added).
|
|
2205
2463
|
*/
|
|
2206
2464
|
addSlots(key, slots) {
|
|
2207
2465
|
if (slots.length === 0) return;
|
|
2208
|
-
|
|
2209
|
-
if (!bucket) {
|
|
2210
|
-
bucket = /* @__PURE__ */ new Set();
|
|
2211
|
-
this.store.setOneByKey(key, bucket);
|
|
2212
|
-
}
|
|
2466
|
+
const bucket = this.setStore.getOrReserveBucket(key);
|
|
2213
2467
|
let changed = false;
|
|
2214
2468
|
for (let i = 0; i < slots.length; i++) {
|
|
2215
2469
|
if (!bucket.has(slots[i])) {
|
|
@@ -2217,25 +2471,27 @@ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
|
|
|
2217
2471
|
changed = true;
|
|
2218
2472
|
}
|
|
2219
2473
|
}
|
|
2220
|
-
if (changed)
|
|
2474
|
+
if (!changed) return;
|
|
2475
|
+
this.setStore.retainBucket(bucket);
|
|
2476
|
+
this.emitBucketChanged(bucket);
|
|
2221
2477
|
}
|
|
2222
2478
|
/**
|
|
2223
2479
|
* Removes slots from a key's bucket IN PLACE (O(removed)) and emits once.
|
|
2224
2480
|
*/
|
|
2225
2481
|
removeSlots(key, slots) {
|
|
2226
2482
|
if (slots.length === 0) return;
|
|
2227
|
-
const bucket = this.
|
|
2483
|
+
const bucket = this.setStore.findBucket(key);
|
|
2228
2484
|
if (!bucket) return;
|
|
2229
2485
|
let changed = false;
|
|
2230
2486
|
for (let i = 0; i < slots.length; i++) {
|
|
2231
2487
|
if (bucket.delete(slots[i])) changed = true;
|
|
2232
2488
|
}
|
|
2233
2489
|
if (!changed) return;
|
|
2234
|
-
if (bucket.size === 0) this.
|
|
2235
|
-
this.
|
|
2490
|
+
if (bucket.size === 0) this.setStore.releaseBucket(bucket);
|
|
2491
|
+
this.emitBucketChanged(bucket);
|
|
2236
2492
|
}
|
|
2237
2493
|
/**
|
|
2238
|
-
* Clear all primary keys for a specific index key, or all keys if
|
|
2494
|
+
* Clear all primary keys for a specific index key, or all keys if none given.
|
|
2239
2495
|
*/
|
|
2240
2496
|
clear(key) {
|
|
2241
2497
|
if (key === void 0) {
|
|
@@ -2245,12 +2501,22 @@ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
|
|
|
2245
2501
|
this.emitUpdate(allKeys);
|
|
2246
2502
|
}
|
|
2247
2503
|
} else {
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2504
|
+
const bucket = this.setStore.findBucket(key);
|
|
2505
|
+
if (bucket && bucket.size > 0) {
|
|
2506
|
+
bucket.clear();
|
|
2507
|
+
this.setStore.releaseBucket(bucket);
|
|
2508
|
+
this.emitBucketChanged(bucket);
|
|
2251
2509
|
}
|
|
2252
2510
|
}
|
|
2253
2511
|
}
|
|
2512
|
+
/**
|
|
2513
|
+
* Emit that a bucket changed. Base (non-reactive) path emits by key on the
|
|
2514
|
+
* plain event emitter; the reactive subclass overrides this to mark the
|
|
2515
|
+
* bucket carrier directly (O(1), no key→carrier lookup).
|
|
2516
|
+
*/
|
|
2517
|
+
emitBucketChanged(bucket) {
|
|
2518
|
+
this.emitUpdateOne(bucket.key);
|
|
2519
|
+
}
|
|
2254
2520
|
};
|
|
2255
2521
|
|
|
2256
2522
|
// src/core/OIMReactiveIndexManualSetBased.ts
|
|
@@ -2259,6 +2525,12 @@ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
|
|
|
2259
2525
|
super(opts);
|
|
2260
2526
|
this.updateEmitter = updateEmitter;
|
|
2261
2527
|
}
|
|
2528
|
+
// Deliver straight off the bucket the write just touched — O(1), no
|
|
2529
|
+
// key→carrier lookup (the whole point of bucket-as-carrier).
|
|
2530
|
+
emitBucketChanged(bucket) {
|
|
2531
|
+
this.updateEmitter.markUpdatedCarrier(bucket);
|
|
2532
|
+
}
|
|
2533
|
+
// clear(all) still emits by key set (rare path).
|
|
2262
2534
|
emitUpdate(keys) {
|
|
2263
2535
|
this.updateEmitter.markUpdatedKeys(keys);
|
|
2264
2536
|
}
|
|
@@ -2268,12 +2540,16 @@ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
|
|
|
2268
2540
|
};
|
|
2269
2541
|
var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
|
|
2270
2542
|
constructor(queue, opts) {
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2543
|
+
const store = opts?.indexOptions?.store ?? new OIMIndexStoreMapDrivenSetBased();
|
|
2544
|
+
super(
|
|
2545
|
+
queue,
|
|
2546
|
+
(updateEmitter) => new OIMIndexManualSetBasedReactive(updateEmitter, {
|
|
2547
|
+
comparePks: opts?.indexOptions?.comparePks,
|
|
2548
|
+
store,
|
|
2549
|
+
pkDomain: opts?.pkDomain
|
|
2550
|
+
}),
|
|
2551
|
+
() => new OIMBucketCarrierResolver(store)
|
|
2552
|
+
);
|
|
2277
2553
|
}
|
|
2278
2554
|
setSlots(key, slots) {
|
|
2279
2555
|
this.index.setSlots(key, slots);
|
|
@@ -2294,71 +2570,26 @@ var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexMan
|
|
|
2294
2570
|
constructor(queue, opts) {
|
|
2295
2571
|
const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
|
|
2296
2572
|
super(queue, {
|
|
2297
|
-
indexOptions: opts.indexOptions
|
|
2573
|
+
indexOptions: opts.indexOptions,
|
|
2574
|
+
pkDomain: opts.collection?.keyDomain
|
|
2298
2575
|
});
|
|
2299
|
-
//
|
|
2300
|
-
//
|
|
2301
|
-
this.
|
|
2576
|
+
// Bound once so the hot pk-write path passes a stable function (no per-call
|
|
2577
|
+
// closure allocation).
|
|
2578
|
+
this.resolveRequiredSlot = (pk) => {
|
|
2579
|
+
const slot = this.resolveSlot(pk);
|
|
2580
|
+
if (!slot) return { pk, item: void 0 };
|
|
2581
|
+
return slot;
|
|
2582
|
+
};
|
|
2302
2583
|
this.resolveSlot = resolveSlot;
|
|
2303
2584
|
}
|
|
2304
2585
|
setPks(key, pks) {
|
|
2305
|
-
|
|
2306
|
-
const slots = [];
|
|
2307
|
-
for (let i = 0; i < pks.length; i++) {
|
|
2308
|
-
const pk = pks[i];
|
|
2309
|
-
const slot = this.resolveRequiredSlot(pk);
|
|
2310
|
-
if (!map.has(pk)) map.set(pk, slot);
|
|
2311
|
-
slots.push(slot);
|
|
2312
|
-
}
|
|
2313
|
-
this.slotByPk.set(key, map);
|
|
2314
|
-
super.setSlots(key, slots);
|
|
2586
|
+
this.index.setPks(key, pks, this.resolveRequiredSlot);
|
|
2315
2587
|
}
|
|
2316
2588
|
addPks(key, pks) {
|
|
2317
|
-
|
|
2318
|
-
const map = this.membershipOf(key);
|
|
2319
|
-
const newSlots = [];
|
|
2320
|
-
for (const pk of pks) {
|
|
2321
|
-
if (map.has(pk)) continue;
|
|
2322
|
-
const slot = this.resolveRequiredSlot(pk);
|
|
2323
|
-
map.set(pk, slot);
|
|
2324
|
-
newSlots.push(slot);
|
|
2325
|
-
}
|
|
2326
|
-
if (newSlots.length > 0) this.addSlots(key, newSlots);
|
|
2589
|
+
this.index.addPks(key, pks, this.resolveRequiredSlot);
|
|
2327
2590
|
}
|
|
2328
2591
|
removePks(key, pks) {
|
|
2329
|
-
|
|
2330
|
-
const map = this.membershipOf(key);
|
|
2331
|
-
if (map.size === 0) return;
|
|
2332
|
-
const removed = [];
|
|
2333
|
-
for (const pk of pks) {
|
|
2334
|
-
const slot = map.get(pk);
|
|
2335
|
-
if (slot) {
|
|
2336
|
-
map.delete(pk);
|
|
2337
|
-
removed.push(slot);
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
if (removed.length === 0) return;
|
|
2341
|
-
if (map.size === 0) this.slotByPk.delete(key);
|
|
2342
|
-
this.removeSlots(key, removed);
|
|
2343
|
-
}
|
|
2344
|
-
clear(key) {
|
|
2345
|
-
if (key === void 0) this.slotByPk.clear();
|
|
2346
|
-
else this.slotByPk.delete(key);
|
|
2347
|
-
super.clear(key);
|
|
2348
|
-
}
|
|
2349
|
-
/**
|
|
2350
|
-
* The pk→slot map for a key, lazily seeded from the current bucket so it
|
|
2351
|
-
* stays correct even if slots were set through a lower-level path.
|
|
2352
|
-
*/
|
|
2353
|
-
membershipOf(key) {
|
|
2354
|
-
let map = this.slotByPk.get(key);
|
|
2355
|
-
if (!map) {
|
|
2356
|
-
map = /* @__PURE__ */ new Map();
|
|
2357
|
-
const existing = this.index.getSlotsByKey(key);
|
|
2358
|
-
for (const slot of existing) map.set(slot.pk, slot);
|
|
2359
|
-
this.slotByPk.set(key, map);
|
|
2360
|
-
}
|
|
2361
|
-
return map;
|
|
2592
|
+
this.index.removePks(key, pks);
|
|
2362
2593
|
}
|
|
2363
2594
|
getEntitiesByKey(key) {
|
|
2364
2595
|
return super.getEntitiesByKey(key);
|
|
@@ -2366,50 +2597,459 @@ var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexMan
|
|
|
2366
2597
|
getEntitiesByKeys(keys) {
|
|
2367
2598
|
return super.getEntitiesByKeys(keys);
|
|
2368
2599
|
}
|
|
2369
|
-
resolveRequiredSlot(pk) {
|
|
2370
|
-
const slot = this.resolveSlot(pk);
|
|
2371
|
-
if (!slot) return { pk, item: void 0 };
|
|
2372
|
-
return slot;
|
|
2373
|
-
}
|
|
2374
2600
|
};
|
|
2375
2601
|
|
|
2376
|
-
// src/core/
|
|
2377
|
-
var
|
|
2378
|
-
constructor(
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
this.
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2602
|
+
// src/core/OIMTrieMap.ts
|
|
2603
|
+
var OIMTrieMap = class {
|
|
2604
|
+
constructor() {
|
|
2605
|
+
this.root = { hasValue: false };
|
|
2606
|
+
this._size = 0;
|
|
2607
|
+
}
|
|
2608
|
+
/** Number of stored paths (terminal nodes), not the number of trie nodes. */
|
|
2609
|
+
get size() {
|
|
2610
|
+
return this._size;
|
|
2611
|
+
}
|
|
2612
|
+
set(path, value) {
|
|
2613
|
+
let node = this.root;
|
|
2614
|
+
for (let i = 0; i < path.length; i++) {
|
|
2615
|
+
let children = node.children;
|
|
2616
|
+
if (!children) {
|
|
2617
|
+
children = /* @__PURE__ */ new Map();
|
|
2618
|
+
node.children = children;
|
|
2388
2619
|
}
|
|
2389
|
-
|
|
2390
|
-
|
|
2620
|
+
let next = children.get(path[i]);
|
|
2621
|
+
if (!next) {
|
|
2622
|
+
next = { hasValue: false };
|
|
2623
|
+
children.set(path[i], next);
|
|
2391
2624
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2625
|
+
node = next;
|
|
2626
|
+
}
|
|
2627
|
+
if (!node.hasValue) {
|
|
2628
|
+
node.hasValue = true;
|
|
2629
|
+
this._size++;
|
|
2630
|
+
}
|
|
2631
|
+
node.value = value;
|
|
2632
|
+
}
|
|
2633
|
+
get(path) {
|
|
2634
|
+
const node = this.findNode(path);
|
|
2635
|
+
return node && node.hasValue ? node.value : void 0;
|
|
2636
|
+
}
|
|
2637
|
+
has(path) {
|
|
2638
|
+
const node = this.findNode(path);
|
|
2639
|
+
return !!node && node.hasValue;
|
|
2640
|
+
}
|
|
2641
|
+
delete(path) {
|
|
2642
|
+
const parents = [];
|
|
2643
|
+
let node = this.root;
|
|
2644
|
+
for (let i = 0; i < path.length; i++) {
|
|
2645
|
+
const next = node.children?.get(path[i]);
|
|
2646
|
+
if (!next) return false;
|
|
2647
|
+
parents.push(node);
|
|
2648
|
+
node = next;
|
|
2649
|
+
}
|
|
2650
|
+
if (!node.hasValue) return false;
|
|
2651
|
+
node.hasValue = false;
|
|
2652
|
+
node.value = void 0;
|
|
2653
|
+
this._size--;
|
|
2654
|
+
let child = node;
|
|
2655
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
2656
|
+
if (child.hasValue || child.children && child.children.size > 0) {
|
|
2657
|
+
break;
|
|
2658
|
+
}
|
|
2659
|
+
const parent = parents[i];
|
|
2660
|
+
parent.children.delete(path[i]);
|
|
2661
|
+
if (parent.children.size === 0) parent.children = void 0;
|
|
2662
|
+
child = parent;
|
|
2401
2663
|
}
|
|
2664
|
+
return true;
|
|
2402
2665
|
}
|
|
2403
|
-
|
|
2404
|
-
this.
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2666
|
+
clear() {
|
|
2667
|
+
this.root.children = void 0;
|
|
2668
|
+
this.root.value = void 0;
|
|
2669
|
+
this.root.hasValue = false;
|
|
2670
|
+
this._size = 0;
|
|
2671
|
+
}
|
|
2672
|
+
/** Depth-first iteration over `[path, value]` for every stored path. */
|
|
2673
|
+
*entries() {
|
|
2674
|
+
const stack = [{ node: this.root, path: [] }];
|
|
2675
|
+
while (stack.length > 0) {
|
|
2676
|
+
const frame = stack.pop();
|
|
2677
|
+
const { node, path } = frame;
|
|
2678
|
+
if (node.hasValue) {
|
|
2679
|
+
yield [path, node.value];
|
|
2680
|
+
}
|
|
2681
|
+
if (node.children) {
|
|
2682
|
+
for (const [segment, child] of node.children) {
|
|
2683
|
+
stack.push({ node: child, path: [...path, segment] });
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
*keys() {
|
|
2689
|
+
for (const [path] of this.entries()) yield path;
|
|
2690
|
+
}
|
|
2691
|
+
*values() {
|
|
2692
|
+
for (const [, value] of this.entries()) yield value;
|
|
2693
|
+
}
|
|
2694
|
+
findNode(path) {
|
|
2695
|
+
let node = this.root;
|
|
2696
|
+
for (let i = 0; i < path.length; i++) {
|
|
2697
|
+
node = node.children?.get(path[i]);
|
|
2698
|
+
if (!node) return void 0;
|
|
2699
|
+
}
|
|
2700
|
+
return node;
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2703
|
+
|
|
2704
|
+
// src/core/OIMIndexStoreTrieDrivenSetBased.ts
|
|
2705
|
+
var OIMIndexStoreTrieDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
2706
|
+
constructor() {
|
|
2707
|
+
super(...arguments);
|
|
2708
|
+
this.slots = new OIMTrieMap();
|
|
2709
|
+
this.reservedBuckets = new OIMTrieMap();
|
|
2710
|
+
}
|
|
2711
|
+
getOrReserveBucket(key) {
|
|
2712
|
+
const live = this.slots.get(key);
|
|
2713
|
+
if (live) return live;
|
|
2714
|
+
let reserved = this.reservedBuckets.get(key);
|
|
2715
|
+
if (!reserved) {
|
|
2716
|
+
reserved = new OIMKeyedBucketSetBased(key);
|
|
2717
|
+
this.reservedBuckets.set(key, reserved);
|
|
2718
|
+
}
|
|
2719
|
+
return reserved;
|
|
2720
|
+
}
|
|
2721
|
+
findBucket(key) {
|
|
2722
|
+
return this.slots.get(key) ?? this.reservedBuckets.get(key);
|
|
2723
|
+
}
|
|
2724
|
+
retainBucket(bucket) {
|
|
2725
|
+
if (bucket.size === 0) return;
|
|
2726
|
+
this.reservedBuckets.delete(bucket.key);
|
|
2727
|
+
if (!this.slots.has(bucket.key)) this.slots.set(bucket.key, bucket);
|
|
2728
|
+
}
|
|
2729
|
+
releaseBucket(bucket) {
|
|
2730
|
+
if (bucket.size > 0) return;
|
|
2731
|
+
this.slots.delete(bucket.key);
|
|
2732
|
+
if (bucket.hasSubscribers()) {
|
|
2733
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
2734
|
+
} else {
|
|
2735
|
+
this.reservedBuckets.delete(bucket.key);
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
dropIfReserved(key) {
|
|
2739
|
+
const reserved = this.reservedBuckets.get(key);
|
|
2740
|
+
if (reserved && reserved.size === 0 && !reserved.hasSubscribers()) {
|
|
2741
|
+
this.reservedBuckets.delete(key);
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
setOneByKey(key, slots) {
|
|
2745
|
+
const bucket = this.getOrReserveBucket(key);
|
|
2746
|
+
if (bucket !== slots) {
|
|
2747
|
+
bucket.clear();
|
|
2748
|
+
for (const slot of slots) bucket.add(slot);
|
|
2749
|
+
}
|
|
2750
|
+
if (bucket.size > 0) this.retainBucket(bucket);
|
|
2751
|
+
else this.releaseBucket(bucket);
|
|
2752
|
+
}
|
|
2753
|
+
removeOneByKey(key) {
|
|
2754
|
+
this.slots.delete(key);
|
|
2755
|
+
this.reservedBuckets.delete(key);
|
|
2756
|
+
}
|
|
2757
|
+
removeManyByKeys(keys) {
|
|
2758
|
+
for (let i = 0; i < keys.length; i++) {
|
|
2759
|
+
this.slots.delete(keys[i]);
|
|
2760
|
+
this.reservedBuckets.delete(keys[i]);
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
getOneByKey(key) {
|
|
2764
|
+
return this.slots.get(key);
|
|
2765
|
+
}
|
|
2766
|
+
getManyByKeys(keys) {
|
|
2767
|
+
const result = /* @__PURE__ */ new Map();
|
|
2768
|
+
for (let i = 0; i < keys.length; i++) {
|
|
2769
|
+
const slots = this.slots.get(keys[i]);
|
|
2770
|
+
if (slots !== void 0) result.set(keys[i], slots);
|
|
2771
|
+
}
|
|
2772
|
+
return result;
|
|
2773
|
+
}
|
|
2774
|
+
getAllKeys() {
|
|
2775
|
+
return Array.from(this.slots.keys());
|
|
2776
|
+
}
|
|
2777
|
+
getAll() {
|
|
2778
|
+
const result = /* @__PURE__ */ new Map();
|
|
2779
|
+
for (const [path, bucket] of this.slots.entries()) {
|
|
2780
|
+
result.set(path, bucket);
|
|
2781
|
+
}
|
|
2782
|
+
return result;
|
|
2783
|
+
}
|
|
2784
|
+
countAll() {
|
|
2785
|
+
return this.slots.size;
|
|
2786
|
+
}
|
|
2787
|
+
clear() {
|
|
2788
|
+
for (const bucket of this.slots.values()) {
|
|
2789
|
+
bucket.clear();
|
|
2790
|
+
if (bucket.hasSubscribers()) {
|
|
2791
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
this.slots.clear();
|
|
2795
|
+
}
|
|
2796
|
+
};
|
|
2797
|
+
|
|
2798
|
+
// src/core/OIMReactiveCollectionIndexCompositeTrieSetBased.ts
|
|
2799
|
+
var OIMReactiveCollectionIndexCompositeTrieSetBased = class extends OIMReactiveIndexManualSetBased {
|
|
2800
|
+
constructor(queue, opts) {
|
|
2801
|
+
const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
|
|
2802
|
+
super(queue, {
|
|
2803
|
+
indexOptions: {
|
|
2804
|
+
comparePks: opts.indexOptions?.comparePks,
|
|
2805
|
+
store: opts.indexOptions?.store ?? new OIMIndexStoreTrieDrivenSetBased()
|
|
2806
|
+
},
|
|
2807
|
+
pkDomain: opts.collection?.keyDomain
|
|
2808
|
+
});
|
|
2809
|
+
this.resolveRequiredSlot = (pk) => {
|
|
2810
|
+
const slot = this.resolveSlot(pk);
|
|
2811
|
+
if (!slot) return { pk, item: void 0 };
|
|
2812
|
+
return slot;
|
|
2813
|
+
};
|
|
2814
|
+
this.resolveSlot = resolveSlot;
|
|
2815
|
+
}
|
|
2816
|
+
setPks(key, pks) {
|
|
2817
|
+
this.index.setPks(key, pks, this.resolveRequiredSlot);
|
|
2818
|
+
}
|
|
2819
|
+
addPks(key, pks) {
|
|
2820
|
+
this.index.addPks(key, pks, this.resolveRequiredSlot);
|
|
2821
|
+
}
|
|
2822
|
+
removePks(key, pks) {
|
|
2823
|
+
this.index.removePks(key, pks);
|
|
2824
|
+
}
|
|
2825
|
+
getEntitiesByKey(key) {
|
|
2826
|
+
return super.getEntitiesByKey(key);
|
|
2827
|
+
}
|
|
2828
|
+
getEntitiesByKeys(keys) {
|
|
2829
|
+
return super.getEntitiesByKeys(keys);
|
|
2830
|
+
}
|
|
2831
|
+
};
|
|
2832
|
+
|
|
2833
|
+
// src/core/OIMIndexStoreTrieDrivenArrayBased.ts
|
|
2834
|
+
var OIMIndexStoreTrieDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
2835
|
+
constructor() {
|
|
2836
|
+
super(...arguments);
|
|
2837
|
+
this.slots = new OIMTrieMap();
|
|
2838
|
+
this.reservedBuckets = new OIMTrieMap();
|
|
2839
|
+
}
|
|
2840
|
+
getOrReserveBucket(key) {
|
|
2841
|
+
const live = this.slots.get(key);
|
|
2842
|
+
if (live) return live;
|
|
2843
|
+
let reserved = this.reservedBuckets.get(key);
|
|
2844
|
+
if (!reserved) {
|
|
2845
|
+
reserved = new OIMKeyedBucketArrayBased(key);
|
|
2846
|
+
this.reservedBuckets.set(key, reserved);
|
|
2847
|
+
}
|
|
2848
|
+
return reserved;
|
|
2849
|
+
}
|
|
2850
|
+
findBucket(key) {
|
|
2851
|
+
return this.slots.get(key) ?? this.reservedBuckets.get(key);
|
|
2852
|
+
}
|
|
2853
|
+
retainBucket(bucket) {
|
|
2854
|
+
if (bucket.length === 0) return;
|
|
2855
|
+
this.reservedBuckets.delete(bucket.key);
|
|
2856
|
+
if (!this.slots.has(bucket.key)) this.slots.set(bucket.key, bucket);
|
|
2857
|
+
}
|
|
2858
|
+
releaseBucket(bucket) {
|
|
2859
|
+
if (bucket.length > 0) return;
|
|
2860
|
+
this.slots.delete(bucket.key);
|
|
2861
|
+
if (bucket.hasSubscribers()) {
|
|
2862
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
2863
|
+
} else {
|
|
2864
|
+
this.reservedBuckets.delete(bucket.key);
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
dropIfReserved(key) {
|
|
2868
|
+
const reserved = this.reservedBuckets.get(key);
|
|
2869
|
+
if (reserved && reserved.length === 0 && !reserved.hasSubscribers()) {
|
|
2870
|
+
this.reservedBuckets.delete(key);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
setOneByKey(key, slots) {
|
|
2874
|
+
const bucket = this.getOrReserveBucket(key);
|
|
2875
|
+
if (bucket !== slots) {
|
|
2876
|
+
bucket.length = 0;
|
|
2877
|
+
for (let i = 0; i < slots.length; i++) bucket.push(slots[i]);
|
|
2878
|
+
}
|
|
2879
|
+
if (bucket.length > 0) this.retainBucket(bucket);
|
|
2880
|
+
else this.releaseBucket(bucket);
|
|
2881
|
+
}
|
|
2882
|
+
removeOneByKey(key) {
|
|
2883
|
+
this.slots.delete(key);
|
|
2884
|
+
this.reservedBuckets.delete(key);
|
|
2885
|
+
}
|
|
2886
|
+
removeManyByKeys(keys) {
|
|
2887
|
+
for (let i = 0; i < keys.length; i++) {
|
|
2888
|
+
this.slots.delete(keys[i]);
|
|
2889
|
+
this.reservedBuckets.delete(keys[i]);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
getOneByKey(key) {
|
|
2893
|
+
return this.slots.get(key);
|
|
2894
|
+
}
|
|
2895
|
+
getManyByKeys(keys) {
|
|
2896
|
+
const result = /* @__PURE__ */ new Map();
|
|
2897
|
+
for (let i = 0; i < keys.length; i++) {
|
|
2898
|
+
const slots = this.slots.get(keys[i]);
|
|
2899
|
+
if (slots !== void 0) result.set(keys[i], slots);
|
|
2900
|
+
}
|
|
2901
|
+
return result;
|
|
2902
|
+
}
|
|
2903
|
+
getAllKeys() {
|
|
2904
|
+
return Array.from(this.slots.keys());
|
|
2905
|
+
}
|
|
2906
|
+
getAll() {
|
|
2907
|
+
const result = /* @__PURE__ */ new Map();
|
|
2908
|
+
for (const [path, bucket] of this.slots.entries()) {
|
|
2909
|
+
result.set(path, bucket);
|
|
2910
|
+
}
|
|
2911
|
+
return result;
|
|
2912
|
+
}
|
|
2913
|
+
countAll() {
|
|
2914
|
+
return this.slots.size;
|
|
2915
|
+
}
|
|
2916
|
+
clear() {
|
|
2917
|
+
for (const bucket of this.slots.values()) {
|
|
2918
|
+
bucket.length = 0;
|
|
2919
|
+
if (bucket.hasSubscribers()) {
|
|
2920
|
+
this.reservedBuckets.set(bucket.key, bucket);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
this.slots.clear();
|
|
2924
|
+
}
|
|
2925
|
+
};
|
|
2926
|
+
|
|
2927
|
+
// src/core/OIMReactiveCollectionIndexCompositeTrieArrayBased.ts
|
|
2928
|
+
var OIMReactiveCollectionIndexCompositeTrieArrayBased = class extends OIMReactiveIndexManualArrayBased {
|
|
2929
|
+
constructor(queue, opts) {
|
|
2930
|
+
const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
|
|
2931
|
+
super(queue, {
|
|
2932
|
+
indexOptions: {
|
|
2933
|
+
comparePks: opts.indexOptions?.comparePks,
|
|
2934
|
+
store: opts.indexOptions?.store ?? new OIMIndexStoreTrieDrivenArrayBased()
|
|
2935
|
+
}
|
|
2936
|
+
});
|
|
2937
|
+
// Persistent per-key-path pk membership for O(1) `addPks` dedup. Trie-keyed
|
|
2938
|
+
// so a rebuilt key path matches by content.
|
|
2939
|
+
this.pksByKey = new OIMTrieMap();
|
|
2940
|
+
this.resolveSlot = resolveSlot;
|
|
2941
|
+
}
|
|
2942
|
+
setPks(key, pks) {
|
|
2943
|
+
const pkSet = /* @__PURE__ */ new Set();
|
|
2944
|
+
for (let i = 0; i < pks.length; i++) pkSet.add(pks[i]);
|
|
2945
|
+
this.pksByKey.set(key, pkSet);
|
|
2946
|
+
super.setSlots(key, this.resolveSlots(pks));
|
|
2947
|
+
}
|
|
2948
|
+
addPks(key, pks) {
|
|
2949
|
+
if (pks.length === 0) return;
|
|
2950
|
+
const pkSet = this.membershipOf(key);
|
|
2951
|
+
const newSlots = [];
|
|
2952
|
+
for (const pk of pks) {
|
|
2953
|
+
if (pkSet.has(pk)) continue;
|
|
2954
|
+
pkSet.add(pk);
|
|
2955
|
+
newSlots.push(this.resolveRequiredSlot(pk));
|
|
2956
|
+
}
|
|
2957
|
+
if (newSlots.length > 0) this.appendSlots(key, newSlots);
|
|
2958
|
+
}
|
|
2959
|
+
removePks(key, pks) {
|
|
2960
|
+
if (pks.length === 0) return;
|
|
2961
|
+
const pksToRemove = new Set(pks);
|
|
2962
|
+
const existingSlots = this.index.getSlotsByKey(key);
|
|
2963
|
+
const nextSlots = existingSlots.filter(
|
|
2964
|
+
(slot) => !pksToRemove.has(slot.pk)
|
|
2965
|
+
);
|
|
2966
|
+
if (nextSlots.length === existingSlots.length) return;
|
|
2967
|
+
const pkSet = this.pksByKey.get(key);
|
|
2968
|
+
if (pkSet) {
|
|
2969
|
+
for (let i = 0; i < pks.length; i++) pkSet.delete(pks[i]);
|
|
2970
|
+
}
|
|
2971
|
+
if (nextSlots.length === 0) this.clear(key);
|
|
2972
|
+
else super.setSlots(key, nextSlots);
|
|
2973
|
+
}
|
|
2974
|
+
clear(key) {
|
|
2975
|
+
if (key === void 0) this.pksByKey.clear();
|
|
2976
|
+
else this.pksByKey.delete(key);
|
|
2977
|
+
super.clear(key);
|
|
2978
|
+
}
|
|
2979
|
+
/**
|
|
2980
|
+
* The membership set for a key path, lazily seeded from the current bucket so
|
|
2981
|
+
* it stays correct even if slots were set through a lower-level path.
|
|
2982
|
+
*/
|
|
2983
|
+
membershipOf(key) {
|
|
2984
|
+
let pkSet = this.pksByKey.get(key);
|
|
2985
|
+
if (!pkSet) {
|
|
2986
|
+
pkSet = /* @__PURE__ */ new Set();
|
|
2987
|
+
const existing = this.index.getSlotsByKey(key);
|
|
2988
|
+
for (let i = 0; i < existing.length; i++) {
|
|
2989
|
+
pkSet.add(existing[i].pk);
|
|
2990
|
+
}
|
|
2991
|
+
this.pksByKey.set(key, pkSet);
|
|
2992
|
+
}
|
|
2993
|
+
return pkSet;
|
|
2994
|
+
}
|
|
2995
|
+
getEntitiesByKey(key) {
|
|
2996
|
+
return super.getEntitiesByKey(key);
|
|
2997
|
+
}
|
|
2998
|
+
getEntitiesByKeys(keys) {
|
|
2999
|
+
return super.getEntitiesByKeys(keys);
|
|
3000
|
+
}
|
|
3001
|
+
resolveSlots(pks) {
|
|
3002
|
+
const slots = [];
|
|
3003
|
+
slots.length = pks.length;
|
|
3004
|
+
for (let i = 0; i < pks.length; i++) {
|
|
3005
|
+
slots[i] = this.resolveRequiredSlot(pks[i]);
|
|
3006
|
+
}
|
|
3007
|
+
return slots;
|
|
3008
|
+
}
|
|
3009
|
+
resolveRequiredSlot(pk) {
|
|
3010
|
+
const slot = this.resolveSlot(pk);
|
|
3011
|
+
if (!slot) return { pk, item: void 0 };
|
|
3012
|
+
return slot;
|
|
3013
|
+
}
|
|
3014
|
+
};
|
|
3015
|
+
|
|
3016
|
+
// src/core/OIMDerivedCollectionIndexSetBased.ts
|
|
3017
|
+
var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndexManualSetBased {
|
|
3018
|
+
constructor(queue, collection, opts) {
|
|
3019
|
+
super(queue, {
|
|
3020
|
+
collection,
|
|
3021
|
+
indexOptions: opts.indexOptions
|
|
3022
|
+
});
|
|
3023
|
+
this.keysByPk = /* @__PURE__ */ new Map();
|
|
3024
|
+
this.onCollectionUpdate = (pks) => {
|
|
3025
|
+
if (pks.length === 0) {
|
|
3026
|
+
this.rebuildFromCollection();
|
|
3027
|
+
return;
|
|
3028
|
+
}
|
|
3029
|
+
for (const pk of pks) {
|
|
3030
|
+
this.syncPk(pk);
|
|
3031
|
+
}
|
|
3032
|
+
};
|
|
3033
|
+
this.collection = collection;
|
|
3034
|
+
this.selectIndexKeys = opts.selectIndexKeys;
|
|
3035
|
+
this.unsubscribeFromCollection = collection.emitter.on(
|
|
3036
|
+
0 /* UPDATE */,
|
|
3037
|
+
(payload) => this.onCollectionUpdate(payload.pks)
|
|
3038
|
+
);
|
|
3039
|
+
if (opts.buildInitial !== false) {
|
|
3040
|
+
this.rebuildFromCollection();
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
rebuildFromCollection() {
|
|
3044
|
+
this.keysByPk.clear();
|
|
3045
|
+
super.clear();
|
|
3046
|
+
const slotsByKey = /* @__PURE__ */ new Map();
|
|
3047
|
+
const slots = this.collection.getAllSlots();
|
|
3048
|
+
for (const slot of slots) {
|
|
3049
|
+
if (slot.item === void 0) continue;
|
|
3050
|
+
const keys = this.normalizeKeys(this.selectIndexKeys(slot.item));
|
|
3051
|
+
if (keys.length === 0) continue;
|
|
3052
|
+
this.keysByPk.set(slot.pk, new Set(keys));
|
|
2413
3053
|
for (const key of keys) {
|
|
2414
3054
|
let slotsForKey = slotsByKey.get(key);
|
|
2415
3055
|
if (!slotsForKey) {
|
|
@@ -3071,6 +3711,30 @@ var OIMCollectionIndexFactory = class {
|
|
|
3071
3711
|
indexOptions: opts.indexOptions
|
|
3072
3712
|
});
|
|
3073
3713
|
}
|
|
3714
|
+
/**
|
|
3715
|
+
* Composite (trie-backed) manual Set-based index keyed by an
|
|
3716
|
+
* arbitrary-length key path, e.g. `setPks([userId, projectId, role], pks)`.
|
|
3717
|
+
* Key paths are matched by content in O(arity) — no string concatenation,
|
|
3718
|
+
* no separator collisions, each segment keeps its type. Primitive-keyed
|
|
3719
|
+
* indexes are unaffected and keep their native-`Map` fast path.
|
|
3720
|
+
*/
|
|
3721
|
+
compositeSetIndex(opts = {}) {
|
|
3722
|
+
return new OIMReactiveCollectionIndexCompositeTrieSetBased(this.queue, {
|
|
3723
|
+
collection: this.collection,
|
|
3724
|
+
indexOptions: opts.indexOptions
|
|
3725
|
+
});
|
|
3726
|
+
}
|
|
3727
|
+
/**
|
|
3728
|
+
* Composite (trie-backed) manual Array-based (ordered) index keyed by an
|
|
3729
|
+
* arbitrary-length key path, e.g. `setPks([channelId, threadId], pks)`.
|
|
3730
|
+
* Ordered counterpart of `compositeSetIndex`.
|
|
3731
|
+
*/
|
|
3732
|
+
compositeArrayIndex(opts = {}) {
|
|
3733
|
+
return new OIMReactiveCollectionIndexCompositeTrieArrayBased(this.queue, {
|
|
3734
|
+
collection: this.collection,
|
|
3735
|
+
indexOptions: opts.indexOptions
|
|
3736
|
+
});
|
|
3737
|
+
}
|
|
3074
3738
|
derivedSetIndex(selectIndexKeys, opts = {}) {
|
|
3075
3739
|
return new OIMDerivedCollectionIndexSetBased(
|
|
3076
3740
|
this.queue,
|
|
@@ -3319,6 +3983,195 @@ var OIMReactiveObject = class extends OIMObject {
|
|
|
3319
3983
|
}
|
|
3320
3984
|
};
|
|
3321
3985
|
|
|
3986
|
+
// src/core/OIMTrieSet.ts
|
|
3987
|
+
var OIMTrieSet = class {
|
|
3988
|
+
constructor() {
|
|
3989
|
+
this.map = new OIMTrieMap();
|
|
3990
|
+
}
|
|
3991
|
+
get size() {
|
|
3992
|
+
return this.map.size;
|
|
3993
|
+
}
|
|
3994
|
+
add(key) {
|
|
3995
|
+
this.map.set(key, true);
|
|
3996
|
+
}
|
|
3997
|
+
has(key) {
|
|
3998
|
+
return this.map.has(key);
|
|
3999
|
+
}
|
|
4000
|
+
delete(key) {
|
|
4001
|
+
return this.map.delete(key);
|
|
4002
|
+
}
|
|
4003
|
+
*values() {
|
|
4004
|
+
yield* this.map.keys();
|
|
4005
|
+
}
|
|
4006
|
+
clear() {
|
|
4007
|
+
this.map.clear();
|
|
4008
|
+
}
|
|
4009
|
+
};
|
|
4010
|
+
|
|
4011
|
+
// src/core/OIMKeyDomainTrie.ts
|
|
4012
|
+
var OIMKeyDomainTrie = class {
|
|
4013
|
+
constructor() {
|
|
4014
|
+
// Interning pool: logical key (by content) → the one canonical reference.
|
|
4015
|
+
this.pool = new OIMTrieMap();
|
|
4016
|
+
}
|
|
4017
|
+
createMap() {
|
|
4018
|
+
return new OIMTrieMap();
|
|
4019
|
+
}
|
|
4020
|
+
createSet() {
|
|
4021
|
+
return new OIMTrieSet();
|
|
4022
|
+
}
|
|
4023
|
+
canonicalize(key) {
|
|
4024
|
+
const existing = this.pool.get(key);
|
|
4025
|
+
if (existing !== void 0) return existing;
|
|
4026
|
+
const canonical = Object.freeze(key.slice());
|
|
4027
|
+
this.pool.set(canonical, canonical);
|
|
4028
|
+
return canonical;
|
|
4029
|
+
}
|
|
4030
|
+
};
|
|
4031
|
+
|
|
4032
|
+
// src/core/OIMCollectionStoreTrieDriven.ts
|
|
4033
|
+
var OIMCollectionStoreTrieDriven = class extends OIMCollectionStore {
|
|
4034
|
+
constructor(keyDomain = new OIMKeyDomainTrie()) {
|
|
4035
|
+
super();
|
|
4036
|
+
this.keyDomain = keyDomain;
|
|
4037
|
+
this.slots = new OIMTrieMap();
|
|
4038
|
+
this.reservedSlots = new OIMTrieMap();
|
|
4039
|
+
}
|
|
4040
|
+
setOneByPk(pk, entity) {
|
|
4041
|
+
const slot = this.slots.get(pk);
|
|
4042
|
+
if (slot) {
|
|
4043
|
+
slot.item = entity;
|
|
4044
|
+
return slot;
|
|
4045
|
+
}
|
|
4046
|
+
const reserved = this.reservedSlots.get(pk);
|
|
4047
|
+
if (reserved) {
|
|
4048
|
+
reserved.item = entity;
|
|
4049
|
+
this.reservedSlots.delete(pk);
|
|
4050
|
+
this.slots.set(pk, reserved);
|
|
4051
|
+
return reserved;
|
|
4052
|
+
}
|
|
4053
|
+
const nextSlot = {
|
|
4054
|
+
pk: this.keyDomain.canonicalize(pk),
|
|
4055
|
+
item: entity
|
|
4056
|
+
};
|
|
4057
|
+
this.slots.set(pk, nextSlot);
|
|
4058
|
+
return nextSlot;
|
|
4059
|
+
}
|
|
4060
|
+
getOrReserveSlotByPk(pk) {
|
|
4061
|
+
const slot = this.slots.get(pk);
|
|
4062
|
+
if (slot) return slot;
|
|
4063
|
+
let reserved = this.reservedSlots.get(pk);
|
|
4064
|
+
if (!reserved) {
|
|
4065
|
+
reserved = { pk: this.keyDomain.canonicalize(pk), item: void 0 };
|
|
4066
|
+
this.reservedSlots.set(pk, reserved);
|
|
4067
|
+
}
|
|
4068
|
+
return reserved;
|
|
4069
|
+
}
|
|
4070
|
+
setManyByPks(pks, entities) {
|
|
4071
|
+
const slots = [];
|
|
4072
|
+
slots.length = pks.length;
|
|
4073
|
+
for (let i = 0; i < pks.length; i++) {
|
|
4074
|
+
slots[i] = this.setOneByPk(pks[i], entities[i]);
|
|
4075
|
+
}
|
|
4076
|
+
return slots;
|
|
4077
|
+
}
|
|
4078
|
+
getSlotByPk(pk) {
|
|
4079
|
+
return this.slots.get(pk);
|
|
4080
|
+
}
|
|
4081
|
+
findSlotByPk(pk) {
|
|
4082
|
+
return this.slots.get(pk) ?? this.reservedSlots.get(pk);
|
|
4083
|
+
}
|
|
4084
|
+
hasSubscribers(slot) {
|
|
4085
|
+
return slot.subscribers !== void 0 && slot.subscribers.size > 0;
|
|
4086
|
+
}
|
|
4087
|
+
getSlotsByPks(pks) {
|
|
4088
|
+
const result = [];
|
|
4089
|
+
result.length = pks.length;
|
|
4090
|
+
let writeIndex = 0;
|
|
4091
|
+
for (let i = 0; i < pks.length; i++) {
|
|
4092
|
+
const slot = this.slots.get(pks[i]);
|
|
4093
|
+
if (slot !== void 0) result[writeIndex++] = slot;
|
|
4094
|
+
}
|
|
4095
|
+
result.length = writeIndex;
|
|
4096
|
+
return result;
|
|
4097
|
+
}
|
|
4098
|
+
getAllSlots() {
|
|
4099
|
+
return Array.from(this.slots.values());
|
|
4100
|
+
}
|
|
4101
|
+
removeOneByPk(pk) {
|
|
4102
|
+
const slot = this.slots.get(pk);
|
|
4103
|
+
if (slot) {
|
|
4104
|
+
slot.item = void 0;
|
|
4105
|
+
this.slots.delete(pk);
|
|
4106
|
+
if (this.hasSubscribers(slot)) {
|
|
4107
|
+
this.reservedSlots.set(pk, slot);
|
|
4108
|
+
}
|
|
4109
|
+
return;
|
|
4110
|
+
}
|
|
4111
|
+
const reserved = this.reservedSlots.get(pk);
|
|
4112
|
+
if (reserved) {
|
|
4113
|
+
reserved.item = void 0;
|
|
4114
|
+
if (!this.hasSubscribers(reserved)) {
|
|
4115
|
+
this.reservedSlots.delete(pk);
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
removeManyByPks(pks) {
|
|
4120
|
+
for (const pk of pks) this.removeOneByPk(pk);
|
|
4121
|
+
}
|
|
4122
|
+
getOneByPk(pk) {
|
|
4123
|
+
return this.slots.get(pk)?.item;
|
|
4124
|
+
}
|
|
4125
|
+
getManyByPks(pks) {
|
|
4126
|
+
const result = [];
|
|
4127
|
+
result.length = pks.length;
|
|
4128
|
+
let writeIndex = 0;
|
|
4129
|
+
for (let i = 0; i < pks.length; i++) {
|
|
4130
|
+
const entity = this.getOneByPk(pks[i]);
|
|
4131
|
+
if (entity !== void 0) result[writeIndex++] = entity;
|
|
4132
|
+
}
|
|
4133
|
+
result.length = writeIndex;
|
|
4134
|
+
return result;
|
|
4135
|
+
}
|
|
4136
|
+
getAll() {
|
|
4137
|
+
const result = [];
|
|
4138
|
+
for (const slot of this.slots.values()) {
|
|
4139
|
+
if (slot.item !== void 0) result.push(slot.item);
|
|
4140
|
+
}
|
|
4141
|
+
return result;
|
|
4142
|
+
}
|
|
4143
|
+
countAll() {
|
|
4144
|
+
return this.slots.size;
|
|
4145
|
+
}
|
|
4146
|
+
clear() {
|
|
4147
|
+
const retained = [];
|
|
4148
|
+
for (const slot of this.slots.values()) {
|
|
4149
|
+
slot.item = void 0;
|
|
4150
|
+
if (this.hasSubscribers(slot)) retained.push(slot);
|
|
4151
|
+
}
|
|
4152
|
+
this.slots.clear();
|
|
4153
|
+
for (const slot of this.reservedSlots.values()) {
|
|
4154
|
+
slot.item = void 0;
|
|
4155
|
+
if (this.hasSubscribers(slot)) retained.push(slot);
|
|
4156
|
+
}
|
|
4157
|
+
this.reservedSlots.clear();
|
|
4158
|
+
for (const slot of retained) {
|
|
4159
|
+
this.reservedSlots.set(slot.pk, slot);
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
getAllPks() {
|
|
4163
|
+
return Array.from(this.slots.keys());
|
|
4164
|
+
}
|
|
4165
|
+
destroy() {
|
|
4166
|
+
this.clear();
|
|
4167
|
+
for (const slot of this.reservedSlots.values()) {
|
|
4168
|
+
slot.subscribers?.clear();
|
|
4169
|
+
slot.subscribers = void 0;
|
|
4170
|
+
}
|
|
4171
|
+
this.reservedSlots.clear();
|
|
4172
|
+
}
|
|
4173
|
+
};
|
|
4174
|
+
|
|
3322
4175
|
// src/core/OIMObjectStoreMapDriven.ts
|
|
3323
4176
|
var OIMObjectStoreMapDriven = class extends OIMObjectStore {
|
|
3324
4177
|
constructor() {
|
|
@@ -3366,6 +4219,27 @@ var OIMObjectStoreMapDriven = class extends OIMObjectStore {
|
|
|
3366
4219
|
}
|
|
3367
4220
|
};
|
|
3368
4221
|
|
|
4222
|
+
// src/core/OIMKeyPathCarrierResolver.ts
|
|
4223
|
+
var OIMKeyPathCarrierResolver = class {
|
|
4224
|
+
constructor() {
|
|
4225
|
+
this.carriers = new OIMTrieMap();
|
|
4226
|
+
}
|
|
4227
|
+
getOrReserveCarrier(key) {
|
|
4228
|
+
let carrier = this.carriers.get(key);
|
|
4229
|
+
if (!carrier) {
|
|
4230
|
+
carrier = { key };
|
|
4231
|
+
this.carriers.set(key, carrier);
|
|
4232
|
+
}
|
|
4233
|
+
return carrier;
|
|
4234
|
+
}
|
|
4235
|
+
findCarrier(key) {
|
|
4236
|
+
return this.carriers.get(key);
|
|
4237
|
+
}
|
|
4238
|
+
onCarrierEmptied(carrier) {
|
|
4239
|
+
this.carriers.delete(carrier.key);
|
|
4240
|
+
}
|
|
4241
|
+
};
|
|
4242
|
+
|
|
3369
4243
|
// src/core/OIMComparatorFactory.ts
|
|
3370
4244
|
var OIMComparatorFactory = class {
|
|
3371
4245
|
createShallowComparator() {
|
|
@@ -3584,6 +4458,78 @@ var OIMMap2Keys = class {
|
|
|
3584
4458
|
}
|
|
3585
4459
|
};
|
|
3586
4460
|
|
|
4461
|
+
// src/core/OIMDisposeScope.ts
|
|
4462
|
+
var OIMDisposeScope = class _OIMDisposeScope {
|
|
4463
|
+
constructor() {
|
|
4464
|
+
this.disposables = [];
|
|
4465
|
+
this.disposed = false;
|
|
4466
|
+
}
|
|
4467
|
+
/** Number of not-yet-disposed items currently held. */
|
|
4468
|
+
get size() {
|
|
4469
|
+
return this.disposables.length;
|
|
4470
|
+
}
|
|
4471
|
+
get isDisposed() {
|
|
4472
|
+
return this.disposed;
|
|
4473
|
+
}
|
|
4474
|
+
/**
|
|
4475
|
+
* Register a disposable (object with `destroy()` or an unsubscribe function)
|
|
4476
|
+
* and return it unchanged, so it can be captured inline. If the scope is
|
|
4477
|
+
* already disposed the item is disposed immediately, so nothing leaks.
|
|
4478
|
+
*/
|
|
4479
|
+
add(disposable) {
|
|
4480
|
+
if (this.disposed) {
|
|
4481
|
+
this.disposeOne(disposable);
|
|
4482
|
+
return disposable;
|
|
4483
|
+
}
|
|
4484
|
+
this.disposables.push(disposable);
|
|
4485
|
+
return disposable;
|
|
4486
|
+
}
|
|
4487
|
+
/** A nested scope, itself registered here — dispose it early or with the parent. */
|
|
4488
|
+
child() {
|
|
4489
|
+
return this.add(new _OIMDisposeScope());
|
|
4490
|
+
}
|
|
4491
|
+
/**
|
|
4492
|
+
* Dispose everything in reverse registration order, then clear. Idempotent.
|
|
4493
|
+
* If any `destroy()`/unsubscribe throws, the rest are still disposed and the
|
|
4494
|
+
* first error is rethrown once teardown completes.
|
|
4495
|
+
*/
|
|
4496
|
+
destroy() {
|
|
4497
|
+
if (this.disposed) return;
|
|
4498
|
+
this.disposed = true;
|
|
4499
|
+
let firstError;
|
|
4500
|
+
let hasError = false;
|
|
4501
|
+
for (let i = this.disposables.length - 1; i >= 0; i--) {
|
|
4502
|
+
try {
|
|
4503
|
+
this.disposeOne(this.disposables[i]);
|
|
4504
|
+
} catch (error) {
|
|
4505
|
+
if (!hasError) {
|
|
4506
|
+
hasError = true;
|
|
4507
|
+
firstError = error;
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
this.disposables.length = 0;
|
|
4512
|
+
if (hasError) throw firstError;
|
|
4513
|
+
}
|
|
4514
|
+
disposeOne(disposable) {
|
|
4515
|
+
if (typeof disposable === "function") disposable();
|
|
4516
|
+
else disposable.destroy();
|
|
4517
|
+
}
|
|
4518
|
+
};
|
|
4519
|
+
function createOIMDisposeScope() {
|
|
4520
|
+
return new OIMDisposeScope();
|
|
4521
|
+
}
|
|
4522
|
+
|
|
4523
|
+
// src/core/OIMPkCodecKeyPath.ts
|
|
4524
|
+
var OIMPkCodecKeyPath = class {
|
|
4525
|
+
encode(pk) {
|
|
4526
|
+
return JSON.stringify(pk);
|
|
4527
|
+
}
|
|
4528
|
+
decode(encoded) {
|
|
4529
|
+
return JSON.parse(encoded);
|
|
4530
|
+
}
|
|
4531
|
+
};
|
|
4532
|
+
|
|
3587
4533
|
// src/modules/compute/core/OIMComputeRuntime.ts
|
|
3588
4534
|
var OIMComputeRuntime = class {
|
|
3589
4535
|
constructor(queue) {
|
|
@@ -4187,6 +5133,27 @@ var OIMCollectionSelectors = class {
|
|
|
4187
5133
|
key
|
|
4188
5134
|
);
|
|
4189
5135
|
}
|
|
5136
|
+
/**
|
|
5137
|
+
* Entities of a composite (key-path) set-based index for one key path, e.g.
|
|
5138
|
+
* `entitiesByCompositeSetIndexKey(index, [projectId, role])`.
|
|
5139
|
+
*/
|
|
5140
|
+
entitiesByCompositeSetIndexKey(index, key) {
|
|
5141
|
+
return new OIMEntitiesByIndexKeySetBasedSelector(
|
|
5142
|
+
this.runtime,
|
|
5143
|
+
this.collection,
|
|
5144
|
+
index,
|
|
5145
|
+
key
|
|
5146
|
+
);
|
|
5147
|
+
}
|
|
5148
|
+
/** Entities of a composite (key-path) array-based index for one key path. */
|
|
5149
|
+
entitiesByCompositeArrayIndexKey(index, key) {
|
|
5150
|
+
return new OIMEntitiesByIndexKeyArrayBasedSelector(
|
|
5151
|
+
this.runtime,
|
|
5152
|
+
this.collection,
|
|
5153
|
+
index,
|
|
5154
|
+
key
|
|
5155
|
+
);
|
|
5156
|
+
}
|
|
4190
5157
|
/** Entities of a keyless (whole-collection) array-based index, ordered. */
|
|
4191
5158
|
entitiesByArrayGlobalIndex(index) {
|
|
4192
5159
|
return new OIMEntitiesByGlobalIndexArrayBasedSelector(
|
|
@@ -4210,7 +5177,16 @@ function createOIMCollectionKit(queue, opts) {
|
|
|
4210
5177
|
const collection = new OIMReactiveCollection(queue, opts);
|
|
4211
5178
|
const indexFactory = new OIMCollectionIndexFactory(queue, collection);
|
|
4212
5179
|
const select = new OIMCollectionSelectors(queue, collection);
|
|
4213
|
-
|
|
5180
|
+
const scope = new OIMDisposeScope();
|
|
5181
|
+
scope.add(collection);
|
|
5182
|
+
return {
|
|
5183
|
+
queue,
|
|
5184
|
+
collection,
|
|
5185
|
+
indexFactory,
|
|
5186
|
+
select,
|
|
5187
|
+
scope,
|
|
5188
|
+
destroy: () => scope.destroy()
|
|
5189
|
+
};
|
|
4214
5190
|
}
|
|
4215
5191
|
|
|
4216
5192
|
// src/modules/wrapper/collection/OIMCollectionChangedFields.ts
|
|
@@ -5398,6 +6374,7 @@ export {
|
|
|
5398
6374
|
EOIMEventQueueSchedulerEventType,
|
|
5399
6375
|
EOIMIndexEventType,
|
|
5400
6376
|
EOIMObjectEventType,
|
|
6377
|
+
OIMBucketCarrierResolver,
|
|
5401
6378
|
OIMCarrierKeyedEmitter,
|
|
5402
6379
|
OIMCarrierSingleEmitter,
|
|
5403
6380
|
OIMCollection,
|
|
@@ -5410,6 +6387,7 @@ export {
|
|
|
5410
6387
|
OIMCollectionSelectors,
|
|
5411
6388
|
OIMCollectionStore,
|
|
5412
6389
|
OIMCollectionStoreMapDriven,
|
|
6390
|
+
OIMCollectionStoreTrieDriven,
|
|
5413
6391
|
OIMComparatorFactory,
|
|
5414
6392
|
OIMComputeRuntime,
|
|
5415
6393
|
OIMComputed,
|
|
@@ -5418,6 +6396,7 @@ export {
|
|
|
5418
6396
|
OIMDerivedCollectionGlobalIndexSetBased,
|
|
5419
6397
|
OIMDerivedCollectionIndexArrayBased,
|
|
5420
6398
|
OIMDerivedCollectionIndexSetBased,
|
|
6399
|
+
OIMDisposeScope,
|
|
5421
6400
|
OIMEffect,
|
|
5422
6401
|
OIMEffectDependencyComputed,
|
|
5423
6402
|
OIMEffectDependencyKeyed,
|
|
@@ -5460,6 +6439,13 @@ export {
|
|
|
5460
6439
|
OIMIndexStoreMapDrivenArrayBased,
|
|
5461
6440
|
OIMIndexStoreMapDrivenSetBased,
|
|
5462
6441
|
OIMIndexStoreSetBased,
|
|
6442
|
+
OIMIndexStoreTrieDrivenArrayBased,
|
|
6443
|
+
OIMIndexStoreTrieDrivenSetBased,
|
|
6444
|
+
OIMKeyDomainNative,
|
|
6445
|
+
OIMKeyDomainTrie,
|
|
6446
|
+
OIMKeyPathCarrierResolver,
|
|
6447
|
+
OIMKeyedBucketArrayBased,
|
|
6448
|
+
OIMKeyedBucketSetBased,
|
|
5463
6449
|
OIMMap2Keys,
|
|
5464
6450
|
OIMObject,
|
|
5465
6451
|
OIMObjectStore,
|
|
@@ -5471,10 +6457,13 @@ export {
|
|
|
5471
6457
|
OIMOrderedListCommandStream,
|
|
5472
6458
|
OIMOrderedListCommandStreamDiffDriven,
|
|
5473
6459
|
OIMOrderedListMappedCommandStream,
|
|
6460
|
+
OIMPkCodecKeyPath,
|
|
5474
6461
|
OIMPkSelectorFactory,
|
|
5475
6462
|
OIMReactiveCollection,
|
|
5476
6463
|
OIMReactiveCollectionGlobalIndexManualArrayBased,
|
|
5477
6464
|
OIMReactiveCollectionGlobalIndexManualSetBased,
|
|
6465
|
+
OIMReactiveCollectionIndexCompositeTrieArrayBased,
|
|
6466
|
+
OIMReactiveCollectionIndexCompositeTrieSetBased,
|
|
5478
6467
|
OIMReactiveCollectionIndexManualArrayBased,
|
|
5479
6468
|
OIMReactiveCollectionIndexManualSetBased,
|
|
5480
6469
|
OIMReactiveGlobalIndex,
|
|
@@ -5489,10 +6478,13 @@ export {
|
|
|
5489
6478
|
OIMReactiveIndexSetBased,
|
|
5490
6479
|
OIMReactiveObject,
|
|
5491
6480
|
OIMSelector,
|
|
6481
|
+
OIMTrieMap,
|
|
6482
|
+
OIMTrieSet,
|
|
5492
6483
|
createInPlaceEntityUpdater,
|
|
5493
6484
|
createMergeEntityUpdater,
|
|
5494
6485
|
createOIMCollectionIndexFactory,
|
|
5495
6486
|
createOIMCollectionKit,
|
|
6487
|
+
createOIMDisposeScope,
|
|
5496
6488
|
createOIMGlobalIndexSlotMap,
|
|
5497
6489
|
createOIMIndexSlotMap,
|
|
5498
6490
|
createOIMOrderedListCommandStreamDiffDriven,
|