@oimdb/core 1.5.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -29,13 +29,19 @@ __export(index_exports, {
29
29
  OIMCollection: () => OIMCollection,
30
30
  OIMCollectionByPkSelector: () => OIMCollectionByPkSelector,
31
31
  OIMCollectionByPksSelector: () => OIMCollectionByPksSelector,
32
- OIMCollectionChangedFieldsWrapper: () => OIMCollectionChangedFieldsWrapper,
32
+ OIMCollectionChangedFields: () => OIMCollectionChangedFields,
33
+ OIMCollectionIndexFactory: () => OIMCollectionIndexFactory,
34
+ OIMCollectionIndexManualOrderedArrayBased: () => OIMCollectionIndexManualOrderedArrayBased,
35
+ OIMCollectionOrderedListCommandStream: () => OIMCollectionOrderedListCommandStream,
36
+ OIMCollectionSelectors: () => OIMCollectionSelectors,
33
37
  OIMCollectionStore: () => OIMCollectionStore,
34
38
  OIMCollectionStoreMapDriven: () => OIMCollectionStoreMapDriven,
35
39
  OIMComparatorFactory: () => OIMComparatorFactory,
36
- OIMComputativeRuntime: () => OIMComputativeRuntime,
40
+ OIMComputeRuntime: () => OIMComputeRuntime,
37
41
  OIMComputed: () => OIMComputed,
38
42
  OIMDBSettings: () => OIMDBSettings,
43
+ OIMDerivedCollectionIndexArrayBased: () => OIMDerivedCollectionIndexArrayBased,
44
+ OIMDerivedCollectionIndexSetBased: () => OIMDerivedCollectionIndexSetBased,
39
45
  OIMEffect: () => OIMEffect,
40
46
  OIMEffectDependencyComputed: () => OIMEffectDependencyComputed,
41
47
  OIMEffectDependencyKeyed: () => OIMEffectDependencyKeyed,
@@ -53,6 +59,7 @@ __export(index_exports, {
53
59
  OIMEventQueueSchedulerImmediate: () => OIMEventQueueSchedulerImmediate,
54
60
  OIMEventQueueSchedulerMicrotask: () => OIMEventQueueSchedulerMicrotask,
55
61
  OIMEventQueueSchedulerTimeout: () => OIMEventQueueSchedulerTimeout,
62
+ OIMIndex: () => OIMIndex,
56
63
  OIMIndexArrayBased: () => OIMIndexArrayBased,
57
64
  OIMIndexComparatorFactory: () => OIMIndexComparatorFactory,
58
65
  OIMIndexManualArrayBased: () => OIMIndexManualArrayBased,
@@ -74,16 +81,20 @@ __export(index_exports, {
74
81
  OIMObjectStoreRecordDriven: () => OIMObjectStoreRecordDriven,
75
82
  OIMObjectValueByKeySelector: () => OIMObjectValueByKeySelector,
76
83
  OIMObjectValuesByKeysSelector: () => OIMObjectValuesByKeysSelector,
77
- OIMOrderedListIndexCommandStreamWrapper: () => OIMOrderedListIndexCommandStreamWrapper,
84
+ OIMOrderedListCommandStream: () => OIMOrderedListCommandStream,
78
85
  OIMPkSelectorFactory: () => OIMPkSelectorFactory,
79
- OIMRICollection: () => OIMRICollection,
80
86
  OIMReactiveCollection: () => OIMReactiveCollection,
87
+ OIMReactiveCollectionIndexManualArrayBased: () => OIMReactiveCollectionIndexManualArrayBased,
88
+ OIMReactiveCollectionIndexManualSetBased: () => OIMReactiveCollectionIndexManualSetBased,
89
+ OIMReactiveIndex: () => OIMReactiveIndex,
81
90
  OIMReactiveIndexArrayBased: () => OIMReactiveIndexArrayBased,
82
91
  OIMReactiveIndexManualArrayBased: () => OIMReactiveIndexManualArrayBased,
83
92
  OIMReactiveIndexManualSetBased: () => OIMReactiveIndexManualSetBased,
84
93
  OIMReactiveIndexSetBased: () => OIMReactiveIndexSetBased,
85
94
  OIMReactiveObject: () => OIMReactiveObject,
86
- OIMSelector: () => OIMSelector
95
+ OIMSelector: () => OIMSelector,
96
+ createOIMCollectionContext: () => createOIMCollectionContext,
97
+ createOIMCollectionIndexFactory: () => createOIMCollectionIndexFactory
87
98
  });
88
99
  module.exports = __toCommonJS(index_exports);
89
100
 
@@ -91,14 +102,6 @@ module.exports = __toCommonJS(index_exports);
91
102
  var OIMCollectionStore = class {
92
103
  };
93
104
 
94
- // src/abstract/OIMIndexStoreSetBased.ts
95
- var OIMIndexStoreSetBased = class {
96
- };
97
-
98
- // src/abstract/OIMIndexStoreArrayBased.ts
99
- var OIMIndexStoreArrayBased = class {
100
- };
101
-
102
105
  // src/core/OIMEventEmitter.ts
103
106
  var OIMEventEmitter = class {
104
107
  constructor() {
@@ -204,7 +207,94 @@ var OIMEventEmitter = class {
204
207
  }
205
208
  };
206
209
 
207
- // src/enum/EOIMEventQueueSchedulerEventType.ts
210
+ // src/enums/EOIMIndexEventType.ts
211
+ var EOIMIndexEventType = /* @__PURE__ */ ((EOIMIndexEventType2) => {
212
+ EOIMIndexEventType2[EOIMIndexEventType2["UPDATE"] = 0] = "UPDATE";
213
+ return EOIMIndexEventType2;
214
+ })(EOIMIndexEventType || {});
215
+
216
+ // src/abstract/OIMIndex.ts
217
+ var OIMIndex = class {
218
+ constructor(store, comparePks) {
219
+ this.emitter = new OIMEventEmitter();
220
+ this.store = store;
221
+ this.comparePks = comparePks;
222
+ }
223
+ hasKey(key) {
224
+ return this.store.getOneByKey(key) !== void 0;
225
+ }
226
+ getKeys() {
227
+ return this.store.getAllKeys();
228
+ }
229
+ getKeySize(key) {
230
+ const bucket = this.store.getOneByKey(key);
231
+ return bucket ? this.getBucketSize(bucket) : 0;
232
+ }
233
+ get size() {
234
+ return this.store.countAll();
235
+ }
236
+ get isEmpty() {
237
+ return this.store.countAll() === 0;
238
+ }
239
+ getEntitiesByKey(key) {
240
+ const bucket = this.store.getOneByKey(key);
241
+ return bucket ? this.slotsToEntities(bucket) : [];
242
+ }
243
+ getEntitiesByKeys(keys) {
244
+ const result = /* @__PURE__ */ new Map();
245
+ const bucketsByKey = this.store.getManyByKeys(keys);
246
+ for (const [key, bucket] of bucketsByKey) {
247
+ result.set(key, this.slotsToEntities(bucket));
248
+ }
249
+ return result;
250
+ }
251
+ getMetrics() {
252
+ let totalPks = 0;
253
+ let maxBucketSize = 0;
254
+ let minBucketSize = Infinity;
255
+ const all = this.store.getAll();
256
+ for (const bucket of all.values()) {
257
+ const size = this.getBucketSize(bucket);
258
+ totalPks += size;
259
+ maxBucketSize = Math.max(maxBucketSize, size);
260
+ minBucketSize = Math.min(minBucketSize, size);
261
+ }
262
+ return {
263
+ totalKeys: all.size,
264
+ totalPks,
265
+ averagePksPerKey: all.size > 0 ? totalPks / all.size : 0,
266
+ maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
267
+ minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
268
+ };
269
+ }
270
+ destroy() {
271
+ this.emitter.offAll();
272
+ this.store.clear();
273
+ }
274
+ emitUpdate(keys) {
275
+ this.emitter.emit(0 /* UPDATE */, { keys });
276
+ }
277
+ emitUpdateOne(key) {
278
+ this.emitUpdate([key]);
279
+ }
280
+ slotsToEntities(slots) {
281
+ const entities = [];
282
+ for (const slot of slots) {
283
+ if (slot.item !== void 0) entities.push(slot.item);
284
+ }
285
+ return entities;
286
+ }
287
+ };
288
+
289
+ // src/abstract/OIMIndexStoreSetBased.ts
290
+ var OIMIndexStoreSetBased = class {
291
+ };
292
+
293
+ // src/abstract/OIMIndexStoreArrayBased.ts
294
+ var OIMIndexStoreArrayBased = class {
295
+ };
296
+
297
+ // src/enums/EOIMEventQueueSchedulerEventType.ts
208
298
  var EOIMEventQueueSchedulerEventType = /* @__PURE__ */ ((EOIMEventQueueSchedulerEventType2) => {
209
299
  EOIMEventQueueSchedulerEventType2[EOIMEventQueueSchedulerEventType2["FLUSH"] = 0] = "FLUSH";
210
300
  return EOIMEventQueueSchedulerEventType2;
@@ -229,12 +319,6 @@ var OIMEventQueueScheduler = class {
229
319
  }
230
320
  };
231
321
 
232
- // src/enum/EOIMIndexEventType.ts
233
- var EOIMIndexEventType = /* @__PURE__ */ ((EOIMIndexEventType2) => {
234
- EOIMIndexEventType2[EOIMIndexEventType2["UPDATE"] = 0] = "UPDATE";
235
- return EOIMIndexEventType2;
236
- })(EOIMIndexEventType || {});
237
-
238
322
  // src/core/OIMIndexStoreMapDrivenSetBased.ts
239
323
  var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
240
324
  constructor() {
@@ -280,17 +364,20 @@ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
280
364
  };
281
365
 
282
366
  // src/abstract/OIMIndexSetBased.ts
283
- var OIMIndexSetBased = class {
367
+ var OIMIndexSetBased = class extends OIMIndex {
284
368
  constructor(options = {}) {
285
- this.fallbackSlots = /* @__PURE__ */ new Map();
286
- this.emitter = new OIMEventEmitter();
287
- this.comparePks = options.comparePks;
288
- this.store = options.store ?? new OIMIndexStoreMapDrivenSetBased();
289
- this.resolveSlot = options.resolveSlot;
369
+ super(
370
+ options.store ?? new OIMIndexStoreMapDrivenSetBased(),
371
+ options.comparePks
372
+ );
373
+ }
374
+ getBucketSize(bucket) {
375
+ return bucket.size;
376
+ }
377
+ getPksByKey(key) {
378
+ const slotsSet = this.store.getOneByKey(key);
379
+ return slotsSet ? this.slotsToPks(slotsSet) : /* @__PURE__ */ new Set();
290
380
  }
291
- /**
292
- * Get primary keys for multiple index keys
293
- */
294
381
  getPksByKeys(keys) {
295
382
  const result = /* @__PURE__ */ new Map();
296
383
  const slotsByKey = this.store.getManyByKeys(keys);
@@ -299,112 +386,25 @@ var OIMIndexSetBased = class {
299
386
  }
300
387
  return result;
301
388
  }
302
- setSlotResolver(resolveSlot) {
303
- this.resolveSlot = resolveSlot;
304
- const allSlots = this.store.getAll();
305
- for (const [key, slots] of allSlots) {
306
- this.store.setOneByKey(
307
- key,
308
- this.resolveSlots(this.slotsToPks(slots))
309
- );
310
- }
311
- }
312
- /**
313
- * @deprecated Use getPksByKey instead
314
- * Get primary keys for a specific index key
315
- */
316
- getPks(key) {
317
- return this.getPksByKey(key);
318
- }
319
- /**
320
- * Get primary keys for a specific index key
321
- */
322
- getPksByKey(key) {
323
- const slotsSet = this.store.getOneByKey(key);
324
- return slotsSet ? this.slotsToPks(slotsSet) : /* @__PURE__ */ new Set();
325
- }
326
389
  getSlotsByKey(key) {
327
- const slotsSet = this.store.getOneByKey(key);
328
- return slotsSet ? slotsSet : /* @__PURE__ */ new Set();
390
+ return this.store.getOneByKey(key) ?? /* @__PURE__ */ new Set();
329
391
  }
330
392
  getSlotsByKeys(keys) {
331
393
  return this.store.getManyByKeys(keys);
332
394
  }
333
- /**
334
- * Check if an index key exists
335
- */
336
- hasKey(key) {
337
- return this.store.getOneByKey(key) !== void 0;
338
- }
339
- /**
340
- * Get all index keys
341
- */
342
- getKeys() {
343
- return this.store.getAllKeys();
344
- }
345
- /**
346
- * Get the number of primary keys for a specific index key
347
- */
348
- getKeySize(key) {
349
- const slotsSet = this.store.getOneByKey(key);
350
- return slotsSet ? slotsSet.size : 0;
351
- }
352
- /**
353
- * Get total number of index keys
354
- */
355
- get size() {
356
- return this.store.countAll();
357
- }
358
- /**
359
- * Check if the index is empty
360
- */
361
- get isEmpty() {
362
- return this.store.countAll() === 0;
363
- }
364
- /**
365
- * Get performance metrics for monitoring and debugging
366
- */
367
- getMetrics() {
368
- let totalPks = 0;
369
- let maxBucketSize = 0;
370
- let minBucketSize = Infinity;
371
- const allPks = this.store.getAll();
372
- for (const slotsSet of allPks.values()) {
373
- totalPks += slotsSet.size;
374
- maxBucketSize = Math.max(maxBucketSize, slotsSet.size);
375
- minBucketSize = Math.min(minBucketSize, slotsSet.size);
376
- }
377
- return {
378
- totalKeys: allPks.size,
379
- totalPks,
380
- averagePksPerKey: allPks.size > 0 ? totalPks / allPks.size : 0,
381
- maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
382
- minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
383
- };
384
- }
385
- /**
386
- * Clean up event listeners when index is no longer needed
387
- */
388
- destroy() {
389
- this.emitter.offAll();
390
- this.store.clear();
391
- }
392
- /**
393
- * Set primary keys for a specific index key with optional comparison.
394
- * If comparator is provided and returns true (no changes), skip the update.
395
- */
396
- setPksWithComparison(key, newPks) {
397
- return this.setSlotsWithComparison(key, this.resolveSlots(newPks));
395
+ slotsToPks(slots) {
396
+ const pks = /* @__PURE__ */ new Set();
397
+ for (const slot of slots) pks.add(slot.pk);
398
+ return pks;
398
399
  }
399
400
  setSlotsWithComparison(key, newSlots) {
400
401
  if (this.comparePks) {
401
402
  const existingSlotsSet = this.store.getOneByKey(key);
402
403
  if (existingSlotsSet && existingSlotsSet.size === newSlots.size) {
403
- const existingPksArray = Array.from(
404
- this.slotsToPks(existingSlotsSet)
405
- );
406
- const newPksArray = Array.from(this.slotsToPks(newSlots));
407
- if (this.comparePks(existingPksArray, newPksArray)) {
404
+ if (this.comparePks(
405
+ Array.from(this.slotsToPks(existingSlotsSet)),
406
+ Array.from(this.slotsToPks(newSlots))
407
+ )) {
408
408
  return false;
409
409
  }
410
410
  } else if (!existingSlotsSet && newSlots.size === 0) {
@@ -414,38 +414,6 @@ var OIMIndexSetBased = class {
414
414
  this.store.setOneByKey(key, newSlots);
415
415
  return true;
416
416
  }
417
- resolveSlots(pks) {
418
- const slots = /* @__PURE__ */ new Set();
419
- for (const pk of pks) slots.add(this.getOrCreateSlot(pk));
420
- return slots;
421
- }
422
- getOrCreateSlot(pk) {
423
- const resolved = this.resolveSlot?.(pk);
424
- if (resolved) return resolved;
425
- let fallback = this.fallbackSlots.get(pk);
426
- if (!fallback) {
427
- fallback = { pk, item: void 0 };
428
- this.fallbackSlots.set(pk, fallback);
429
- }
430
- return fallback;
431
- }
432
- slotsToPks(slots) {
433
- const pks = /* @__PURE__ */ new Set();
434
- for (const slot of slots) pks.add(slot.pk);
435
- return pks;
436
- }
437
- /**
438
- * Emit update event with changed keys
439
- */
440
- emitUpdate(keys) {
441
- this.emitter.emit(0 /* UPDATE */, { keys });
442
- }
443
- /**
444
- * Emit update event for a single key (fast-path override point).
445
- */
446
- emitUpdateOne(key) {
447
- this.emitUpdate([key]);
448
- }
449
417
  };
450
418
 
451
419
  // src/core/OIMIndexStoreMapDrivenArrayBased.ts
@@ -493,17 +461,20 @@ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
493
461
  };
494
462
 
495
463
  // src/abstract/OIMIndexArrayBased.ts
496
- var OIMIndexArrayBased = class {
464
+ var OIMIndexArrayBased = class extends OIMIndex {
497
465
  constructor(options = {}) {
498
- this.fallbackSlots = /* @__PURE__ */ new Map();
499
- this.emitter = new OIMEventEmitter();
500
- this.comparePks = options.comparePks;
501
- this.store = options.store ?? new OIMIndexStoreMapDrivenArrayBased();
502
- this.resolveSlot = options.resolveSlot;
466
+ super(
467
+ options.store ?? new OIMIndexStoreMapDrivenArrayBased(),
468
+ options.comparePks
469
+ );
470
+ }
471
+ getBucketSize(bucket) {
472
+ return bucket.length;
473
+ }
474
+ getPksByKey(key) {
475
+ const slotsArray = this.store.getOneByKey(key);
476
+ return slotsArray ? this.slotsToPks(slotsArray) : [];
503
477
  }
504
- /**
505
- * Get primary keys for multiple index keys
506
- */
507
478
  getPksByKeys(keys) {
508
479
  const result = /* @__PURE__ */ new Map();
509
480
  const slotsByKey = this.store.getManyByKeys(keys);
@@ -512,95 +483,17 @@ var OIMIndexArrayBased = class {
512
483
  }
513
484
  return result;
514
485
  }
515
- setSlotResolver(resolveSlot) {
516
- this.resolveSlot = resolveSlot;
517
- const allSlots = this.store.getAll();
518
- for (const [key, slots] of allSlots) {
519
- this.store.setOneByKey(
520
- key,
521
- slots.map((slot) => this.getOrCreateSlot(slot.pk))
522
- );
523
- }
524
- }
525
- /**
526
- * Get primary keys for a specific index key
527
- */
528
- getPksByKey(key) {
529
- const slotsArray = this.store.getOneByKey(key);
530
- return slotsArray ? this.slotsToPks(slotsArray) : [];
531
- }
532
486
  getSlotsByKey(key) {
533
- const slotsArray = this.store.getOneByKey(key);
534
- return slotsArray ? slotsArray : [];
487
+ return this.store.getOneByKey(key) ?? [];
535
488
  }
536
489
  getSlotsByKeys(keys) {
537
490
  return this.store.getManyByKeys(keys);
538
491
  }
539
- /**
540
- * Check if an index key exists
541
- */
542
- hasKey(key) {
543
- return this.store.getOneByKey(key) !== void 0;
544
- }
545
- /**
546
- * Get all index keys
547
- */
548
- getKeys() {
549
- return this.store.getAllKeys();
550
- }
551
- /**
552
- * Get the number of primary keys for a specific index key
553
- */
554
- getKeySize(key) {
555
- const slotsArray = this.store.getOneByKey(key);
556
- return slotsArray ? slotsArray.length : 0;
557
- }
558
- /**
559
- * Get total number of index keys
560
- */
561
- get size() {
562
- return this.store.countAll();
563
- }
564
- /**
565
- * Check if the index is empty
566
- */
567
- get isEmpty() {
568
- return this.store.countAll() === 0;
569
- }
570
- /**
571
- * Get performance metrics for monitoring and debugging
572
- */
573
- getMetrics() {
574
- let totalPks = 0;
575
- let maxBucketSize = 0;
576
- let minBucketSize = Infinity;
577
- const allPks = this.store.getAll();
578
- for (const slotsArray of allPks.values()) {
579
- totalPks += slotsArray.length;
580
- maxBucketSize = Math.max(maxBucketSize, slotsArray.length);
581
- minBucketSize = Math.min(minBucketSize, slotsArray.length);
582
- }
583
- return {
584
- totalKeys: allPks.size,
585
- totalPks,
586
- averagePksPerKey: allPks.size > 0 ? totalPks / allPks.size : 0,
587
- maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
588
- minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
589
- };
590
- }
591
- /**
592
- * Clean up event listeners when index is no longer needed
593
- */
594
- destroy() {
595
- this.emitter.offAll();
596
- this.store.clear();
597
- }
598
- /**
599
- * Set primary keys for a specific index key with optional comparison.
600
- * If comparator is provided and returns true (no changes), skip the update.
601
- */
602
- setPksWithComparison(key, newPks) {
603
- return this.setSlotsWithComparison(key, this.resolveSlots(newPks));
492
+ slotsToPks(slots) {
493
+ const pks = [];
494
+ pks.length = slots.length;
495
+ for (let i = 0; i < slots.length; i++) pks[i] = slots[i].pk;
496
+ return pks;
604
497
  }
605
498
  setSlotsWithComparison(key, newSlots) {
606
499
  if (this.comparePks) {
@@ -619,45 +512,9 @@ var OIMIndexArrayBased = class {
619
512
  this.store.setOneByKey(key, newSlots);
620
513
  return true;
621
514
  }
622
- resolveSlots(pks) {
623
- const slots = [];
624
- slots.length = pks.length;
625
- for (let i = 0; i < pks.length; i++) {
626
- slots[i] = this.getOrCreateSlot(pks[i]);
627
- }
628
- return slots;
629
- }
630
- getOrCreateSlot(pk) {
631
- const resolved = this.resolveSlot?.(pk);
632
- if (resolved) return resolved;
633
- let fallback = this.fallbackSlots.get(pk);
634
- if (!fallback) {
635
- fallback = { pk, item: void 0 };
636
- this.fallbackSlots.set(pk, fallback);
637
- }
638
- return fallback;
639
- }
640
- slotsToPks(slots) {
641
- const pks = [];
642
- pks.length = slots.length;
643
- for (let i = 0; i < slots.length; i++) pks[i] = slots[i].pk;
644
- return pks;
645
- }
646
- /**
647
- * Emit update event with changed keys
648
- */
649
- emitUpdate(keys) {
650
- this.emitter.emit(0 /* UPDATE */, { keys });
651
- }
652
- /**
653
- * Emit update event for a single key (fast-path override point).
654
- */
655
- emitUpdateOne(key) {
656
- this.emitUpdate([key]);
657
- }
658
515
  };
659
516
 
660
- // src/enum/EOIMEventQueueEventType.ts
517
+ // src/enums/EOIMEventQueueEventType.ts
661
518
  var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
662
519
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
663
520
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
@@ -911,23 +768,17 @@ var OIMUpdateEventEmitter = class {
911
768
  }
912
769
  };
913
770
 
914
- // src/abstract/OIMReactiveIndexSetBased.ts
915
- var OIMReactiveIndexSetBased = class {
771
+ // src/abstract/OIMReactiveIndex.ts
772
+ var OIMReactiveIndex = class {
916
773
  constructor(queue, createIndex) {
917
774
  this.updateEmitter = new OIMUpdateEventEmitter(queue);
918
775
  this.index = createIndex(this.updateEmitter);
919
776
  }
920
- getPksByKey(key) {
921
- return this.index.getPksByKey(key);
922
- }
923
- getPksByKeys(keys) {
924
- return this.index.getPksByKeys(keys);
925
- }
926
- getSlotsByKey(key) {
927
- return this.index.getSlotsByKey(key);
777
+ getEntitiesByKey(key) {
778
+ return this.index.getEntitiesByKey(key);
928
779
  }
929
- getSlotsByKeys(keys) {
930
- return this.index.getSlotsByKeys(keys);
780
+ getEntitiesByKeys(keys) {
781
+ return this.index.getEntitiesByKeys(keys);
931
782
  }
932
783
  hasKey(key) {
933
784
  return this.index.hasKey(key);
@@ -974,11 +825,10 @@ var OIMReactiveIndexSetBased = class {
974
825
  }
975
826
  };
976
827
 
977
- // src/abstract/OIMReactiveIndexArrayBased.ts
978
- var OIMReactiveIndexArrayBased = class {
828
+ // src/abstract/OIMReactiveIndexSetBased.ts
829
+ var OIMReactiveIndexSetBased = class extends OIMReactiveIndex {
979
830
  constructor(queue, createIndex) {
980
- this.updateEmitter = new OIMUpdateEventEmitter(queue);
981
- this.index = createIndex(this.updateEmitter);
831
+ super(queue, createIndex);
982
832
  }
983
833
  getPksByKey(key) {
984
834
  return this.index.getPksByKey(key);
@@ -992,48 +842,24 @@ var OIMReactiveIndexArrayBased = class {
992
842
  getSlotsByKeys(keys) {
993
843
  return this.index.getSlotsByKeys(keys);
994
844
  }
995
- hasKey(key) {
996
- return this.index.hasKey(key);
997
- }
998
- getKeys() {
999
- return this.index.getKeys();
1000
- }
1001
- getKeySize(key) {
1002
- return this.index.getKeySize(key);
1003
- }
1004
- get size() {
1005
- return this.index.size;
1006
- }
1007
- get isEmpty() {
1008
- return this.index.isEmpty;
1009
- }
1010
- getMetrics() {
1011
- return this.index.getMetrics();
1012
- }
1013
- subscribeOnKey(key, handler) {
1014
- return this.updateEmitter.subscribeOnKey(key, handler);
1015
- }
1016
- subscribeOnKeys(keys, handler) {
1017
- return this.updateEmitter.subscribeOnKeys(keys, handler);
1018
- }
1019
- unsubscribeFromKey(key, handler) {
1020
- this.updateEmitter.unsubscribeFromKey(key, handler);
1021
- }
1022
- unsubscribeFromKeys(keys, handler) {
1023
- this.updateEmitter.unsubscribeFromKeys(keys, handler);
845
+ };
846
+
847
+ // src/abstract/OIMReactiveIndexArrayBased.ts
848
+ var OIMReactiveIndexArrayBased = class extends OIMReactiveIndex {
849
+ constructor(queue, createIndex) {
850
+ super(queue, createIndex);
1024
851
  }
1025
- hasSubscriptions() {
1026
- return this.updateEmitter.hasSubscriptions();
852
+ getPksByKey(key) {
853
+ return this.index.getPksByKey(key);
1027
854
  }
1028
- getHandlerCount(key) {
1029
- return this.updateEmitter.getHandlerCount(key);
855
+ getPksByKeys(keys) {
856
+ return this.index.getPksByKeys(keys);
1030
857
  }
1031
- getSubscriptionMetrics() {
1032
- return this.updateEmitter.getMetrics();
858
+ getSlotsByKey(key) {
859
+ return this.index.getSlotsByKey(key);
1033
860
  }
1034
- destroy() {
1035
- this.updateEmitter.destroy();
1036
- this.index.destroy();
861
+ getSlotsByKeys(keys) {
862
+ return this.index.getSlotsByKeys(keys);
1037
863
  }
1038
864
  };
1039
865
 
@@ -1058,14 +884,20 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
1058
884
  const slot = this.slots.get(pk);
1059
885
  if (slot) {
1060
886
  slot.item = entity;
887
+ return slot;
1061
888
  } else {
1062
- this.slots.set(pk, { pk, item: entity });
889
+ const nextSlot = { pk, item: entity };
890
+ this.slots.set(pk, nextSlot);
891
+ return nextSlot;
1063
892
  }
1064
893
  }
1065
894
  setManyByPks(pks, entities) {
895
+ const slots = [];
896
+ slots.length = pks.length;
1066
897
  for (let i = 0; i < pks.length; i++) {
1067
- this.setOneByPk(pks[i], entities[i]);
898
+ slots[i] = this.setOneByPk(pks[i], entities[i]);
1068
899
  }
900
+ return slots;
1069
901
  }
1070
902
  getSlotByPk(pk) {
1071
903
  return this.slots.get(pk);
@@ -1145,7 +977,7 @@ var OIMEntityUpdaterFactory = class {
1145
977
  }
1146
978
  };
1147
979
 
1148
- // src/enum/EOIMCollectionEventType.ts
980
+ // src/enums/EOIMCollectionEventType.ts
1149
981
  var EOIMCollectionEventType = /* @__PURE__ */ ((EOIMCollectionEventType2) => {
1150
982
  EOIMCollectionEventType2[EOIMCollectionEventType2["UPDATE"] = 0] = "UPDATE";
1151
983
  return EOIMCollectionEventType2;
@@ -1175,18 +1007,23 @@ var OIMCollection = class {
1175
1007
  return this.store.getAllSlots();
1176
1008
  }
1177
1009
  upsertOneByPk(pk, entity) {
1178
- this.upsertOneWithoutNotificationsByPk(pk, entity);
1010
+ const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
1179
1011
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1012
+ return slot;
1180
1013
  }
1181
1014
  upsertOne(entity) {
1182
- const pk = this.upsertOneWithoutNotifications(entity);
1183
- this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1015
+ const slot = this.upsertOneWithoutNotifications(entity);
1016
+ this.emitter.emit(0 /* UPDATE */, { pks: [slot.pk] });
1017
+ return slot;
1184
1018
  }
1185
1019
  upsertMany(entities) {
1186
- const pks = entities.map(
1020
+ if (entities.length === 0) return [];
1021
+ const slots = entities.map(
1187
1022
  (entity) => this.upsertOneWithoutNotifications(entity)
1188
1023
  );
1024
+ const pks = slots.map((slot) => slot.pk);
1189
1025
  this.emitter.emit(0 /* UPDATE */, { pks });
1026
+ return slots;
1190
1027
  }
1191
1028
  removeOne(entity) {
1192
1029
  const pk = this.selectPk(entity);
@@ -1194,6 +1031,7 @@ var OIMCollection = class {
1194
1031
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1195
1032
  }
1196
1033
  removeMany(entities) {
1034
+ if (entities.length === 0) return;
1197
1035
  const pks = entities.map(this.selectPk);
1198
1036
  this.store.removeManyByPks(pks);
1199
1037
  this.emitter.emit(0 /* UPDATE */, { pks });
@@ -1203,6 +1041,7 @@ var OIMCollection = class {
1203
1041
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1204
1042
  }
1205
1043
  removeManyByPks(pks) {
1044
+ if (pks.length === 0) return;
1206
1045
  this.store.removeManyByPks(pks);
1207
1046
  this.emitter.emit(0 /* UPDATE */, { pks });
1208
1047
  }
@@ -1221,8 +1060,7 @@ var OIMCollection = class {
1221
1060
  }
1222
1061
  upsertOneWithoutNotifications(entity) {
1223
1062
  const pk = this.selectPk(entity);
1224
- this.upsertOneWithoutNotificationsByPk(pk, entity);
1225
- return pk;
1063
+ return this.upsertOneWithoutNotificationsByPk(pk, entity);
1226
1064
  }
1227
1065
  upsertOneWithoutNotificationsByPk(pk, entity) {
1228
1066
  if (!pk) {
@@ -1232,12 +1070,12 @@ var OIMCollection = class {
1232
1070
  }
1233
1071
  const existingEntity = this.store.getOneByPk(pk);
1234
1072
  if (existingEntity) {
1235
- this.store.setOneByPk(
1073
+ return this.store.setOneByPk(
1236
1074
  pk,
1237
1075
  this.updateEntity(entity, existingEntity)
1238
1076
  );
1239
1077
  } else {
1240
- this.store.setOneByPk(pk, entity);
1078
+ return this.store.setOneByPk(pk, entity);
1241
1079
  }
1242
1080
  }
1243
1081
  destroy() {
@@ -1298,24 +1136,29 @@ var OIMReactiveCollection = class extends OIMCollection {
1298
1136
  };
1299
1137
  }
1300
1138
  upsertOneByPk(pk, entity) {
1301
- this.upsertOneWithoutNotificationsByPk(pk, entity);
1139
+ const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
1302
1140
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1303
1141
  this.updateEmitter.markUpdatedKey(pk);
1304
1142
  this.trackAnyUpdatePk(pk);
1143
+ return slot;
1305
1144
  }
1306
1145
  upsertOne(entity) {
1307
- const pk = this.upsertOneWithoutNotifications(entity);
1308
- this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1309
- this.updateEmitter.markUpdatedKey(pk);
1310
- this.trackAnyUpdatePk(pk);
1146
+ const slot = this.upsertOneWithoutNotifications(entity);
1147
+ this.emitter.emit(0 /* UPDATE */, { pks: [slot.pk] });
1148
+ this.updateEmitter.markUpdatedKey(slot.pk);
1149
+ this.trackAnyUpdatePk(slot.pk);
1150
+ return slot;
1311
1151
  }
1312
1152
  upsertMany(entities) {
1313
- const pks = entities.map(
1153
+ if (entities.length === 0) return [];
1154
+ const slots = entities.map(
1314
1155
  (entity) => this.upsertOneWithoutNotifications(entity)
1315
1156
  );
1157
+ const pks = slots.map((slot) => slot.pk);
1316
1158
  this.emitter.emit(0 /* UPDATE */, { pks });
1317
1159
  this.updateEmitter.markUpdatedKeys(pks);
1318
1160
  this.trackAnyUpdatePks(pks);
1161
+ return slots;
1319
1162
  }
1320
1163
  removeOne(entity) {
1321
1164
  const pk = this.selectPk(entity);
@@ -1325,6 +1168,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1325
1168
  this.trackAnyUpdatePk(pk);
1326
1169
  }
1327
1170
  removeMany(entities) {
1171
+ if (entities.length === 0) return;
1328
1172
  const pks = entities.map(this.selectPk);
1329
1173
  this.store.removeManyByPks(pks);
1330
1174
  this.emitter.emit(0 /* UPDATE */, { pks });
@@ -1338,6 +1182,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1338
1182
  this.trackAnyUpdatePk(pk);
1339
1183
  }
1340
1184
  removeManyByPks(pks) {
1185
+ if (pks.length === 0) return;
1341
1186
  this.store.removeManyByPks(pks);
1342
1187
  this.emitter.emit(0 /* UPDATE */, { pks });
1343
1188
  this.updateEmitter.markUpdatedKeys(pks);
@@ -1383,202 +1228,347 @@ var OIMReactiveCollection = class extends OIMCollection {
1383
1228
  }
1384
1229
  };
1385
1230
 
1386
- // src/core/OIMObjectStoreRecordDriven.ts
1387
- var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
1388
- constructor(initial) {
1389
- super();
1390
- this.properties = Object.assign(/* @__PURE__ */ Object.create(null), initial ?? {});
1391
- }
1392
- setProperty(key, value) {
1393
- this.properties[key] = value;
1394
- }
1395
- merge(draft) {
1396
- Object.assign(this.properties, draft);
1397
- }
1398
- delete(key) {
1399
- delete this.properties[key];
1400
- }
1401
- get(key) {
1402
- return this.properties[key];
1403
- }
1404
- getAll() {
1405
- return { ...this.properties };
1231
+ // src/modules/wrapper/index/OIMIndexManualOrderedArrayBased.ts
1232
+ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
1233
+ clear(key) {
1234
+ if (key === void 0) {
1235
+ const keys = this.getKeys();
1236
+ if (keys.length > 0) {
1237
+ this.store.clear();
1238
+ this.emitUpdate(Array.from(keys));
1239
+ }
1240
+ return;
1241
+ }
1242
+ if (this.store.getOneByKey(key) !== void 0) {
1243
+ this.store.removeOneByKey(key);
1244
+ this.emitUpdate([key]);
1245
+ }
1406
1246
  }
1407
- clear() {
1408
- this.properties = /* @__PURE__ */ Object.create(null);
1247
+ pushSlot(key, slot) {
1248
+ const list = this.getOrCreateList(key);
1249
+ const index = list.length;
1250
+ list.push(slot);
1251
+ this.emitUpdate([key]);
1252
+ return index;
1409
1253
  }
1410
- count() {
1411
- return Object.keys(this.properties).length;
1254
+ insertSlotAt(key, index, slot) {
1255
+ const list = this.getOrCreateList(key);
1256
+ const safeIndex = Math.max(0, Math.min(index, list.length));
1257
+ list.splice(safeIndex, 0, slot);
1258
+ this.emitUpdate([key]);
1259
+ return safeIndex;
1412
1260
  }
1413
- keys() {
1414
- return Object.keys(this.properties);
1261
+ removeAt(key, index) {
1262
+ const list = this.store.getOneByKey(key);
1263
+ if (!list) return void 0;
1264
+ if (index < 0 || index >= list.length) return void 0;
1265
+ const [removed] = list.splice(index, 1);
1266
+ if (list.length === 0) {
1267
+ this.store.removeOneByKey(key);
1268
+ }
1269
+ this.emitUpdate([key]);
1270
+ return removed;
1415
1271
  }
1416
- values() {
1417
- return Object.values(this.properties);
1272
+ move(key, fromIndex, toIndex) {
1273
+ const list = this.store.getOneByKey(key);
1274
+ if (!list) return void 0;
1275
+ if (fromIndex < 0 || fromIndex >= list.length) return void 0;
1276
+ if (toIndex < 0 || toIndex >= list.length) return void 0;
1277
+ if (fromIndex === toIndex) return list[fromIndex];
1278
+ const [slot] = list.splice(fromIndex, 1);
1279
+ if (slot === void 0) return void 0;
1280
+ list.splice(toIndex, 0, slot);
1281
+ this.emitUpdate([key]);
1282
+ return slot;
1418
1283
  }
1419
- entries() {
1420
- return Object.entries(this.properties);
1284
+ resetSlots(key, slots) {
1285
+ if (slots.length === 0) {
1286
+ if (this.store.getOneByKey(key) !== void 0) {
1287
+ this.store.removeOneByKey(key);
1288
+ this.emitUpdate([key]);
1289
+ }
1290
+ return;
1291
+ }
1292
+ this.store.setOneByKey(key, slots.slice());
1293
+ this.emitUpdate([key]);
1421
1294
  }
1422
- destroy() {
1423
- this.clear();
1295
+ getOrCreateList(key) {
1296
+ let list = this.store.getOneByKey(key);
1297
+ if (!list) {
1298
+ list = [];
1299
+ this.store.setOneByKey(key, list);
1300
+ }
1301
+ return list;
1424
1302
  }
1425
1303
  };
1426
1304
 
1427
- // src/enum/EOIMObjectEventType.ts
1428
- var EOIMObjectEventType = /* @__PURE__ */ ((EOIMObjectEventType2) => {
1429
- EOIMObjectEventType2[EOIMObjectEventType2["UPDATE"] = 0] = "UPDATE";
1430
- return EOIMObjectEventType2;
1431
- })(EOIMObjectEventType || {});
1432
-
1433
- // src/core/OIMObject.ts
1434
- var OIMObject = class {
1305
+ // src/modules/wrapper/index/OIMCollectionIndexManualOrderedArrayBased.ts
1306
+ var OIMCollectionIndexManualOrderedArrayBased = class extends OIMIndexManualOrderedArrayBased {
1435
1307
  constructor(opts) {
1436
- this.emitter = new OIMEventEmitter();
1437
- this.store = opts?.store ?? new OIMObjectStoreRecordDriven();
1308
+ super();
1309
+ this.resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1438
1310
  }
1439
- setProperty(key, value) {
1440
- this.store.setProperty(key, value);
1441
- this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1311
+ push(key, pk) {
1312
+ return this.pushSlot(key, this.resolveRequiredSlot(pk));
1442
1313
  }
1443
- merge(draft) {
1444
- this.store.merge(draft);
1445
- this.emitter.emit(0 /* UPDATE */, {
1446
- keys: Object.keys(draft)
1447
- });
1314
+ insertAt(key, index, pk) {
1315
+ return this.insertSlotAt(key, index, this.resolveRequiredSlot(pk));
1448
1316
  }
1449
- delete(key) {
1450
- this.store.delete(key);
1451
- this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1317
+ reset(key, pks) {
1318
+ this.resetSlots(key, this.resolveSlots(pks));
1452
1319
  }
1453
- get(key) {
1454
- return this.store.get(key);
1320
+ getEntitiesByKey(key) {
1321
+ return super.getEntitiesByKey(key);
1455
1322
  }
1456
- getAll() {
1457
- return this.store.getAll();
1323
+ getEntitiesByKeys(keys) {
1324
+ return super.getEntitiesByKeys(keys);
1458
1325
  }
1459
- clear() {
1460
- this.store.clear();
1461
- this.emitter.emit(0 /* UPDATE */, { keys: [] });
1326
+ resolvePk(pk) {
1327
+ return this.resolveRequiredSlot(pk);
1462
1328
  }
1463
- count() {
1464
- return this.store.count();
1329
+ resolveSlots(pks) {
1330
+ const slots = [];
1331
+ slots.length = pks.length;
1332
+ for (let i = 0; i < pks.length; i++) {
1333
+ slots[i] = this.resolveRequiredSlot(pks[i]);
1334
+ }
1335
+ return slots;
1465
1336
  }
1466
- keys() {
1467
- return this.store.keys();
1337
+ resolveRequiredSlot(pk) {
1338
+ const slot = this.resolveSlot(pk);
1339
+ if (!slot) {
1340
+ throw new Error(
1341
+ `[OIMCollectionIndexManualOrderedArrayBased]: Unable to resolve slot for PK "${String(pk)}".`
1342
+ );
1343
+ }
1344
+ return slot;
1468
1345
  }
1469
- values() {
1470
- return this.store.values();
1346
+ };
1347
+
1348
+ // src/modules/wrapper/index/OIMOrderedListCommandStream.ts
1349
+ var OIMOrderedListCommandStream = class {
1350
+ constructor(queue, index) {
1351
+ this.isInWrite = false;
1352
+ this.commandsByKey = /* @__PURE__ */ new Map();
1353
+ this.onAfterFlush = () => {
1354
+ this.commandsByKey.clear();
1355
+ };
1356
+ this.onIndexUpdate = (payload) => {
1357
+ if (this.isInWrite) return;
1358
+ if (payload.keys.length === 0) return;
1359
+ for (let i = 0; i < payload.keys.length; i++) {
1360
+ const key = payload.keys[i];
1361
+ this.appendSetCommand(key, this.getSlotsByKey(key));
1362
+ }
1363
+ };
1364
+ this.commandsEventEmitter = new OIMUpdateEventEmitter(
1365
+ queue,
1366
+ "after_flush"
1367
+ );
1368
+ this.index = index ?? new OIMIndexManualOrderedArrayBased();
1369
+ this.unsubscribeAfterFlush = queue.emitter.on(
1370
+ 0 /* AFTER_FLUSH */,
1371
+ this.onAfterFlush
1372
+ );
1373
+ this.unsubscribeFromIndexEmitter = this.index.emitter.on(
1374
+ 0 /* UPDATE */,
1375
+ this.onIndexUpdate
1376
+ );
1471
1377
  }
1472
- entries() {
1473
- return this.store.entries();
1378
+ getPksByKey(key) {
1379
+ return this.index.getPksByKey(key);
1474
1380
  }
1475
- destroy() {
1476
- this.store.destroy();
1477
- this.emitter.offAll();
1381
+ getSlotsByKey(key) {
1382
+ return this.index.getSlotsByKey(
1383
+ key
1384
+ );
1478
1385
  }
1479
- };
1480
-
1481
- // src/core/OIMReactiveObject.ts
1482
- var OIMReactiveObject = class extends OIMObject {
1483
- constructor(queue, opts) {
1484
- super(opts);
1485
- this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
1486
- this.emitter.on(0 /* UPDATE */, (payload) => {
1487
- this.updateEmitter.markUpdatedKeys(payload.keys);
1488
- });
1386
+ getEntitiesByKey(key) {
1387
+ return this.index.getEntitiesByKey(key);
1489
1388
  }
1490
- subscribeOnKey(key, handler) {
1491
- return this.updateEmitter.subscribeOnKey(key, handler);
1389
+ hasKey(key) {
1390
+ return this.index.hasKey(key);
1492
1391
  }
1493
- subscribeOnKeys(keys, handler) {
1494
- return this.updateEmitter.subscribeOnKeys(keys, handler);
1392
+ getKeys() {
1393
+ return this.index.getKeys();
1495
1394
  }
1496
- unsubscribeFromKey(key, handler) {
1497
- this.updateEmitter.unsubscribeFromKey(key, handler);
1395
+ pushSlot(key, slot) {
1396
+ this.withWrite(() => {
1397
+ const index = this.index.pushSlot(key, slot);
1398
+ this.appendCommand(key, {
1399
+ type: "insert",
1400
+ pk: slot.pk,
1401
+ slot,
1402
+ index
1403
+ });
1404
+ });
1498
1405
  }
1499
- unsubscribeFromKeys(keys, handler) {
1500
- this.updateEmitter.unsubscribeFromKeys(keys, handler);
1406
+ insertSlotAt(key, index, slot) {
1407
+ this.withWrite(() => {
1408
+ const safeIndex = this.index.insertSlotAt(key, index, slot);
1409
+ this.appendCommand(key, {
1410
+ type: "insert",
1411
+ pk: slot.pk,
1412
+ slot,
1413
+ index: safeIndex
1414
+ });
1415
+ });
1501
1416
  }
1502
- hasSubscriptions() {
1503
- return this.updateEmitter.hasSubscriptions();
1417
+ removeAt(key, index) {
1418
+ this.withWrite(() => {
1419
+ const slot = this.index.removeAt(key, index);
1420
+ if (slot === void 0) return;
1421
+ this.appendCommand(key, {
1422
+ type: "remove",
1423
+ pk: slot.pk,
1424
+ slot,
1425
+ index
1426
+ });
1427
+ });
1504
1428
  }
1505
- getHandlerCount(key) {
1506
- return this.updateEmitter.getHandlerCount(key);
1429
+ move(key, fromIndex, toIndex) {
1430
+ this.withWrite(() => {
1431
+ const slot = this.index.move(key, fromIndex, toIndex);
1432
+ if (slot === void 0) return;
1433
+ this.appendCommand(key, {
1434
+ type: "move",
1435
+ pk: slot.pk,
1436
+ slot,
1437
+ fromIndex,
1438
+ toIndex
1439
+ });
1440
+ });
1507
1441
  }
1508
- getMetrics() {
1509
- return this.updateEmitter.getMetrics();
1442
+ setSlots(key, slots) {
1443
+ this.withWrite(() => {
1444
+ this.index.resetSlots(key, slots);
1445
+ this.appendSetCommand(key, slots);
1446
+ });
1510
1447
  }
1511
- destroy() {
1512
- this.updateEmitter.destroy();
1513
- super.destroy();
1448
+ getBufferedCommands(key) {
1449
+ return this.commandsByKey.get(key) ?? [];
1514
1450
  }
1515
- };
1516
-
1517
- // src/core/OIMIndexManualSetBased.ts
1518
- var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1519
- constructor(options = {}) {
1520
- super(options);
1451
+ consumeCommands(key) {
1452
+ const cmds = this.commandsByKey.get(key);
1453
+ if (!cmds || cmds.length === 0) return [];
1454
+ return cmds.slice();
1521
1455
  }
1522
- /**
1523
- * Set primary keys for a specific index key, replacing any existing values.
1524
- * Uses optional comparator to skip updates if PKs haven't actually changed.
1525
- */
1526
- setPks(key, pks) {
1527
- const hasChanges = this.setPksWithComparison(key, new Set(pks));
1528
- if (hasChanges) {
1529
- this.emitUpdateOne(key);
1456
+ clear(key) {
1457
+ if (key === void 0) {
1458
+ this.index.destroy();
1459
+ this.commandsByKey.clear();
1460
+ return;
1530
1461
  }
1462
+ this.index.clear(key);
1463
+ this.commandsByKey.delete(key);
1531
1464
  }
1532
- setSlots(key, slots) {
1533
- const hasChanges = this.setSlotsWithComparison(key, new Set(slots));
1534
- if (hasChanges) {
1535
- this.emitUpdateOne(key);
1536
- }
1465
+ destroy() {
1466
+ this.commandsEventEmitter.destroy();
1467
+ this.unsubscribeAfterFlush();
1468
+ this.unsubscribeFromIndexEmitter();
1469
+ this.clear();
1537
1470
  }
1538
- /**
1539
- * Add primary keys to a specific index key
1540
- */
1541
- addPks(key, pks) {
1542
- if (pks.length === 0) return;
1543
- let slotsSet = this.store.getOneByKey(key);
1544
- if (!slotsSet) {
1545
- slotsSet = /* @__PURE__ */ new Set();
1546
- this.store.setOneByKey(key, slotsSet);
1547
- }
1548
- const pksSet = this.slotsToPks(slotsSet);
1549
- let hasChanges = false;
1550
- for (const pk of pks) {
1551
- if (!pksSet.has(pk)) {
1552
- pksSet.add(pk);
1553
- slotsSet.add(this.getOrCreateSlot(pk));
1554
- hasChanges = true;
1555
- }
1471
+ withWrite(fn) {
1472
+ if (this.isInWrite) {
1473
+ fn();
1474
+ return;
1556
1475
  }
1557
- if (hasChanges) {
1558
- this.store.setOneByKey(key, slotsSet);
1559
- this.emitUpdateOne(key);
1476
+ this.isInWrite = true;
1477
+ try {
1478
+ fn();
1479
+ } finally {
1480
+ this.isInWrite = false;
1560
1481
  }
1561
1482
  }
1562
- /**
1563
- * Remove primary keys from a specific index key
1564
- */
1565
- removePks(key, pks) {
1566
- if (pks.length === 0) return;
1567
- const slotsSet = this.store.getOneByKey(key);
1568
- if (!slotsSet) return;
1569
- let hasChanges = false;
1570
- const pksToRemove = new Set(pks);
1571
- for (const slot of Array.from(slotsSet)) {
1572
- if (pksToRemove.has(slot.pk) && slotsSet.delete(slot)) {
1573
- hasChanges = true;
1574
- }
1483
+ appendCommand(key, cmd) {
1484
+ let cmds = this.commandsByKey.get(key);
1485
+ if (!cmds) {
1486
+ cmds = [];
1487
+ this.commandsByKey.set(key, cmds);
1575
1488
  }
1576
- if (slotsSet.size === 0) {
1577
- this.store.removeOneByKey(key);
1578
- hasChanges = true;
1579
- } else if (hasChanges) {
1580
- this.store.setOneByKey(key, slotsSet);
1489
+ if (cmd.type === "set") {
1490
+ cmds.length = 0;
1491
+ cmds.push(cmd);
1492
+ } else if (cmds.length > 0 && cmds[0].type === "set") {
1493
+ const slots = this.getSlotsByKey(key);
1494
+ cmds[0] = this.createSetCommand(slots);
1495
+ } else {
1496
+ cmds.push(cmd);
1581
1497
  }
1498
+ this.commandsEventEmitter.markUpdatedKeys([key]);
1499
+ }
1500
+ appendSetCommand(key, slots) {
1501
+ this.appendCommand(key, this.createSetCommand(slots));
1502
+ }
1503
+ createSetCommand(slots) {
1504
+ return {
1505
+ type: "set",
1506
+ pks: slots.map((slot) => slot.pk),
1507
+ slots
1508
+ };
1509
+ }
1510
+ };
1511
+
1512
+ // src/modules/wrapper/index/OIMCollectionOrderedListCommandStream.ts
1513
+ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandStream {
1514
+ constructor(queue, opts) {
1515
+ const index = opts.index ?? new OIMCollectionIndexManualOrderedArrayBased(
1516
+ opts.collection !== void 0 ? { collection: opts.collection } : { resolveSlot: opts.resolveSlot }
1517
+ );
1518
+ super(queue, index);
1519
+ this.index = index;
1520
+ }
1521
+ push(key, pk) {
1522
+ this.withWrite(() => {
1523
+ const slot = this.resolvePk(pk);
1524
+ const index = this.index.pushSlot(key, slot);
1525
+ this.appendCommand(key, {
1526
+ type: "insert",
1527
+ pk,
1528
+ slot,
1529
+ index
1530
+ });
1531
+ });
1532
+ }
1533
+ insertAt(key, index, pk) {
1534
+ this.withWrite(() => {
1535
+ const slot = this.resolvePk(pk);
1536
+ const safeIndex = this.index.insertSlotAt(key, index, slot);
1537
+ this.appendCommand(key, {
1538
+ type: "insert",
1539
+ pk,
1540
+ slot,
1541
+ index: safeIndex
1542
+ });
1543
+ });
1544
+ }
1545
+ set(key, pks) {
1546
+ this.withWrite(() => {
1547
+ const slots = pks.map((pk) => this.resolvePk(pk));
1548
+ this.index.resetSlots(key, slots);
1549
+ this.appendSetCommand(key, slots);
1550
+ });
1551
+ }
1552
+ getSlotsByKey(key) {
1553
+ return this.index.getSlotsByKey(
1554
+ key
1555
+ );
1556
+ }
1557
+ getEntitiesByKey(key) {
1558
+ return this.index.getEntitiesByKey(key);
1559
+ }
1560
+ resolvePk(pk) {
1561
+ return this.index.resolvePk(pk);
1562
+ }
1563
+ };
1564
+
1565
+ // src/core/OIMIndexManualArrayBased.ts
1566
+ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1567
+ constructor(options = {}) {
1568
+ super(options);
1569
+ }
1570
+ setSlots(key, slots) {
1571
+ const hasChanges = this.setSlotsWithComparison(key, slots);
1582
1572
  if (hasChanges) {
1583
1573
  this.emitUpdateOne(key);
1584
1574
  }
@@ -1602,8 +1592,8 @@ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1602
1592
  }
1603
1593
  };
1604
1594
 
1605
- // src/core/OIMReactiveIndexManualSetBased.ts
1606
- var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1595
+ // src/core/OIMReactiveIndexManualArrayBased.ts
1596
+ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1607
1597
  constructor(updateEmitter, opts) {
1608
1598
  super(opts);
1609
1599
  this.updateEmitter = updateEmitter;
@@ -1615,94 +1605,93 @@ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1615
1605
  this.updateEmitter.markUpdatedKey(key);
1616
1606
  }
1617
1607
  };
1618
- var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1608
+ var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
1619
1609
  constructor(queue, opts) {
1620
1610
  super(queue, (updateEmitter) => {
1621
- return new OIMIndexManualSetBasedReactive(
1611
+ return new OIMIndexManualArrayBasedReactive(
1622
1612
  updateEmitter,
1623
1613
  opts?.indexOptions
1624
1614
  );
1625
1615
  });
1626
1616
  }
1627
- setPks(key, pks) {
1628
- this.index.setPks(key, pks);
1629
- }
1630
1617
  setSlots(key, slots) {
1631
1618
  this.index.setSlots(key, slots);
1632
1619
  }
1633
- addPks(key, pks) {
1634
- this.index.addPks(key, pks);
1635
- }
1636
- removePks(key, pks) {
1637
- this.index.removePks(key, pks);
1638
- }
1639
1620
  clear(key) {
1640
1621
  this.index.clear(key);
1641
1622
  }
1642
1623
  };
1643
1624
 
1644
- // src/core/OIMIndexManualArrayBased.ts
1645
- var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1646
- constructor(options = {}) {
1647
- super(options);
1625
+ // src/core/OIMReactiveCollectionIndexManualArrayBased.ts
1626
+ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexManualArrayBased {
1627
+ constructor(queue, opts) {
1628
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1629
+ super(queue, {
1630
+ indexOptions: opts.indexOptions
1631
+ });
1632
+ this.resolveSlot = resolveSlot;
1648
1633
  }
1649
- /**
1650
- * Set primary keys for a specific index key, replacing any existing values.
1651
- * Uses optional comparator to skip updates if PKs haven't actually changed.
1652
- */
1653
1634
  setPks(key, pks) {
1654
- const hasChanges = this.setPksWithComparison(key, pks);
1655
- if (hasChanges) {
1656
- this.emitUpdateOne(key);
1657
- }
1658
- }
1659
- setSlots(key, slots) {
1660
- const hasChanges = this.setSlotsWithComparison(key, slots);
1661
- if (hasChanges) {
1662
- this.emitUpdateOne(key);
1663
- }
1635
+ this.setSlots(key, this.resolveSlots(pks));
1664
1636
  }
1665
- /**
1666
- * Add primary keys to a specific index key
1667
- */
1668
1637
  addPks(key, pks) {
1669
1638
  if (pks.length === 0) return;
1670
- let slotsArray = this.store.getOneByKey(key);
1671
- if (!slotsArray) {
1672
- slotsArray = [];
1673
- this.store.setOneByKey(key, slotsArray);
1674
- }
1675
- const pksSet = new Set(slotsArray.map((slot) => slot.pk));
1639
+ const existingSlots = this.index.getSlotsByKey(key);
1640
+ const existingPks = new Set(existingSlots.map((slot) => slot.pk));
1641
+ const nextSlots = existingSlots.slice();
1676
1642
  let hasChanges = false;
1677
- const nextSlots = slotsArray.slice();
1678
1643
  for (const pk of pks) {
1679
- if (!pksSet.has(pk)) {
1680
- pksSet.add(pk);
1681
- nextSlots.push(this.getOrCreateSlot(pk));
1682
- hasChanges = true;
1683
- }
1684
- }
1685
- if (hasChanges) {
1686
- this.store.setOneByKey(key, nextSlots);
1687
- this.emitUpdateOne(key);
1644
+ if (existingPks.has(pk)) continue;
1645
+ nextSlots.push(this.resolveRequiredSlot(pk));
1646
+ existingPks.add(pk);
1647
+ hasChanges = true;
1688
1648
  }
1649
+ if (hasChanges) this.setSlots(key, nextSlots);
1689
1650
  }
1690
- /**
1691
- * Remove primary keys from a specific index key
1692
- */
1693
1651
  removePks(key, pks) {
1694
1652
  if (pks.length === 0) return;
1695
- const slotsArray = this.store.getOneByKey(key);
1696
- if (!slotsArray) return;
1697
- const pksSet = new Set(pks);
1698
- const filtered = slotsArray.filter((slot) => !pksSet.has(slot.pk));
1699
- const hasChanges = filtered.length !== slotsArray.length;
1653
+ const pksToRemove = new Set(pks);
1654
+ const existingSlots = this.index.getSlotsByKey(key);
1655
+ const nextSlots = existingSlots.filter(
1656
+ (slot) => !pksToRemove.has(slot.pk)
1657
+ );
1658
+ if (nextSlots.length === existingSlots.length) return;
1659
+ if (nextSlots.length === 0) this.clear(key);
1660
+ else this.setSlots(key, nextSlots);
1661
+ }
1662
+ getEntitiesByKey(key) {
1663
+ return super.getEntitiesByKey(key);
1664
+ }
1665
+ getEntitiesByKeys(keys) {
1666
+ return super.getEntitiesByKeys(keys);
1667
+ }
1668
+ resolveSlots(pks) {
1669
+ const slots = [];
1670
+ slots.length = pks.length;
1671
+ for (let i = 0; i < pks.length; i++) {
1672
+ slots[i] = this.resolveRequiredSlot(pks[i]);
1673
+ }
1674
+ return slots;
1675
+ }
1676
+ resolveRequiredSlot(pk) {
1677
+ const slot = this.resolveSlot(pk);
1678
+ if (!slot) {
1679
+ throw new Error(
1680
+ `[OIMReactiveCollectionIndexManualArrayBased]: Unable to resolve slot for PK "${String(pk)}".`
1681
+ );
1682
+ }
1683
+ return slot;
1684
+ }
1685
+ };
1686
+
1687
+ // src/core/OIMIndexManualSetBased.ts
1688
+ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1689
+ constructor(options = {}) {
1690
+ super(options);
1691
+ }
1692
+ setSlots(key, slots) {
1693
+ const hasChanges = this.setSlotsWithComparison(key, new Set(slots));
1700
1694
  if (hasChanges) {
1701
- if (filtered.length === 0) {
1702
- this.store.removeOneByKey(key);
1703
- } else {
1704
- this.store.setOneByKey(key, filtered);
1705
- }
1706
1695
  this.emitUpdateOne(key);
1707
1696
  }
1708
1697
  }
@@ -1725,8 +1714,8 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1725
1714
  }
1726
1715
  };
1727
1716
 
1728
- // src/core/OIMReactiveIndexManualArrayBased.ts
1729
- var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1717
+ // src/core/OIMReactiveIndexManualSetBased.ts
1718
+ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1730
1719
  constructor(updateEmitter, opts) {
1731
1720
  super(opts);
1732
1721
  this.updateEmitter = updateEmitter;
@@ -1738,310 +1727,1256 @@ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1738
1727
  this.updateEmitter.markUpdatedKey(key);
1739
1728
  }
1740
1729
  };
1741
- var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
1730
+ var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1742
1731
  constructor(queue, opts) {
1743
1732
  super(queue, (updateEmitter) => {
1744
- return new OIMIndexManualArrayBasedReactive(
1733
+ return new OIMIndexManualSetBasedReactive(
1745
1734
  updateEmitter,
1746
1735
  opts?.indexOptions
1747
1736
  );
1748
1737
  });
1749
1738
  }
1750
- setPks(key, pks) {
1751
- this.index.setPks(key, pks);
1752
- }
1753
1739
  setSlots(key, slots) {
1754
1740
  this.index.setSlots(key, slots);
1755
1741
  }
1756
- addPks(key, pks) {
1757
- this.index.addPks(key, pks);
1758
- }
1759
- removePks(key, pks) {
1760
- this.index.removePks(key, pks);
1761
- }
1762
1742
  clear(key) {
1763
1743
  this.index.clear(key);
1764
1744
  }
1765
1745
  };
1766
1746
 
1767
- // src/core/OIMRICollection.ts
1768
- var OIMRICollection = class extends OIMReactiveCollection {
1747
+ // src/core/OIMReactiveCollectionIndexManualSetBased.ts
1748
+ var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexManualSetBased {
1769
1749
  constructor(queue, opts) {
1770
- super(queue, opts.collectionOpts);
1771
- this.indexes = opts.indexes || {};
1772
- const indexes = Object.values(this.indexes);
1773
- for (const index of indexes) {
1774
- index.index.setSlotResolver((pk) => this.getSlotByPk(pk));
1775
- }
1776
- }
1777
- };
1778
-
1779
- // src/core/OIMObjectStoreMapDriven.ts
1780
- var OIMObjectStoreMapDriven = class extends OIMObjectStore {
1781
- constructor() {
1782
- super(...arguments);
1783
- this.properties = /* @__PURE__ */ new Map();
1750
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1751
+ super(queue, {
1752
+ indexOptions: opts.indexOptions
1753
+ });
1754
+ this.resolveSlot = resolveSlot;
1784
1755
  }
1785
- setProperty(key, value) {
1786
- this.properties.set(key, value);
1756
+ setPks(key, pks) {
1757
+ this.setSlots(key, this.resolveSlots(pks));
1787
1758
  }
1788
- merge(draft) {
1789
- for (const key in draft) {
1790
- if (!Object.prototype.hasOwnProperty.call(draft, key)) continue;
1791
- this.properties.set(
1792
- key,
1793
- draft[key]
1794
- );
1759
+ addPks(key, pks) {
1760
+ if (pks.length === 0) return;
1761
+ const existingSlots = this.index.getSlotsByKey(key);
1762
+ const existingPks = this.index.getPksByKey(key);
1763
+ const nextSlots = new Set(existingSlots);
1764
+ let hasChanges = false;
1765
+ for (const pk of pks) {
1766
+ if (existingPks.has(pk)) continue;
1767
+ const slot = this.resolveRequiredSlot(pk);
1768
+ nextSlots.add(slot);
1769
+ existingPks.add(pk);
1770
+ hasChanges = true;
1795
1771
  }
1772
+ if (hasChanges) this.setSlots(key, nextSlots);
1796
1773
  }
1797
- delete(key) {
1798
- this.properties.delete(key);
1774
+ removePks(key, pks) {
1775
+ if (pks.length === 0) return;
1776
+ const pksToRemove = new Set(pks);
1777
+ const existingSlots = this.index.getSlotsByKey(key);
1778
+ const nextSlots = /* @__PURE__ */ new Set();
1779
+ let hasChanges = false;
1780
+ for (const slot of existingSlots) {
1781
+ if (pksToRemove.has(slot.pk)) {
1782
+ hasChanges = true;
1783
+ } else {
1784
+ nextSlots.add(slot);
1785
+ }
1786
+ }
1787
+ if (!hasChanges) return;
1788
+ if (nextSlots.size === 0) this.clear(key);
1789
+ else this.setSlots(key, nextSlots);
1799
1790
  }
1800
- get(key) {
1801
- return this.properties.get(key);
1791
+ getEntitiesByKey(key) {
1792
+ return super.getEntitiesByKey(key);
1802
1793
  }
1803
- getAll() {
1804
- return Object.fromEntries(this.properties.entries());
1794
+ getEntitiesByKeys(keys) {
1795
+ return super.getEntitiesByKeys(keys);
1805
1796
  }
1806
- clear() {
1807
- this.properties.clear();
1797
+ resolveSlots(pks) {
1798
+ const slots = /* @__PURE__ */ new Set();
1799
+ for (const pk of pks) slots.add(this.resolveRequiredSlot(pk));
1800
+ return slots;
1808
1801
  }
1809
- count() {
1810
- return this.properties.size;
1802
+ resolveRequiredSlot(pk) {
1803
+ const slot = this.resolveSlot(pk);
1804
+ if (!slot) {
1805
+ throw new Error(
1806
+ `[OIMReactiveCollectionIndexManualSetBased]: Unable to resolve slot for PK "${String(pk)}".`
1807
+ );
1808
+ }
1809
+ return slot;
1811
1810
  }
1812
- keys() {
1813
- return Array.from(this.properties.keys());
1811
+ };
1812
+
1813
+ // src/core/OIMDerivedCollectionIndexSetBased.ts
1814
+ var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndexManualSetBased {
1815
+ constructor(queue, collection, opts) {
1816
+ super(queue, {
1817
+ collection,
1818
+ indexOptions: opts.indexOptions
1819
+ });
1820
+ this.keysByPk = /* @__PURE__ */ new Map();
1821
+ this.onCollectionUpdate = (pks) => {
1822
+ if (pks.length === 0) {
1823
+ this.rebuildFromCollection();
1824
+ return;
1825
+ }
1826
+ for (const pk of pks) {
1827
+ this.syncPk(pk);
1828
+ }
1829
+ };
1830
+ this.collection = collection;
1831
+ this.selectIndexKeys = opts.selectIndexKeys;
1832
+ this.unsubscribeFromCollection = collection.emitter.on(
1833
+ 0 /* UPDATE */,
1834
+ (payload) => this.onCollectionUpdate(payload.pks)
1835
+ );
1836
+ if (opts.buildInitial !== false) {
1837
+ this.rebuildFromCollection();
1838
+ }
1814
1839
  }
1815
- values() {
1816
- return Array.from(this.properties.values());
1840
+ rebuildFromCollection() {
1841
+ this.keysByPk.clear();
1842
+ super.clear();
1843
+ const slotsByKey = /* @__PURE__ */ new Map();
1844
+ const slots = this.collection.getAllSlots();
1845
+ for (const slot of slots) {
1846
+ if (slot.item === void 0) continue;
1847
+ const keys = this.normalizeKeys(this.selectIndexKeys(slot.item));
1848
+ if (keys.length === 0) continue;
1849
+ this.keysByPk.set(slot.pk, new Set(keys));
1850
+ for (const key of keys) {
1851
+ let slotsForKey = slotsByKey.get(key);
1852
+ if (!slotsForKey) {
1853
+ slotsForKey = /* @__PURE__ */ new Set();
1854
+ slotsByKey.set(key, slotsForKey);
1855
+ }
1856
+ slotsForKey.add(slot);
1857
+ }
1858
+ }
1859
+ for (const [key, keySlots] of slotsByKey) {
1860
+ this.setSlots(key, keySlots);
1861
+ }
1817
1862
  }
1818
- entries() {
1819
- return Array.from(this.properties.entries());
1863
+ clear(key) {
1864
+ if (key === void 0) {
1865
+ this.keysByPk.clear();
1866
+ } else {
1867
+ for (const keys of this.keysByPk.values()) {
1868
+ keys.delete(key);
1869
+ }
1870
+ }
1871
+ super.clear(key);
1820
1872
  }
1821
1873
  destroy() {
1822
- this.properties.clear();
1874
+ this.unsubscribeFromCollection();
1875
+ this.keysByPk.clear();
1876
+ super.destroy();
1823
1877
  }
1824
- };
1825
-
1826
- // src/core/OIMComparatorFactory.ts
1827
- var OIMComparatorFactory = class {
1828
- createShallowComparator() {
1829
- return (a, b) => {
1830
- if (a === b) return false;
1831
- const ka = Object.keys(a);
1832
- const kb = Object.keys(b);
1833
- if (ka.length !== kb.length) return true;
1834
- for (let i = 0; i < ka.length; i++) {
1835
- const k = ka[i];
1836
- if (b[k] !== a[k])
1837
- return true;
1878
+ syncPk(pk) {
1879
+ const prevKeys = this.keysByPk.get(pk) ?? /* @__PURE__ */ new Set();
1880
+ const slot = this.collection.getSlotByPk(pk);
1881
+ const entity = slot?.item;
1882
+ const nextKeys = entity === void 0 ? /* @__PURE__ */ new Set() : new Set(this.normalizeKeys(this.selectIndexKeys(entity)));
1883
+ for (const key of prevKeys) {
1884
+ if (!nextKeys.has(key)) {
1885
+ this.removePks(key, [pk]);
1838
1886
  }
1839
- return false;
1840
- };
1887
+ }
1888
+ if (entity !== void 0) {
1889
+ for (const key of nextKeys) {
1890
+ if (!prevKeys.has(key)) {
1891
+ this.addPks(key, [pk]);
1892
+ }
1893
+ }
1894
+ }
1895
+ if (nextKeys.size === 0) {
1896
+ this.keysByPk.delete(pk);
1897
+ } else {
1898
+ this.keysByPk.set(pk, nextKeys);
1899
+ }
1841
1900
  }
1842
- };
1843
-
1844
- // src/core/OIMEventQueue.ts
1845
- var OIMEventQueue = class {
1846
- constructor(options = {}) {
1847
- this.emitter = new OIMEventEmitter();
1848
- this.tasks = /* @__PURE__ */ new Set();
1849
- this.isFlushing = false;
1850
- this.scheduler = options.scheduler;
1851
- if (this.scheduler) {
1852
- this.flushBound = () => this.flush();
1853
- this.scheduler.on(
1854
- 0 /* FLUSH */,
1855
- this.flushBound
1856
- );
1901
+ normalizeKeys(keys) {
1902
+ if (keys === void 0 || keys === null) return [];
1903
+ const rawKeys = Array.isArray(keys) ? keys : [keys];
1904
+ const result = [];
1905
+ const seen = /* @__PURE__ */ new Set();
1906
+ for (const key of rawKeys) {
1907
+ if (seen.has(key)) continue;
1908
+ seen.add(key);
1909
+ result.push(key);
1857
1910
  }
1911
+ return result;
1858
1912
  }
1859
- /**
1860
- * Enqueue a one-shot task to be executed on the next flush.
1861
- * Returns a dequeue function that can be called to cancel the task before it runs.
1862
- */
1863
- enqueue(fn) {
1864
- let isActive = true;
1865
- const task = () => {
1866
- if (!isActive) return;
1867
- fn();
1868
- };
1869
- this.tasks.add(task);
1870
- this.ensureScheduled();
1871
- return () => {
1872
- if (!isActive) return;
1873
- isActive = false;
1874
- this.tasks.delete(task);
1913
+ };
1914
+
1915
+ // src/core/OIMDerivedCollectionIndexArrayBased.ts
1916
+ var OIMDerivedCollectionIndexArrayBased = class extends OIMReactiveCollectionIndexManualArrayBased {
1917
+ constructor(queue, collection, opts) {
1918
+ super(queue, {
1919
+ collection,
1920
+ indexOptions: opts.indexOptions
1921
+ });
1922
+ this.keysByPk = /* @__PURE__ */ new Map();
1923
+ this.onCollectionUpdate = (pks) => {
1924
+ if (pks.length === 0) {
1925
+ this.rebuildFromCollection();
1926
+ return;
1927
+ }
1928
+ const affectedKeys = /* @__PURE__ */ new Set();
1929
+ for (const pk of pks) {
1930
+ this.trackPkKeys(pk, affectedKeys);
1931
+ }
1932
+ for (const key of affectedKeys) {
1933
+ this.rebuildKey(key);
1934
+ }
1875
1935
  };
1936
+ this.collection = collection;
1937
+ this.selectIndexKeys = opts.selectIndexKeys;
1938
+ this.compareEntities = opts.compareEntities;
1939
+ this.orderBy = opts.orderBy;
1940
+ this.unsubscribeFromCollection = collection.emitter.on(
1941
+ 0 /* UPDATE */,
1942
+ (payload) => this.onCollectionUpdate(payload.pks)
1943
+ );
1944
+ if (opts.buildInitial !== false) {
1945
+ this.rebuildFromCollection();
1946
+ }
1876
1947
  }
1877
- get isInFlush() {
1878
- return this.isFlushing;
1879
- }
1880
- /**
1881
- * Dequeue (cancel) a previously enqueued task by its dequeue function.
1882
- */
1883
- dequeue(dequeueFn) {
1884
- dequeueFn();
1885
- }
1886
- /**
1887
- * Execute all pending tasks.
1888
- *
1889
- * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
1890
- * executed within the same flush.
1891
- */
1892
- flush() {
1893
- this.scheduler?.cancel();
1894
- this.isFlushing = true;
1895
- this.emitter.emit(1 /* BEFORE_FLUSH */, void 0);
1896
- const snapshot = Array.from(this.tasks);
1897
- this.tasks.clear();
1898
- for (let i = 0; i < snapshot.length; i++) {
1899
- snapshot[i]();
1948
+ rebuildFromCollection() {
1949
+ this.keysByPk.clear();
1950
+ super.clear();
1951
+ const slotsByKey = /* @__PURE__ */ new Map();
1952
+ const slots = this.collection.getAllSlots();
1953
+ for (const slot of slots) {
1954
+ if (slot.item === void 0) continue;
1955
+ const keys = this.normalizeKeys(this.selectIndexKeys(slot.item));
1956
+ if (keys.length === 0) continue;
1957
+ this.keysByPk.set(slot.pk, new Set(keys));
1958
+ for (const key of keys) {
1959
+ let slotsForKey = slotsByKey.get(key);
1960
+ if (!slotsForKey) {
1961
+ slotsForKey = [];
1962
+ slotsByKey.set(key, slotsForKey);
1963
+ }
1964
+ slotsForKey.push(slot);
1965
+ }
1966
+ }
1967
+ for (const [key, keySlots] of slotsByKey) {
1968
+ this.setSlots(key, this.sortSlots(keySlots));
1900
1969
  }
1901
- this.isFlushing = false;
1902
- this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
1903
1970
  }
1904
- /**
1905
- * Get the current number of queued functions.
1906
- */
1907
- get length() {
1908
- return this.tasks.size;
1971
+ clear(key) {
1972
+ if (key === void 0) {
1973
+ this.keysByPk.clear();
1974
+ } else {
1975
+ for (const keys of this.keysByPk.values()) {
1976
+ keys.delete(key);
1977
+ }
1978
+ }
1979
+ super.clear(key);
1909
1980
  }
1910
- /**
1911
- * Check if the queue is empty.
1912
- */
1913
- get isEmpty() {
1914
- return this.tasks.size === 0;
1981
+ destroy() {
1982
+ this.unsubscribeFromCollection();
1983
+ this.keysByPk.clear();
1984
+ super.destroy();
1915
1985
  }
1916
- /**
1917
- * Clear the queue without executing functions and cancel any scheduled flush.
1918
- */
1919
- clear() {
1920
- this.tasks.clear();
1921
- this.scheduler?.cancel();
1986
+ trackPkKeys(pk, affectedKeys) {
1987
+ const prevKeys = this.keysByPk.get(pk) ?? /* @__PURE__ */ new Set();
1988
+ for (const key of prevKeys) affectedKeys.add(key);
1989
+ const slot = this.collection.getSlotByPk(pk);
1990
+ const entity = slot?.item;
1991
+ const nextKeys = entity === void 0 ? /* @__PURE__ */ new Set() : new Set(this.normalizeKeys(this.selectIndexKeys(entity)));
1992
+ for (const key of nextKeys) affectedKeys.add(key);
1993
+ if (nextKeys.size === 0) {
1994
+ this.keysByPk.delete(pk);
1995
+ } else {
1996
+ this.keysByPk.set(pk, nextKeys);
1997
+ }
1922
1998
  }
1923
- ensureScheduled() {
1924
- if (!this.scheduler) return;
1925
- if (this.isFlushing) return;
1926
- if (this.tasks.size === 1) {
1927
- this.scheduler.schedule();
1999
+ rebuildKey(key) {
2000
+ const keySlots = [];
2001
+ const slots = this.collection.getAllSlots();
2002
+ for (const slot of slots) {
2003
+ if (slot.item === void 0) continue;
2004
+ const keys = this.keysByPk.get(slot.pk);
2005
+ if (keys?.has(key)) keySlots.push(slot);
2006
+ }
2007
+ if (keySlots.length === 0) {
2008
+ super.clear(key);
2009
+ } else {
2010
+ this.setSlots(key, this.sortSlots(keySlots));
1928
2011
  }
1929
2012
  }
1930
- /**
1931
- * Clean up scheduler subscription when queue is no longer needed.
1932
- */
1933
- destroy() {
1934
- if (this.scheduler && this.flushBound) {
1935
- this.scheduler.off(
1936
- 0 /* FLUSH */,
1937
- this.flushBound
1938
- );
2013
+ sortSlots(slots) {
2014
+ if (!this.compareEntities && !this.orderBy) return slots;
2015
+ return slots.slice().sort((a, b) => {
2016
+ const aItem = a.item;
2017
+ const bItem = b.item;
2018
+ if (aItem === void 0 || bItem === void 0) return 0;
2019
+ if (this.compareEntities) {
2020
+ return this.compareEntities(aItem, bItem);
2021
+ }
2022
+ const aValue = this.orderBy(aItem);
2023
+ const bValue = this.orderBy(bItem);
2024
+ if (aValue < bValue) return -1;
2025
+ if (aValue > bValue) return 1;
2026
+ return 0;
2027
+ });
2028
+ }
2029
+ normalizeKeys(keys) {
2030
+ if (keys === void 0 || keys === null) return [];
2031
+ const rawKeys = Array.isArray(keys) ? keys : [keys];
2032
+ const result = [];
2033
+ const seen = /* @__PURE__ */ new Set();
2034
+ for (const key of rawKeys) {
2035
+ if (seen.has(key)) continue;
2036
+ seen.add(key);
2037
+ result.push(key);
1939
2038
  }
1940
- this.clear();
2039
+ return result;
1941
2040
  }
1942
2041
  };
1943
2042
 
1944
- // src/core/OIMIndexComparatorFactory.ts
1945
- var OIMIndexComparatorFactory = class {
1946
- /**
1947
- * Create an element-wise comparator that checks arrays for strict equality.
1948
- * Compares array lengths and each element using strict equality (===).
1949
- */
1950
- static createElementWiseComparator() {
1951
- return (existingPks, newPks) => {
1952
- if (existingPks.length !== newPks.length) {
1953
- return false;
2043
+ // src/core/OIMCollectionIndexFactory.ts
2044
+ var OIMCollectionIndexFactory = class {
2045
+ constructor(queue, collection) {
2046
+ this.queue = queue;
2047
+ this.collection = collection;
2048
+ }
2049
+ setBasedIndex(opts = {}) {
2050
+ return new OIMReactiveCollectionIndexManualSetBased(this.queue, {
2051
+ collection: this.collection,
2052
+ indexOptions: opts.indexOptions
2053
+ });
2054
+ }
2055
+ derivedSetIndex(selectIndexKeys, opts = {}) {
2056
+ return new OIMDerivedCollectionIndexSetBased(
2057
+ this.queue,
2058
+ this.collection,
2059
+ {
2060
+ selectIndexKeys,
2061
+ buildInitial: opts.buildInitial,
2062
+ indexOptions: opts.indexOptions
1954
2063
  }
1955
- for (let i = 0; i < existingPks.length; i++) {
1956
- if (existingPks[i] !== newPks[i]) {
1957
- return false;
1958
- }
2064
+ );
2065
+ }
2066
+ arrayBasedIndex(opts = {}) {
2067
+ return new OIMReactiveCollectionIndexManualArrayBased(this.queue, {
2068
+ collection: this.collection,
2069
+ indexOptions: opts.indexOptions
2070
+ });
2071
+ }
2072
+ derivedArrayIndex(selectIndexKeys, opts = {}) {
2073
+ return new OIMDerivedCollectionIndexArrayBased(
2074
+ this.queue,
2075
+ this.collection,
2076
+ {
2077
+ selectIndexKeys,
2078
+ buildInitial: opts.buildInitial,
2079
+ compareEntities: opts.compareEntities,
2080
+ orderBy: opts.orderBy,
2081
+ indexOptions: opts.indexOptions
1959
2082
  }
1960
- return true;
1961
- };
2083
+ );
1962
2084
  }
1963
- /**
1964
- * Create a set-based comparator that checks if arrays contain the same elements.
1965
- * Order doesn't matter, only the presence of elements.
1966
- */
1967
- static createSetBasedComparator() {
1968
- return (existingPks, newPks) => {
1969
- if (existingPks.length !== newPks.length) {
1970
- return false;
1971
- }
1972
- const existingSet = new Set(existingPks);
1973
- const newSet = new Set(newPks);
1974
- if (existingSet.size !== newSet.size || existingSet.size !== existingPks.length) {
1975
- return false;
1976
- }
1977
- for (const pk of Array.from(existingSet)) {
1978
- if (!newSet.has(pk)) {
1979
- return false;
1980
- }
2085
+ orderedIndex() {
2086
+ return new OIMCollectionIndexManualOrderedArrayBased({
2087
+ collection: this.collection
2088
+ });
2089
+ }
2090
+ orderedList(opts = {}) {
2091
+ return new OIMCollectionOrderedListCommandStream(
2092
+ this.queue,
2093
+ opts.index ? { collection: this.collection, index: opts.index } : { collection: this.collection }
2094
+ );
2095
+ }
2096
+ };
2097
+ function createOIMCollectionIndexFactory(queue, collection) {
2098
+ return new OIMCollectionIndexFactory(queue, collection);
2099
+ }
2100
+
2101
+ // src/core/OIMObjectStoreRecordDriven.ts
2102
+ var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
2103
+ constructor(initial) {
2104
+ super();
2105
+ this.properties = Object.assign(/* @__PURE__ */ Object.create(null), initial ?? {});
2106
+ }
2107
+ setProperty(key, value) {
2108
+ this.properties[key] = value;
2109
+ }
2110
+ merge(draft) {
2111
+ Object.assign(this.properties, draft);
2112
+ }
2113
+ delete(key) {
2114
+ delete this.properties[key];
2115
+ }
2116
+ get(key) {
2117
+ return this.properties[key];
2118
+ }
2119
+ getAll() {
2120
+ return { ...this.properties };
2121
+ }
2122
+ clear() {
2123
+ this.properties = /* @__PURE__ */ Object.create(null);
2124
+ }
2125
+ count() {
2126
+ return Object.keys(this.properties).length;
2127
+ }
2128
+ keys() {
2129
+ return Object.keys(this.properties);
2130
+ }
2131
+ values() {
2132
+ return Object.values(this.properties);
2133
+ }
2134
+ entries() {
2135
+ return Object.entries(this.properties);
2136
+ }
2137
+ destroy() {
2138
+ this.clear();
2139
+ }
2140
+ };
2141
+
2142
+ // src/enums/EOIMObjectEventType.ts
2143
+ var EOIMObjectEventType = /* @__PURE__ */ ((EOIMObjectEventType2) => {
2144
+ EOIMObjectEventType2[EOIMObjectEventType2["UPDATE"] = 0] = "UPDATE";
2145
+ return EOIMObjectEventType2;
2146
+ })(EOIMObjectEventType || {});
2147
+
2148
+ // src/core/OIMObject.ts
2149
+ var OIMObject = class {
2150
+ constructor(opts) {
2151
+ this.emitter = new OIMEventEmitter();
2152
+ this.store = opts?.store ?? new OIMObjectStoreRecordDriven();
2153
+ }
2154
+ setProperty(key, value) {
2155
+ this.store.setProperty(key, value);
2156
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
2157
+ }
2158
+ merge(draft) {
2159
+ this.store.merge(draft);
2160
+ this.emitter.emit(0 /* UPDATE */, {
2161
+ keys: Object.keys(draft)
2162
+ });
2163
+ }
2164
+ delete(key) {
2165
+ this.store.delete(key);
2166
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
2167
+ }
2168
+ get(key) {
2169
+ return this.store.get(key);
2170
+ }
2171
+ getAll() {
2172
+ return this.store.getAll();
2173
+ }
2174
+ clear() {
2175
+ this.store.clear();
2176
+ this.emitter.emit(0 /* UPDATE */, { keys: [] });
2177
+ }
2178
+ count() {
2179
+ return this.store.count();
2180
+ }
2181
+ keys() {
2182
+ return this.store.keys();
2183
+ }
2184
+ values() {
2185
+ return this.store.values();
2186
+ }
2187
+ entries() {
2188
+ return this.store.entries();
2189
+ }
2190
+ destroy() {
2191
+ this.store.destroy();
2192
+ this.emitter.offAll();
2193
+ }
2194
+ };
2195
+
2196
+ // src/core/OIMReactiveObject.ts
2197
+ var OIMReactiveObject = class extends OIMObject {
2198
+ constructor(queue, opts) {
2199
+ super(opts);
2200
+ this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
2201
+ this.emitter.on(0 /* UPDATE */, (payload) => {
2202
+ this.updateEmitter.markUpdatedKeys(payload.keys);
2203
+ });
2204
+ }
2205
+ subscribeOnKey(key, handler) {
2206
+ return this.updateEmitter.subscribeOnKey(key, handler);
2207
+ }
2208
+ subscribeOnKeys(keys, handler) {
2209
+ return this.updateEmitter.subscribeOnKeys(keys, handler);
2210
+ }
2211
+ unsubscribeFromKey(key, handler) {
2212
+ this.updateEmitter.unsubscribeFromKey(key, handler);
2213
+ }
2214
+ unsubscribeFromKeys(keys, handler) {
2215
+ this.updateEmitter.unsubscribeFromKeys(keys, handler);
2216
+ }
2217
+ hasSubscriptions() {
2218
+ return this.updateEmitter.hasSubscriptions();
2219
+ }
2220
+ getHandlerCount(key) {
2221
+ return this.updateEmitter.getHandlerCount(key);
2222
+ }
2223
+ getMetrics() {
2224
+ return this.updateEmitter.getMetrics();
2225
+ }
2226
+ destroy() {
2227
+ this.updateEmitter.destroy();
2228
+ super.destroy();
2229
+ }
2230
+ };
2231
+
2232
+ // src/core/OIMObjectStoreMapDriven.ts
2233
+ var OIMObjectStoreMapDriven = class extends OIMObjectStore {
2234
+ constructor() {
2235
+ super(...arguments);
2236
+ this.properties = /* @__PURE__ */ new Map();
2237
+ }
2238
+ setProperty(key, value) {
2239
+ this.properties.set(key, value);
2240
+ }
2241
+ merge(draft) {
2242
+ for (const key in draft) {
2243
+ if (!Object.prototype.hasOwnProperty.call(draft, key)) continue;
2244
+ this.properties.set(
2245
+ key,
2246
+ draft[key]
2247
+ );
2248
+ }
2249
+ }
2250
+ delete(key) {
2251
+ this.properties.delete(key);
2252
+ }
2253
+ get(key) {
2254
+ return this.properties.get(key);
2255
+ }
2256
+ getAll() {
2257
+ return Object.fromEntries(this.properties.entries());
2258
+ }
2259
+ clear() {
2260
+ this.properties.clear();
2261
+ }
2262
+ count() {
2263
+ return this.properties.size;
2264
+ }
2265
+ keys() {
2266
+ return Array.from(this.properties.keys());
2267
+ }
2268
+ values() {
2269
+ return Array.from(this.properties.values());
2270
+ }
2271
+ entries() {
2272
+ return Array.from(this.properties.entries());
2273
+ }
2274
+ destroy() {
2275
+ this.properties.clear();
2276
+ }
2277
+ };
2278
+
2279
+ // src/core/OIMComparatorFactory.ts
2280
+ var OIMComparatorFactory = class {
2281
+ createShallowComparator() {
2282
+ return (a, b) => {
2283
+ if (a === b) return false;
2284
+ const ka = Object.keys(a);
2285
+ const kb = Object.keys(b);
2286
+ if (ka.length !== kb.length) return true;
2287
+ for (let i = 0; i < ka.length; i++) {
2288
+ const k = ka[i];
2289
+ if (b[k] !== a[k])
2290
+ return true;
1981
2291
  }
1982
- return true;
2292
+ return false;
1983
2293
  };
1984
2294
  }
2295
+ };
2296
+
2297
+ // src/core/OIMEventQueue.ts
2298
+ var OIMEventQueue = class {
2299
+ constructor(options = {}) {
2300
+ this.emitter = new OIMEventEmitter();
2301
+ this.tasks = /* @__PURE__ */ new Set();
2302
+ this.isFlushing = false;
2303
+ this.scheduler = options.scheduler;
2304
+ if (this.scheduler) {
2305
+ this.flushBound = () => this.flush();
2306
+ this.scheduler.on(
2307
+ 0 /* FLUSH */,
2308
+ this.flushBound
2309
+ );
2310
+ }
2311
+ }
1985
2312
  /**
1986
- * Create a shallow comparator that only checks array references.
1987
- * Fastest but only works if you're reusing the same array instances.
2313
+ * Enqueue a one-shot task to be executed on the next flush.
2314
+ * Returns a dequeue function that can be called to cancel the task before it runs.
1988
2315
  */
1989
- static createShallowComparator() {
1990
- return (existingPks, newPks) => {
1991
- return existingPks === newPks;
2316
+ enqueue(fn) {
2317
+ let isActive = true;
2318
+ const task = () => {
2319
+ if (!isActive) return;
2320
+ fn();
1992
2321
  };
2322
+ this.tasks.add(task);
2323
+ this.ensureScheduled();
2324
+ return () => {
2325
+ if (!isActive) return;
2326
+ isActive = false;
2327
+ this.tasks.delete(task);
2328
+ };
2329
+ }
2330
+ get isInFlush() {
2331
+ return this.isFlushing;
1993
2332
  }
1994
2333
  /**
1995
- * Create a no-comparison comparator that always returns false (always updates).
1996
- * Useful for disabling comparison while keeping the same interface.
2334
+ * Dequeue (cancel) a previously enqueued task by its dequeue function.
1997
2335
  */
1998
- static createAlwaysUpdateComparator() {
1999
- return () => false;
2336
+ dequeue(dequeueFn) {
2337
+ dequeueFn();
2338
+ }
2339
+ /**
2340
+ * Execute all pending tasks.
2341
+ *
2342
+ * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
2343
+ * executed within the same flush.
2344
+ */
2345
+ flush() {
2346
+ this.scheduler?.cancel();
2347
+ this.isFlushing = true;
2348
+ this.emitter.emit(1 /* BEFORE_FLUSH */, void 0);
2349
+ const snapshot = Array.from(this.tasks);
2350
+ this.tasks.clear();
2351
+ for (let i = 0; i < snapshot.length; i++) {
2352
+ snapshot[i]();
2353
+ }
2354
+ this.isFlushing = false;
2355
+ this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
2356
+ }
2357
+ /**
2358
+ * Get the current number of queued functions.
2359
+ */
2360
+ get length() {
2361
+ return this.tasks.size;
2362
+ }
2363
+ /**
2364
+ * Check if the queue is empty.
2365
+ */
2366
+ get isEmpty() {
2367
+ return this.tasks.size === 0;
2368
+ }
2369
+ /**
2370
+ * Clear the queue without executing functions and cancel any scheduled flush.
2371
+ */
2372
+ clear() {
2373
+ this.tasks.clear();
2374
+ this.scheduler?.cancel();
2375
+ }
2376
+ ensureScheduled() {
2377
+ if (!this.scheduler) return;
2378
+ if (this.isFlushing) return;
2379
+ if (this.tasks.size === 1) {
2380
+ this.scheduler.schedule();
2381
+ }
2382
+ }
2383
+ /**
2384
+ * Clean up scheduler subscription when queue is no longer needed.
2385
+ */
2386
+ destroy() {
2387
+ if (this.scheduler && this.flushBound) {
2388
+ this.scheduler.off(
2389
+ 0 /* FLUSH */,
2390
+ this.flushBound
2391
+ );
2392
+ }
2393
+ this.clear();
2394
+ }
2395
+ };
2396
+
2397
+ // src/core/OIMIndexComparatorFactory.ts
2398
+ var OIMIndexComparatorFactory = class {
2399
+ /**
2400
+ * Create an element-wise comparator that checks arrays for strict equality.
2401
+ * Compares array lengths and each element using strict equality (===).
2402
+ */
2403
+ static createElementWiseComparator() {
2404
+ return (existingPks, newPks) => {
2405
+ if (existingPks.length !== newPks.length) {
2406
+ return false;
2407
+ }
2408
+ for (let i = 0; i < existingPks.length; i++) {
2409
+ if (existingPks[i] !== newPks[i]) {
2410
+ return false;
2411
+ }
2412
+ }
2413
+ return true;
2414
+ };
2415
+ }
2416
+ /**
2417
+ * Create a set-based comparator that checks if arrays contain the same elements.
2418
+ * Order doesn't matter, only the presence of elements.
2419
+ */
2420
+ static createSetBasedComparator() {
2421
+ return (existingPks, newPks) => {
2422
+ if (existingPks.length !== newPks.length) {
2423
+ return false;
2424
+ }
2425
+ const existingSet = new Set(existingPks);
2426
+ const newSet = new Set(newPks);
2427
+ if (existingSet.size !== newSet.size || existingSet.size !== existingPks.length) {
2428
+ return false;
2429
+ }
2430
+ for (const pk of Array.from(existingSet)) {
2431
+ if (!newSet.has(pk)) {
2432
+ return false;
2433
+ }
2434
+ }
2435
+ return true;
2436
+ };
2437
+ }
2438
+ /**
2439
+ * Create a shallow comparator that only checks array references.
2440
+ * Fastest but only works if you're reusing the same array instances.
2441
+ */
2442
+ static createShallowComparator() {
2443
+ return (existingPks, newPks) => {
2444
+ return existingPks === newPks;
2445
+ };
2446
+ }
2447
+ /**
2448
+ * Create a no-comparison comparator that always returns false (always updates).
2449
+ * Useful for disabling comparison while keeping the same interface.
2450
+ */
2451
+ static createAlwaysUpdateComparator() {
2452
+ return () => false;
2453
+ }
2454
+ };
2455
+
2456
+ // src/core/OIMMap2Keys.ts
2457
+ var OIMMap2Keys = class {
2458
+ constructor() {
2459
+ this.map = /* @__PURE__ */ new Map();
2460
+ this._size = 0;
2461
+ }
2462
+ get size() {
2463
+ return this._size;
2464
+ }
2465
+ set(k1, k2, v) {
2466
+ let m = this.map.get(k1);
2467
+ if (!m) {
2468
+ m = /* @__PURE__ */ new Map();
2469
+ this.map.set(k1, m);
2470
+ }
2471
+ if (!m.has(k2)) this._size++;
2472
+ m.set(k2, v);
2473
+ }
2474
+ get(k1, k2) {
2475
+ return this.map.get(k1)?.get(k2);
2476
+ }
2477
+ has(k1, k2) {
2478
+ return this.map.get(k1)?.has(k2);
2479
+ }
2480
+ delete(k1, k2) {
2481
+ const m = this.map.get(k1);
2482
+ if (!m) return false;
2483
+ const had = m.delete(k2);
2484
+ if (had) {
2485
+ this._size--;
2486
+ if (m.size === 0) this.map.delete(k1);
2487
+ }
2488
+ return had;
2489
+ }
2490
+ clear() {
2491
+ this.map.clear();
2492
+ this._size = 0;
2493
+ }
2494
+ };
2495
+
2496
+ // src/modules/compute/core/OIMComputeRuntime.ts
2497
+ var OIMComputeRuntime = class {
2498
+ constructor(queue) {
2499
+ this.queue = queue;
2500
+ this.isFlushScheduled = false;
2501
+ this.isFlushing = false;
2502
+ this.minLevel = Infinity;
2503
+ this.maxLevel = -Infinity;
2504
+ this.dirtyByLevel = /* @__PURE__ */ new Map();
2505
+ this.afterFlushTasks = /* @__PURE__ */ new Set();
2506
+ this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
2507
+ if (!this.isFlushScheduled) return;
2508
+ this.flush();
2509
+ });
2510
+ }
2511
+ /**
2512
+ * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
2513
+ * ordered by `level` ascending.
2514
+ */
2515
+ schedule(task, level = 0) {
2516
+ let isActive = true;
2517
+ const wrapped = () => {
2518
+ if (!isActive) return;
2519
+ task();
2520
+ };
2521
+ let bucket = this.dirtyByLevel.get(level);
2522
+ if (!bucket) {
2523
+ bucket = /* @__PURE__ */ new Set();
2524
+ this.dirtyByLevel.set(level, bucket);
2525
+ }
2526
+ bucket.add(wrapped);
2527
+ if (level < this.minLevel) this.minLevel = level;
2528
+ if (level > this.maxLevel) this.maxLevel = level;
2529
+ if (!this.isFlushing) this.isFlushScheduled = true;
2530
+ return () => {
2531
+ if (!isActive) return;
2532
+ isActive = false;
2533
+ bucket.delete(wrapped);
2534
+ };
2535
+ }
2536
+ /**
2537
+ * Run once at the start of the next runtime flush (i.e. after queue.flush()).
2538
+ * Useful for resubscribing dependencies exactly once per source flush.
2539
+ */
2540
+ scheduleAfterFlush(task) {
2541
+ let isActive = true;
2542
+ const wrapped = () => {
2543
+ if (!isActive) return;
2544
+ task();
2545
+ };
2546
+ this.afterFlushTasks.add(wrapped);
2547
+ if (!this.isFlushing) this.isFlushScheduled = true;
2548
+ return () => {
2549
+ if (!isActive) return;
2550
+ isActive = false;
2551
+ this.afterFlushTasks.delete(wrapped);
2552
+ };
2553
+ }
2554
+ flush() {
2555
+ this.isFlushScheduled = false;
2556
+ if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
2557
+ return;
2558
+ this.isFlushing = true;
2559
+ try {
2560
+ if (this.afterFlushTasks.size > 0) {
2561
+ const snapshot = Array.from(this.afterFlushTasks);
2562
+ this.afterFlushTasks.clear();
2563
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
2564
+ }
2565
+ if (this.dirtyByLevel.size === 0) return;
2566
+ let level = this.minLevel;
2567
+ let max = this.maxLevel;
2568
+ while (level <= max) {
2569
+ for (; ; ) {
2570
+ const bucket = this.dirtyByLevel.get(level);
2571
+ if (!bucket || bucket.size === 0) break;
2572
+ this.dirtyByLevel.delete(level);
2573
+ const snapshot = Array.from(bucket);
2574
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
2575
+ }
2576
+ if (this.maxLevel > max) max = this.maxLevel;
2577
+ level++;
2578
+ }
2579
+ } finally {
2580
+ this.isFlushing = false;
2581
+ if (this.dirtyByLevel.size === 0) {
2582
+ this.minLevel = Infinity;
2583
+ this.maxLevel = -Infinity;
2584
+ } else {
2585
+ let nextMin = Infinity;
2586
+ let nextMax = -Infinity;
2587
+ this.dirtyByLevel.forEach((_bucket, lvl) => {
2588
+ if (lvl < nextMin) nextMin = lvl;
2589
+ if (lvl > nextMax) nextMax = lvl;
2590
+ });
2591
+ this.minLevel = nextMin;
2592
+ this.maxLevel = nextMax;
2593
+ this.isFlushScheduled = true;
2594
+ }
2595
+ }
2596
+ }
2597
+ };
2598
+
2599
+ // src/modules/selector/core/OIMSelector.ts
2600
+ var OIMSelector = class {
2601
+ constructor(runtime, sourceDeps, computedDeps = []) {
2602
+ this.runtime = runtime;
2603
+ this.sourceDeps = sourceDeps;
2604
+ this.computedDeps = computedDeps;
2605
+ this.listeners = /* @__PURE__ */ new Set();
2606
+ this.sourceUnsubscribers = [];
2607
+ this.computedUnsubscribers = [];
2608
+ this.isResubscribeEnqueued = false;
2609
+ this.isTemporarilyUnsubscribedForFlush = false;
2610
+ this.isDeliveryEnqueued = false;
2611
+ this.hasLastValue = false;
2612
+ this.onSourceInvalidate = () => {
2613
+ if (this.listeners.size === 0) return;
2614
+ if (!this.isTemporarilyUnsubscribedForFlush) {
2615
+ this.isTemporarilyUnsubscribedForFlush = true;
2616
+ this.unsubscribeFromSourceDeps();
2617
+ this.ensureResubscribeAfterFlush();
2618
+ }
2619
+ if (this.isDeliveryEnqueued) return;
2620
+ this.isDeliveryEnqueued = true;
2621
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2622
+ };
2623
+ this.onComputedInvalidate = () => {
2624
+ if (this.listeners.size === 0) return;
2625
+ if (this.isDeliveryEnqueued) return;
2626
+ this.isDeliveryEnqueued = true;
2627
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2628
+ };
2629
+ this.deliver = () => {
2630
+ this.isDeliveryEnqueued = false;
2631
+ this.dequeueDelivery = void 0;
2632
+ if (this.listeners.size === 0) return;
2633
+ const next = this.getValue();
2634
+ if (this.hasLastValue && this.areEqual(this.lastValue, next)) return;
2635
+ this.setLastValue(next);
2636
+ const snapshot = Array.from(this.listeners);
2637
+ for (let i = 0; i < snapshot.length; i++) {
2638
+ const listener = snapshot[i];
2639
+ if (this.listeners.has(listener)) listener(next);
2640
+ }
2641
+ };
2642
+ this.onAfterFlush = () => {
2643
+ if (!this.isResubscribeEnqueued) return;
2644
+ this.isResubscribeEnqueued = false;
2645
+ this.dequeueResubscribe = void 0;
2646
+ if (this.listeners.size === 0) return;
2647
+ this.isTemporarilyUnsubscribedForFlush = false;
2648
+ if (this.sourceUnsubscribers.length === 0) {
2649
+ for (const dep of this.sourceDeps) {
2650
+ this.sourceUnsubscribers.push(
2651
+ dep.subscribe(this.onSourceInvalidate)
2652
+ );
2653
+ }
2654
+ }
2655
+ };
2656
+ }
2657
+ get queue() {
2658
+ return this.runtime.queue;
2659
+ }
2660
+ /**
2661
+ * Start watching this selector. The callback is called immediately with the current value,
2662
+ * then coalesced and delivered on `queue.flush()` when the selector invalidates.
2663
+ */
2664
+ watch(onValue) {
2665
+ this.listeners.add(onValue);
2666
+ if (this.listeners.size === 1) {
2667
+ this.subscribeToDeps();
2668
+ }
2669
+ const current = this.getValue();
2670
+ this.setLastValue(current);
2671
+ onValue(current);
2672
+ return () => {
2673
+ this.listeners.delete(onValue);
2674
+ if (this.listeners.size > 0) return;
2675
+ this.unsubscribeFromAllDeps();
2676
+ if (this.dequeueResubscribe) {
2677
+ this.dequeueResubscribe();
2678
+ this.dequeueResubscribe = void 0;
2679
+ }
2680
+ this.isResubscribeEnqueued = false;
2681
+ this.isTemporarilyUnsubscribedForFlush = false;
2682
+ if (this.dequeueDelivery) {
2683
+ this.dequeueDelivery();
2684
+ this.dequeueDelivery = void 0;
2685
+ }
2686
+ this.isDeliveryEnqueued = false;
2687
+ this.hasLastValue = false;
2688
+ };
2689
+ }
2690
+ areEqual(prev, next) {
2691
+ return Object.is(prev, next);
2692
+ }
2693
+ setLastValue(value) {
2694
+ this.lastValue = value;
2695
+ this.hasLastValue = true;
2696
+ }
2697
+ subscribeToDeps() {
2698
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2699
+ return;
2700
+ for (const dep of this.sourceDeps) {
2701
+ this.sourceUnsubscribers.push(
2702
+ dep.subscribe(this.onSourceInvalidate)
2703
+ );
2704
+ }
2705
+ for (const dep of this.computedDeps) {
2706
+ this.computedUnsubscribers.push(
2707
+ dep.subscribe(this.onComputedInvalidate)
2708
+ );
2709
+ }
2710
+ }
2711
+ unsubscribeFromSourceDeps() {
2712
+ if (this.sourceUnsubscribers.length === 0) return;
2713
+ for (const unsub of this.sourceUnsubscribers) unsub();
2714
+ this.sourceUnsubscribers = [];
2715
+ }
2716
+ unsubscribeFromAllDeps() {
2717
+ this.unsubscribeFromSourceDeps();
2718
+ if (this.computedUnsubscribers.length === 0) return;
2719
+ for (const unsub of this.computedUnsubscribers) unsub();
2720
+ this.computedUnsubscribers = [];
2721
+ }
2722
+ ensureResubscribeAfterFlush() {
2723
+ if (this.isResubscribeEnqueued) return;
2724
+ this.isResubscribeEnqueued = true;
2725
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2726
+ this.onAfterFlush
2727
+ );
2728
+ }
2729
+ };
2730
+
2731
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKey.ts
2732
+ var OIMSelectorSourceDependencyUpdateEventEmitterKey = class {
2733
+ constructor(subscription, key) {
2734
+ this.subscription = subscription;
2735
+ this.key = key;
2736
+ }
2737
+ subscribe(onUpdate) {
2738
+ return this.subscription.subscribeOnKey(this.key, onUpdate);
2739
+ }
2740
+ };
2741
+
2742
+ // src/modules/selector/core/OIMCollectionByPkSelector.ts
2743
+ var OIMCollectionByPkSelector = class extends OIMSelector {
2744
+ constructor(runtime, collection, pk) {
2745
+ super(runtime, [
2746
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2747
+ collection,
2748
+ pk
2749
+ )
2750
+ ]);
2751
+ this.collection = collection;
2752
+ this.pk = pk;
2753
+ }
2754
+ getValue() {
2755
+ return this.collection.getOneByPk(this.pk);
2756
+ }
2757
+ };
2758
+
2759
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKeys.ts
2760
+ var OIMSelectorSourceDependencyUpdateEventEmitterKeys = class {
2761
+ constructor(subscription, keys) {
2762
+ this.subscription = subscription;
2763
+ this.keys = keys;
2764
+ }
2765
+ subscribe(onUpdate) {
2766
+ return this.subscription.subscribeOnKeys(this.keys, onUpdate);
2767
+ }
2768
+ };
2769
+
2770
+ // src/modules/selector/core/OIMCollectionByPksSelector.ts
2771
+ var OIMCollectionByPksSelector = class extends OIMSelector {
2772
+ constructor(runtime, collection, pks) {
2773
+ super(runtime, [
2774
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2775
+ collection,
2776
+ pks
2777
+ )
2778
+ ]);
2779
+ this.collection = collection;
2780
+ this.pks = pks;
2781
+ }
2782
+ getValue() {
2783
+ return this.pks.map((pk) => this.collection.getOneByPk(pk));
2784
+ }
2785
+ areEqual(prev, next) {
2786
+ if (prev === next) return true;
2787
+ if (prev.length !== next.length) return false;
2788
+ for (let i = 0; i < prev.length; i++) {
2789
+ if (prev[i] !== next[i]) return false;
2790
+ }
2791
+ return true;
2792
+ }
2793
+ };
2794
+
2795
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased.ts
2796
+ var OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased = class {
2797
+ constructor(collection, reactiveIndex, key) {
2798
+ this.collection = collection;
2799
+ this.reactiveIndex = reactiveIndex;
2800
+ this.key = key;
2801
+ this.currentPks = [];
2802
+ }
2803
+ subscribe(onUpdate) {
2804
+ const resubscribeCollection = () => {
2805
+ const nextPks = this.reactiveIndex.getPksByKey(this.key);
2806
+ if (this.sameArray(this.currentPks, nextPks)) return;
2807
+ this.unsubscribeFromCollectionKeys?.();
2808
+ this.unsubscribeFromCollectionKeys = void 0;
2809
+ this.currentPks = nextPks;
2810
+ if (this.currentPks.length === 0) return;
2811
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2812
+ };
2813
+ const onIndexUpdate = () => {
2814
+ resubscribeCollection();
2815
+ onUpdate();
2816
+ };
2817
+ resubscribeCollection();
2818
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2819
+ this.key,
2820
+ onIndexUpdate
2821
+ );
2822
+ return () => {
2823
+ unsubscribeFromIndex();
2824
+ this.unsubscribeFromCollectionKeys?.();
2825
+ this.unsubscribeFromCollectionKeys = void 0;
2826
+ this.currentPks = [];
2827
+ };
2828
+ }
2829
+ sameArray(a, b) {
2830
+ if (a === b) return true;
2831
+ if (a.length !== b.length) return false;
2832
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2833
+ return true;
2834
+ }
2835
+ };
2836
+
2837
+ // src/modules/selector/core/OIMEntitiesByIndexKeyArrayBasedSelector.ts
2838
+ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
2839
+ constructor(runtime, collection, reactiveIndex, key) {
2840
+ super(runtime, [
2841
+ new OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased(collection, reactiveIndex, key)
2842
+ ]);
2843
+ this.collection = collection;
2844
+ this.reactiveIndex = reactiveIndex;
2845
+ this.key = key;
2846
+ }
2847
+ getValue() {
2848
+ const pks = this.reactiveIndex.getPksByKey(this.key);
2849
+ const entities = [];
2850
+ entities.length = pks.length;
2851
+ for (let i = 0; i < pks.length; i++) {
2852
+ entities[i] = this.collection.getOneByPk(pks[i]);
2853
+ }
2854
+ return entities;
2855
+ }
2856
+ areEqual(prev, next) {
2857
+ if (prev === next) return true;
2858
+ if (prev.length !== next.length) return false;
2859
+ for (let i = 0; i < prev.length; i++)
2860
+ if (prev[i] !== next[i]) return false;
2861
+ return true;
2862
+ }
2863
+ };
2864
+
2865
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeySetBased.ts
2866
+ var OIMSelectorSourceDependencyEntitiesByIndexKeySetBased = class {
2867
+ constructor(collection, reactiveIndex, key) {
2868
+ this.collection = collection;
2869
+ this.reactiveIndex = reactiveIndex;
2870
+ this.key = key;
2871
+ this.currentPks = [];
2872
+ }
2873
+ subscribe(onUpdate) {
2874
+ const resubscribeCollection = () => {
2875
+ const nextPks = Array.from(this.reactiveIndex.getPksByKey(this.key));
2876
+ if (this.sameArray(this.currentPks, nextPks)) return;
2877
+ this.unsubscribeFromCollectionKeys?.();
2878
+ this.unsubscribeFromCollectionKeys = void 0;
2879
+ this.currentPks = nextPks;
2880
+ if (this.currentPks.length === 0) return;
2881
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2882
+ };
2883
+ const onIndexUpdate = () => {
2884
+ resubscribeCollection();
2885
+ onUpdate();
2886
+ };
2887
+ resubscribeCollection();
2888
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2889
+ this.key,
2890
+ onIndexUpdate
2891
+ );
2892
+ return () => {
2893
+ unsubscribeFromIndex();
2894
+ this.unsubscribeFromCollectionKeys?.();
2895
+ this.unsubscribeFromCollectionKeys = void 0;
2896
+ this.currentPks = [];
2897
+ };
2898
+ }
2899
+ sameArray(a, b) {
2900
+ if (a === b) return true;
2901
+ if (a.length !== b.length) return false;
2902
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2903
+ return true;
2000
2904
  }
2001
2905
  };
2002
2906
 
2003
- // src/core/OIMMap2Keys.ts
2004
- var OIMMap2Keys = class {
2005
- constructor() {
2006
- this.map = /* @__PURE__ */ new Map();
2007
- this._size = 0;
2008
- }
2009
- get size() {
2010
- return this._size;
2907
+ // src/modules/selector/core/OIMEntitiesByIndexKeySetBasedSelector.ts
2908
+ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
2909
+ constructor(runtime, collection, reactiveIndex, key) {
2910
+ super(runtime, [
2911
+ new OIMSelectorSourceDependencyEntitiesByIndexKeySetBased(collection, reactiveIndex, key)
2912
+ ]);
2913
+ this.collection = collection;
2914
+ this.reactiveIndex = reactiveIndex;
2915
+ this.key = key;
2011
2916
  }
2012
- set(k1, k2, v) {
2013
- let m = this.map.get(k1);
2014
- if (!m) {
2015
- m = /* @__PURE__ */ new Map();
2016
- this.map.set(k1, m);
2917
+ getValue() {
2918
+ const pks = this.reactiveIndex.getPksByKey(this.key);
2919
+ const entities = [];
2920
+ entities.length = pks.size;
2921
+ let writeIndex = 0;
2922
+ for (const pk of pks) {
2923
+ entities[writeIndex++] = this.collection.getOneByPk(pk);
2017
2924
  }
2018
- if (!m.has(k2)) this._size++;
2019
- m.set(k2, v);
2925
+ return entities;
2020
2926
  }
2021
- get(k1, k2) {
2022
- return this.map.get(k1)?.get(k2);
2927
+ areEqual(prev, next) {
2928
+ if (prev === next) return true;
2929
+ if (prev.length !== next.length) return false;
2930
+ for (let i = 0; i < prev.length; i++)
2931
+ if (prev[i] !== next[i]) return false;
2932
+ return true;
2023
2933
  }
2024
- has(k1, k2) {
2025
- return this.map.get(k1)?.has(k2);
2934
+ };
2935
+
2936
+ // src/dx/OIMCollectionSelectors.ts
2937
+ var OIMCollectionSelectors = class {
2938
+ constructor(queue, collection) {
2939
+ this.collection = collection;
2940
+ this.runtime = new OIMComputeRuntime(queue);
2026
2941
  }
2027
- delete(k1, k2) {
2028
- const m = this.map.get(k1);
2029
- if (!m) return false;
2030
- const had = m.delete(k2);
2031
- if (had) {
2032
- this._size--;
2033
- if (m.size === 0) this.map.delete(k1);
2034
- }
2035
- return had;
2942
+ byPk(pk) {
2943
+ return new OIMCollectionByPkSelector(this.runtime, this.collection, pk);
2036
2944
  }
2037
- clear() {
2038
- this.map.clear();
2039
- this._size = 0;
2945
+ byPks(pks) {
2946
+ return new OIMCollectionByPksSelector(
2947
+ this.runtime,
2948
+ this.collection,
2949
+ pks
2950
+ );
2951
+ }
2952
+ entitiesBySetIndexKey(index, key) {
2953
+ return new OIMEntitiesByIndexKeySetBasedSelector(
2954
+ this.runtime,
2955
+ this.collection,
2956
+ index,
2957
+ key
2958
+ );
2959
+ }
2960
+ entitiesByArrayIndexKey(index, key) {
2961
+ return new OIMEntitiesByIndexKeyArrayBasedSelector(
2962
+ this.runtime,
2963
+ this.collection,
2964
+ index,
2965
+ key
2966
+ );
2040
2967
  }
2041
2968
  };
2042
2969
 
2043
- // src/modules/wrapper/collection/OIMCollectionChangedFieldsWrapper.ts
2044
- var OIMCollectionChangedFieldsWrapper = class {
2970
+ // src/dx/OIMCollectionContext.ts
2971
+ function createOIMCollectionContext(queue, opts) {
2972
+ const collection = new OIMReactiveCollection(queue, opts);
2973
+ const indexFactory = new OIMCollectionIndexFactory(queue, collection);
2974
+ const select = new OIMCollectionSelectors(queue, collection);
2975
+ return { queue, collection, indexFactory, select };
2976
+ }
2977
+
2978
+ // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
2979
+ var OIMCollectionChangedFields = class {
2045
2980
  constructor(queue, collection, opts) {
2046
2981
  this.isInWrite = false;
2047
2982
  this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
@@ -2061,7 +2996,7 @@ var OIMCollectionChangedFieldsWrapper = class {
2061
2996
  this.collection = collection;
2062
2997
  this.selectPk = opts?.selectPk ?? (() => {
2063
2998
  throw new Error(
2064
- `[OIMCollectionChangedFieldsWrapper]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
2999
+ `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
2065
3000
  );
2066
3001
  });
2067
3002
  this.changedPksEventEmitter = new OIMUpdateEventEmitter(
@@ -2084,31 +3019,34 @@ var OIMCollectionChangedFieldsWrapper = class {
2084
3019
  );
2085
3020
  }
2086
3021
  upsertOneByPk(pk, patch) {
2087
- this.withWrite(() => {
3022
+ const slot = this.withWrite(() => {
2088
3023
  this.trackPatch(pk, patch);
2089
- this.collection.upsertOneByPk(pk, patch);
3024
+ return this.collection.upsertOneByPk(pk, patch);
2090
3025
  });
2091
3026
  this.updateEntitySnapshot(pk);
3027
+ return slot;
2092
3028
  }
2093
3029
  upsertOne(entity) {
2094
- this.withWrite(() => {
3030
+ const slot = this.withWrite(() => {
2095
3031
  const pk = this.selectPk(entity);
2096
3032
  this.trackPatch(pk, entity);
2097
- this.collection.upsertOne(entity);
3033
+ return this.collection.upsertOne(entity);
2098
3034
  });
2099
- this.updateEntitySnapshot(this.selectPk(entity));
3035
+ this.updateEntitySnapshot(slot.pk);
3036
+ return slot;
2100
3037
  }
2101
3038
  upsertMany(entities) {
2102
3039
  const pks = [];
2103
- this.withWrite(() => {
3040
+ const slots = this.withWrite(() => {
2104
3041
  for (const entity of entities) {
2105
3042
  const pk = this.selectPk(entity);
2106
3043
  pks.push(pk);
2107
3044
  this.trackPatch(pk, entity);
2108
3045
  }
2109
- this.collection.upsertMany(entities);
3046
+ return this.collection.upsertMany(entities);
2110
3047
  });
2111
3048
  for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
3049
+ return slots;
2112
3050
  }
2113
3051
  getChangedFieldsByPk(pk) {
2114
3052
  return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
@@ -2139,12 +3077,11 @@ var OIMCollectionChangedFieldsWrapper = class {
2139
3077
  }
2140
3078
  withWrite(fn) {
2141
3079
  if (this.isInWrite) {
2142
- fn();
2143
- return;
3080
+ return fn();
2144
3081
  }
2145
3082
  this.isInWrite = true;
2146
3083
  try {
2147
- fn();
3084
+ return fn();
2148
3085
  } finally {
2149
3086
  this.isInWrite = false;
2150
3087
  }
@@ -2176,248 +3113,43 @@ var OIMCollectionChangedFieldsWrapper = class {
2176
3113
  });
2177
3114
  return changed;
2178
3115
  }
2179
- updateEntitySnapshot(pk) {
2180
- const entity = this.collection.getOneByPk(pk);
2181
- if (!entity) {
2182
- this.lastEntitySnapshotByPk.delete(pk);
2183
- return;
2184
- }
2185
- this.lastEntitySnapshotByPk.set(pk, {
2186
- ...entity
2187
- });
2188
- }
2189
- trackChangedKeys(pk, keys) {
2190
- let pkFields = this.changedFieldsByPk.get(pk);
2191
- if (!pkFields) {
2192
- pkFields = /* @__PURE__ */ new Set();
2193
- this.changedFieldsByPk.set(pk, pkFields);
2194
- }
2195
- for (const key of keys) {
2196
- pkFields.add(key);
2197
- let fieldPks = this.changedPksByField.get(key);
2198
- if (!fieldPks) {
2199
- fieldPks = /* @__PURE__ */ new Set();
2200
- this.changedPksByField.set(key, fieldPks);
2201
- }
2202
- fieldPks.add(pk);
2203
- }
2204
- this.changedPksEventEmitter.markUpdatedKeys([pk]);
2205
- this.changedFieldsEventEmitter.markUpdatedKeys(keys);
2206
- }
2207
- trackPatch(pk, patch) {
2208
- if (pk === void 0 || pk === null || pk === "") {
2209
- throw new Error(
2210
- `[OIMCollectionChangedFieldsWrapper]: PK is required to track changes.`
2211
- );
2212
- }
2213
- const keys = Object.keys(patch);
2214
- if (keys.length === 0) return;
2215
- this.trackChangedKeys(pk, keys);
2216
- }
2217
- };
2218
-
2219
- // src/modules/wrapper/index/OIMIndexManualOrderedArrayBased.ts
2220
- var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
2221
- clear(key) {
2222
- if (key === void 0) {
2223
- const keys = this.getKeys();
2224
- if (keys.length > 0) {
2225
- this.store.clear();
2226
- this.emitUpdate(Array.from(keys));
2227
- }
2228
- return;
2229
- }
2230
- if (this.store.getOneByKey(key) !== void 0) {
2231
- this.store.removeOneByKey(key);
2232
- this.emitUpdate([key]);
2233
- }
2234
- }
2235
- push(key, itemKey) {
2236
- const list = this.getOrCreateList(key);
2237
- const index = list.length;
2238
- list.push(this.getOrCreateSlot(itemKey));
2239
- this.emitUpdate([key]);
2240
- return index;
2241
- }
2242
- insertAt(key, index, itemKey) {
2243
- const list = this.getOrCreateList(key);
2244
- const safeIndex = Math.max(0, Math.min(index, list.length));
2245
- list.splice(safeIndex, 0, this.getOrCreateSlot(itemKey));
2246
- this.emitUpdate([key]);
2247
- }
2248
- removeAt(key, index) {
2249
- const list = this.store.getOneByKey(key);
2250
- if (!list) return void 0;
2251
- if (index < 0 || index >= list.length) return void 0;
2252
- const [removed] = list.splice(index, 1);
2253
- if (list.length === 0) {
2254
- this.store.removeOneByKey(key);
2255
- }
2256
- this.emitUpdate([key]);
2257
- return removed?.pk;
2258
- }
2259
- move(key, fromIndex, toIndex) {
2260
- const list = this.store.getOneByKey(key);
2261
- if (!list) return void 0;
2262
- if (fromIndex < 0 || fromIndex >= list.length) return void 0;
2263
- if (toIndex < 0 || toIndex >= list.length) return void 0;
2264
- if (fromIndex === toIndex) return list[fromIndex]?.pk;
2265
- const [itemKey] = list.splice(fromIndex, 1);
2266
- if (itemKey === void 0) return void 0;
2267
- list.splice(toIndex, 0, itemKey);
2268
- this.emitUpdate([key]);
2269
- return itemKey.pk;
2270
- }
2271
- reset(key, itemKeys) {
2272
- if (itemKeys.length === 0) {
2273
- if (this.store.getOneByKey(key) !== void 0) {
2274
- this.store.removeOneByKey(key);
2275
- this.emitUpdate([key]);
2276
- }
2277
- return;
2278
- }
2279
- this.store.setOneByKey(key, this.resolveSlots(itemKeys));
2280
- this.emitUpdate([key]);
2281
- }
2282
- getOrCreateList(key) {
2283
- let list = this.store.getOneByKey(key);
2284
- if (!list) {
2285
- list = [];
2286
- this.store.setOneByKey(key, list);
2287
- }
2288
- return list;
2289
- }
2290
- };
2291
-
2292
- // src/modules/wrapper/index/OIMOrderedListIndexCommandStreamWrapper.ts
2293
- var OIMOrderedListIndexCommandStreamWrapper = class {
2294
- constructor(queue, index) {
2295
- this.isInWrite = false;
2296
- this.commandsByKey = /* @__PURE__ */ new Map();
2297
- this.onAfterFlush = () => {
2298
- this.commandsByKey.clear();
2299
- };
2300
- this.onIndexUpdate = (payload) => {
2301
- if (this.isInWrite) return;
2302
- if (payload.keys.length === 0) return;
2303
- for (let i = 0; i < payload.keys.length; i++) {
2304
- const key = payload.keys[i];
2305
- const list = this.index.getPksByKey(key);
2306
- this.appendCommand(key, { type: "set", keys: Array.from(list) });
2307
- }
2308
- };
2309
- this.commandsEventEmitter = new OIMUpdateEventEmitter(
2310
- queue,
2311
- "after_flush"
2312
- );
2313
- this.index = index ?? new OIMIndexManualOrderedArrayBased();
2314
- this.unsubscribeAfterFlush = queue.emitter.on(
2315
- 0 /* AFTER_FLUSH */,
2316
- this.onAfterFlush
2317
- );
2318
- this.unsubscribeFromIndexEmitter = this.index.emitter.on(
2319
- 0 /* UPDATE */,
2320
- this.onIndexUpdate
2321
- );
2322
- }
2323
- getPksByKey(key) {
2324
- return this.index.getPksByKey(key);
2325
- }
2326
- hasKey(key) {
2327
- return this.index.hasKey(key);
2328
- }
2329
- getKeys() {
2330
- return this.index.getKeys();
2331
- }
2332
- push(key, itemKey) {
2333
- this.withWrite(() => {
2334
- const index = this.index.push(key, itemKey);
2335
- this.appendCommand(key, { type: "add", key: itemKey, index });
2336
- });
2337
- }
2338
- removeAt(key, index) {
2339
- this.withWrite(() => {
2340
- const itemKey = this.index.removeAt(key, index);
2341
- if (itemKey === void 0) return;
2342
- this.appendCommand(key, { type: "remove", key: itemKey, index });
2343
- });
2344
- }
2345
- move(key, fromIndex, toIndex) {
2346
- this.withWrite(() => {
2347
- const itemKey = this.index.move(key, fromIndex, toIndex);
2348
- if (itemKey === void 0) return;
2349
- this.appendCommand(key, {
2350
- type: "move",
2351
- key: itemKey,
2352
- fromIndex,
2353
- toIndex
2354
- });
2355
- });
2356
- }
2357
- set(key, keys) {
2358
- this.withWrite(() => {
2359
- this.index.reset(key, keys);
2360
- this.appendCommand(key, { type: "set", keys });
2361
- });
2362
- }
2363
- /**
2364
- * Read-only peek at buffered commands without clearing them.
2365
- */
2366
- getBufferedCommands(key) {
2367
- return this.commandsByKey.get(key) ?? [];
2368
- }
2369
- /**
2370
- * Drain buffered commands for a key, preserving order.
2371
- * Intended usage: call inside a `commandsEventEmitter.subscribeOnKey(key, ...)` handler.
2372
- */
2373
- consumeCommands(key) {
2374
- const cmds = this.commandsByKey.get(key);
2375
- if (!cmds || cmds.length === 0) return [];
2376
- return cmds.slice();
2377
- }
2378
- clear(key) {
2379
- if (key === void 0) {
2380
- this.index.destroy();
2381
- this.commandsByKey.clear();
2382
- return;
2383
- }
2384
- this.index.clear(key);
2385
- this.commandsByKey.delete(key);
2386
- }
2387
- destroy() {
2388
- this.commandsEventEmitter.destroy();
2389
- this.unsubscribeAfterFlush();
2390
- this.unsubscribeFromIndexEmitter();
2391
- this.clear();
2392
- }
2393
- withWrite(fn) {
2394
- if (this.isInWrite) {
2395
- fn();
3116
+ updateEntitySnapshot(pk) {
3117
+ const entity = this.collection.getOneByPk(pk);
3118
+ if (!entity) {
3119
+ this.lastEntitySnapshotByPk.delete(pk);
2396
3120
  return;
2397
3121
  }
2398
- this.isInWrite = true;
2399
- try {
2400
- fn();
2401
- } finally {
2402
- this.isInWrite = false;
2403
- }
3122
+ this.lastEntitySnapshotByPk.set(pk, {
3123
+ ...entity
3124
+ });
2404
3125
  }
2405
- appendCommand(key, cmd) {
2406
- let cmds = this.commandsByKey.get(key);
2407
- if (!cmds) {
2408
- cmds = [];
2409
- this.commandsByKey.set(key, cmds);
3126
+ trackChangedKeys(pk, keys) {
3127
+ let pkFields = this.changedFieldsByPk.get(pk);
3128
+ if (!pkFields) {
3129
+ pkFields = /* @__PURE__ */ new Set();
3130
+ this.changedFieldsByPk.set(pk, pkFields);
2410
3131
  }
2411
- if (cmd.type === "set") {
2412
- cmds.length = 0;
2413
- cmds.push(cmd);
2414
- } else if (cmds.length > 0 && cmds[0].type === "set") {
2415
- const setCmd = cmds[0];
2416
- setCmd.keys = Array.from(this.index.getPksByKey(key));
2417
- } else {
2418
- cmds.push(cmd);
3132
+ for (const key of keys) {
3133
+ pkFields.add(key);
3134
+ let fieldPks = this.changedPksByField.get(key);
3135
+ if (!fieldPks) {
3136
+ fieldPks = /* @__PURE__ */ new Set();
3137
+ this.changedPksByField.set(key, fieldPks);
3138
+ }
3139
+ fieldPks.add(pk);
2419
3140
  }
2420
- this.commandsEventEmitter.markUpdatedKeys([key]);
3141
+ this.changedPksEventEmitter.markUpdatedKeys([pk]);
3142
+ this.changedFieldsEventEmitter.markUpdatedKeys(keys);
3143
+ }
3144
+ trackPatch(pk, patch) {
3145
+ if (pk === void 0 || pk === null || pk === "") {
3146
+ throw new Error(
3147
+ `[OIMCollectionChangedFields]: PK is required to track changes.`
3148
+ );
3149
+ }
3150
+ const keys = Object.keys(patch);
3151
+ if (keys.length === 0) return;
3152
+ this.trackChangedKeys(pk, keys);
2421
3153
  }
2422
3154
  };
2423
3155
 
@@ -2533,7 +3265,9 @@ var OIMEventQueueSchedulerImmediate = class extends OIMEventQueueScheduler {
2533
3265
  cancel() {
2534
3266
  if (this.immediateId === void 0) return;
2535
3267
  if (this.useSetImmediate) {
2536
- clearImmediate(this.immediateId);
3268
+ clearImmediate(
3269
+ this.immediateId
3270
+ );
2537
3271
  } else if (this.useMessageChannel) {
2538
3272
  this.pendingCallback = void 0;
2539
3273
  } else {
@@ -2590,7 +3324,7 @@ var OIMEventQueueSchedulerFactory = class {
2590
3324
  }
2591
3325
  };
2592
3326
 
2593
- // src/const/OIMDBSettings.ts
3327
+ // src/constants/OIMDBSettings.ts
2594
3328
  var OIMDBSettings = class {
2595
3329
  };
2596
3330
  /**
@@ -2602,11 +3336,11 @@ OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
2602
3336
 
2603
3337
  // src/modules/effect/core/OIMEffectDependencyComputed.ts
2604
3338
  var OIMEffectDependencyComputed = class {
2605
- constructor(computed) {
2606
- this.computed = computed;
3339
+ constructor(source) {
3340
+ this.source = source;
2607
3341
  }
2608
3342
  subscribe(onUpdate) {
2609
- return this.computed.emitter.on("update", onUpdate);
3343
+ return this.source.emitter.on("update", onUpdate);
2610
3344
  }
2611
3345
  };
2612
3346
 
@@ -2718,7 +3452,7 @@ var OIMEffect = class {
2718
3452
  }
2719
3453
  };
2720
3454
 
2721
- // src/modules/computed/enum/EOIMComputedEventType.ts
3455
+ // src/modules/computed/enums/EOIMComputedEventType.ts
2722
3456
  var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
2723
3457
  EOIMComputedEventType2["UPDATE"] = "update";
2724
3458
  return EOIMComputedEventType2;
@@ -2733,13 +3467,13 @@ var OIMComputed = class {
2733
3467
  this.runtime = runtime;
2734
3468
  this.compute = opts.compute;
2735
3469
  this.compare = opts.compare ?? Object.is;
2736
- this.deps = opts.deps ?? [];
3470
+ this._deps = opts.deps ?? [];
2737
3471
  this.updateEventEmitter = new OIMUpdateEventEmitter(
2738
3472
  this.runtime.queue,
2739
3473
  "immediate"
2740
3474
  );
2741
3475
  this.effect = new OIMEffect(this.runtime, {
2742
- deps: this.deps,
3476
+ deps: this._deps,
2743
3477
  onUpdate: () => {
2744
3478
  this.isDirty = true;
2745
3479
  },
@@ -2748,6 +3482,15 @@ var OIMComputed = class {
2748
3482
  }
2749
3483
  });
2750
3484
  }
3485
+ get needsRecompute() {
3486
+ return this.isDirty;
3487
+ }
3488
+ get isReady() {
3489
+ return this.hasValue;
3490
+ }
3491
+ get deps() {
3492
+ return this._deps;
3493
+ }
2751
3494
  get() {
2752
3495
  this.recomputeAndEmitIfChanged();
2753
3496
  return this.value;
@@ -2782,8 +3525,8 @@ function isReadonlyArray(value) {
2782
3525
  return Array.isArray(value);
2783
3526
  }
2784
3527
  var OIMEffectDependencyKeyed = class {
2785
- constructor(subscription, keyOrKeys) {
2786
- this.subscription = subscription;
3528
+ constructor(source, keyOrKeys) {
3529
+ this.source = source;
2787
3530
  if (isReadonlyArray(keyOrKeys)) {
2788
3531
  this.keys = keyOrKeys;
2789
3532
  } else {
@@ -2792,11 +3535,11 @@ var OIMEffectDependencyKeyed = class {
2792
3535
  }
2793
3536
  subscribe(onUpdate) {
2794
3537
  if (this.keys !== void 0) {
2795
- return this.subscription.subscribeOnKeys(this.keys, onUpdate);
3538
+ return this.source.subscribeOnKeys(this.keys, onUpdate);
2796
3539
  }
2797
3540
  if (this.key === void 0) return () => {
2798
3541
  };
2799
- return this.subscription.subscribeOnKey(this.key, onUpdate);
3542
+ return this.source.subscribeOnKey(this.key, onUpdate);
2800
3543
  }
2801
3544
  };
2802
3545
 
@@ -2808,6 +3551,9 @@ var OIMEffectDependencyKeyedObject = class {
2808
3551
  keyOrKeys
2809
3552
  );
2810
3553
  }
3554
+ get source() {
3555
+ return this.dep.source;
3556
+ }
2811
3557
  subscribe(onUpdate) {
2812
3558
  return this.dep.subscribe(onUpdate);
2813
3559
  }
@@ -2821,217 +3567,27 @@ var OIMEffectDependencyKeyedCollection = class {
2821
3567
  pkOrPks
2822
3568
  );
2823
3569
  }
2824
- subscribe(onUpdate) {
2825
- return this.dep.subscribe(onUpdate);
2826
- }
2827
- };
2828
-
2829
- // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
2830
- var OIMEffectDependencyKeyedIndex = class {
2831
- constructor(index, keyOrKeys) {
2832
- this.dep = new OIMEffectDependencyKeyed(
2833
- index,
2834
- keyOrKeys
2835
- );
3570
+ get source() {
3571
+ return this.dep.source;
2836
3572
  }
2837
3573
  subscribe(onUpdate) {
2838
3574
  return this.dep.subscribe(onUpdate);
2839
3575
  }
2840
- };
2841
-
2842
- // src/modules/selector/core/OIMSelector.ts
2843
- var OIMSelector = class {
2844
- constructor(runtime, sourceDeps, computedDeps = []) {
2845
- this.runtime = runtime;
2846
- this.sourceDeps = sourceDeps;
2847
- this.computedDeps = computedDeps;
2848
- this.listeners = /* @__PURE__ */ new Set();
2849
- this.sourceUnsubscribers = [];
2850
- this.computedUnsubscribers = [];
2851
- this.isResubscribeEnqueued = false;
2852
- this.isTemporarilyUnsubscribedForFlush = false;
2853
- this.isDeliveryEnqueued = false;
2854
- this.hasLastValue = false;
2855
- this.onSourceInvalidate = () => {
2856
- if (this.listeners.size === 0) return;
2857
- if (!this.isTemporarilyUnsubscribedForFlush) {
2858
- this.isTemporarilyUnsubscribedForFlush = true;
2859
- this.unsubscribeFromSourceDeps();
2860
- this.ensureResubscribeAfterFlush();
2861
- }
2862
- if (this.isDeliveryEnqueued) return;
2863
- this.isDeliveryEnqueued = true;
2864
- this.dequeueDelivery = this.runtime.schedule(this.deliver);
2865
- };
2866
- this.onComputedInvalidate = () => {
2867
- if (this.listeners.size === 0) return;
2868
- if (this.isDeliveryEnqueued) return;
2869
- this.isDeliveryEnqueued = true;
2870
- this.dequeueDelivery = this.runtime.schedule(this.deliver);
2871
- };
2872
- this.deliver = () => {
2873
- this.isDeliveryEnqueued = false;
2874
- this.dequeueDelivery = void 0;
2875
- if (this.listeners.size === 0) return;
2876
- const next = this.getValue();
2877
- if (this.hasLastValue && this.areEqual(this.lastValue, next)) return;
2878
- this.setLastValue(next);
2879
- const snapshot = Array.from(this.listeners);
2880
- for (let i = 0; i < snapshot.length; i++) {
2881
- const listener = snapshot[i];
2882
- if (this.listeners.has(listener)) listener(next);
2883
- }
2884
- };
2885
- this.onAfterFlush = () => {
2886
- if (!this.isResubscribeEnqueued) return;
2887
- this.isResubscribeEnqueued = false;
2888
- this.dequeueResubscribe = void 0;
2889
- if (this.listeners.size === 0) return;
2890
- this.isTemporarilyUnsubscribedForFlush = false;
2891
- if (this.sourceUnsubscribers.length === 0) {
2892
- for (const dep of this.sourceDeps) {
2893
- this.sourceUnsubscribers.push(
2894
- dep.subscribe(this.onSourceInvalidate)
2895
- );
2896
- }
2897
- }
2898
- };
2899
- }
2900
- get queue() {
2901
- return this.runtime.queue;
2902
- }
2903
- /**
2904
- * Start watching this selector. The callback is called immediately with the current value,
2905
- * then coalesced and delivered on `queue.flush()` when the selector invalidates.
2906
- */
2907
- watch(onValue) {
2908
- this.listeners.add(onValue);
2909
- if (this.listeners.size === 1) {
2910
- this.subscribeToDeps();
2911
- }
2912
- const current = this.getValue();
2913
- this.setLastValue(current);
2914
- onValue(current);
2915
- return () => {
2916
- this.listeners.delete(onValue);
2917
- if (this.listeners.size > 0) return;
2918
- this.unsubscribeFromAllDeps();
2919
- if (this.dequeueResubscribe) {
2920
- this.dequeueResubscribe();
2921
- this.dequeueResubscribe = void 0;
2922
- }
2923
- this.isResubscribeEnqueued = false;
2924
- this.isTemporarilyUnsubscribedForFlush = false;
2925
- if (this.dequeueDelivery) {
2926
- this.dequeueDelivery();
2927
- this.dequeueDelivery = void 0;
2928
- }
2929
- this.isDeliveryEnqueued = false;
2930
- this.hasLastValue = false;
2931
- };
2932
- }
2933
- areEqual(prev, next) {
2934
- return Object.is(prev, next);
2935
- }
2936
- setLastValue(value) {
2937
- this.lastValue = value;
2938
- this.hasLastValue = true;
2939
- }
2940
- subscribeToDeps() {
2941
- if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2942
- return;
2943
- for (const dep of this.sourceDeps) {
2944
- this.sourceUnsubscribers.push(
2945
- dep.subscribe(this.onSourceInvalidate)
2946
- );
2947
- }
2948
- for (const dep of this.computedDeps) {
2949
- this.computedUnsubscribers.push(
2950
- dep.subscribe(this.onComputedInvalidate)
2951
- );
2952
- }
2953
- }
2954
- unsubscribeFromSourceDeps() {
2955
- if (this.sourceUnsubscribers.length === 0) return;
2956
- for (const unsub of this.sourceUnsubscribers) unsub();
2957
- this.sourceUnsubscribers = [];
2958
- }
2959
- unsubscribeFromAllDeps() {
2960
- this.unsubscribeFromSourceDeps();
2961
- if (this.computedUnsubscribers.length === 0) return;
2962
- for (const unsub of this.computedUnsubscribers) unsub();
2963
- this.computedUnsubscribers = [];
2964
- }
2965
- ensureResubscribeAfterFlush() {
2966
- if (this.isResubscribeEnqueued) return;
2967
- this.isResubscribeEnqueued = true;
2968
- this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2969
- this.onAfterFlush
2970
- );
2971
- }
2972
- };
2973
-
2974
- // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKey.ts
2975
- var OIMSelectorSourceDependencyUpdateEventEmitterKey = class {
2976
- constructor(subscription, key) {
2977
- this.subscription = subscription;
2978
- this.key = key;
2979
- }
2980
- subscribe(onUpdate) {
2981
- return this.subscription.subscribeOnKey(this.key, onUpdate);
2982
- }
2983
- };
2984
-
2985
- // src/modules/selector/core/OIMCollectionByPkSelector.ts
2986
- var OIMCollectionByPkSelector = class extends OIMSelector {
2987
- constructor(runtime, collection, pk) {
2988
- super(runtime, [
2989
- new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2990
- collection,
2991
- pk
2992
- )
2993
- ]);
2994
- this.collection = collection;
2995
- this.pk = pk;
2996
- }
2997
- getValue() {
2998
- return this.collection.getOneByPk(this.pk);
2999
- }
3000
- };
3001
-
3002
- // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKeys.ts
3003
- var OIMSelectorSourceDependencyUpdateEventEmitterKeys = class {
3004
- constructor(subscription, keys) {
3005
- this.subscription = subscription;
3006
- this.keys = keys;
3007
- }
3008
- subscribe(onUpdate) {
3009
- return this.subscription.subscribeOnKeys(this.keys, onUpdate);
3010
- }
3011
- };
3012
-
3013
- // src/modules/selector/core/OIMCollectionByPksSelector.ts
3014
- var OIMCollectionByPksSelector = class extends OIMSelector {
3015
- constructor(runtime, collection, pks) {
3016
- super(runtime, [
3017
- new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
3018
- collection,
3019
- pks
3020
- )
3021
- ]);
3022
- this.collection = collection;
3023
- this.pks = pks;
3576
+ };
3577
+
3578
+ // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
3579
+ var OIMEffectDependencyKeyedIndex = class {
3580
+ constructor(index, keyOrKeys) {
3581
+ this.dep = new OIMEffectDependencyKeyed(
3582
+ index,
3583
+ keyOrKeys
3584
+ );
3024
3585
  }
3025
- getValue() {
3026
- return this.pks.map((pk) => this.collection.getOneByPk(pk));
3586
+ get source() {
3587
+ return this.dep.source;
3027
3588
  }
3028
- areEqual(prev, next) {
3029
- if (prev === next) return true;
3030
- if (prev.length !== next.length) return false;
3031
- for (let i = 0; i < prev.length; i++) {
3032
- if (prev[i] !== next[i]) return false;
3033
- }
3034
- return true;
3589
+ subscribe(onUpdate) {
3590
+ return this.dep.subscribe(onUpdate);
3035
3591
  }
3036
3592
  };
3037
3593
 
@@ -3165,259 +3721,6 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
3165
3721
  return true;
3166
3722
  }
3167
3723
  };
3168
-
3169
- // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeySetBased.ts
3170
- var OIMSelectorSourceDependencyEntitiesByIndexKeySetBased = class {
3171
- constructor(collection, reactiveIndex, key) {
3172
- this.collection = collection;
3173
- this.reactiveIndex = reactiveIndex;
3174
- this.key = key;
3175
- this.currentPks = [];
3176
- }
3177
- subscribe(onUpdate) {
3178
- const resubscribeCollection = () => {
3179
- const nextPks = Array.from(
3180
- this.reactiveIndex.getSlotsByKey(this.key),
3181
- (slot) => slot.pk
3182
- );
3183
- if (this.sameArray(this.currentPks, nextPks)) return;
3184
- this.unsubscribeFromCollectionKeys?.();
3185
- this.unsubscribeFromCollectionKeys = void 0;
3186
- this.currentPks = nextPks;
3187
- if (this.currentPks.length === 0) return;
3188
- this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
3189
- };
3190
- const onIndexUpdate = () => {
3191
- resubscribeCollection();
3192
- onUpdate();
3193
- };
3194
- resubscribeCollection();
3195
- const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
3196
- this.key,
3197
- onIndexUpdate
3198
- );
3199
- return () => {
3200
- unsubscribeFromIndex();
3201
- this.unsubscribeFromCollectionKeys?.();
3202
- this.unsubscribeFromCollectionKeys = void 0;
3203
- this.currentPks = [];
3204
- };
3205
- }
3206
- sameArray(a, b) {
3207
- if (a === b) return true;
3208
- if (a.length !== b.length) return false;
3209
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3210
- return true;
3211
- }
3212
- };
3213
-
3214
- // src/modules/selector/core/OIMEntitiesByIndexKeySetBasedSelector.ts
3215
- var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
3216
- constructor(runtime, collection, reactiveIndex, key) {
3217
- super(runtime, [
3218
- new OIMSelectorSourceDependencyEntitiesByIndexKeySetBased(collection, reactiveIndex, key)
3219
- ]);
3220
- this.collection = collection;
3221
- this.reactiveIndex = reactiveIndex;
3222
- this.key = key;
3223
- this.reactiveIndex.index.setSlotResolver(
3224
- (pk) => this.collection.getSlotByPk(pk)
3225
- );
3226
- }
3227
- getValue() {
3228
- const slots = this.reactiveIndex.getSlotsByKey(this.key);
3229
- const entities = [];
3230
- entities.length = slots.size;
3231
- let writeIndex = 0;
3232
- for (const slot of slots) {
3233
- entities[writeIndex++] = slot.item;
3234
- }
3235
- return entities;
3236
- }
3237
- areEqual(prev, next) {
3238
- if (prev === next) return true;
3239
- if (prev.length !== next.length) return false;
3240
- for (let i = 0; i < prev.length; i++)
3241
- if (prev[i] !== next[i]) return false;
3242
- return true;
3243
- }
3244
- };
3245
-
3246
- // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased.ts
3247
- var OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased = class {
3248
- constructor(collection, reactiveIndex, key) {
3249
- this.collection = collection;
3250
- this.reactiveIndex = reactiveIndex;
3251
- this.key = key;
3252
- this.currentPks = [];
3253
- }
3254
- subscribe(onUpdate) {
3255
- const resubscribeCollection = () => {
3256
- const nextPks = this.reactiveIndex.getSlotsByKey(this.key).map((slot) => slot.pk);
3257
- if (this.sameArray(this.currentPks, nextPks)) return;
3258
- this.unsubscribeFromCollectionKeys?.();
3259
- this.unsubscribeFromCollectionKeys = void 0;
3260
- this.currentPks = nextPks;
3261
- if (this.currentPks.length === 0) return;
3262
- this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
3263
- };
3264
- const onIndexUpdate = () => {
3265
- resubscribeCollection();
3266
- onUpdate();
3267
- };
3268
- resubscribeCollection();
3269
- const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
3270
- this.key,
3271
- onIndexUpdate
3272
- );
3273
- return () => {
3274
- unsubscribeFromIndex();
3275
- this.unsubscribeFromCollectionKeys?.();
3276
- this.unsubscribeFromCollectionKeys = void 0;
3277
- this.currentPks = [];
3278
- };
3279
- }
3280
- sameArray(a, b) {
3281
- if (a === b) return true;
3282
- if (a.length !== b.length) return false;
3283
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3284
- return true;
3285
- }
3286
- };
3287
-
3288
- // src/modules/selector/core/OIMEntitiesByIndexKeyArrayBasedSelector.ts
3289
- var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
3290
- constructor(runtime, collection, reactiveIndex, key) {
3291
- super(runtime, [
3292
- new OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased(collection, reactiveIndex, key)
3293
- ]);
3294
- this.collection = collection;
3295
- this.reactiveIndex = reactiveIndex;
3296
- this.key = key;
3297
- this.reactiveIndex.index.setSlotResolver(
3298
- (pk) => this.collection.getSlotByPk(pk)
3299
- );
3300
- }
3301
- getValue() {
3302
- const slots = this.reactiveIndex.getSlotsByKey(this.key);
3303
- const entities = [];
3304
- entities.length = slots.length;
3305
- for (let i = 0; i < slots.length; i++) {
3306
- entities[i] = slots[i].item;
3307
- }
3308
- return entities;
3309
- }
3310
- areEqual(prev, next) {
3311
- if (prev === next) return true;
3312
- if (prev.length !== next.length) return false;
3313
- for (let i = 0; i < prev.length; i++)
3314
- if (prev[i] !== next[i]) return false;
3315
- return true;
3316
- }
3317
- };
3318
-
3319
- // src/modules/computative/core/OIMComputativeRuntime.ts
3320
- var OIMComputativeRuntime = class {
3321
- constructor(queue) {
3322
- this.queue = queue;
3323
- this.isFlushScheduled = false;
3324
- this.isFlushing = false;
3325
- this.minLevel = Infinity;
3326
- this.maxLevel = -Infinity;
3327
- this.dirtyByLevel = /* @__PURE__ */ new Map();
3328
- this.afterFlushTasks = /* @__PURE__ */ new Set();
3329
- this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
3330
- if (!this.isFlushScheduled) return;
3331
- this.flush();
3332
- });
3333
- }
3334
- /**
3335
- * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
3336
- * ordered by `level` ascending.
3337
- */
3338
- schedule(task, level = 0) {
3339
- let isActive = true;
3340
- const wrapped = () => {
3341
- if (!isActive) return;
3342
- task();
3343
- };
3344
- let bucket = this.dirtyByLevel.get(level);
3345
- if (!bucket) {
3346
- bucket = /* @__PURE__ */ new Set();
3347
- this.dirtyByLevel.set(level, bucket);
3348
- }
3349
- bucket.add(wrapped);
3350
- if (level < this.minLevel) this.minLevel = level;
3351
- if (level > this.maxLevel) this.maxLevel = level;
3352
- if (!this.isFlushing) this.isFlushScheduled = true;
3353
- return () => {
3354
- if (!isActive) return;
3355
- isActive = false;
3356
- bucket.delete(wrapped);
3357
- };
3358
- }
3359
- /**
3360
- * Run once at the start of the next runtime flush (i.e. after queue.flush()).
3361
- * Useful for resubscribing dependencies exactly once per source flush.
3362
- */
3363
- scheduleAfterFlush(task) {
3364
- let isActive = true;
3365
- const wrapped = () => {
3366
- if (!isActive) return;
3367
- task();
3368
- };
3369
- this.afterFlushTasks.add(wrapped);
3370
- if (!this.isFlushing) this.isFlushScheduled = true;
3371
- return () => {
3372
- if (!isActive) return;
3373
- isActive = false;
3374
- this.afterFlushTasks.delete(wrapped);
3375
- };
3376
- }
3377
- flush() {
3378
- this.isFlushScheduled = false;
3379
- if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
3380
- return;
3381
- this.isFlushing = true;
3382
- try {
3383
- if (this.afterFlushTasks.size > 0) {
3384
- const snapshot = Array.from(this.afterFlushTasks);
3385
- this.afterFlushTasks.clear();
3386
- for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3387
- }
3388
- if (this.dirtyByLevel.size === 0) return;
3389
- let level = this.minLevel;
3390
- let max = this.maxLevel;
3391
- while (level <= max) {
3392
- for (; ; ) {
3393
- const bucket = this.dirtyByLevel.get(level);
3394
- if (!bucket || bucket.size === 0) break;
3395
- this.dirtyByLevel.delete(level);
3396
- const snapshot = Array.from(bucket);
3397
- for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3398
- }
3399
- if (this.maxLevel > max) max = this.maxLevel;
3400
- level++;
3401
- }
3402
- } finally {
3403
- this.isFlushing = false;
3404
- if (this.dirtyByLevel.size === 0) {
3405
- this.minLevel = Infinity;
3406
- this.maxLevel = -Infinity;
3407
- } else {
3408
- let nextMin = Infinity;
3409
- let nextMax = -Infinity;
3410
- this.dirtyByLevel.forEach((_bucket, lvl) => {
3411
- if (lvl < nextMin) nextMin = lvl;
3412
- if (lvl > nextMax) nextMax = lvl;
3413
- });
3414
- this.minLevel = nextMin;
3415
- this.maxLevel = nextMax;
3416
- this.isFlushScheduled = true;
3417
- }
3418
- }
3419
- }
3420
- };
3421
3724
  // Annotate the CommonJS export names for ESM import in node:
3422
3725
  0 && (module.exports = {
3423
3726
  EOIMCollectionEventType,
@@ -3429,13 +3732,19 @@ var OIMComputativeRuntime = class {
3429
3732
  OIMCollection,
3430
3733
  OIMCollectionByPkSelector,
3431
3734
  OIMCollectionByPksSelector,
3432
- OIMCollectionChangedFieldsWrapper,
3735
+ OIMCollectionChangedFields,
3736
+ OIMCollectionIndexFactory,
3737
+ OIMCollectionIndexManualOrderedArrayBased,
3738
+ OIMCollectionOrderedListCommandStream,
3739
+ OIMCollectionSelectors,
3433
3740
  OIMCollectionStore,
3434
3741
  OIMCollectionStoreMapDriven,
3435
3742
  OIMComparatorFactory,
3436
- OIMComputativeRuntime,
3743
+ OIMComputeRuntime,
3437
3744
  OIMComputed,
3438
3745
  OIMDBSettings,
3746
+ OIMDerivedCollectionIndexArrayBased,
3747
+ OIMDerivedCollectionIndexSetBased,
3439
3748
  OIMEffect,
3440
3749
  OIMEffectDependencyComputed,
3441
3750
  OIMEffectDependencyKeyed,
@@ -3453,6 +3762,7 @@ var OIMComputativeRuntime = class {
3453
3762
  OIMEventQueueSchedulerImmediate,
3454
3763
  OIMEventQueueSchedulerMicrotask,
3455
3764
  OIMEventQueueSchedulerTimeout,
3765
+ OIMIndex,
3456
3766
  OIMIndexArrayBased,
3457
3767
  OIMIndexComparatorFactory,
3458
3768
  OIMIndexManualArrayBased,
@@ -3474,14 +3784,18 @@ var OIMComputativeRuntime = class {
3474
3784
  OIMObjectStoreRecordDriven,
3475
3785
  OIMObjectValueByKeySelector,
3476
3786
  OIMObjectValuesByKeysSelector,
3477
- OIMOrderedListIndexCommandStreamWrapper,
3787
+ OIMOrderedListCommandStream,
3478
3788
  OIMPkSelectorFactory,
3479
- OIMRICollection,
3480
3789
  OIMReactiveCollection,
3790
+ OIMReactiveCollectionIndexManualArrayBased,
3791
+ OIMReactiveCollectionIndexManualSetBased,
3792
+ OIMReactiveIndex,
3481
3793
  OIMReactiveIndexArrayBased,
3482
3794
  OIMReactiveIndexManualArrayBased,
3483
3795
  OIMReactiveIndexManualSetBased,
3484
3796
  OIMReactiveIndexSetBased,
3485
3797
  OIMReactiveObject,
3486
- OIMSelector
3798
+ OIMSelector,
3799
+ createOIMCollectionContext,
3800
+ createOIMCollectionIndexFactory
3487
3801
  });