@oimdb/core 1.5.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,14 +2,6 @@
2
2
  var OIMCollectionStore = class {
3
3
  };
4
4
 
5
- // src/abstract/OIMIndexStoreSetBased.ts
6
- var OIMIndexStoreSetBased = class {
7
- };
8
-
9
- // src/abstract/OIMIndexStoreArrayBased.ts
10
- var OIMIndexStoreArrayBased = class {
11
- };
12
-
13
5
  // src/core/OIMEventEmitter.ts
14
6
  var OIMEventEmitter = class {
15
7
  constructor() {
@@ -115,7 +107,94 @@ var OIMEventEmitter = class {
115
107
  }
116
108
  };
117
109
 
118
- // src/enum/EOIMEventQueueSchedulerEventType.ts
110
+ // src/enums/EOIMIndexEventType.ts
111
+ var EOIMIndexEventType = /* @__PURE__ */ ((EOIMIndexEventType2) => {
112
+ EOIMIndexEventType2[EOIMIndexEventType2["UPDATE"] = 0] = "UPDATE";
113
+ return EOIMIndexEventType2;
114
+ })(EOIMIndexEventType || {});
115
+
116
+ // src/abstract/OIMIndex.ts
117
+ var OIMIndex = class {
118
+ constructor(store, comparePks) {
119
+ this.emitter = new OIMEventEmitter();
120
+ this.store = store;
121
+ this.comparePks = comparePks;
122
+ }
123
+ hasKey(key) {
124
+ return this.store.getOneByKey(key) !== void 0;
125
+ }
126
+ getKeys() {
127
+ return this.store.getAllKeys();
128
+ }
129
+ getKeySize(key) {
130
+ const bucket = this.store.getOneByKey(key);
131
+ return bucket ? this.getBucketSize(bucket) : 0;
132
+ }
133
+ get size() {
134
+ return this.store.countAll();
135
+ }
136
+ get isEmpty() {
137
+ return this.store.countAll() === 0;
138
+ }
139
+ getEntitiesByKey(key) {
140
+ const bucket = this.store.getOneByKey(key);
141
+ return bucket ? this.slotsToEntities(bucket) : [];
142
+ }
143
+ getEntitiesByKeys(keys) {
144
+ const result = /* @__PURE__ */ new Map();
145
+ const bucketsByKey = this.store.getManyByKeys(keys);
146
+ for (const [key, bucket] of bucketsByKey) {
147
+ result.set(key, this.slotsToEntities(bucket));
148
+ }
149
+ return result;
150
+ }
151
+ getMetrics() {
152
+ let totalPks = 0;
153
+ let maxBucketSize = 0;
154
+ let minBucketSize = Infinity;
155
+ const all = this.store.getAll();
156
+ for (const bucket of all.values()) {
157
+ const size = this.getBucketSize(bucket);
158
+ totalPks += size;
159
+ maxBucketSize = Math.max(maxBucketSize, size);
160
+ minBucketSize = Math.min(minBucketSize, size);
161
+ }
162
+ return {
163
+ totalKeys: all.size,
164
+ totalPks,
165
+ averagePksPerKey: all.size > 0 ? totalPks / all.size : 0,
166
+ maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
167
+ minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
168
+ };
169
+ }
170
+ destroy() {
171
+ this.emitter.offAll();
172
+ this.store.clear();
173
+ }
174
+ emitUpdate(keys) {
175
+ this.emitter.emit(0 /* UPDATE */, { keys });
176
+ }
177
+ emitUpdateOne(key) {
178
+ this.emitUpdate([key]);
179
+ }
180
+ slotsToEntities(slots) {
181
+ const entities = [];
182
+ for (const slot of slots) {
183
+ if (slot.item !== void 0) entities.push(slot.item);
184
+ }
185
+ return entities;
186
+ }
187
+ };
188
+
189
+ // src/abstract/OIMIndexStoreSetBased.ts
190
+ var OIMIndexStoreSetBased = class {
191
+ };
192
+
193
+ // src/abstract/OIMIndexStoreArrayBased.ts
194
+ var OIMIndexStoreArrayBased = class {
195
+ };
196
+
197
+ // src/enums/EOIMEventQueueSchedulerEventType.ts
119
198
  var EOIMEventQueueSchedulerEventType = /* @__PURE__ */ ((EOIMEventQueueSchedulerEventType2) => {
120
199
  EOIMEventQueueSchedulerEventType2[EOIMEventQueueSchedulerEventType2["FLUSH"] = 0] = "FLUSH";
121
200
  return EOIMEventQueueSchedulerEventType2;
@@ -140,12 +219,6 @@ var OIMEventQueueScheduler = class {
140
219
  }
141
220
  };
142
221
 
143
- // src/enum/EOIMIndexEventType.ts
144
- var EOIMIndexEventType = /* @__PURE__ */ ((EOIMIndexEventType2) => {
145
- EOIMIndexEventType2[EOIMIndexEventType2["UPDATE"] = 0] = "UPDATE";
146
- return EOIMIndexEventType2;
147
- })(EOIMIndexEventType || {});
148
-
149
222
  // src/core/OIMIndexStoreMapDrivenSetBased.ts
150
223
  var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
151
224
  constructor() {
@@ -191,17 +264,20 @@ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
191
264
  };
192
265
 
193
266
  // src/abstract/OIMIndexSetBased.ts
194
- var OIMIndexSetBased = class {
267
+ var OIMIndexSetBased = class extends OIMIndex {
195
268
  constructor(options = {}) {
196
- this.fallbackSlots = /* @__PURE__ */ new Map();
197
- this.emitter = new OIMEventEmitter();
198
- this.comparePks = options.comparePks;
199
- this.store = options.store ?? new OIMIndexStoreMapDrivenSetBased();
200
- this.resolveSlot = options.resolveSlot;
269
+ super(
270
+ options.store ?? new OIMIndexStoreMapDrivenSetBased(),
271
+ options.comparePks
272
+ );
273
+ }
274
+ getBucketSize(bucket) {
275
+ return bucket.size;
276
+ }
277
+ getPksByKey(key) {
278
+ const slotsSet = this.store.getOneByKey(key);
279
+ return slotsSet ? this.slotsToPks(slotsSet) : /* @__PURE__ */ new Set();
201
280
  }
202
- /**
203
- * Get primary keys for multiple index keys
204
- */
205
281
  getPksByKeys(keys) {
206
282
  const result = /* @__PURE__ */ new Map();
207
283
  const slotsByKey = this.store.getManyByKeys(keys);
@@ -210,112 +286,25 @@ var OIMIndexSetBased = class {
210
286
  }
211
287
  return result;
212
288
  }
213
- setSlotResolver(resolveSlot) {
214
- this.resolveSlot = resolveSlot;
215
- const allSlots = this.store.getAll();
216
- for (const [key, slots] of allSlots) {
217
- this.store.setOneByKey(
218
- key,
219
- this.resolveSlots(this.slotsToPks(slots))
220
- );
221
- }
222
- }
223
- /**
224
- * @deprecated Use getPksByKey instead
225
- * Get primary keys for a specific index key
226
- */
227
- getPks(key) {
228
- return this.getPksByKey(key);
229
- }
230
- /**
231
- * Get primary keys for a specific index key
232
- */
233
- getPksByKey(key) {
234
- const slotsSet = this.store.getOneByKey(key);
235
- return slotsSet ? this.slotsToPks(slotsSet) : /* @__PURE__ */ new Set();
236
- }
237
289
  getSlotsByKey(key) {
238
- const slotsSet = this.store.getOneByKey(key);
239
- return slotsSet ? slotsSet : /* @__PURE__ */ new Set();
290
+ return this.store.getOneByKey(key) ?? /* @__PURE__ */ new Set();
240
291
  }
241
292
  getSlotsByKeys(keys) {
242
293
  return this.store.getManyByKeys(keys);
243
294
  }
244
- /**
245
- * Check if an index key exists
246
- */
247
- hasKey(key) {
248
- return this.store.getOneByKey(key) !== void 0;
249
- }
250
- /**
251
- * Get all index keys
252
- */
253
- getKeys() {
254
- return this.store.getAllKeys();
255
- }
256
- /**
257
- * Get the number of primary keys for a specific index key
258
- */
259
- getKeySize(key) {
260
- const slotsSet = this.store.getOneByKey(key);
261
- return slotsSet ? slotsSet.size : 0;
262
- }
263
- /**
264
- * Get total number of index keys
265
- */
266
- get size() {
267
- return this.store.countAll();
268
- }
269
- /**
270
- * Check if the index is empty
271
- */
272
- get isEmpty() {
273
- return this.store.countAll() === 0;
274
- }
275
- /**
276
- * Get performance metrics for monitoring and debugging
277
- */
278
- getMetrics() {
279
- let totalPks = 0;
280
- let maxBucketSize = 0;
281
- let minBucketSize = Infinity;
282
- const allPks = this.store.getAll();
283
- for (const slotsSet of allPks.values()) {
284
- totalPks += slotsSet.size;
285
- maxBucketSize = Math.max(maxBucketSize, slotsSet.size);
286
- minBucketSize = Math.min(minBucketSize, slotsSet.size);
287
- }
288
- return {
289
- totalKeys: allPks.size,
290
- totalPks,
291
- averagePksPerKey: allPks.size > 0 ? totalPks / allPks.size : 0,
292
- maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
293
- minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
294
- };
295
- }
296
- /**
297
- * Clean up event listeners when index is no longer needed
298
- */
299
- destroy() {
300
- this.emitter.offAll();
301
- this.store.clear();
302
- }
303
- /**
304
- * Set primary keys for a specific index key with optional comparison.
305
- * If comparator is provided and returns true (no changes), skip the update.
306
- */
307
- setPksWithComparison(key, newPks) {
308
- return this.setSlotsWithComparison(key, this.resolveSlots(newPks));
295
+ slotsToPks(slots) {
296
+ const pks = /* @__PURE__ */ new Set();
297
+ for (const slot of slots) pks.add(slot.pk);
298
+ return pks;
309
299
  }
310
300
  setSlotsWithComparison(key, newSlots) {
311
301
  if (this.comparePks) {
312
302
  const existingSlotsSet = this.store.getOneByKey(key);
313
303
  if (existingSlotsSet && existingSlotsSet.size === newSlots.size) {
314
- const existingPksArray = Array.from(
315
- this.slotsToPks(existingSlotsSet)
316
- );
317
- const newPksArray = Array.from(this.slotsToPks(newSlots));
318
- if (this.comparePks(existingPksArray, newPksArray)) {
304
+ if (this.comparePks(
305
+ Array.from(this.slotsToPks(existingSlotsSet)),
306
+ Array.from(this.slotsToPks(newSlots))
307
+ )) {
319
308
  return false;
320
309
  }
321
310
  } else if (!existingSlotsSet && newSlots.size === 0) {
@@ -325,38 +314,6 @@ var OIMIndexSetBased = class {
325
314
  this.store.setOneByKey(key, newSlots);
326
315
  return true;
327
316
  }
328
- resolveSlots(pks) {
329
- const slots = /* @__PURE__ */ new Set();
330
- for (const pk of pks) slots.add(this.getOrCreateSlot(pk));
331
- return slots;
332
- }
333
- getOrCreateSlot(pk) {
334
- const resolved = this.resolveSlot?.(pk);
335
- if (resolved) return resolved;
336
- let fallback = this.fallbackSlots.get(pk);
337
- if (!fallback) {
338
- fallback = { pk, item: void 0 };
339
- this.fallbackSlots.set(pk, fallback);
340
- }
341
- return fallback;
342
- }
343
- slotsToPks(slots) {
344
- const pks = /* @__PURE__ */ new Set();
345
- for (const slot of slots) pks.add(slot.pk);
346
- return pks;
347
- }
348
- /**
349
- * Emit update event with changed keys
350
- */
351
- emitUpdate(keys) {
352
- this.emitter.emit(0 /* UPDATE */, { keys });
353
- }
354
- /**
355
- * Emit update event for a single key (fast-path override point).
356
- */
357
- emitUpdateOne(key) {
358
- this.emitUpdate([key]);
359
- }
360
317
  };
361
318
 
362
319
  // src/core/OIMIndexStoreMapDrivenArrayBased.ts
@@ -404,17 +361,20 @@ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
404
361
  };
405
362
 
406
363
  // src/abstract/OIMIndexArrayBased.ts
407
- var OIMIndexArrayBased = class {
364
+ var OIMIndexArrayBased = class extends OIMIndex {
408
365
  constructor(options = {}) {
409
- this.fallbackSlots = /* @__PURE__ */ new Map();
410
- this.emitter = new OIMEventEmitter();
411
- this.comparePks = options.comparePks;
412
- this.store = options.store ?? new OIMIndexStoreMapDrivenArrayBased();
413
- this.resolveSlot = options.resolveSlot;
366
+ super(
367
+ options.store ?? new OIMIndexStoreMapDrivenArrayBased(),
368
+ options.comparePks
369
+ );
370
+ }
371
+ getBucketSize(bucket) {
372
+ return bucket.length;
373
+ }
374
+ getPksByKey(key) {
375
+ const slotsArray = this.store.getOneByKey(key);
376
+ return slotsArray ? this.slotsToPks(slotsArray) : [];
414
377
  }
415
- /**
416
- * Get primary keys for multiple index keys
417
- */
418
378
  getPksByKeys(keys) {
419
379
  const result = /* @__PURE__ */ new Map();
420
380
  const slotsByKey = this.store.getManyByKeys(keys);
@@ -423,95 +383,17 @@ var OIMIndexArrayBased = class {
423
383
  }
424
384
  return result;
425
385
  }
426
- setSlotResolver(resolveSlot) {
427
- this.resolveSlot = resolveSlot;
428
- const allSlots = this.store.getAll();
429
- for (const [key, slots] of allSlots) {
430
- this.store.setOneByKey(
431
- key,
432
- slots.map((slot) => this.getOrCreateSlot(slot.pk))
433
- );
434
- }
435
- }
436
- /**
437
- * Get primary keys for a specific index key
438
- */
439
- getPksByKey(key) {
440
- const slotsArray = this.store.getOneByKey(key);
441
- return slotsArray ? this.slotsToPks(slotsArray) : [];
442
- }
443
386
  getSlotsByKey(key) {
444
- const slotsArray = this.store.getOneByKey(key);
445
- return slotsArray ? slotsArray : [];
387
+ return this.store.getOneByKey(key) ?? [];
446
388
  }
447
389
  getSlotsByKeys(keys) {
448
390
  return this.store.getManyByKeys(keys);
449
391
  }
450
- /**
451
- * Check if an index key exists
452
- */
453
- hasKey(key) {
454
- return this.store.getOneByKey(key) !== void 0;
455
- }
456
- /**
457
- * Get all index keys
458
- */
459
- getKeys() {
460
- return this.store.getAllKeys();
461
- }
462
- /**
463
- * Get the number of primary keys for a specific index key
464
- */
465
- getKeySize(key) {
466
- const slotsArray = this.store.getOneByKey(key);
467
- return slotsArray ? slotsArray.length : 0;
468
- }
469
- /**
470
- * Get total number of index keys
471
- */
472
- get size() {
473
- return this.store.countAll();
474
- }
475
- /**
476
- * Check if the index is empty
477
- */
478
- get isEmpty() {
479
- return this.store.countAll() === 0;
480
- }
481
- /**
482
- * Get performance metrics for monitoring and debugging
483
- */
484
- getMetrics() {
485
- let totalPks = 0;
486
- let maxBucketSize = 0;
487
- let minBucketSize = Infinity;
488
- const allPks = this.store.getAll();
489
- for (const slotsArray of allPks.values()) {
490
- totalPks += slotsArray.length;
491
- maxBucketSize = Math.max(maxBucketSize, slotsArray.length);
492
- minBucketSize = Math.min(minBucketSize, slotsArray.length);
493
- }
494
- return {
495
- totalKeys: allPks.size,
496
- totalPks,
497
- averagePksPerKey: allPks.size > 0 ? totalPks / allPks.size : 0,
498
- maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
499
- minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
500
- };
501
- }
502
- /**
503
- * Clean up event listeners when index is no longer needed
504
- */
505
- destroy() {
506
- this.emitter.offAll();
507
- this.store.clear();
508
- }
509
- /**
510
- * Set primary keys for a specific index key with optional comparison.
511
- * If comparator is provided and returns true (no changes), skip the update.
512
- */
513
- setPksWithComparison(key, newPks) {
514
- return this.setSlotsWithComparison(key, this.resolveSlots(newPks));
392
+ slotsToPks(slots) {
393
+ const pks = [];
394
+ pks.length = slots.length;
395
+ for (let i = 0; i < slots.length; i++) pks[i] = slots[i].pk;
396
+ return pks;
515
397
  }
516
398
  setSlotsWithComparison(key, newSlots) {
517
399
  if (this.comparePks) {
@@ -530,45 +412,9 @@ var OIMIndexArrayBased = class {
530
412
  this.store.setOneByKey(key, newSlots);
531
413
  return true;
532
414
  }
533
- resolveSlots(pks) {
534
- const slots = [];
535
- slots.length = pks.length;
536
- for (let i = 0; i < pks.length; i++) {
537
- slots[i] = this.getOrCreateSlot(pks[i]);
538
- }
539
- return slots;
540
- }
541
- getOrCreateSlot(pk) {
542
- const resolved = this.resolveSlot?.(pk);
543
- if (resolved) return resolved;
544
- let fallback = this.fallbackSlots.get(pk);
545
- if (!fallback) {
546
- fallback = { pk, item: void 0 };
547
- this.fallbackSlots.set(pk, fallback);
548
- }
549
- return fallback;
550
- }
551
- slotsToPks(slots) {
552
- const pks = [];
553
- pks.length = slots.length;
554
- for (let i = 0; i < slots.length; i++) pks[i] = slots[i].pk;
555
- return pks;
556
- }
557
- /**
558
- * Emit update event with changed keys
559
- */
560
- emitUpdate(keys) {
561
- this.emitter.emit(0 /* UPDATE */, { keys });
562
- }
563
- /**
564
- * Emit update event for a single key (fast-path override point).
565
- */
566
- emitUpdateOne(key) {
567
- this.emitUpdate([key]);
568
- }
569
415
  };
570
416
 
571
- // src/enum/EOIMEventQueueEventType.ts
417
+ // src/enums/EOIMEventQueueEventType.ts
572
418
  var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
573
419
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
574
420
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
@@ -822,23 +668,17 @@ var OIMUpdateEventEmitter = class {
822
668
  }
823
669
  };
824
670
 
825
- // src/abstract/OIMReactiveIndexSetBased.ts
826
- var OIMReactiveIndexSetBased = class {
671
+ // src/abstract/OIMReactiveIndex.ts
672
+ var OIMReactiveIndex = class {
827
673
  constructor(queue, createIndex) {
828
674
  this.updateEmitter = new OIMUpdateEventEmitter(queue);
829
675
  this.index = createIndex(this.updateEmitter);
830
676
  }
831
- getPksByKey(key) {
832
- return this.index.getPksByKey(key);
833
- }
834
- getPksByKeys(keys) {
835
- return this.index.getPksByKeys(keys);
836
- }
837
- getSlotsByKey(key) {
838
- return this.index.getSlotsByKey(key);
677
+ getEntitiesByKey(key) {
678
+ return this.index.getEntitiesByKey(key);
839
679
  }
840
- getSlotsByKeys(keys) {
841
- return this.index.getSlotsByKeys(keys);
680
+ getEntitiesByKeys(keys) {
681
+ return this.index.getEntitiesByKeys(keys);
842
682
  }
843
683
  hasKey(key) {
844
684
  return this.index.hasKey(key);
@@ -885,11 +725,10 @@ var OIMReactiveIndexSetBased = class {
885
725
  }
886
726
  };
887
727
 
888
- // src/abstract/OIMReactiveIndexArrayBased.ts
889
- var OIMReactiveIndexArrayBased = class {
728
+ // src/abstract/OIMReactiveIndexSetBased.ts
729
+ var OIMReactiveIndexSetBased = class extends OIMReactiveIndex {
890
730
  constructor(queue, createIndex) {
891
- this.updateEmitter = new OIMUpdateEventEmitter(queue);
892
- this.index = createIndex(this.updateEmitter);
731
+ super(queue, createIndex);
893
732
  }
894
733
  getPksByKey(key) {
895
734
  return this.index.getPksByKey(key);
@@ -903,48 +742,24 @@ var OIMReactiveIndexArrayBased = class {
903
742
  getSlotsByKeys(keys) {
904
743
  return this.index.getSlotsByKeys(keys);
905
744
  }
906
- hasKey(key) {
907
- return this.index.hasKey(key);
908
- }
909
- getKeys() {
910
- return this.index.getKeys();
911
- }
912
- getKeySize(key) {
913
- return this.index.getKeySize(key);
914
- }
915
- get size() {
916
- return this.index.size;
917
- }
918
- get isEmpty() {
919
- return this.index.isEmpty;
920
- }
921
- getMetrics() {
922
- return this.index.getMetrics();
923
- }
924
- subscribeOnKey(key, handler) {
925
- return this.updateEmitter.subscribeOnKey(key, handler);
926
- }
927
- subscribeOnKeys(keys, handler) {
928
- return this.updateEmitter.subscribeOnKeys(keys, handler);
929
- }
930
- unsubscribeFromKey(key, handler) {
931
- this.updateEmitter.unsubscribeFromKey(key, handler);
932
- }
933
- unsubscribeFromKeys(keys, handler) {
934
- this.updateEmitter.unsubscribeFromKeys(keys, handler);
745
+ };
746
+
747
+ // src/abstract/OIMReactiveIndexArrayBased.ts
748
+ var OIMReactiveIndexArrayBased = class extends OIMReactiveIndex {
749
+ constructor(queue, createIndex) {
750
+ super(queue, createIndex);
935
751
  }
936
- hasSubscriptions() {
937
- return this.updateEmitter.hasSubscriptions();
752
+ getPksByKey(key) {
753
+ return this.index.getPksByKey(key);
938
754
  }
939
- getHandlerCount(key) {
940
- return this.updateEmitter.getHandlerCount(key);
755
+ getPksByKeys(keys) {
756
+ return this.index.getPksByKeys(keys);
941
757
  }
942
- getSubscriptionMetrics() {
943
- return this.updateEmitter.getMetrics();
758
+ getSlotsByKey(key) {
759
+ return this.index.getSlotsByKey(key);
944
760
  }
945
- destroy() {
946
- this.updateEmitter.destroy();
947
- this.index.destroy();
761
+ getSlotsByKeys(keys) {
762
+ return this.index.getSlotsByKeys(keys);
948
763
  }
949
764
  };
950
765
 
@@ -969,14 +784,20 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
969
784
  const slot = this.slots.get(pk);
970
785
  if (slot) {
971
786
  slot.item = entity;
787
+ return slot;
972
788
  } else {
973
- this.slots.set(pk, { pk, item: entity });
789
+ const nextSlot = { pk, item: entity };
790
+ this.slots.set(pk, nextSlot);
791
+ return nextSlot;
974
792
  }
975
793
  }
976
794
  setManyByPks(pks, entities) {
795
+ const slots = [];
796
+ slots.length = pks.length;
977
797
  for (let i = 0; i < pks.length; i++) {
978
- this.setOneByPk(pks[i], entities[i]);
798
+ slots[i] = this.setOneByPk(pks[i], entities[i]);
979
799
  }
800
+ return slots;
980
801
  }
981
802
  getSlotByPk(pk) {
982
803
  return this.slots.get(pk);
@@ -1056,7 +877,7 @@ var OIMEntityUpdaterFactory = class {
1056
877
  }
1057
878
  };
1058
879
 
1059
- // src/enum/EOIMCollectionEventType.ts
880
+ // src/enums/EOIMCollectionEventType.ts
1060
881
  var EOIMCollectionEventType = /* @__PURE__ */ ((EOIMCollectionEventType2) => {
1061
882
  EOIMCollectionEventType2[EOIMCollectionEventType2["UPDATE"] = 0] = "UPDATE";
1062
883
  return EOIMCollectionEventType2;
@@ -1086,18 +907,23 @@ var OIMCollection = class {
1086
907
  return this.store.getAllSlots();
1087
908
  }
1088
909
  upsertOneByPk(pk, entity) {
1089
- this.upsertOneWithoutNotificationsByPk(pk, entity);
910
+ const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
1090
911
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
912
+ return slot;
1091
913
  }
1092
914
  upsertOne(entity) {
1093
- const pk = this.upsertOneWithoutNotifications(entity);
1094
- this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
915
+ const slot = this.upsertOneWithoutNotifications(entity);
916
+ this.emitter.emit(0 /* UPDATE */, { pks: [slot.pk] });
917
+ return slot;
1095
918
  }
1096
919
  upsertMany(entities) {
1097
- const pks = entities.map(
920
+ if (entities.length === 0) return [];
921
+ const slots = entities.map(
1098
922
  (entity) => this.upsertOneWithoutNotifications(entity)
1099
923
  );
924
+ const pks = slots.map((slot) => slot.pk);
1100
925
  this.emitter.emit(0 /* UPDATE */, { pks });
926
+ return slots;
1101
927
  }
1102
928
  removeOne(entity) {
1103
929
  const pk = this.selectPk(entity);
@@ -1105,6 +931,7 @@ var OIMCollection = class {
1105
931
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1106
932
  }
1107
933
  removeMany(entities) {
934
+ if (entities.length === 0) return;
1108
935
  const pks = entities.map(this.selectPk);
1109
936
  this.store.removeManyByPks(pks);
1110
937
  this.emitter.emit(0 /* UPDATE */, { pks });
@@ -1114,6 +941,7 @@ var OIMCollection = class {
1114
941
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1115
942
  }
1116
943
  removeManyByPks(pks) {
944
+ if (pks.length === 0) return;
1117
945
  this.store.removeManyByPks(pks);
1118
946
  this.emitter.emit(0 /* UPDATE */, { pks });
1119
947
  }
@@ -1132,8 +960,7 @@ var OIMCollection = class {
1132
960
  }
1133
961
  upsertOneWithoutNotifications(entity) {
1134
962
  const pk = this.selectPk(entity);
1135
- this.upsertOneWithoutNotificationsByPk(pk, entity);
1136
- return pk;
963
+ return this.upsertOneWithoutNotificationsByPk(pk, entity);
1137
964
  }
1138
965
  upsertOneWithoutNotificationsByPk(pk, entity) {
1139
966
  if (!pk) {
@@ -1143,12 +970,12 @@ var OIMCollection = class {
1143
970
  }
1144
971
  const existingEntity = this.store.getOneByPk(pk);
1145
972
  if (existingEntity) {
1146
- this.store.setOneByPk(
973
+ return this.store.setOneByPk(
1147
974
  pk,
1148
975
  this.updateEntity(entity, existingEntity)
1149
976
  );
1150
977
  } else {
1151
- this.store.setOneByPk(pk, entity);
978
+ return this.store.setOneByPk(pk, entity);
1152
979
  }
1153
980
  }
1154
981
  destroy() {
@@ -1209,24 +1036,29 @@ var OIMReactiveCollection = class extends OIMCollection {
1209
1036
  };
1210
1037
  }
1211
1038
  upsertOneByPk(pk, entity) {
1212
- this.upsertOneWithoutNotificationsByPk(pk, entity);
1039
+ const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
1213
1040
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1214
1041
  this.updateEmitter.markUpdatedKey(pk);
1215
1042
  this.trackAnyUpdatePk(pk);
1043
+ return slot;
1216
1044
  }
1217
1045
  upsertOne(entity) {
1218
- const pk = this.upsertOneWithoutNotifications(entity);
1219
- this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1220
- this.updateEmitter.markUpdatedKey(pk);
1221
- this.trackAnyUpdatePk(pk);
1046
+ const slot = this.upsertOneWithoutNotifications(entity);
1047
+ this.emitter.emit(0 /* UPDATE */, { pks: [slot.pk] });
1048
+ this.updateEmitter.markUpdatedKey(slot.pk);
1049
+ this.trackAnyUpdatePk(slot.pk);
1050
+ return slot;
1222
1051
  }
1223
1052
  upsertMany(entities) {
1224
- const pks = entities.map(
1053
+ if (entities.length === 0) return [];
1054
+ const slots = entities.map(
1225
1055
  (entity) => this.upsertOneWithoutNotifications(entity)
1226
1056
  );
1057
+ const pks = slots.map((slot) => slot.pk);
1227
1058
  this.emitter.emit(0 /* UPDATE */, { pks });
1228
1059
  this.updateEmitter.markUpdatedKeys(pks);
1229
1060
  this.trackAnyUpdatePks(pks);
1061
+ return slots;
1230
1062
  }
1231
1063
  removeOne(entity) {
1232
1064
  const pk = this.selectPk(entity);
@@ -1236,6 +1068,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1236
1068
  this.trackAnyUpdatePk(pk);
1237
1069
  }
1238
1070
  removeMany(entities) {
1071
+ if (entities.length === 0) return;
1239
1072
  const pks = entities.map(this.selectPk);
1240
1073
  this.store.removeManyByPks(pks);
1241
1074
  this.emitter.emit(0 /* UPDATE */, { pks });
@@ -1249,6 +1082,7 @@ var OIMReactiveCollection = class extends OIMCollection {
1249
1082
  this.trackAnyUpdatePk(pk);
1250
1083
  }
1251
1084
  removeManyByPks(pks) {
1085
+ if (pks.length === 0) return;
1252
1086
  this.store.removeManyByPks(pks);
1253
1087
  this.emitter.emit(0 /* UPDATE */, { pks });
1254
1088
  this.updateEmitter.markUpdatedKeys(pks);
@@ -1294,202 +1128,347 @@ var OIMReactiveCollection = class extends OIMCollection {
1294
1128
  }
1295
1129
  };
1296
1130
 
1297
- // src/core/OIMObjectStoreRecordDriven.ts
1298
- var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
1299
- constructor(initial) {
1300
- super();
1301
- this.properties = Object.assign(/* @__PURE__ */ Object.create(null), initial ?? {});
1302
- }
1303
- setProperty(key, value) {
1304
- this.properties[key] = value;
1305
- }
1306
- merge(draft) {
1307
- Object.assign(this.properties, draft);
1308
- }
1309
- delete(key) {
1310
- delete this.properties[key];
1311
- }
1312
- get(key) {
1313
- return this.properties[key];
1314
- }
1315
- getAll() {
1316
- return { ...this.properties };
1131
+ // src/modules/wrapper/index/OIMIndexManualOrderedArrayBased.ts
1132
+ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
1133
+ clear(key) {
1134
+ if (key === void 0) {
1135
+ const keys = this.getKeys();
1136
+ if (keys.length > 0) {
1137
+ this.store.clear();
1138
+ this.emitUpdate(Array.from(keys));
1139
+ }
1140
+ return;
1141
+ }
1142
+ if (this.store.getOneByKey(key) !== void 0) {
1143
+ this.store.removeOneByKey(key);
1144
+ this.emitUpdate([key]);
1145
+ }
1317
1146
  }
1318
- clear() {
1319
- this.properties = /* @__PURE__ */ Object.create(null);
1147
+ pushSlot(key, slot) {
1148
+ const list = this.getOrCreateList(key);
1149
+ const index = list.length;
1150
+ list.push(slot);
1151
+ this.emitUpdate([key]);
1152
+ return index;
1320
1153
  }
1321
- count() {
1322
- return Object.keys(this.properties).length;
1154
+ insertSlotAt(key, index, slot) {
1155
+ const list = this.getOrCreateList(key);
1156
+ const safeIndex = Math.max(0, Math.min(index, list.length));
1157
+ list.splice(safeIndex, 0, slot);
1158
+ this.emitUpdate([key]);
1159
+ return safeIndex;
1323
1160
  }
1324
- keys() {
1325
- return Object.keys(this.properties);
1161
+ removeAt(key, index) {
1162
+ const list = this.store.getOneByKey(key);
1163
+ if (!list) return void 0;
1164
+ if (index < 0 || index >= list.length) return void 0;
1165
+ const [removed] = list.splice(index, 1);
1166
+ if (list.length === 0) {
1167
+ this.store.removeOneByKey(key);
1168
+ }
1169
+ this.emitUpdate([key]);
1170
+ return removed;
1326
1171
  }
1327
- values() {
1328
- return Object.values(this.properties);
1172
+ move(key, fromIndex, toIndex) {
1173
+ const list = this.store.getOneByKey(key);
1174
+ if (!list) return void 0;
1175
+ if (fromIndex < 0 || fromIndex >= list.length) return void 0;
1176
+ if (toIndex < 0 || toIndex >= list.length) return void 0;
1177
+ if (fromIndex === toIndex) return list[fromIndex];
1178
+ const [slot] = list.splice(fromIndex, 1);
1179
+ if (slot === void 0) return void 0;
1180
+ list.splice(toIndex, 0, slot);
1181
+ this.emitUpdate([key]);
1182
+ return slot;
1329
1183
  }
1330
- entries() {
1331
- return Object.entries(this.properties);
1184
+ resetSlots(key, slots) {
1185
+ if (slots.length === 0) {
1186
+ if (this.store.getOneByKey(key) !== void 0) {
1187
+ this.store.removeOneByKey(key);
1188
+ this.emitUpdate([key]);
1189
+ }
1190
+ return;
1191
+ }
1192
+ this.store.setOneByKey(key, slots.slice());
1193
+ this.emitUpdate([key]);
1332
1194
  }
1333
- destroy() {
1334
- this.clear();
1195
+ getOrCreateList(key) {
1196
+ let list = this.store.getOneByKey(key);
1197
+ if (!list) {
1198
+ list = [];
1199
+ this.store.setOneByKey(key, list);
1200
+ }
1201
+ return list;
1335
1202
  }
1336
1203
  };
1337
1204
 
1338
- // src/enum/EOIMObjectEventType.ts
1339
- var EOIMObjectEventType = /* @__PURE__ */ ((EOIMObjectEventType2) => {
1340
- EOIMObjectEventType2[EOIMObjectEventType2["UPDATE"] = 0] = "UPDATE";
1341
- return EOIMObjectEventType2;
1342
- })(EOIMObjectEventType || {});
1343
-
1344
- // src/core/OIMObject.ts
1345
- var OIMObject = class {
1205
+ // src/modules/wrapper/index/OIMCollectionIndexManualOrderedArrayBased.ts
1206
+ var OIMCollectionIndexManualOrderedArrayBased = class extends OIMIndexManualOrderedArrayBased {
1346
1207
  constructor(opts) {
1347
- this.emitter = new OIMEventEmitter();
1348
- this.store = opts?.store ?? new OIMObjectStoreRecordDriven();
1208
+ super();
1209
+ this.resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1349
1210
  }
1350
- setProperty(key, value) {
1351
- this.store.setProperty(key, value);
1352
- this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1211
+ push(key, pk) {
1212
+ return this.pushSlot(key, this.resolveRequiredSlot(pk));
1353
1213
  }
1354
- merge(draft) {
1355
- this.store.merge(draft);
1356
- this.emitter.emit(0 /* UPDATE */, {
1357
- keys: Object.keys(draft)
1358
- });
1214
+ insertAt(key, index, pk) {
1215
+ return this.insertSlotAt(key, index, this.resolveRequiredSlot(pk));
1359
1216
  }
1360
- delete(key) {
1361
- this.store.delete(key);
1362
- this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1217
+ reset(key, pks) {
1218
+ this.resetSlots(key, this.resolveSlots(pks));
1363
1219
  }
1364
- get(key) {
1365
- return this.store.get(key);
1220
+ getEntitiesByKey(key) {
1221
+ return super.getEntitiesByKey(key);
1366
1222
  }
1367
- getAll() {
1368
- return this.store.getAll();
1223
+ getEntitiesByKeys(keys) {
1224
+ return super.getEntitiesByKeys(keys);
1369
1225
  }
1370
- clear() {
1371
- this.store.clear();
1372
- this.emitter.emit(0 /* UPDATE */, { keys: [] });
1226
+ resolvePk(pk) {
1227
+ return this.resolveRequiredSlot(pk);
1373
1228
  }
1374
- count() {
1375
- return this.store.count();
1229
+ resolveSlots(pks) {
1230
+ const slots = [];
1231
+ slots.length = pks.length;
1232
+ for (let i = 0; i < pks.length; i++) {
1233
+ slots[i] = this.resolveRequiredSlot(pks[i]);
1234
+ }
1235
+ return slots;
1376
1236
  }
1377
- keys() {
1378
- return this.store.keys();
1237
+ resolveRequiredSlot(pk) {
1238
+ const slot = this.resolveSlot(pk);
1239
+ if (!slot) {
1240
+ throw new Error(
1241
+ `[OIMCollectionIndexManualOrderedArrayBased]: Unable to resolve slot for PK "${String(pk)}".`
1242
+ );
1243
+ }
1244
+ return slot;
1379
1245
  }
1380
- values() {
1381
- return this.store.values();
1246
+ };
1247
+
1248
+ // src/modules/wrapper/index/OIMOrderedListCommandStream.ts
1249
+ var OIMOrderedListCommandStream = class {
1250
+ constructor(queue, index) {
1251
+ this.isInWrite = false;
1252
+ this.commandsByKey = /* @__PURE__ */ new Map();
1253
+ this.onAfterFlush = () => {
1254
+ this.commandsByKey.clear();
1255
+ };
1256
+ this.onIndexUpdate = (payload) => {
1257
+ if (this.isInWrite) return;
1258
+ if (payload.keys.length === 0) return;
1259
+ for (let i = 0; i < payload.keys.length; i++) {
1260
+ const key = payload.keys[i];
1261
+ this.appendSetCommand(key, this.getSlotsByKey(key));
1262
+ }
1263
+ };
1264
+ this.commandsEventEmitter = new OIMUpdateEventEmitter(
1265
+ queue,
1266
+ "after_flush"
1267
+ );
1268
+ this.index = index ?? new OIMIndexManualOrderedArrayBased();
1269
+ this.unsubscribeAfterFlush = queue.emitter.on(
1270
+ 0 /* AFTER_FLUSH */,
1271
+ this.onAfterFlush
1272
+ );
1273
+ this.unsubscribeFromIndexEmitter = this.index.emitter.on(
1274
+ 0 /* UPDATE */,
1275
+ this.onIndexUpdate
1276
+ );
1382
1277
  }
1383
- entries() {
1384
- return this.store.entries();
1278
+ getPksByKey(key) {
1279
+ return this.index.getPksByKey(key);
1385
1280
  }
1386
- destroy() {
1387
- this.store.destroy();
1388
- this.emitter.offAll();
1281
+ getSlotsByKey(key) {
1282
+ return this.index.getSlotsByKey(
1283
+ key
1284
+ );
1389
1285
  }
1390
- };
1391
-
1392
- // src/core/OIMReactiveObject.ts
1393
- var OIMReactiveObject = class extends OIMObject {
1394
- constructor(queue, opts) {
1395
- super(opts);
1396
- this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
1397
- this.emitter.on(0 /* UPDATE */, (payload) => {
1398
- this.updateEmitter.markUpdatedKeys(payload.keys);
1399
- });
1286
+ getEntitiesByKey(key) {
1287
+ return this.index.getEntitiesByKey(key);
1400
1288
  }
1401
- subscribeOnKey(key, handler) {
1402
- return this.updateEmitter.subscribeOnKey(key, handler);
1289
+ hasKey(key) {
1290
+ return this.index.hasKey(key);
1403
1291
  }
1404
- subscribeOnKeys(keys, handler) {
1405
- return this.updateEmitter.subscribeOnKeys(keys, handler);
1292
+ getKeys() {
1293
+ return this.index.getKeys();
1406
1294
  }
1407
- unsubscribeFromKey(key, handler) {
1408
- this.updateEmitter.unsubscribeFromKey(key, handler);
1295
+ pushSlot(key, slot) {
1296
+ this.withWrite(() => {
1297
+ const index = this.index.pushSlot(key, slot);
1298
+ this.appendCommand(key, {
1299
+ type: "insert",
1300
+ pk: slot.pk,
1301
+ slot,
1302
+ index
1303
+ });
1304
+ });
1409
1305
  }
1410
- unsubscribeFromKeys(keys, handler) {
1411
- this.updateEmitter.unsubscribeFromKeys(keys, handler);
1306
+ insertSlotAt(key, index, slot) {
1307
+ this.withWrite(() => {
1308
+ const safeIndex = this.index.insertSlotAt(key, index, slot);
1309
+ this.appendCommand(key, {
1310
+ type: "insert",
1311
+ pk: slot.pk,
1312
+ slot,
1313
+ index: safeIndex
1314
+ });
1315
+ });
1412
1316
  }
1413
- hasSubscriptions() {
1414
- return this.updateEmitter.hasSubscriptions();
1317
+ removeAt(key, index) {
1318
+ this.withWrite(() => {
1319
+ const slot = this.index.removeAt(key, index);
1320
+ if (slot === void 0) return;
1321
+ this.appendCommand(key, {
1322
+ type: "remove",
1323
+ pk: slot.pk,
1324
+ slot,
1325
+ index
1326
+ });
1327
+ });
1415
1328
  }
1416
- getHandlerCount(key) {
1417
- return this.updateEmitter.getHandlerCount(key);
1329
+ move(key, fromIndex, toIndex) {
1330
+ this.withWrite(() => {
1331
+ const slot = this.index.move(key, fromIndex, toIndex);
1332
+ if (slot === void 0) return;
1333
+ this.appendCommand(key, {
1334
+ type: "move",
1335
+ pk: slot.pk,
1336
+ slot,
1337
+ fromIndex,
1338
+ toIndex
1339
+ });
1340
+ });
1418
1341
  }
1419
- getMetrics() {
1420
- return this.updateEmitter.getMetrics();
1342
+ setSlots(key, slots) {
1343
+ this.withWrite(() => {
1344
+ this.index.resetSlots(key, slots);
1345
+ this.appendSetCommand(key, slots);
1346
+ });
1421
1347
  }
1422
- destroy() {
1423
- this.updateEmitter.destroy();
1424
- super.destroy();
1348
+ getBufferedCommands(key) {
1349
+ return this.commandsByKey.get(key) ?? [];
1425
1350
  }
1426
- };
1427
-
1428
- // src/core/OIMIndexManualSetBased.ts
1429
- var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1430
- constructor(options = {}) {
1431
- super(options);
1351
+ consumeCommands(key) {
1352
+ const cmds = this.commandsByKey.get(key);
1353
+ if (!cmds || cmds.length === 0) return [];
1354
+ return cmds.slice();
1432
1355
  }
1433
- /**
1434
- * Set primary keys for a specific index key, replacing any existing values.
1435
- * Uses optional comparator to skip updates if PKs haven't actually changed.
1436
- */
1437
- setPks(key, pks) {
1438
- const hasChanges = this.setPksWithComparison(key, new Set(pks));
1439
- if (hasChanges) {
1440
- this.emitUpdateOne(key);
1356
+ clear(key) {
1357
+ if (key === void 0) {
1358
+ this.index.destroy();
1359
+ this.commandsByKey.clear();
1360
+ return;
1441
1361
  }
1362
+ this.index.clear(key);
1363
+ this.commandsByKey.delete(key);
1442
1364
  }
1443
- setSlots(key, slots) {
1444
- const hasChanges = this.setSlotsWithComparison(key, new Set(slots));
1445
- if (hasChanges) {
1446
- this.emitUpdateOne(key);
1447
- }
1365
+ destroy() {
1366
+ this.commandsEventEmitter.destroy();
1367
+ this.unsubscribeAfterFlush();
1368
+ this.unsubscribeFromIndexEmitter();
1369
+ this.clear();
1448
1370
  }
1449
- /**
1450
- * Add primary keys to a specific index key
1451
- */
1452
- addPks(key, pks) {
1453
- if (pks.length === 0) return;
1454
- let slotsSet = this.store.getOneByKey(key);
1455
- if (!slotsSet) {
1456
- slotsSet = /* @__PURE__ */ new Set();
1457
- this.store.setOneByKey(key, slotsSet);
1458
- }
1459
- const pksSet = this.slotsToPks(slotsSet);
1460
- let hasChanges = false;
1461
- for (const pk of pks) {
1462
- if (!pksSet.has(pk)) {
1463
- pksSet.add(pk);
1464
- slotsSet.add(this.getOrCreateSlot(pk));
1465
- hasChanges = true;
1466
- }
1371
+ withWrite(fn) {
1372
+ if (this.isInWrite) {
1373
+ fn();
1374
+ return;
1467
1375
  }
1468
- if (hasChanges) {
1469
- this.store.setOneByKey(key, slotsSet);
1470
- this.emitUpdateOne(key);
1376
+ this.isInWrite = true;
1377
+ try {
1378
+ fn();
1379
+ } finally {
1380
+ this.isInWrite = false;
1471
1381
  }
1472
1382
  }
1473
- /**
1474
- * Remove primary keys from a specific index key
1475
- */
1476
- removePks(key, pks) {
1477
- if (pks.length === 0) return;
1478
- const slotsSet = this.store.getOneByKey(key);
1479
- if (!slotsSet) return;
1480
- let hasChanges = false;
1481
- const pksToRemove = new Set(pks);
1482
- for (const slot of Array.from(slotsSet)) {
1483
- if (pksToRemove.has(slot.pk) && slotsSet.delete(slot)) {
1484
- hasChanges = true;
1485
- }
1383
+ appendCommand(key, cmd) {
1384
+ let cmds = this.commandsByKey.get(key);
1385
+ if (!cmds) {
1386
+ cmds = [];
1387
+ this.commandsByKey.set(key, cmds);
1486
1388
  }
1487
- if (slotsSet.size === 0) {
1488
- this.store.removeOneByKey(key);
1489
- hasChanges = true;
1490
- } else if (hasChanges) {
1491
- this.store.setOneByKey(key, slotsSet);
1389
+ if (cmd.type === "set") {
1390
+ cmds.length = 0;
1391
+ cmds.push(cmd);
1392
+ } else if (cmds.length > 0 && cmds[0].type === "set") {
1393
+ const slots = this.getSlotsByKey(key);
1394
+ cmds[0] = this.createSetCommand(slots);
1395
+ } else {
1396
+ cmds.push(cmd);
1492
1397
  }
1398
+ this.commandsEventEmitter.markUpdatedKeys([key]);
1399
+ }
1400
+ appendSetCommand(key, slots) {
1401
+ this.appendCommand(key, this.createSetCommand(slots));
1402
+ }
1403
+ createSetCommand(slots) {
1404
+ return {
1405
+ type: "set",
1406
+ pks: slots.map((slot) => slot.pk),
1407
+ slots
1408
+ };
1409
+ }
1410
+ };
1411
+
1412
+ // src/modules/wrapper/index/OIMCollectionOrderedListCommandStream.ts
1413
+ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandStream {
1414
+ constructor(queue, opts) {
1415
+ const index = opts.index ?? new OIMCollectionIndexManualOrderedArrayBased(
1416
+ opts.collection !== void 0 ? { collection: opts.collection } : { resolveSlot: opts.resolveSlot }
1417
+ );
1418
+ super(queue, index);
1419
+ this.index = index;
1420
+ }
1421
+ push(key, pk) {
1422
+ this.withWrite(() => {
1423
+ const slot = this.resolvePk(pk);
1424
+ const index = this.index.pushSlot(key, slot);
1425
+ this.appendCommand(key, {
1426
+ type: "insert",
1427
+ pk,
1428
+ slot,
1429
+ index
1430
+ });
1431
+ });
1432
+ }
1433
+ insertAt(key, index, pk) {
1434
+ this.withWrite(() => {
1435
+ const slot = this.resolvePk(pk);
1436
+ const safeIndex = this.index.insertSlotAt(key, index, slot);
1437
+ this.appendCommand(key, {
1438
+ type: "insert",
1439
+ pk,
1440
+ slot,
1441
+ index: safeIndex
1442
+ });
1443
+ });
1444
+ }
1445
+ set(key, pks) {
1446
+ this.withWrite(() => {
1447
+ const slots = pks.map((pk) => this.resolvePk(pk));
1448
+ this.index.resetSlots(key, slots);
1449
+ this.appendSetCommand(key, slots);
1450
+ });
1451
+ }
1452
+ getSlotsByKey(key) {
1453
+ return this.index.getSlotsByKey(
1454
+ key
1455
+ );
1456
+ }
1457
+ getEntitiesByKey(key) {
1458
+ return this.index.getEntitiesByKey(key);
1459
+ }
1460
+ resolvePk(pk) {
1461
+ return this.index.resolvePk(pk);
1462
+ }
1463
+ };
1464
+
1465
+ // src/core/OIMIndexManualArrayBased.ts
1466
+ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1467
+ constructor(options = {}) {
1468
+ super(options);
1469
+ }
1470
+ setSlots(key, slots) {
1471
+ const hasChanges = this.setSlotsWithComparison(key, slots);
1493
1472
  if (hasChanges) {
1494
1473
  this.emitUpdateOne(key);
1495
1474
  }
@@ -1513,8 +1492,8 @@ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1513
1492
  }
1514
1493
  };
1515
1494
 
1516
- // src/core/OIMReactiveIndexManualSetBased.ts
1517
- var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1495
+ // src/core/OIMReactiveIndexManualArrayBased.ts
1496
+ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1518
1497
  constructor(updateEmitter, opts) {
1519
1498
  super(opts);
1520
1499
  this.updateEmitter = updateEmitter;
@@ -1526,94 +1505,93 @@ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1526
1505
  this.updateEmitter.markUpdatedKey(key);
1527
1506
  }
1528
1507
  };
1529
- var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1508
+ var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
1530
1509
  constructor(queue, opts) {
1531
1510
  super(queue, (updateEmitter) => {
1532
- return new OIMIndexManualSetBasedReactive(
1511
+ return new OIMIndexManualArrayBasedReactive(
1533
1512
  updateEmitter,
1534
1513
  opts?.indexOptions
1535
1514
  );
1536
1515
  });
1537
1516
  }
1538
- setPks(key, pks) {
1539
- this.index.setPks(key, pks);
1540
- }
1541
1517
  setSlots(key, slots) {
1542
1518
  this.index.setSlots(key, slots);
1543
1519
  }
1544
- addPks(key, pks) {
1545
- this.index.addPks(key, pks);
1546
- }
1547
- removePks(key, pks) {
1548
- this.index.removePks(key, pks);
1549
- }
1550
1520
  clear(key) {
1551
1521
  this.index.clear(key);
1552
1522
  }
1553
1523
  };
1554
1524
 
1555
- // src/core/OIMIndexManualArrayBased.ts
1556
- var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1557
- constructor(options = {}) {
1558
- super(options);
1525
+ // src/core/OIMReactiveCollectionIndexManualArrayBased.ts
1526
+ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexManualArrayBased {
1527
+ constructor(queue, opts) {
1528
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1529
+ super(queue, {
1530
+ indexOptions: opts.indexOptions
1531
+ });
1532
+ this.resolveSlot = resolveSlot;
1559
1533
  }
1560
- /**
1561
- * Set primary keys for a specific index key, replacing any existing values.
1562
- * Uses optional comparator to skip updates if PKs haven't actually changed.
1563
- */
1564
1534
  setPks(key, pks) {
1565
- const hasChanges = this.setPksWithComparison(key, pks);
1566
- if (hasChanges) {
1567
- this.emitUpdateOne(key);
1568
- }
1569
- }
1570
- setSlots(key, slots) {
1571
- const hasChanges = this.setSlotsWithComparison(key, slots);
1572
- if (hasChanges) {
1573
- this.emitUpdateOne(key);
1574
- }
1535
+ this.setSlots(key, this.resolveSlots(pks));
1575
1536
  }
1576
- /**
1577
- * Add primary keys to a specific index key
1578
- */
1579
1537
  addPks(key, pks) {
1580
1538
  if (pks.length === 0) return;
1581
- let slotsArray = this.store.getOneByKey(key);
1582
- if (!slotsArray) {
1583
- slotsArray = [];
1584
- this.store.setOneByKey(key, slotsArray);
1585
- }
1586
- const pksSet = new Set(slotsArray.map((slot) => slot.pk));
1539
+ const existingSlots = this.index.getSlotsByKey(key);
1540
+ const existingPks = new Set(existingSlots.map((slot) => slot.pk));
1541
+ const nextSlots = existingSlots.slice();
1587
1542
  let hasChanges = false;
1588
- const nextSlots = slotsArray.slice();
1589
1543
  for (const pk of pks) {
1590
- if (!pksSet.has(pk)) {
1591
- pksSet.add(pk);
1592
- nextSlots.push(this.getOrCreateSlot(pk));
1593
- hasChanges = true;
1594
- }
1595
- }
1596
- if (hasChanges) {
1597
- this.store.setOneByKey(key, nextSlots);
1598
- this.emitUpdateOne(key);
1544
+ if (existingPks.has(pk)) continue;
1545
+ nextSlots.push(this.resolveRequiredSlot(pk));
1546
+ existingPks.add(pk);
1547
+ hasChanges = true;
1599
1548
  }
1549
+ if (hasChanges) this.setSlots(key, nextSlots);
1600
1550
  }
1601
- /**
1602
- * Remove primary keys from a specific index key
1603
- */
1604
1551
  removePks(key, pks) {
1605
1552
  if (pks.length === 0) return;
1606
- const slotsArray = this.store.getOneByKey(key);
1607
- if (!slotsArray) return;
1608
- const pksSet = new Set(pks);
1609
- const filtered = slotsArray.filter((slot) => !pksSet.has(slot.pk));
1610
- const hasChanges = filtered.length !== slotsArray.length;
1553
+ const pksToRemove = new Set(pks);
1554
+ const existingSlots = this.index.getSlotsByKey(key);
1555
+ const nextSlots = existingSlots.filter(
1556
+ (slot) => !pksToRemove.has(slot.pk)
1557
+ );
1558
+ if (nextSlots.length === existingSlots.length) return;
1559
+ if (nextSlots.length === 0) this.clear(key);
1560
+ else this.setSlots(key, nextSlots);
1561
+ }
1562
+ getEntitiesByKey(key) {
1563
+ return super.getEntitiesByKey(key);
1564
+ }
1565
+ getEntitiesByKeys(keys) {
1566
+ return super.getEntitiesByKeys(keys);
1567
+ }
1568
+ resolveSlots(pks) {
1569
+ const slots = [];
1570
+ slots.length = pks.length;
1571
+ for (let i = 0; i < pks.length; i++) {
1572
+ slots[i] = this.resolveRequiredSlot(pks[i]);
1573
+ }
1574
+ return slots;
1575
+ }
1576
+ resolveRequiredSlot(pk) {
1577
+ const slot = this.resolveSlot(pk);
1578
+ if (!slot) {
1579
+ throw new Error(
1580
+ `[OIMReactiveCollectionIndexManualArrayBased]: Unable to resolve slot for PK "${String(pk)}".`
1581
+ );
1582
+ }
1583
+ return slot;
1584
+ }
1585
+ };
1586
+
1587
+ // src/core/OIMIndexManualSetBased.ts
1588
+ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1589
+ constructor(options = {}) {
1590
+ super(options);
1591
+ }
1592
+ setSlots(key, slots) {
1593
+ const hasChanges = this.setSlotsWithComparison(key, new Set(slots));
1611
1594
  if (hasChanges) {
1612
- if (filtered.length === 0) {
1613
- this.store.removeOneByKey(key);
1614
- } else {
1615
- this.store.setOneByKey(key, filtered);
1616
- }
1617
1595
  this.emitUpdateOne(key);
1618
1596
  }
1619
1597
  }
@@ -1636,8 +1614,8 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1636
1614
  }
1637
1615
  };
1638
1616
 
1639
- // src/core/OIMReactiveIndexManualArrayBased.ts
1640
- var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1617
+ // src/core/OIMReactiveIndexManualSetBased.ts
1618
+ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1641
1619
  constructor(updateEmitter, opts) {
1642
1620
  super(opts);
1643
1621
  this.updateEmitter = updateEmitter;
@@ -1649,310 +1627,1256 @@ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1649
1627
  this.updateEmitter.markUpdatedKey(key);
1650
1628
  }
1651
1629
  };
1652
- var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
1630
+ var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1653
1631
  constructor(queue, opts) {
1654
1632
  super(queue, (updateEmitter) => {
1655
- return new OIMIndexManualArrayBasedReactive(
1633
+ return new OIMIndexManualSetBasedReactive(
1656
1634
  updateEmitter,
1657
1635
  opts?.indexOptions
1658
1636
  );
1659
1637
  });
1660
1638
  }
1661
- setPks(key, pks) {
1662
- this.index.setPks(key, pks);
1663
- }
1664
1639
  setSlots(key, slots) {
1665
1640
  this.index.setSlots(key, slots);
1666
1641
  }
1667
- addPks(key, pks) {
1668
- this.index.addPks(key, pks);
1669
- }
1670
- removePks(key, pks) {
1671
- this.index.removePks(key, pks);
1672
- }
1673
1642
  clear(key) {
1674
1643
  this.index.clear(key);
1675
1644
  }
1676
1645
  };
1677
1646
 
1678
- // src/core/OIMRICollection.ts
1679
- var OIMRICollection = class extends OIMReactiveCollection {
1647
+ // src/core/OIMReactiveCollectionIndexManualSetBased.ts
1648
+ var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexManualSetBased {
1680
1649
  constructor(queue, opts) {
1681
- super(queue, opts.collectionOpts);
1682
- this.indexes = opts.indexes || {};
1683
- const indexes = Object.values(this.indexes);
1684
- for (const index of indexes) {
1685
- index.index.setSlotResolver((pk) => this.getSlotByPk(pk));
1686
- }
1687
- }
1688
- };
1689
-
1690
- // src/core/OIMObjectStoreMapDriven.ts
1691
- var OIMObjectStoreMapDriven = class extends OIMObjectStore {
1692
- constructor() {
1693
- super(...arguments);
1694
- this.properties = /* @__PURE__ */ new Map();
1650
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1651
+ super(queue, {
1652
+ indexOptions: opts.indexOptions
1653
+ });
1654
+ this.resolveSlot = resolveSlot;
1695
1655
  }
1696
- setProperty(key, value) {
1697
- this.properties.set(key, value);
1656
+ setPks(key, pks) {
1657
+ this.setSlots(key, this.resolveSlots(pks));
1698
1658
  }
1699
- merge(draft) {
1700
- for (const key in draft) {
1701
- if (!Object.prototype.hasOwnProperty.call(draft, key)) continue;
1702
- this.properties.set(
1703
- key,
1704
- draft[key]
1705
- );
1659
+ addPks(key, pks) {
1660
+ if (pks.length === 0) return;
1661
+ const existingSlots = this.index.getSlotsByKey(key);
1662
+ const existingPks = this.index.getPksByKey(key);
1663
+ const nextSlots = new Set(existingSlots);
1664
+ let hasChanges = false;
1665
+ for (const pk of pks) {
1666
+ if (existingPks.has(pk)) continue;
1667
+ const slot = this.resolveRequiredSlot(pk);
1668
+ nextSlots.add(slot);
1669
+ existingPks.add(pk);
1670
+ hasChanges = true;
1706
1671
  }
1672
+ if (hasChanges) this.setSlots(key, nextSlots);
1707
1673
  }
1708
- delete(key) {
1709
- this.properties.delete(key);
1674
+ removePks(key, pks) {
1675
+ if (pks.length === 0) return;
1676
+ const pksToRemove = new Set(pks);
1677
+ const existingSlots = this.index.getSlotsByKey(key);
1678
+ const nextSlots = /* @__PURE__ */ new Set();
1679
+ let hasChanges = false;
1680
+ for (const slot of existingSlots) {
1681
+ if (pksToRemove.has(slot.pk)) {
1682
+ hasChanges = true;
1683
+ } else {
1684
+ nextSlots.add(slot);
1685
+ }
1686
+ }
1687
+ if (!hasChanges) return;
1688
+ if (nextSlots.size === 0) this.clear(key);
1689
+ else this.setSlots(key, nextSlots);
1710
1690
  }
1711
- get(key) {
1712
- return this.properties.get(key);
1691
+ getEntitiesByKey(key) {
1692
+ return super.getEntitiesByKey(key);
1713
1693
  }
1714
- getAll() {
1715
- return Object.fromEntries(this.properties.entries());
1694
+ getEntitiesByKeys(keys) {
1695
+ return super.getEntitiesByKeys(keys);
1716
1696
  }
1717
- clear() {
1718
- this.properties.clear();
1697
+ resolveSlots(pks) {
1698
+ const slots = /* @__PURE__ */ new Set();
1699
+ for (const pk of pks) slots.add(this.resolveRequiredSlot(pk));
1700
+ return slots;
1719
1701
  }
1720
- count() {
1721
- return this.properties.size;
1702
+ resolveRequiredSlot(pk) {
1703
+ const slot = this.resolveSlot(pk);
1704
+ if (!slot) {
1705
+ throw new Error(
1706
+ `[OIMReactiveCollectionIndexManualSetBased]: Unable to resolve slot for PK "${String(pk)}".`
1707
+ );
1708
+ }
1709
+ return slot;
1722
1710
  }
1723
- keys() {
1724
- return Array.from(this.properties.keys());
1711
+ };
1712
+
1713
+ // src/core/OIMDerivedCollectionIndexSetBased.ts
1714
+ var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndexManualSetBased {
1715
+ constructor(queue, collection, opts) {
1716
+ super(queue, {
1717
+ collection,
1718
+ indexOptions: opts.indexOptions
1719
+ });
1720
+ this.keysByPk = /* @__PURE__ */ new Map();
1721
+ this.onCollectionUpdate = (pks) => {
1722
+ if (pks.length === 0) {
1723
+ this.rebuildFromCollection();
1724
+ return;
1725
+ }
1726
+ for (const pk of pks) {
1727
+ this.syncPk(pk);
1728
+ }
1729
+ };
1730
+ this.collection = collection;
1731
+ this.selectIndexKeys = opts.selectIndexKeys;
1732
+ this.unsubscribeFromCollection = collection.emitter.on(
1733
+ 0 /* UPDATE */,
1734
+ (payload) => this.onCollectionUpdate(payload.pks)
1735
+ );
1736
+ if (opts.buildInitial !== false) {
1737
+ this.rebuildFromCollection();
1738
+ }
1725
1739
  }
1726
- values() {
1727
- return Array.from(this.properties.values());
1740
+ rebuildFromCollection() {
1741
+ this.keysByPk.clear();
1742
+ super.clear();
1743
+ const slotsByKey = /* @__PURE__ */ new Map();
1744
+ const slots = this.collection.getAllSlots();
1745
+ for (const slot of slots) {
1746
+ if (slot.item === void 0) continue;
1747
+ const keys = this.normalizeKeys(this.selectIndexKeys(slot.item));
1748
+ if (keys.length === 0) continue;
1749
+ this.keysByPk.set(slot.pk, new Set(keys));
1750
+ for (const key of keys) {
1751
+ let slotsForKey = slotsByKey.get(key);
1752
+ if (!slotsForKey) {
1753
+ slotsForKey = /* @__PURE__ */ new Set();
1754
+ slotsByKey.set(key, slotsForKey);
1755
+ }
1756
+ slotsForKey.add(slot);
1757
+ }
1758
+ }
1759
+ for (const [key, keySlots] of slotsByKey) {
1760
+ this.setSlots(key, keySlots);
1761
+ }
1728
1762
  }
1729
- entries() {
1730
- return Array.from(this.properties.entries());
1763
+ clear(key) {
1764
+ if (key === void 0) {
1765
+ this.keysByPk.clear();
1766
+ } else {
1767
+ for (const keys of this.keysByPk.values()) {
1768
+ keys.delete(key);
1769
+ }
1770
+ }
1771
+ super.clear(key);
1731
1772
  }
1732
1773
  destroy() {
1733
- this.properties.clear();
1774
+ this.unsubscribeFromCollection();
1775
+ this.keysByPk.clear();
1776
+ super.destroy();
1734
1777
  }
1735
- };
1736
-
1737
- // src/core/OIMComparatorFactory.ts
1738
- var OIMComparatorFactory = class {
1739
- createShallowComparator() {
1740
- return (a, b) => {
1741
- if (a === b) return false;
1742
- const ka = Object.keys(a);
1743
- const kb = Object.keys(b);
1744
- if (ka.length !== kb.length) return true;
1745
- for (let i = 0; i < ka.length; i++) {
1746
- const k = ka[i];
1747
- if (b[k] !== a[k])
1748
- return true;
1778
+ syncPk(pk) {
1779
+ const prevKeys = this.keysByPk.get(pk) ?? /* @__PURE__ */ new Set();
1780
+ const slot = this.collection.getSlotByPk(pk);
1781
+ const entity = slot?.item;
1782
+ const nextKeys = entity === void 0 ? /* @__PURE__ */ new Set() : new Set(this.normalizeKeys(this.selectIndexKeys(entity)));
1783
+ for (const key of prevKeys) {
1784
+ if (!nextKeys.has(key)) {
1785
+ this.removePks(key, [pk]);
1749
1786
  }
1750
- return false;
1751
- };
1787
+ }
1788
+ if (entity !== void 0) {
1789
+ for (const key of nextKeys) {
1790
+ if (!prevKeys.has(key)) {
1791
+ this.addPks(key, [pk]);
1792
+ }
1793
+ }
1794
+ }
1795
+ if (nextKeys.size === 0) {
1796
+ this.keysByPk.delete(pk);
1797
+ } else {
1798
+ this.keysByPk.set(pk, nextKeys);
1799
+ }
1752
1800
  }
1753
- };
1754
-
1755
- // src/core/OIMEventQueue.ts
1756
- var OIMEventQueue = class {
1757
- constructor(options = {}) {
1758
- this.emitter = new OIMEventEmitter();
1759
- this.tasks = /* @__PURE__ */ new Set();
1760
- this.isFlushing = false;
1761
- this.scheduler = options.scheduler;
1762
- if (this.scheduler) {
1763
- this.flushBound = () => this.flush();
1764
- this.scheduler.on(
1765
- 0 /* FLUSH */,
1766
- this.flushBound
1767
- );
1801
+ normalizeKeys(keys) {
1802
+ if (keys === void 0 || keys === null) return [];
1803
+ const rawKeys = Array.isArray(keys) ? keys : [keys];
1804
+ const result = [];
1805
+ const seen = /* @__PURE__ */ new Set();
1806
+ for (const key of rawKeys) {
1807
+ if (seen.has(key)) continue;
1808
+ seen.add(key);
1809
+ result.push(key);
1768
1810
  }
1811
+ return result;
1769
1812
  }
1770
- /**
1771
- * Enqueue a one-shot task to be executed on the next flush.
1772
- * Returns a dequeue function that can be called to cancel the task before it runs.
1773
- */
1774
- enqueue(fn) {
1775
- let isActive = true;
1776
- const task = () => {
1777
- if (!isActive) return;
1778
- fn();
1779
- };
1780
- this.tasks.add(task);
1781
- this.ensureScheduled();
1782
- return () => {
1783
- if (!isActive) return;
1784
- isActive = false;
1785
- this.tasks.delete(task);
1813
+ };
1814
+
1815
+ // src/core/OIMDerivedCollectionIndexArrayBased.ts
1816
+ var OIMDerivedCollectionIndexArrayBased = class extends OIMReactiveCollectionIndexManualArrayBased {
1817
+ constructor(queue, collection, opts) {
1818
+ super(queue, {
1819
+ collection,
1820
+ indexOptions: opts.indexOptions
1821
+ });
1822
+ this.keysByPk = /* @__PURE__ */ new Map();
1823
+ this.onCollectionUpdate = (pks) => {
1824
+ if (pks.length === 0) {
1825
+ this.rebuildFromCollection();
1826
+ return;
1827
+ }
1828
+ const affectedKeys = /* @__PURE__ */ new Set();
1829
+ for (const pk of pks) {
1830
+ this.trackPkKeys(pk, affectedKeys);
1831
+ }
1832
+ for (const key of affectedKeys) {
1833
+ this.rebuildKey(key);
1834
+ }
1786
1835
  };
1836
+ this.collection = collection;
1837
+ this.selectIndexKeys = opts.selectIndexKeys;
1838
+ this.compareEntities = opts.compareEntities;
1839
+ this.orderBy = opts.orderBy;
1840
+ this.unsubscribeFromCollection = collection.emitter.on(
1841
+ 0 /* UPDATE */,
1842
+ (payload) => this.onCollectionUpdate(payload.pks)
1843
+ );
1844
+ if (opts.buildInitial !== false) {
1845
+ this.rebuildFromCollection();
1846
+ }
1787
1847
  }
1788
- get isInFlush() {
1789
- return this.isFlushing;
1790
- }
1791
- /**
1792
- * Dequeue (cancel) a previously enqueued task by its dequeue function.
1793
- */
1794
- dequeue(dequeueFn) {
1795
- dequeueFn();
1796
- }
1797
- /**
1798
- * Execute all pending tasks.
1799
- *
1800
- * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
1801
- * executed within the same flush.
1802
- */
1803
- flush() {
1804
- this.scheduler?.cancel();
1805
- this.isFlushing = true;
1806
- this.emitter.emit(1 /* BEFORE_FLUSH */, void 0);
1807
- const snapshot = Array.from(this.tasks);
1808
- this.tasks.clear();
1809
- for (let i = 0; i < snapshot.length; i++) {
1810
- snapshot[i]();
1848
+ rebuildFromCollection() {
1849
+ this.keysByPk.clear();
1850
+ super.clear();
1851
+ const slotsByKey = /* @__PURE__ */ new Map();
1852
+ const slots = this.collection.getAllSlots();
1853
+ for (const slot of slots) {
1854
+ if (slot.item === void 0) continue;
1855
+ const keys = this.normalizeKeys(this.selectIndexKeys(slot.item));
1856
+ if (keys.length === 0) continue;
1857
+ this.keysByPk.set(slot.pk, new Set(keys));
1858
+ for (const key of keys) {
1859
+ let slotsForKey = slotsByKey.get(key);
1860
+ if (!slotsForKey) {
1861
+ slotsForKey = [];
1862
+ slotsByKey.set(key, slotsForKey);
1863
+ }
1864
+ slotsForKey.push(slot);
1865
+ }
1866
+ }
1867
+ for (const [key, keySlots] of slotsByKey) {
1868
+ this.setSlots(key, this.sortSlots(keySlots));
1811
1869
  }
1812
- this.isFlushing = false;
1813
- this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
1814
1870
  }
1815
- /**
1816
- * Get the current number of queued functions.
1817
- */
1818
- get length() {
1819
- return this.tasks.size;
1871
+ clear(key) {
1872
+ if (key === void 0) {
1873
+ this.keysByPk.clear();
1874
+ } else {
1875
+ for (const keys of this.keysByPk.values()) {
1876
+ keys.delete(key);
1877
+ }
1878
+ }
1879
+ super.clear(key);
1820
1880
  }
1821
- /**
1822
- * Check if the queue is empty.
1823
- */
1824
- get isEmpty() {
1825
- return this.tasks.size === 0;
1881
+ destroy() {
1882
+ this.unsubscribeFromCollection();
1883
+ this.keysByPk.clear();
1884
+ super.destroy();
1826
1885
  }
1827
- /**
1828
- * Clear the queue without executing functions and cancel any scheduled flush.
1829
- */
1830
- clear() {
1831
- this.tasks.clear();
1832
- this.scheduler?.cancel();
1886
+ trackPkKeys(pk, affectedKeys) {
1887
+ const prevKeys = this.keysByPk.get(pk) ?? /* @__PURE__ */ new Set();
1888
+ for (const key of prevKeys) affectedKeys.add(key);
1889
+ const slot = this.collection.getSlotByPk(pk);
1890
+ const entity = slot?.item;
1891
+ const nextKeys = entity === void 0 ? /* @__PURE__ */ new Set() : new Set(this.normalizeKeys(this.selectIndexKeys(entity)));
1892
+ for (const key of nextKeys) affectedKeys.add(key);
1893
+ if (nextKeys.size === 0) {
1894
+ this.keysByPk.delete(pk);
1895
+ } else {
1896
+ this.keysByPk.set(pk, nextKeys);
1897
+ }
1833
1898
  }
1834
- ensureScheduled() {
1835
- if (!this.scheduler) return;
1836
- if (this.isFlushing) return;
1837
- if (this.tasks.size === 1) {
1838
- this.scheduler.schedule();
1899
+ rebuildKey(key) {
1900
+ const keySlots = [];
1901
+ const slots = this.collection.getAllSlots();
1902
+ for (const slot of slots) {
1903
+ if (slot.item === void 0) continue;
1904
+ const keys = this.keysByPk.get(slot.pk);
1905
+ if (keys?.has(key)) keySlots.push(slot);
1906
+ }
1907
+ if (keySlots.length === 0) {
1908
+ super.clear(key);
1909
+ } else {
1910
+ this.setSlots(key, this.sortSlots(keySlots));
1839
1911
  }
1840
1912
  }
1841
- /**
1842
- * Clean up scheduler subscription when queue is no longer needed.
1843
- */
1844
- destroy() {
1845
- if (this.scheduler && this.flushBound) {
1846
- this.scheduler.off(
1847
- 0 /* FLUSH */,
1848
- this.flushBound
1849
- );
1913
+ sortSlots(slots) {
1914
+ if (!this.compareEntities && !this.orderBy) return slots;
1915
+ return slots.slice().sort((a, b) => {
1916
+ const aItem = a.item;
1917
+ const bItem = b.item;
1918
+ if (aItem === void 0 || bItem === void 0) return 0;
1919
+ if (this.compareEntities) {
1920
+ return this.compareEntities(aItem, bItem);
1921
+ }
1922
+ const aValue = this.orderBy(aItem);
1923
+ const bValue = this.orderBy(bItem);
1924
+ if (aValue < bValue) return -1;
1925
+ if (aValue > bValue) return 1;
1926
+ return 0;
1927
+ });
1928
+ }
1929
+ normalizeKeys(keys) {
1930
+ if (keys === void 0 || keys === null) return [];
1931
+ const rawKeys = Array.isArray(keys) ? keys : [keys];
1932
+ const result = [];
1933
+ const seen = /* @__PURE__ */ new Set();
1934
+ for (const key of rawKeys) {
1935
+ if (seen.has(key)) continue;
1936
+ seen.add(key);
1937
+ result.push(key);
1850
1938
  }
1851
- this.clear();
1939
+ return result;
1852
1940
  }
1853
1941
  };
1854
1942
 
1855
- // src/core/OIMIndexComparatorFactory.ts
1856
- var OIMIndexComparatorFactory = class {
1857
- /**
1858
- * Create an element-wise comparator that checks arrays for strict equality.
1859
- * Compares array lengths and each element using strict equality (===).
1860
- */
1861
- static createElementWiseComparator() {
1862
- return (existingPks, newPks) => {
1863
- if (existingPks.length !== newPks.length) {
1864
- return false;
1943
+ // src/core/OIMCollectionIndexFactory.ts
1944
+ var OIMCollectionIndexFactory = class {
1945
+ constructor(queue, collection) {
1946
+ this.queue = queue;
1947
+ this.collection = collection;
1948
+ }
1949
+ setBasedIndex(opts = {}) {
1950
+ return new OIMReactiveCollectionIndexManualSetBased(this.queue, {
1951
+ collection: this.collection,
1952
+ indexOptions: opts.indexOptions
1953
+ });
1954
+ }
1955
+ derivedSetIndex(selectIndexKeys, opts = {}) {
1956
+ return new OIMDerivedCollectionIndexSetBased(
1957
+ this.queue,
1958
+ this.collection,
1959
+ {
1960
+ selectIndexKeys,
1961
+ buildInitial: opts.buildInitial,
1962
+ indexOptions: opts.indexOptions
1865
1963
  }
1866
- for (let i = 0; i < existingPks.length; i++) {
1867
- if (existingPks[i] !== newPks[i]) {
1868
- return false;
1869
- }
1964
+ );
1965
+ }
1966
+ arrayBasedIndex(opts = {}) {
1967
+ return new OIMReactiveCollectionIndexManualArrayBased(this.queue, {
1968
+ collection: this.collection,
1969
+ indexOptions: opts.indexOptions
1970
+ });
1971
+ }
1972
+ derivedArrayIndex(selectIndexKeys, opts = {}) {
1973
+ return new OIMDerivedCollectionIndexArrayBased(
1974
+ this.queue,
1975
+ this.collection,
1976
+ {
1977
+ selectIndexKeys,
1978
+ buildInitial: opts.buildInitial,
1979
+ compareEntities: opts.compareEntities,
1980
+ orderBy: opts.orderBy,
1981
+ indexOptions: opts.indexOptions
1870
1982
  }
1871
- return true;
1872
- };
1983
+ );
1873
1984
  }
1874
- /**
1875
- * Create a set-based comparator that checks if arrays contain the same elements.
1876
- * Order doesn't matter, only the presence of elements.
1877
- */
1878
- static createSetBasedComparator() {
1879
- return (existingPks, newPks) => {
1880
- if (existingPks.length !== newPks.length) {
1881
- return false;
1882
- }
1883
- const existingSet = new Set(existingPks);
1884
- const newSet = new Set(newPks);
1885
- if (existingSet.size !== newSet.size || existingSet.size !== existingPks.length) {
1886
- return false;
1887
- }
1888
- for (const pk of Array.from(existingSet)) {
1889
- if (!newSet.has(pk)) {
1890
- return false;
1891
- }
1985
+ orderedIndex() {
1986
+ return new OIMCollectionIndexManualOrderedArrayBased({
1987
+ collection: this.collection
1988
+ });
1989
+ }
1990
+ orderedList(opts = {}) {
1991
+ return new OIMCollectionOrderedListCommandStream(
1992
+ this.queue,
1993
+ opts.index ? { collection: this.collection, index: opts.index } : { collection: this.collection }
1994
+ );
1995
+ }
1996
+ };
1997
+ function createOIMCollectionIndexFactory(queue, collection) {
1998
+ return new OIMCollectionIndexFactory(queue, collection);
1999
+ }
2000
+
2001
+ // src/core/OIMObjectStoreRecordDriven.ts
2002
+ var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
2003
+ constructor(initial) {
2004
+ super();
2005
+ this.properties = Object.assign(/* @__PURE__ */ Object.create(null), initial ?? {});
2006
+ }
2007
+ setProperty(key, value) {
2008
+ this.properties[key] = value;
2009
+ }
2010
+ merge(draft) {
2011
+ Object.assign(this.properties, draft);
2012
+ }
2013
+ delete(key) {
2014
+ delete this.properties[key];
2015
+ }
2016
+ get(key) {
2017
+ return this.properties[key];
2018
+ }
2019
+ getAll() {
2020
+ return { ...this.properties };
2021
+ }
2022
+ clear() {
2023
+ this.properties = /* @__PURE__ */ Object.create(null);
2024
+ }
2025
+ count() {
2026
+ return Object.keys(this.properties).length;
2027
+ }
2028
+ keys() {
2029
+ return Object.keys(this.properties);
2030
+ }
2031
+ values() {
2032
+ return Object.values(this.properties);
2033
+ }
2034
+ entries() {
2035
+ return Object.entries(this.properties);
2036
+ }
2037
+ destroy() {
2038
+ this.clear();
2039
+ }
2040
+ };
2041
+
2042
+ // src/enums/EOIMObjectEventType.ts
2043
+ var EOIMObjectEventType = /* @__PURE__ */ ((EOIMObjectEventType2) => {
2044
+ EOIMObjectEventType2[EOIMObjectEventType2["UPDATE"] = 0] = "UPDATE";
2045
+ return EOIMObjectEventType2;
2046
+ })(EOIMObjectEventType || {});
2047
+
2048
+ // src/core/OIMObject.ts
2049
+ var OIMObject = class {
2050
+ constructor(opts) {
2051
+ this.emitter = new OIMEventEmitter();
2052
+ this.store = opts?.store ?? new OIMObjectStoreRecordDriven();
2053
+ }
2054
+ setProperty(key, value) {
2055
+ this.store.setProperty(key, value);
2056
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
2057
+ }
2058
+ merge(draft) {
2059
+ this.store.merge(draft);
2060
+ this.emitter.emit(0 /* UPDATE */, {
2061
+ keys: Object.keys(draft)
2062
+ });
2063
+ }
2064
+ delete(key) {
2065
+ this.store.delete(key);
2066
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
2067
+ }
2068
+ get(key) {
2069
+ return this.store.get(key);
2070
+ }
2071
+ getAll() {
2072
+ return this.store.getAll();
2073
+ }
2074
+ clear() {
2075
+ this.store.clear();
2076
+ this.emitter.emit(0 /* UPDATE */, { keys: [] });
2077
+ }
2078
+ count() {
2079
+ return this.store.count();
2080
+ }
2081
+ keys() {
2082
+ return this.store.keys();
2083
+ }
2084
+ values() {
2085
+ return this.store.values();
2086
+ }
2087
+ entries() {
2088
+ return this.store.entries();
2089
+ }
2090
+ destroy() {
2091
+ this.store.destroy();
2092
+ this.emitter.offAll();
2093
+ }
2094
+ };
2095
+
2096
+ // src/core/OIMReactiveObject.ts
2097
+ var OIMReactiveObject = class extends OIMObject {
2098
+ constructor(queue, opts) {
2099
+ super(opts);
2100
+ this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
2101
+ this.emitter.on(0 /* UPDATE */, (payload) => {
2102
+ this.updateEmitter.markUpdatedKeys(payload.keys);
2103
+ });
2104
+ }
2105
+ subscribeOnKey(key, handler) {
2106
+ return this.updateEmitter.subscribeOnKey(key, handler);
2107
+ }
2108
+ subscribeOnKeys(keys, handler) {
2109
+ return this.updateEmitter.subscribeOnKeys(keys, handler);
2110
+ }
2111
+ unsubscribeFromKey(key, handler) {
2112
+ this.updateEmitter.unsubscribeFromKey(key, handler);
2113
+ }
2114
+ unsubscribeFromKeys(keys, handler) {
2115
+ this.updateEmitter.unsubscribeFromKeys(keys, handler);
2116
+ }
2117
+ hasSubscriptions() {
2118
+ return this.updateEmitter.hasSubscriptions();
2119
+ }
2120
+ getHandlerCount(key) {
2121
+ return this.updateEmitter.getHandlerCount(key);
2122
+ }
2123
+ getMetrics() {
2124
+ return this.updateEmitter.getMetrics();
2125
+ }
2126
+ destroy() {
2127
+ this.updateEmitter.destroy();
2128
+ super.destroy();
2129
+ }
2130
+ };
2131
+
2132
+ // src/core/OIMObjectStoreMapDriven.ts
2133
+ var OIMObjectStoreMapDriven = class extends OIMObjectStore {
2134
+ constructor() {
2135
+ super(...arguments);
2136
+ this.properties = /* @__PURE__ */ new Map();
2137
+ }
2138
+ setProperty(key, value) {
2139
+ this.properties.set(key, value);
2140
+ }
2141
+ merge(draft) {
2142
+ for (const key in draft) {
2143
+ if (!Object.prototype.hasOwnProperty.call(draft, key)) continue;
2144
+ this.properties.set(
2145
+ key,
2146
+ draft[key]
2147
+ );
2148
+ }
2149
+ }
2150
+ delete(key) {
2151
+ this.properties.delete(key);
2152
+ }
2153
+ get(key) {
2154
+ return this.properties.get(key);
2155
+ }
2156
+ getAll() {
2157
+ return Object.fromEntries(this.properties.entries());
2158
+ }
2159
+ clear() {
2160
+ this.properties.clear();
2161
+ }
2162
+ count() {
2163
+ return this.properties.size;
2164
+ }
2165
+ keys() {
2166
+ return Array.from(this.properties.keys());
2167
+ }
2168
+ values() {
2169
+ return Array.from(this.properties.values());
2170
+ }
2171
+ entries() {
2172
+ return Array.from(this.properties.entries());
2173
+ }
2174
+ destroy() {
2175
+ this.properties.clear();
2176
+ }
2177
+ };
2178
+
2179
+ // src/core/OIMComparatorFactory.ts
2180
+ var OIMComparatorFactory = class {
2181
+ createShallowComparator() {
2182
+ return (a, b) => {
2183
+ if (a === b) return false;
2184
+ const ka = Object.keys(a);
2185
+ const kb = Object.keys(b);
2186
+ if (ka.length !== kb.length) return true;
2187
+ for (let i = 0; i < ka.length; i++) {
2188
+ const k = ka[i];
2189
+ if (b[k] !== a[k])
2190
+ return true;
1892
2191
  }
1893
- return true;
2192
+ return false;
1894
2193
  };
1895
2194
  }
2195
+ };
2196
+
2197
+ // src/core/OIMEventQueue.ts
2198
+ var OIMEventQueue = class {
2199
+ constructor(options = {}) {
2200
+ this.emitter = new OIMEventEmitter();
2201
+ this.tasks = /* @__PURE__ */ new Set();
2202
+ this.isFlushing = false;
2203
+ this.scheduler = options.scheduler;
2204
+ if (this.scheduler) {
2205
+ this.flushBound = () => this.flush();
2206
+ this.scheduler.on(
2207
+ 0 /* FLUSH */,
2208
+ this.flushBound
2209
+ );
2210
+ }
2211
+ }
1896
2212
  /**
1897
- * Create a shallow comparator that only checks array references.
1898
- * Fastest but only works if you're reusing the same array instances.
2213
+ * Enqueue a one-shot task to be executed on the next flush.
2214
+ * Returns a dequeue function that can be called to cancel the task before it runs.
1899
2215
  */
1900
- static createShallowComparator() {
1901
- return (existingPks, newPks) => {
1902
- return existingPks === newPks;
2216
+ enqueue(fn) {
2217
+ let isActive = true;
2218
+ const task = () => {
2219
+ if (!isActive) return;
2220
+ fn();
1903
2221
  };
2222
+ this.tasks.add(task);
2223
+ this.ensureScheduled();
2224
+ return () => {
2225
+ if (!isActive) return;
2226
+ isActive = false;
2227
+ this.tasks.delete(task);
2228
+ };
2229
+ }
2230
+ get isInFlush() {
2231
+ return this.isFlushing;
1904
2232
  }
1905
2233
  /**
1906
- * Create a no-comparison comparator that always returns false (always updates).
1907
- * Useful for disabling comparison while keeping the same interface.
2234
+ * Dequeue (cancel) a previously enqueued task by its dequeue function.
1908
2235
  */
1909
- static createAlwaysUpdateComparator() {
1910
- return () => false;
2236
+ dequeue(dequeueFn) {
2237
+ dequeueFn();
2238
+ }
2239
+ /**
2240
+ * Execute all pending tasks.
2241
+ *
2242
+ * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
2243
+ * executed within the same flush.
2244
+ */
2245
+ flush() {
2246
+ this.scheduler?.cancel();
2247
+ this.isFlushing = true;
2248
+ this.emitter.emit(1 /* BEFORE_FLUSH */, void 0);
2249
+ const snapshot = Array.from(this.tasks);
2250
+ this.tasks.clear();
2251
+ for (let i = 0; i < snapshot.length; i++) {
2252
+ snapshot[i]();
2253
+ }
2254
+ this.isFlushing = false;
2255
+ this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
2256
+ }
2257
+ /**
2258
+ * Get the current number of queued functions.
2259
+ */
2260
+ get length() {
2261
+ return this.tasks.size;
2262
+ }
2263
+ /**
2264
+ * Check if the queue is empty.
2265
+ */
2266
+ get isEmpty() {
2267
+ return this.tasks.size === 0;
2268
+ }
2269
+ /**
2270
+ * Clear the queue without executing functions and cancel any scheduled flush.
2271
+ */
2272
+ clear() {
2273
+ this.tasks.clear();
2274
+ this.scheduler?.cancel();
2275
+ }
2276
+ ensureScheduled() {
2277
+ if (!this.scheduler) return;
2278
+ if (this.isFlushing) return;
2279
+ if (this.tasks.size === 1) {
2280
+ this.scheduler.schedule();
2281
+ }
2282
+ }
2283
+ /**
2284
+ * Clean up scheduler subscription when queue is no longer needed.
2285
+ */
2286
+ destroy() {
2287
+ if (this.scheduler && this.flushBound) {
2288
+ this.scheduler.off(
2289
+ 0 /* FLUSH */,
2290
+ this.flushBound
2291
+ );
2292
+ }
2293
+ this.clear();
2294
+ }
2295
+ };
2296
+
2297
+ // src/core/OIMIndexComparatorFactory.ts
2298
+ var OIMIndexComparatorFactory = class {
2299
+ /**
2300
+ * Create an element-wise comparator that checks arrays for strict equality.
2301
+ * Compares array lengths and each element using strict equality (===).
2302
+ */
2303
+ static createElementWiseComparator() {
2304
+ return (existingPks, newPks) => {
2305
+ if (existingPks.length !== newPks.length) {
2306
+ return false;
2307
+ }
2308
+ for (let i = 0; i < existingPks.length; i++) {
2309
+ if (existingPks[i] !== newPks[i]) {
2310
+ return false;
2311
+ }
2312
+ }
2313
+ return true;
2314
+ };
2315
+ }
2316
+ /**
2317
+ * Create a set-based comparator that checks if arrays contain the same elements.
2318
+ * Order doesn't matter, only the presence of elements.
2319
+ */
2320
+ static createSetBasedComparator() {
2321
+ return (existingPks, newPks) => {
2322
+ if (existingPks.length !== newPks.length) {
2323
+ return false;
2324
+ }
2325
+ const existingSet = new Set(existingPks);
2326
+ const newSet = new Set(newPks);
2327
+ if (existingSet.size !== newSet.size || existingSet.size !== existingPks.length) {
2328
+ return false;
2329
+ }
2330
+ for (const pk of Array.from(existingSet)) {
2331
+ if (!newSet.has(pk)) {
2332
+ return false;
2333
+ }
2334
+ }
2335
+ return true;
2336
+ };
2337
+ }
2338
+ /**
2339
+ * Create a shallow comparator that only checks array references.
2340
+ * Fastest but only works if you're reusing the same array instances.
2341
+ */
2342
+ static createShallowComparator() {
2343
+ return (existingPks, newPks) => {
2344
+ return existingPks === newPks;
2345
+ };
2346
+ }
2347
+ /**
2348
+ * Create a no-comparison comparator that always returns false (always updates).
2349
+ * Useful for disabling comparison while keeping the same interface.
2350
+ */
2351
+ static createAlwaysUpdateComparator() {
2352
+ return () => false;
2353
+ }
2354
+ };
2355
+
2356
+ // src/core/OIMMap2Keys.ts
2357
+ var OIMMap2Keys = class {
2358
+ constructor() {
2359
+ this.map = /* @__PURE__ */ new Map();
2360
+ this._size = 0;
2361
+ }
2362
+ get size() {
2363
+ return this._size;
2364
+ }
2365
+ set(k1, k2, v) {
2366
+ let m = this.map.get(k1);
2367
+ if (!m) {
2368
+ m = /* @__PURE__ */ new Map();
2369
+ this.map.set(k1, m);
2370
+ }
2371
+ if (!m.has(k2)) this._size++;
2372
+ m.set(k2, v);
2373
+ }
2374
+ get(k1, k2) {
2375
+ return this.map.get(k1)?.get(k2);
2376
+ }
2377
+ has(k1, k2) {
2378
+ return this.map.get(k1)?.has(k2);
2379
+ }
2380
+ delete(k1, k2) {
2381
+ const m = this.map.get(k1);
2382
+ if (!m) return false;
2383
+ const had = m.delete(k2);
2384
+ if (had) {
2385
+ this._size--;
2386
+ if (m.size === 0) this.map.delete(k1);
2387
+ }
2388
+ return had;
2389
+ }
2390
+ clear() {
2391
+ this.map.clear();
2392
+ this._size = 0;
2393
+ }
2394
+ };
2395
+
2396
+ // src/modules/compute/core/OIMComputeRuntime.ts
2397
+ var OIMComputeRuntime = class {
2398
+ constructor(queue) {
2399
+ this.queue = queue;
2400
+ this.isFlushScheduled = false;
2401
+ this.isFlushing = false;
2402
+ this.minLevel = Infinity;
2403
+ this.maxLevel = -Infinity;
2404
+ this.dirtyByLevel = /* @__PURE__ */ new Map();
2405
+ this.afterFlushTasks = /* @__PURE__ */ new Set();
2406
+ this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
2407
+ if (!this.isFlushScheduled) return;
2408
+ this.flush();
2409
+ });
2410
+ }
2411
+ /**
2412
+ * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
2413
+ * ordered by `level` ascending.
2414
+ */
2415
+ schedule(task, level = 0) {
2416
+ let isActive = true;
2417
+ const wrapped = () => {
2418
+ if (!isActive) return;
2419
+ task();
2420
+ };
2421
+ let bucket = this.dirtyByLevel.get(level);
2422
+ if (!bucket) {
2423
+ bucket = /* @__PURE__ */ new Set();
2424
+ this.dirtyByLevel.set(level, bucket);
2425
+ }
2426
+ bucket.add(wrapped);
2427
+ if (level < this.minLevel) this.minLevel = level;
2428
+ if (level > this.maxLevel) this.maxLevel = level;
2429
+ if (!this.isFlushing) this.isFlushScheduled = true;
2430
+ return () => {
2431
+ if (!isActive) return;
2432
+ isActive = false;
2433
+ bucket.delete(wrapped);
2434
+ };
2435
+ }
2436
+ /**
2437
+ * Run once at the start of the next runtime flush (i.e. after queue.flush()).
2438
+ * Useful for resubscribing dependencies exactly once per source flush.
2439
+ */
2440
+ scheduleAfterFlush(task) {
2441
+ let isActive = true;
2442
+ const wrapped = () => {
2443
+ if (!isActive) return;
2444
+ task();
2445
+ };
2446
+ this.afterFlushTasks.add(wrapped);
2447
+ if (!this.isFlushing) this.isFlushScheduled = true;
2448
+ return () => {
2449
+ if (!isActive) return;
2450
+ isActive = false;
2451
+ this.afterFlushTasks.delete(wrapped);
2452
+ };
2453
+ }
2454
+ flush() {
2455
+ this.isFlushScheduled = false;
2456
+ if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
2457
+ return;
2458
+ this.isFlushing = true;
2459
+ try {
2460
+ if (this.afterFlushTasks.size > 0) {
2461
+ const snapshot = Array.from(this.afterFlushTasks);
2462
+ this.afterFlushTasks.clear();
2463
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
2464
+ }
2465
+ if (this.dirtyByLevel.size === 0) return;
2466
+ let level = this.minLevel;
2467
+ let max = this.maxLevel;
2468
+ while (level <= max) {
2469
+ for (; ; ) {
2470
+ const bucket = this.dirtyByLevel.get(level);
2471
+ if (!bucket || bucket.size === 0) break;
2472
+ this.dirtyByLevel.delete(level);
2473
+ const snapshot = Array.from(bucket);
2474
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
2475
+ }
2476
+ if (this.maxLevel > max) max = this.maxLevel;
2477
+ level++;
2478
+ }
2479
+ } finally {
2480
+ this.isFlushing = false;
2481
+ if (this.dirtyByLevel.size === 0) {
2482
+ this.minLevel = Infinity;
2483
+ this.maxLevel = -Infinity;
2484
+ } else {
2485
+ let nextMin = Infinity;
2486
+ let nextMax = -Infinity;
2487
+ this.dirtyByLevel.forEach((_bucket, lvl) => {
2488
+ if (lvl < nextMin) nextMin = lvl;
2489
+ if (lvl > nextMax) nextMax = lvl;
2490
+ });
2491
+ this.minLevel = nextMin;
2492
+ this.maxLevel = nextMax;
2493
+ this.isFlushScheduled = true;
2494
+ }
2495
+ }
2496
+ }
2497
+ };
2498
+
2499
+ // src/modules/selector/core/OIMSelector.ts
2500
+ var OIMSelector = class {
2501
+ constructor(runtime, sourceDeps, computedDeps = []) {
2502
+ this.runtime = runtime;
2503
+ this.sourceDeps = sourceDeps;
2504
+ this.computedDeps = computedDeps;
2505
+ this.listeners = /* @__PURE__ */ new Set();
2506
+ this.sourceUnsubscribers = [];
2507
+ this.computedUnsubscribers = [];
2508
+ this.isResubscribeEnqueued = false;
2509
+ this.isTemporarilyUnsubscribedForFlush = false;
2510
+ this.isDeliveryEnqueued = false;
2511
+ this.hasLastValue = false;
2512
+ this.onSourceInvalidate = () => {
2513
+ if (this.listeners.size === 0) return;
2514
+ if (!this.isTemporarilyUnsubscribedForFlush) {
2515
+ this.isTemporarilyUnsubscribedForFlush = true;
2516
+ this.unsubscribeFromSourceDeps();
2517
+ this.ensureResubscribeAfterFlush();
2518
+ }
2519
+ if (this.isDeliveryEnqueued) return;
2520
+ this.isDeliveryEnqueued = true;
2521
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2522
+ };
2523
+ this.onComputedInvalidate = () => {
2524
+ if (this.listeners.size === 0) return;
2525
+ if (this.isDeliveryEnqueued) return;
2526
+ this.isDeliveryEnqueued = true;
2527
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2528
+ };
2529
+ this.deliver = () => {
2530
+ this.isDeliveryEnqueued = false;
2531
+ this.dequeueDelivery = void 0;
2532
+ if (this.listeners.size === 0) return;
2533
+ const next = this.getValue();
2534
+ if (this.hasLastValue && this.areEqual(this.lastValue, next)) return;
2535
+ this.setLastValue(next);
2536
+ const snapshot = Array.from(this.listeners);
2537
+ for (let i = 0; i < snapshot.length; i++) {
2538
+ const listener = snapshot[i];
2539
+ if (this.listeners.has(listener)) listener(next);
2540
+ }
2541
+ };
2542
+ this.onAfterFlush = () => {
2543
+ if (!this.isResubscribeEnqueued) return;
2544
+ this.isResubscribeEnqueued = false;
2545
+ this.dequeueResubscribe = void 0;
2546
+ if (this.listeners.size === 0) return;
2547
+ this.isTemporarilyUnsubscribedForFlush = false;
2548
+ if (this.sourceUnsubscribers.length === 0) {
2549
+ for (const dep of this.sourceDeps) {
2550
+ this.sourceUnsubscribers.push(
2551
+ dep.subscribe(this.onSourceInvalidate)
2552
+ );
2553
+ }
2554
+ }
2555
+ };
2556
+ }
2557
+ get queue() {
2558
+ return this.runtime.queue;
2559
+ }
2560
+ /**
2561
+ * Start watching this selector. The callback is called immediately with the current value,
2562
+ * then coalesced and delivered on `queue.flush()` when the selector invalidates.
2563
+ */
2564
+ watch(onValue) {
2565
+ this.listeners.add(onValue);
2566
+ if (this.listeners.size === 1) {
2567
+ this.subscribeToDeps();
2568
+ }
2569
+ const current = this.getValue();
2570
+ this.setLastValue(current);
2571
+ onValue(current);
2572
+ return () => {
2573
+ this.listeners.delete(onValue);
2574
+ if (this.listeners.size > 0) return;
2575
+ this.unsubscribeFromAllDeps();
2576
+ if (this.dequeueResubscribe) {
2577
+ this.dequeueResubscribe();
2578
+ this.dequeueResubscribe = void 0;
2579
+ }
2580
+ this.isResubscribeEnqueued = false;
2581
+ this.isTemporarilyUnsubscribedForFlush = false;
2582
+ if (this.dequeueDelivery) {
2583
+ this.dequeueDelivery();
2584
+ this.dequeueDelivery = void 0;
2585
+ }
2586
+ this.isDeliveryEnqueued = false;
2587
+ this.hasLastValue = false;
2588
+ };
2589
+ }
2590
+ areEqual(prev, next) {
2591
+ return Object.is(prev, next);
2592
+ }
2593
+ setLastValue(value) {
2594
+ this.lastValue = value;
2595
+ this.hasLastValue = true;
2596
+ }
2597
+ subscribeToDeps() {
2598
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2599
+ return;
2600
+ for (const dep of this.sourceDeps) {
2601
+ this.sourceUnsubscribers.push(
2602
+ dep.subscribe(this.onSourceInvalidate)
2603
+ );
2604
+ }
2605
+ for (const dep of this.computedDeps) {
2606
+ this.computedUnsubscribers.push(
2607
+ dep.subscribe(this.onComputedInvalidate)
2608
+ );
2609
+ }
2610
+ }
2611
+ unsubscribeFromSourceDeps() {
2612
+ if (this.sourceUnsubscribers.length === 0) return;
2613
+ for (const unsub of this.sourceUnsubscribers) unsub();
2614
+ this.sourceUnsubscribers = [];
2615
+ }
2616
+ unsubscribeFromAllDeps() {
2617
+ this.unsubscribeFromSourceDeps();
2618
+ if (this.computedUnsubscribers.length === 0) return;
2619
+ for (const unsub of this.computedUnsubscribers) unsub();
2620
+ this.computedUnsubscribers = [];
2621
+ }
2622
+ ensureResubscribeAfterFlush() {
2623
+ if (this.isResubscribeEnqueued) return;
2624
+ this.isResubscribeEnqueued = true;
2625
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2626
+ this.onAfterFlush
2627
+ );
2628
+ }
2629
+ };
2630
+
2631
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKey.ts
2632
+ var OIMSelectorSourceDependencyUpdateEventEmitterKey = class {
2633
+ constructor(subscription, key) {
2634
+ this.subscription = subscription;
2635
+ this.key = key;
2636
+ }
2637
+ subscribe(onUpdate) {
2638
+ return this.subscription.subscribeOnKey(this.key, onUpdate);
2639
+ }
2640
+ };
2641
+
2642
+ // src/modules/selector/core/OIMCollectionByPkSelector.ts
2643
+ var OIMCollectionByPkSelector = class extends OIMSelector {
2644
+ constructor(runtime, collection, pk) {
2645
+ super(runtime, [
2646
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2647
+ collection,
2648
+ pk
2649
+ )
2650
+ ]);
2651
+ this.collection = collection;
2652
+ this.pk = pk;
2653
+ }
2654
+ getValue() {
2655
+ return this.collection.getOneByPk(this.pk);
2656
+ }
2657
+ };
2658
+
2659
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKeys.ts
2660
+ var OIMSelectorSourceDependencyUpdateEventEmitterKeys = class {
2661
+ constructor(subscription, keys) {
2662
+ this.subscription = subscription;
2663
+ this.keys = keys;
2664
+ }
2665
+ subscribe(onUpdate) {
2666
+ return this.subscription.subscribeOnKeys(this.keys, onUpdate);
2667
+ }
2668
+ };
2669
+
2670
+ // src/modules/selector/core/OIMCollectionByPksSelector.ts
2671
+ var OIMCollectionByPksSelector = class extends OIMSelector {
2672
+ constructor(runtime, collection, pks) {
2673
+ super(runtime, [
2674
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2675
+ collection,
2676
+ pks
2677
+ )
2678
+ ]);
2679
+ this.collection = collection;
2680
+ this.pks = pks;
2681
+ }
2682
+ getValue() {
2683
+ return this.pks.map((pk) => this.collection.getOneByPk(pk));
2684
+ }
2685
+ areEqual(prev, next) {
2686
+ if (prev === next) return true;
2687
+ if (prev.length !== next.length) return false;
2688
+ for (let i = 0; i < prev.length; i++) {
2689
+ if (prev[i] !== next[i]) return false;
2690
+ }
2691
+ return true;
2692
+ }
2693
+ };
2694
+
2695
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased.ts
2696
+ var OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased = class {
2697
+ constructor(collection, reactiveIndex, key) {
2698
+ this.collection = collection;
2699
+ this.reactiveIndex = reactiveIndex;
2700
+ this.key = key;
2701
+ this.currentPks = [];
2702
+ }
2703
+ subscribe(onUpdate) {
2704
+ const resubscribeCollection = () => {
2705
+ const nextPks = this.reactiveIndex.getPksByKey(this.key);
2706
+ if (this.sameArray(this.currentPks, nextPks)) return;
2707
+ this.unsubscribeFromCollectionKeys?.();
2708
+ this.unsubscribeFromCollectionKeys = void 0;
2709
+ this.currentPks = nextPks;
2710
+ if (this.currentPks.length === 0) return;
2711
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2712
+ };
2713
+ const onIndexUpdate = () => {
2714
+ resubscribeCollection();
2715
+ onUpdate();
2716
+ };
2717
+ resubscribeCollection();
2718
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2719
+ this.key,
2720
+ onIndexUpdate
2721
+ );
2722
+ return () => {
2723
+ unsubscribeFromIndex();
2724
+ this.unsubscribeFromCollectionKeys?.();
2725
+ this.unsubscribeFromCollectionKeys = void 0;
2726
+ this.currentPks = [];
2727
+ };
2728
+ }
2729
+ sameArray(a, b) {
2730
+ if (a === b) return true;
2731
+ if (a.length !== b.length) return false;
2732
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2733
+ return true;
2734
+ }
2735
+ };
2736
+
2737
+ // src/modules/selector/core/OIMEntitiesByIndexKeyArrayBasedSelector.ts
2738
+ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
2739
+ constructor(runtime, collection, reactiveIndex, key) {
2740
+ super(runtime, [
2741
+ new OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased(collection, reactiveIndex, key)
2742
+ ]);
2743
+ this.collection = collection;
2744
+ this.reactiveIndex = reactiveIndex;
2745
+ this.key = key;
2746
+ }
2747
+ getValue() {
2748
+ const pks = this.reactiveIndex.getPksByKey(this.key);
2749
+ const entities = [];
2750
+ entities.length = pks.length;
2751
+ for (let i = 0; i < pks.length; i++) {
2752
+ entities[i] = this.collection.getOneByPk(pks[i]);
2753
+ }
2754
+ return entities;
2755
+ }
2756
+ areEqual(prev, next) {
2757
+ if (prev === next) return true;
2758
+ if (prev.length !== next.length) return false;
2759
+ for (let i = 0; i < prev.length; i++)
2760
+ if (prev[i] !== next[i]) return false;
2761
+ return true;
2762
+ }
2763
+ };
2764
+
2765
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeySetBased.ts
2766
+ var OIMSelectorSourceDependencyEntitiesByIndexKeySetBased = class {
2767
+ constructor(collection, reactiveIndex, key) {
2768
+ this.collection = collection;
2769
+ this.reactiveIndex = reactiveIndex;
2770
+ this.key = key;
2771
+ this.currentPks = [];
2772
+ }
2773
+ subscribe(onUpdate) {
2774
+ const resubscribeCollection = () => {
2775
+ const nextPks = Array.from(this.reactiveIndex.getPksByKey(this.key));
2776
+ if (this.sameArray(this.currentPks, nextPks)) return;
2777
+ this.unsubscribeFromCollectionKeys?.();
2778
+ this.unsubscribeFromCollectionKeys = void 0;
2779
+ this.currentPks = nextPks;
2780
+ if (this.currentPks.length === 0) return;
2781
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2782
+ };
2783
+ const onIndexUpdate = () => {
2784
+ resubscribeCollection();
2785
+ onUpdate();
2786
+ };
2787
+ resubscribeCollection();
2788
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2789
+ this.key,
2790
+ onIndexUpdate
2791
+ );
2792
+ return () => {
2793
+ unsubscribeFromIndex();
2794
+ this.unsubscribeFromCollectionKeys?.();
2795
+ this.unsubscribeFromCollectionKeys = void 0;
2796
+ this.currentPks = [];
2797
+ };
2798
+ }
2799
+ sameArray(a, b) {
2800
+ if (a === b) return true;
2801
+ if (a.length !== b.length) return false;
2802
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2803
+ return true;
1911
2804
  }
1912
2805
  };
1913
2806
 
1914
- // src/core/OIMMap2Keys.ts
1915
- var OIMMap2Keys = class {
1916
- constructor() {
1917
- this.map = /* @__PURE__ */ new Map();
1918
- this._size = 0;
1919
- }
1920
- get size() {
1921
- return this._size;
2807
+ // src/modules/selector/core/OIMEntitiesByIndexKeySetBasedSelector.ts
2808
+ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
2809
+ constructor(runtime, collection, reactiveIndex, key) {
2810
+ super(runtime, [
2811
+ new OIMSelectorSourceDependencyEntitiesByIndexKeySetBased(collection, reactiveIndex, key)
2812
+ ]);
2813
+ this.collection = collection;
2814
+ this.reactiveIndex = reactiveIndex;
2815
+ this.key = key;
1922
2816
  }
1923
- set(k1, k2, v) {
1924
- let m = this.map.get(k1);
1925
- if (!m) {
1926
- m = /* @__PURE__ */ new Map();
1927
- this.map.set(k1, m);
2817
+ getValue() {
2818
+ const pks = this.reactiveIndex.getPksByKey(this.key);
2819
+ const entities = [];
2820
+ entities.length = pks.size;
2821
+ let writeIndex = 0;
2822
+ for (const pk of pks) {
2823
+ entities[writeIndex++] = this.collection.getOneByPk(pk);
1928
2824
  }
1929
- if (!m.has(k2)) this._size++;
1930
- m.set(k2, v);
2825
+ return entities;
1931
2826
  }
1932
- get(k1, k2) {
1933
- return this.map.get(k1)?.get(k2);
2827
+ areEqual(prev, next) {
2828
+ if (prev === next) return true;
2829
+ if (prev.length !== next.length) return false;
2830
+ for (let i = 0; i < prev.length; i++)
2831
+ if (prev[i] !== next[i]) return false;
2832
+ return true;
1934
2833
  }
1935
- has(k1, k2) {
1936
- return this.map.get(k1)?.has(k2);
2834
+ };
2835
+
2836
+ // src/dx/OIMCollectionSelectors.ts
2837
+ var OIMCollectionSelectors = class {
2838
+ constructor(queue, collection) {
2839
+ this.collection = collection;
2840
+ this.runtime = new OIMComputeRuntime(queue);
1937
2841
  }
1938
- delete(k1, k2) {
1939
- const m = this.map.get(k1);
1940
- if (!m) return false;
1941
- const had = m.delete(k2);
1942
- if (had) {
1943
- this._size--;
1944
- if (m.size === 0) this.map.delete(k1);
1945
- }
1946
- return had;
2842
+ byPk(pk) {
2843
+ return new OIMCollectionByPkSelector(this.runtime, this.collection, pk);
1947
2844
  }
1948
- clear() {
1949
- this.map.clear();
1950
- this._size = 0;
2845
+ byPks(pks) {
2846
+ return new OIMCollectionByPksSelector(
2847
+ this.runtime,
2848
+ this.collection,
2849
+ pks
2850
+ );
2851
+ }
2852
+ entitiesBySetIndexKey(index, key) {
2853
+ return new OIMEntitiesByIndexKeySetBasedSelector(
2854
+ this.runtime,
2855
+ this.collection,
2856
+ index,
2857
+ key
2858
+ );
2859
+ }
2860
+ entitiesByArrayIndexKey(index, key) {
2861
+ return new OIMEntitiesByIndexKeyArrayBasedSelector(
2862
+ this.runtime,
2863
+ this.collection,
2864
+ index,
2865
+ key
2866
+ );
1951
2867
  }
1952
2868
  };
1953
2869
 
1954
- // src/modules/wrapper/collection/OIMCollectionChangedFieldsWrapper.ts
1955
- var OIMCollectionChangedFieldsWrapper = class {
2870
+ // src/dx/OIMCollectionContext.ts
2871
+ function createOIMCollectionContext(queue, opts) {
2872
+ const collection = new OIMReactiveCollection(queue, opts);
2873
+ const indexFactory = new OIMCollectionIndexFactory(queue, collection);
2874
+ const select = new OIMCollectionSelectors(queue, collection);
2875
+ return { queue, collection, indexFactory, select };
2876
+ }
2877
+
2878
+ // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
2879
+ var OIMCollectionChangedFields = class {
1956
2880
  constructor(queue, collection, opts) {
1957
2881
  this.isInWrite = false;
1958
2882
  this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
@@ -1972,7 +2896,7 @@ var OIMCollectionChangedFieldsWrapper = class {
1972
2896
  this.collection = collection;
1973
2897
  this.selectPk = opts?.selectPk ?? (() => {
1974
2898
  throw new Error(
1975
- `[OIMCollectionChangedFieldsWrapper]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
2899
+ `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
1976
2900
  );
1977
2901
  });
1978
2902
  this.changedPksEventEmitter = new OIMUpdateEventEmitter(
@@ -1995,31 +2919,34 @@ var OIMCollectionChangedFieldsWrapper = class {
1995
2919
  );
1996
2920
  }
1997
2921
  upsertOneByPk(pk, patch) {
1998
- this.withWrite(() => {
2922
+ const slot = this.withWrite(() => {
1999
2923
  this.trackPatch(pk, patch);
2000
- this.collection.upsertOneByPk(pk, patch);
2924
+ return this.collection.upsertOneByPk(pk, patch);
2001
2925
  });
2002
2926
  this.updateEntitySnapshot(pk);
2927
+ return slot;
2003
2928
  }
2004
2929
  upsertOne(entity) {
2005
- this.withWrite(() => {
2930
+ const slot = this.withWrite(() => {
2006
2931
  const pk = this.selectPk(entity);
2007
2932
  this.trackPatch(pk, entity);
2008
- this.collection.upsertOne(entity);
2933
+ return this.collection.upsertOne(entity);
2009
2934
  });
2010
- this.updateEntitySnapshot(this.selectPk(entity));
2935
+ this.updateEntitySnapshot(slot.pk);
2936
+ return slot;
2011
2937
  }
2012
2938
  upsertMany(entities) {
2013
2939
  const pks = [];
2014
- this.withWrite(() => {
2940
+ const slots = this.withWrite(() => {
2015
2941
  for (const entity of entities) {
2016
2942
  const pk = this.selectPk(entity);
2017
2943
  pks.push(pk);
2018
2944
  this.trackPatch(pk, entity);
2019
2945
  }
2020
- this.collection.upsertMany(entities);
2946
+ return this.collection.upsertMany(entities);
2021
2947
  });
2022
2948
  for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
2949
+ return slots;
2023
2950
  }
2024
2951
  getChangedFieldsByPk(pk) {
2025
2952
  return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
@@ -2050,12 +2977,11 @@ var OIMCollectionChangedFieldsWrapper = class {
2050
2977
  }
2051
2978
  withWrite(fn) {
2052
2979
  if (this.isInWrite) {
2053
- fn();
2054
- return;
2980
+ return fn();
2055
2981
  }
2056
2982
  this.isInWrite = true;
2057
2983
  try {
2058
- fn();
2984
+ return fn();
2059
2985
  } finally {
2060
2986
  this.isInWrite = false;
2061
2987
  }
@@ -2087,248 +3013,43 @@ var OIMCollectionChangedFieldsWrapper = class {
2087
3013
  });
2088
3014
  return changed;
2089
3015
  }
2090
- updateEntitySnapshot(pk) {
2091
- const entity = this.collection.getOneByPk(pk);
2092
- if (!entity) {
2093
- this.lastEntitySnapshotByPk.delete(pk);
2094
- return;
2095
- }
2096
- this.lastEntitySnapshotByPk.set(pk, {
2097
- ...entity
2098
- });
2099
- }
2100
- trackChangedKeys(pk, keys) {
2101
- let pkFields = this.changedFieldsByPk.get(pk);
2102
- if (!pkFields) {
2103
- pkFields = /* @__PURE__ */ new Set();
2104
- this.changedFieldsByPk.set(pk, pkFields);
2105
- }
2106
- for (const key of keys) {
2107
- pkFields.add(key);
2108
- let fieldPks = this.changedPksByField.get(key);
2109
- if (!fieldPks) {
2110
- fieldPks = /* @__PURE__ */ new Set();
2111
- this.changedPksByField.set(key, fieldPks);
2112
- }
2113
- fieldPks.add(pk);
2114
- }
2115
- this.changedPksEventEmitter.markUpdatedKeys([pk]);
2116
- this.changedFieldsEventEmitter.markUpdatedKeys(keys);
2117
- }
2118
- trackPatch(pk, patch) {
2119
- if (pk === void 0 || pk === null || pk === "") {
2120
- throw new Error(
2121
- `[OIMCollectionChangedFieldsWrapper]: PK is required to track changes.`
2122
- );
2123
- }
2124
- const keys = Object.keys(patch);
2125
- if (keys.length === 0) return;
2126
- this.trackChangedKeys(pk, keys);
2127
- }
2128
- };
2129
-
2130
- // src/modules/wrapper/index/OIMIndexManualOrderedArrayBased.ts
2131
- var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
2132
- clear(key) {
2133
- if (key === void 0) {
2134
- const keys = this.getKeys();
2135
- if (keys.length > 0) {
2136
- this.store.clear();
2137
- this.emitUpdate(Array.from(keys));
2138
- }
2139
- return;
2140
- }
2141
- if (this.store.getOneByKey(key) !== void 0) {
2142
- this.store.removeOneByKey(key);
2143
- this.emitUpdate([key]);
2144
- }
2145
- }
2146
- push(key, itemKey) {
2147
- const list = this.getOrCreateList(key);
2148
- const index = list.length;
2149
- list.push(this.getOrCreateSlot(itemKey));
2150
- this.emitUpdate([key]);
2151
- return index;
2152
- }
2153
- insertAt(key, index, itemKey) {
2154
- const list = this.getOrCreateList(key);
2155
- const safeIndex = Math.max(0, Math.min(index, list.length));
2156
- list.splice(safeIndex, 0, this.getOrCreateSlot(itemKey));
2157
- this.emitUpdate([key]);
2158
- }
2159
- removeAt(key, index) {
2160
- const list = this.store.getOneByKey(key);
2161
- if (!list) return void 0;
2162
- if (index < 0 || index >= list.length) return void 0;
2163
- const [removed] = list.splice(index, 1);
2164
- if (list.length === 0) {
2165
- this.store.removeOneByKey(key);
2166
- }
2167
- this.emitUpdate([key]);
2168
- return removed?.pk;
2169
- }
2170
- move(key, fromIndex, toIndex) {
2171
- const list = this.store.getOneByKey(key);
2172
- if (!list) return void 0;
2173
- if (fromIndex < 0 || fromIndex >= list.length) return void 0;
2174
- if (toIndex < 0 || toIndex >= list.length) return void 0;
2175
- if (fromIndex === toIndex) return list[fromIndex]?.pk;
2176
- const [itemKey] = list.splice(fromIndex, 1);
2177
- if (itemKey === void 0) return void 0;
2178
- list.splice(toIndex, 0, itemKey);
2179
- this.emitUpdate([key]);
2180
- return itemKey.pk;
2181
- }
2182
- reset(key, itemKeys) {
2183
- if (itemKeys.length === 0) {
2184
- if (this.store.getOneByKey(key) !== void 0) {
2185
- this.store.removeOneByKey(key);
2186
- this.emitUpdate([key]);
2187
- }
2188
- return;
2189
- }
2190
- this.store.setOneByKey(key, this.resolveSlots(itemKeys));
2191
- this.emitUpdate([key]);
2192
- }
2193
- getOrCreateList(key) {
2194
- let list = this.store.getOneByKey(key);
2195
- if (!list) {
2196
- list = [];
2197
- this.store.setOneByKey(key, list);
2198
- }
2199
- return list;
2200
- }
2201
- };
2202
-
2203
- // src/modules/wrapper/index/OIMOrderedListIndexCommandStreamWrapper.ts
2204
- var OIMOrderedListIndexCommandStreamWrapper = class {
2205
- constructor(queue, index) {
2206
- this.isInWrite = false;
2207
- this.commandsByKey = /* @__PURE__ */ new Map();
2208
- this.onAfterFlush = () => {
2209
- this.commandsByKey.clear();
2210
- };
2211
- this.onIndexUpdate = (payload) => {
2212
- if (this.isInWrite) return;
2213
- if (payload.keys.length === 0) return;
2214
- for (let i = 0; i < payload.keys.length; i++) {
2215
- const key = payload.keys[i];
2216
- const list = this.index.getPksByKey(key);
2217
- this.appendCommand(key, { type: "set", keys: Array.from(list) });
2218
- }
2219
- };
2220
- this.commandsEventEmitter = new OIMUpdateEventEmitter(
2221
- queue,
2222
- "after_flush"
2223
- );
2224
- this.index = index ?? new OIMIndexManualOrderedArrayBased();
2225
- this.unsubscribeAfterFlush = queue.emitter.on(
2226
- 0 /* AFTER_FLUSH */,
2227
- this.onAfterFlush
2228
- );
2229
- this.unsubscribeFromIndexEmitter = this.index.emitter.on(
2230
- 0 /* UPDATE */,
2231
- this.onIndexUpdate
2232
- );
2233
- }
2234
- getPksByKey(key) {
2235
- return this.index.getPksByKey(key);
2236
- }
2237
- hasKey(key) {
2238
- return this.index.hasKey(key);
2239
- }
2240
- getKeys() {
2241
- return this.index.getKeys();
2242
- }
2243
- push(key, itemKey) {
2244
- this.withWrite(() => {
2245
- const index = this.index.push(key, itemKey);
2246
- this.appendCommand(key, { type: "add", key: itemKey, index });
2247
- });
2248
- }
2249
- removeAt(key, index) {
2250
- this.withWrite(() => {
2251
- const itemKey = this.index.removeAt(key, index);
2252
- if (itemKey === void 0) return;
2253
- this.appendCommand(key, { type: "remove", key: itemKey, index });
2254
- });
2255
- }
2256
- move(key, fromIndex, toIndex) {
2257
- this.withWrite(() => {
2258
- const itemKey = this.index.move(key, fromIndex, toIndex);
2259
- if (itemKey === void 0) return;
2260
- this.appendCommand(key, {
2261
- type: "move",
2262
- key: itemKey,
2263
- fromIndex,
2264
- toIndex
2265
- });
2266
- });
2267
- }
2268
- set(key, keys) {
2269
- this.withWrite(() => {
2270
- this.index.reset(key, keys);
2271
- this.appendCommand(key, { type: "set", keys });
2272
- });
2273
- }
2274
- /**
2275
- * Read-only peek at buffered commands without clearing them.
2276
- */
2277
- getBufferedCommands(key) {
2278
- return this.commandsByKey.get(key) ?? [];
2279
- }
2280
- /**
2281
- * Drain buffered commands for a key, preserving order.
2282
- * Intended usage: call inside a `commandsEventEmitter.subscribeOnKey(key, ...)` handler.
2283
- */
2284
- consumeCommands(key) {
2285
- const cmds = this.commandsByKey.get(key);
2286
- if (!cmds || cmds.length === 0) return [];
2287
- return cmds.slice();
2288
- }
2289
- clear(key) {
2290
- if (key === void 0) {
2291
- this.index.destroy();
2292
- this.commandsByKey.clear();
2293
- return;
2294
- }
2295
- this.index.clear(key);
2296
- this.commandsByKey.delete(key);
2297
- }
2298
- destroy() {
2299
- this.commandsEventEmitter.destroy();
2300
- this.unsubscribeAfterFlush();
2301
- this.unsubscribeFromIndexEmitter();
2302
- this.clear();
2303
- }
2304
- withWrite(fn) {
2305
- if (this.isInWrite) {
2306
- fn();
3016
+ updateEntitySnapshot(pk) {
3017
+ const entity = this.collection.getOneByPk(pk);
3018
+ if (!entity) {
3019
+ this.lastEntitySnapshotByPk.delete(pk);
2307
3020
  return;
2308
3021
  }
2309
- this.isInWrite = true;
2310
- try {
2311
- fn();
2312
- } finally {
2313
- this.isInWrite = false;
2314
- }
3022
+ this.lastEntitySnapshotByPk.set(pk, {
3023
+ ...entity
3024
+ });
2315
3025
  }
2316
- appendCommand(key, cmd) {
2317
- let cmds = this.commandsByKey.get(key);
2318
- if (!cmds) {
2319
- cmds = [];
2320
- this.commandsByKey.set(key, cmds);
3026
+ trackChangedKeys(pk, keys) {
3027
+ let pkFields = this.changedFieldsByPk.get(pk);
3028
+ if (!pkFields) {
3029
+ pkFields = /* @__PURE__ */ new Set();
3030
+ this.changedFieldsByPk.set(pk, pkFields);
2321
3031
  }
2322
- if (cmd.type === "set") {
2323
- cmds.length = 0;
2324
- cmds.push(cmd);
2325
- } else if (cmds.length > 0 && cmds[0].type === "set") {
2326
- const setCmd = cmds[0];
2327
- setCmd.keys = Array.from(this.index.getPksByKey(key));
2328
- } else {
2329
- cmds.push(cmd);
3032
+ for (const key of keys) {
3033
+ pkFields.add(key);
3034
+ let fieldPks = this.changedPksByField.get(key);
3035
+ if (!fieldPks) {
3036
+ fieldPks = /* @__PURE__ */ new Set();
3037
+ this.changedPksByField.set(key, fieldPks);
3038
+ }
3039
+ fieldPks.add(pk);
2330
3040
  }
2331
- this.commandsEventEmitter.markUpdatedKeys([key]);
3041
+ this.changedPksEventEmitter.markUpdatedKeys([pk]);
3042
+ this.changedFieldsEventEmitter.markUpdatedKeys(keys);
3043
+ }
3044
+ trackPatch(pk, patch) {
3045
+ if (pk === void 0 || pk === null || pk === "") {
3046
+ throw new Error(
3047
+ `[OIMCollectionChangedFields]: PK is required to track changes.`
3048
+ );
3049
+ }
3050
+ const keys = Object.keys(patch);
3051
+ if (keys.length === 0) return;
3052
+ this.trackChangedKeys(pk, keys);
2332
3053
  }
2333
3054
  };
2334
3055
 
@@ -2444,7 +3165,9 @@ var OIMEventQueueSchedulerImmediate = class extends OIMEventQueueScheduler {
2444
3165
  cancel() {
2445
3166
  if (this.immediateId === void 0) return;
2446
3167
  if (this.useSetImmediate) {
2447
- clearImmediate(this.immediateId);
3168
+ clearImmediate(
3169
+ this.immediateId
3170
+ );
2448
3171
  } else if (this.useMessageChannel) {
2449
3172
  this.pendingCallback = void 0;
2450
3173
  } else {
@@ -2501,7 +3224,7 @@ var OIMEventQueueSchedulerFactory = class {
2501
3224
  }
2502
3225
  };
2503
3226
 
2504
- // src/const/OIMDBSettings.ts
3227
+ // src/constants/OIMDBSettings.ts
2505
3228
  var OIMDBSettings = class {
2506
3229
  };
2507
3230
  /**
@@ -2513,11 +3236,11 @@ OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
2513
3236
 
2514
3237
  // src/modules/effect/core/OIMEffectDependencyComputed.ts
2515
3238
  var OIMEffectDependencyComputed = class {
2516
- constructor(computed) {
2517
- this.computed = computed;
3239
+ constructor(source) {
3240
+ this.source = source;
2518
3241
  }
2519
3242
  subscribe(onUpdate) {
2520
- return this.computed.emitter.on("update", onUpdate);
3243
+ return this.source.emitter.on("update", onUpdate);
2521
3244
  }
2522
3245
  };
2523
3246
 
@@ -2629,7 +3352,7 @@ var OIMEffect = class {
2629
3352
  }
2630
3353
  };
2631
3354
 
2632
- // src/modules/computed/enum/EOIMComputedEventType.ts
3355
+ // src/modules/computed/enums/EOIMComputedEventType.ts
2633
3356
  var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
2634
3357
  EOIMComputedEventType2["UPDATE"] = "update";
2635
3358
  return EOIMComputedEventType2;
@@ -2644,13 +3367,13 @@ var OIMComputed = class {
2644
3367
  this.runtime = runtime;
2645
3368
  this.compute = opts.compute;
2646
3369
  this.compare = opts.compare ?? Object.is;
2647
- this.deps = opts.deps ?? [];
3370
+ this._deps = opts.deps ?? [];
2648
3371
  this.updateEventEmitter = new OIMUpdateEventEmitter(
2649
3372
  this.runtime.queue,
2650
3373
  "immediate"
2651
3374
  );
2652
3375
  this.effect = new OIMEffect(this.runtime, {
2653
- deps: this.deps,
3376
+ deps: this._deps,
2654
3377
  onUpdate: () => {
2655
3378
  this.isDirty = true;
2656
3379
  },
@@ -2659,6 +3382,15 @@ var OIMComputed = class {
2659
3382
  }
2660
3383
  });
2661
3384
  }
3385
+ get needsRecompute() {
3386
+ return this.isDirty;
3387
+ }
3388
+ get isReady() {
3389
+ return this.hasValue;
3390
+ }
3391
+ get deps() {
3392
+ return this._deps;
3393
+ }
2662
3394
  get() {
2663
3395
  this.recomputeAndEmitIfChanged();
2664
3396
  return this.value;
@@ -2693,8 +3425,8 @@ function isReadonlyArray(value) {
2693
3425
  return Array.isArray(value);
2694
3426
  }
2695
3427
  var OIMEffectDependencyKeyed = class {
2696
- constructor(subscription, keyOrKeys) {
2697
- this.subscription = subscription;
3428
+ constructor(source, keyOrKeys) {
3429
+ this.source = source;
2698
3430
  if (isReadonlyArray(keyOrKeys)) {
2699
3431
  this.keys = keyOrKeys;
2700
3432
  } else {
@@ -2703,11 +3435,11 @@ var OIMEffectDependencyKeyed = class {
2703
3435
  }
2704
3436
  subscribe(onUpdate) {
2705
3437
  if (this.keys !== void 0) {
2706
- return this.subscription.subscribeOnKeys(this.keys, onUpdate);
3438
+ return this.source.subscribeOnKeys(this.keys, onUpdate);
2707
3439
  }
2708
3440
  if (this.key === void 0) return () => {
2709
3441
  };
2710
- return this.subscription.subscribeOnKey(this.key, onUpdate);
3442
+ return this.source.subscribeOnKey(this.key, onUpdate);
2711
3443
  }
2712
3444
  };
2713
3445
 
@@ -2719,6 +3451,9 @@ var OIMEffectDependencyKeyedObject = class {
2719
3451
  keyOrKeys
2720
3452
  );
2721
3453
  }
3454
+ get source() {
3455
+ return this.dep.source;
3456
+ }
2722
3457
  subscribe(onUpdate) {
2723
3458
  return this.dep.subscribe(onUpdate);
2724
3459
  }
@@ -2732,217 +3467,27 @@ var OIMEffectDependencyKeyedCollection = class {
2732
3467
  pkOrPks
2733
3468
  );
2734
3469
  }
2735
- subscribe(onUpdate) {
2736
- return this.dep.subscribe(onUpdate);
2737
- }
2738
- };
2739
-
2740
- // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
2741
- var OIMEffectDependencyKeyedIndex = class {
2742
- constructor(index, keyOrKeys) {
2743
- this.dep = new OIMEffectDependencyKeyed(
2744
- index,
2745
- keyOrKeys
2746
- );
3470
+ get source() {
3471
+ return this.dep.source;
2747
3472
  }
2748
3473
  subscribe(onUpdate) {
2749
3474
  return this.dep.subscribe(onUpdate);
2750
3475
  }
2751
- };
2752
-
2753
- // src/modules/selector/core/OIMSelector.ts
2754
- var OIMSelector = class {
2755
- constructor(runtime, sourceDeps, computedDeps = []) {
2756
- this.runtime = runtime;
2757
- this.sourceDeps = sourceDeps;
2758
- this.computedDeps = computedDeps;
2759
- this.listeners = /* @__PURE__ */ new Set();
2760
- this.sourceUnsubscribers = [];
2761
- this.computedUnsubscribers = [];
2762
- this.isResubscribeEnqueued = false;
2763
- this.isTemporarilyUnsubscribedForFlush = false;
2764
- this.isDeliveryEnqueued = false;
2765
- this.hasLastValue = false;
2766
- this.onSourceInvalidate = () => {
2767
- if (this.listeners.size === 0) return;
2768
- if (!this.isTemporarilyUnsubscribedForFlush) {
2769
- this.isTemporarilyUnsubscribedForFlush = true;
2770
- this.unsubscribeFromSourceDeps();
2771
- this.ensureResubscribeAfterFlush();
2772
- }
2773
- if (this.isDeliveryEnqueued) return;
2774
- this.isDeliveryEnqueued = true;
2775
- this.dequeueDelivery = this.runtime.schedule(this.deliver);
2776
- };
2777
- this.onComputedInvalidate = () => {
2778
- if (this.listeners.size === 0) return;
2779
- if (this.isDeliveryEnqueued) return;
2780
- this.isDeliveryEnqueued = true;
2781
- this.dequeueDelivery = this.runtime.schedule(this.deliver);
2782
- };
2783
- this.deliver = () => {
2784
- this.isDeliveryEnqueued = false;
2785
- this.dequeueDelivery = void 0;
2786
- if (this.listeners.size === 0) return;
2787
- const next = this.getValue();
2788
- if (this.hasLastValue && this.areEqual(this.lastValue, next)) return;
2789
- this.setLastValue(next);
2790
- const snapshot = Array.from(this.listeners);
2791
- for (let i = 0; i < snapshot.length; i++) {
2792
- const listener = snapshot[i];
2793
- if (this.listeners.has(listener)) listener(next);
2794
- }
2795
- };
2796
- this.onAfterFlush = () => {
2797
- if (!this.isResubscribeEnqueued) return;
2798
- this.isResubscribeEnqueued = false;
2799
- this.dequeueResubscribe = void 0;
2800
- if (this.listeners.size === 0) return;
2801
- this.isTemporarilyUnsubscribedForFlush = false;
2802
- if (this.sourceUnsubscribers.length === 0) {
2803
- for (const dep of this.sourceDeps) {
2804
- this.sourceUnsubscribers.push(
2805
- dep.subscribe(this.onSourceInvalidate)
2806
- );
2807
- }
2808
- }
2809
- };
2810
- }
2811
- get queue() {
2812
- return this.runtime.queue;
2813
- }
2814
- /**
2815
- * Start watching this selector. The callback is called immediately with the current value,
2816
- * then coalesced and delivered on `queue.flush()` when the selector invalidates.
2817
- */
2818
- watch(onValue) {
2819
- this.listeners.add(onValue);
2820
- if (this.listeners.size === 1) {
2821
- this.subscribeToDeps();
2822
- }
2823
- const current = this.getValue();
2824
- this.setLastValue(current);
2825
- onValue(current);
2826
- return () => {
2827
- this.listeners.delete(onValue);
2828
- if (this.listeners.size > 0) return;
2829
- this.unsubscribeFromAllDeps();
2830
- if (this.dequeueResubscribe) {
2831
- this.dequeueResubscribe();
2832
- this.dequeueResubscribe = void 0;
2833
- }
2834
- this.isResubscribeEnqueued = false;
2835
- this.isTemporarilyUnsubscribedForFlush = false;
2836
- if (this.dequeueDelivery) {
2837
- this.dequeueDelivery();
2838
- this.dequeueDelivery = void 0;
2839
- }
2840
- this.isDeliveryEnqueued = false;
2841
- this.hasLastValue = false;
2842
- };
2843
- }
2844
- areEqual(prev, next) {
2845
- return Object.is(prev, next);
2846
- }
2847
- setLastValue(value) {
2848
- this.lastValue = value;
2849
- this.hasLastValue = true;
2850
- }
2851
- subscribeToDeps() {
2852
- if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2853
- return;
2854
- for (const dep of this.sourceDeps) {
2855
- this.sourceUnsubscribers.push(
2856
- dep.subscribe(this.onSourceInvalidate)
2857
- );
2858
- }
2859
- for (const dep of this.computedDeps) {
2860
- this.computedUnsubscribers.push(
2861
- dep.subscribe(this.onComputedInvalidate)
2862
- );
2863
- }
2864
- }
2865
- unsubscribeFromSourceDeps() {
2866
- if (this.sourceUnsubscribers.length === 0) return;
2867
- for (const unsub of this.sourceUnsubscribers) unsub();
2868
- this.sourceUnsubscribers = [];
2869
- }
2870
- unsubscribeFromAllDeps() {
2871
- this.unsubscribeFromSourceDeps();
2872
- if (this.computedUnsubscribers.length === 0) return;
2873
- for (const unsub of this.computedUnsubscribers) unsub();
2874
- this.computedUnsubscribers = [];
2875
- }
2876
- ensureResubscribeAfterFlush() {
2877
- if (this.isResubscribeEnqueued) return;
2878
- this.isResubscribeEnqueued = true;
2879
- this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2880
- this.onAfterFlush
2881
- );
2882
- }
2883
- };
2884
-
2885
- // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKey.ts
2886
- var OIMSelectorSourceDependencyUpdateEventEmitterKey = class {
2887
- constructor(subscription, key) {
2888
- this.subscription = subscription;
2889
- this.key = key;
2890
- }
2891
- subscribe(onUpdate) {
2892
- return this.subscription.subscribeOnKey(this.key, onUpdate);
2893
- }
2894
- };
2895
-
2896
- // src/modules/selector/core/OIMCollectionByPkSelector.ts
2897
- var OIMCollectionByPkSelector = class extends OIMSelector {
2898
- constructor(runtime, collection, pk) {
2899
- super(runtime, [
2900
- new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2901
- collection,
2902
- pk
2903
- )
2904
- ]);
2905
- this.collection = collection;
2906
- this.pk = pk;
2907
- }
2908
- getValue() {
2909
- return this.collection.getOneByPk(this.pk);
2910
- }
2911
- };
2912
-
2913
- // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKeys.ts
2914
- var OIMSelectorSourceDependencyUpdateEventEmitterKeys = class {
2915
- constructor(subscription, keys) {
2916
- this.subscription = subscription;
2917
- this.keys = keys;
2918
- }
2919
- subscribe(onUpdate) {
2920
- return this.subscription.subscribeOnKeys(this.keys, onUpdate);
2921
- }
2922
- };
2923
-
2924
- // src/modules/selector/core/OIMCollectionByPksSelector.ts
2925
- var OIMCollectionByPksSelector = class extends OIMSelector {
2926
- constructor(runtime, collection, pks) {
2927
- super(runtime, [
2928
- new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2929
- collection,
2930
- pks
2931
- )
2932
- ]);
2933
- this.collection = collection;
2934
- this.pks = pks;
3476
+ };
3477
+
3478
+ // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
3479
+ var OIMEffectDependencyKeyedIndex = class {
3480
+ constructor(index, keyOrKeys) {
3481
+ this.dep = new OIMEffectDependencyKeyed(
3482
+ index,
3483
+ keyOrKeys
3484
+ );
2935
3485
  }
2936
- getValue() {
2937
- return this.pks.map((pk) => this.collection.getOneByPk(pk));
3486
+ get source() {
3487
+ return this.dep.source;
2938
3488
  }
2939
- areEqual(prev, next) {
2940
- if (prev === next) return true;
2941
- if (prev.length !== next.length) return false;
2942
- for (let i = 0; i < prev.length; i++) {
2943
- if (prev[i] !== next[i]) return false;
2944
- }
2945
- return true;
3489
+ subscribe(onUpdate) {
3490
+ return this.dep.subscribe(onUpdate);
2946
3491
  }
2947
3492
  };
2948
3493
 
@@ -3076,259 +3621,6 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
3076
3621
  return true;
3077
3622
  }
3078
3623
  };
3079
-
3080
- // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeySetBased.ts
3081
- var OIMSelectorSourceDependencyEntitiesByIndexKeySetBased = class {
3082
- constructor(collection, reactiveIndex, key) {
3083
- this.collection = collection;
3084
- this.reactiveIndex = reactiveIndex;
3085
- this.key = key;
3086
- this.currentPks = [];
3087
- }
3088
- subscribe(onUpdate) {
3089
- const resubscribeCollection = () => {
3090
- const nextPks = Array.from(
3091
- this.reactiveIndex.getSlotsByKey(this.key),
3092
- (slot) => slot.pk
3093
- );
3094
- if (this.sameArray(this.currentPks, nextPks)) return;
3095
- this.unsubscribeFromCollectionKeys?.();
3096
- this.unsubscribeFromCollectionKeys = void 0;
3097
- this.currentPks = nextPks;
3098
- if (this.currentPks.length === 0) return;
3099
- this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
3100
- };
3101
- const onIndexUpdate = () => {
3102
- resubscribeCollection();
3103
- onUpdate();
3104
- };
3105
- resubscribeCollection();
3106
- const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
3107
- this.key,
3108
- onIndexUpdate
3109
- );
3110
- return () => {
3111
- unsubscribeFromIndex();
3112
- this.unsubscribeFromCollectionKeys?.();
3113
- this.unsubscribeFromCollectionKeys = void 0;
3114
- this.currentPks = [];
3115
- };
3116
- }
3117
- sameArray(a, b) {
3118
- if (a === b) return true;
3119
- if (a.length !== b.length) return false;
3120
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3121
- return true;
3122
- }
3123
- };
3124
-
3125
- // src/modules/selector/core/OIMEntitiesByIndexKeySetBasedSelector.ts
3126
- var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
3127
- constructor(runtime, collection, reactiveIndex, key) {
3128
- super(runtime, [
3129
- new OIMSelectorSourceDependencyEntitiesByIndexKeySetBased(collection, reactiveIndex, key)
3130
- ]);
3131
- this.collection = collection;
3132
- this.reactiveIndex = reactiveIndex;
3133
- this.key = key;
3134
- this.reactiveIndex.index.setSlotResolver(
3135
- (pk) => this.collection.getSlotByPk(pk)
3136
- );
3137
- }
3138
- getValue() {
3139
- const slots = this.reactiveIndex.getSlotsByKey(this.key);
3140
- const entities = [];
3141
- entities.length = slots.size;
3142
- let writeIndex = 0;
3143
- for (const slot of slots) {
3144
- entities[writeIndex++] = slot.item;
3145
- }
3146
- return entities;
3147
- }
3148
- areEqual(prev, next) {
3149
- if (prev === next) return true;
3150
- if (prev.length !== next.length) return false;
3151
- for (let i = 0; i < prev.length; i++)
3152
- if (prev[i] !== next[i]) return false;
3153
- return true;
3154
- }
3155
- };
3156
-
3157
- // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased.ts
3158
- var OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased = class {
3159
- constructor(collection, reactiveIndex, key) {
3160
- this.collection = collection;
3161
- this.reactiveIndex = reactiveIndex;
3162
- this.key = key;
3163
- this.currentPks = [];
3164
- }
3165
- subscribe(onUpdate) {
3166
- const resubscribeCollection = () => {
3167
- const nextPks = this.reactiveIndex.getSlotsByKey(this.key).map((slot) => slot.pk);
3168
- if (this.sameArray(this.currentPks, nextPks)) return;
3169
- this.unsubscribeFromCollectionKeys?.();
3170
- this.unsubscribeFromCollectionKeys = void 0;
3171
- this.currentPks = nextPks;
3172
- if (this.currentPks.length === 0) return;
3173
- this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
3174
- };
3175
- const onIndexUpdate = () => {
3176
- resubscribeCollection();
3177
- onUpdate();
3178
- };
3179
- resubscribeCollection();
3180
- const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
3181
- this.key,
3182
- onIndexUpdate
3183
- );
3184
- return () => {
3185
- unsubscribeFromIndex();
3186
- this.unsubscribeFromCollectionKeys?.();
3187
- this.unsubscribeFromCollectionKeys = void 0;
3188
- this.currentPks = [];
3189
- };
3190
- }
3191
- sameArray(a, b) {
3192
- if (a === b) return true;
3193
- if (a.length !== b.length) return false;
3194
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3195
- return true;
3196
- }
3197
- };
3198
-
3199
- // src/modules/selector/core/OIMEntitiesByIndexKeyArrayBasedSelector.ts
3200
- var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
3201
- constructor(runtime, collection, reactiveIndex, key) {
3202
- super(runtime, [
3203
- new OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased(collection, reactiveIndex, key)
3204
- ]);
3205
- this.collection = collection;
3206
- this.reactiveIndex = reactiveIndex;
3207
- this.key = key;
3208
- this.reactiveIndex.index.setSlotResolver(
3209
- (pk) => this.collection.getSlotByPk(pk)
3210
- );
3211
- }
3212
- getValue() {
3213
- const slots = this.reactiveIndex.getSlotsByKey(this.key);
3214
- const entities = [];
3215
- entities.length = slots.length;
3216
- for (let i = 0; i < slots.length; i++) {
3217
- entities[i] = slots[i].item;
3218
- }
3219
- return entities;
3220
- }
3221
- areEqual(prev, next) {
3222
- if (prev === next) return true;
3223
- if (prev.length !== next.length) return false;
3224
- for (let i = 0; i < prev.length; i++)
3225
- if (prev[i] !== next[i]) return false;
3226
- return true;
3227
- }
3228
- };
3229
-
3230
- // src/modules/computative/core/OIMComputativeRuntime.ts
3231
- var OIMComputativeRuntime = class {
3232
- constructor(queue) {
3233
- this.queue = queue;
3234
- this.isFlushScheduled = false;
3235
- this.isFlushing = false;
3236
- this.minLevel = Infinity;
3237
- this.maxLevel = -Infinity;
3238
- this.dirtyByLevel = /* @__PURE__ */ new Map();
3239
- this.afterFlushTasks = /* @__PURE__ */ new Set();
3240
- this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
3241
- if (!this.isFlushScheduled) return;
3242
- this.flush();
3243
- });
3244
- }
3245
- /**
3246
- * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
3247
- * ordered by `level` ascending.
3248
- */
3249
- schedule(task, level = 0) {
3250
- let isActive = true;
3251
- const wrapped = () => {
3252
- if (!isActive) return;
3253
- task();
3254
- };
3255
- let bucket = this.dirtyByLevel.get(level);
3256
- if (!bucket) {
3257
- bucket = /* @__PURE__ */ new Set();
3258
- this.dirtyByLevel.set(level, bucket);
3259
- }
3260
- bucket.add(wrapped);
3261
- if (level < this.minLevel) this.minLevel = level;
3262
- if (level > this.maxLevel) this.maxLevel = level;
3263
- if (!this.isFlushing) this.isFlushScheduled = true;
3264
- return () => {
3265
- if (!isActive) return;
3266
- isActive = false;
3267
- bucket.delete(wrapped);
3268
- };
3269
- }
3270
- /**
3271
- * Run once at the start of the next runtime flush (i.e. after queue.flush()).
3272
- * Useful for resubscribing dependencies exactly once per source flush.
3273
- */
3274
- scheduleAfterFlush(task) {
3275
- let isActive = true;
3276
- const wrapped = () => {
3277
- if (!isActive) return;
3278
- task();
3279
- };
3280
- this.afterFlushTasks.add(wrapped);
3281
- if (!this.isFlushing) this.isFlushScheduled = true;
3282
- return () => {
3283
- if (!isActive) return;
3284
- isActive = false;
3285
- this.afterFlushTasks.delete(wrapped);
3286
- };
3287
- }
3288
- flush() {
3289
- this.isFlushScheduled = false;
3290
- if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
3291
- return;
3292
- this.isFlushing = true;
3293
- try {
3294
- if (this.afterFlushTasks.size > 0) {
3295
- const snapshot = Array.from(this.afterFlushTasks);
3296
- this.afterFlushTasks.clear();
3297
- for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3298
- }
3299
- if (this.dirtyByLevel.size === 0) return;
3300
- let level = this.minLevel;
3301
- let max = this.maxLevel;
3302
- while (level <= max) {
3303
- for (; ; ) {
3304
- const bucket = this.dirtyByLevel.get(level);
3305
- if (!bucket || bucket.size === 0) break;
3306
- this.dirtyByLevel.delete(level);
3307
- const snapshot = Array.from(bucket);
3308
- for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3309
- }
3310
- if (this.maxLevel > max) max = this.maxLevel;
3311
- level++;
3312
- }
3313
- } finally {
3314
- this.isFlushing = false;
3315
- if (this.dirtyByLevel.size === 0) {
3316
- this.minLevel = Infinity;
3317
- this.maxLevel = -Infinity;
3318
- } else {
3319
- let nextMin = Infinity;
3320
- let nextMax = -Infinity;
3321
- this.dirtyByLevel.forEach((_bucket, lvl) => {
3322
- if (lvl < nextMin) nextMin = lvl;
3323
- if (lvl > nextMax) nextMax = lvl;
3324
- });
3325
- this.minLevel = nextMin;
3326
- this.maxLevel = nextMax;
3327
- this.isFlushScheduled = true;
3328
- }
3329
- }
3330
- }
3331
- };
3332
3624
  export {
3333
3625
  EOIMCollectionEventType,
3334
3626
  EOIMComputedEventType,
@@ -3339,13 +3631,19 @@ export {
3339
3631
  OIMCollection,
3340
3632
  OIMCollectionByPkSelector,
3341
3633
  OIMCollectionByPksSelector,
3342
- OIMCollectionChangedFieldsWrapper,
3634
+ OIMCollectionChangedFields,
3635
+ OIMCollectionIndexFactory,
3636
+ OIMCollectionIndexManualOrderedArrayBased,
3637
+ OIMCollectionOrderedListCommandStream,
3638
+ OIMCollectionSelectors,
3343
3639
  OIMCollectionStore,
3344
3640
  OIMCollectionStoreMapDriven,
3345
3641
  OIMComparatorFactory,
3346
- OIMComputativeRuntime,
3642
+ OIMComputeRuntime,
3347
3643
  OIMComputed,
3348
3644
  OIMDBSettings,
3645
+ OIMDerivedCollectionIndexArrayBased,
3646
+ OIMDerivedCollectionIndexSetBased,
3349
3647
  OIMEffect,
3350
3648
  OIMEffectDependencyComputed,
3351
3649
  OIMEffectDependencyKeyed,
@@ -3363,6 +3661,7 @@ export {
3363
3661
  OIMEventQueueSchedulerImmediate,
3364
3662
  OIMEventQueueSchedulerMicrotask,
3365
3663
  OIMEventQueueSchedulerTimeout,
3664
+ OIMIndex,
3366
3665
  OIMIndexArrayBased,
3367
3666
  OIMIndexComparatorFactory,
3368
3667
  OIMIndexManualArrayBased,
@@ -3384,14 +3683,18 @@ export {
3384
3683
  OIMObjectStoreRecordDriven,
3385
3684
  OIMObjectValueByKeySelector,
3386
3685
  OIMObjectValuesByKeysSelector,
3387
- OIMOrderedListIndexCommandStreamWrapper,
3686
+ OIMOrderedListCommandStream,
3388
3687
  OIMPkSelectorFactory,
3389
- OIMRICollection,
3390
3688
  OIMReactiveCollection,
3689
+ OIMReactiveCollectionIndexManualArrayBased,
3690
+ OIMReactiveCollectionIndexManualSetBased,
3691
+ OIMReactiveIndex,
3391
3692
  OIMReactiveIndexArrayBased,
3392
3693
  OIMReactiveIndexManualArrayBased,
3393
3694
  OIMReactiveIndexManualSetBased,
3394
3695
  OIMReactiveIndexSetBased,
3395
3696
  OIMReactiveObject,
3396
- OIMSelector
3697
+ OIMSelector,
3698
+ createOIMCollectionContext,
3699
+ createOIMCollectionIndexFactory
3397
3700
  };