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