@oimdb/core 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,8 @@ This package exports all the core classes, interfaces, and types needed to build
15
15
  ### Core Classes
16
16
  - **OIMReactiveCollection**: Reactive entity storage with automatic change notifications
17
17
  - **OIMRICollection**: Reactive collection with integrated indexing capabilities
18
- - **OIMReactiveIndexManual**: Reactive index with manual key-to-entity mapping
18
+ - **OIMReactiveIndexManualSetBased**: Reactive index with Set-based storage (efficient for incremental updates)
19
+ - **OIMReactiveIndexManualArrayBased**: Reactive index with Array-based storage (efficient for full replacements)
19
20
  - **OIMEventQueue**: Configurable event processing queue with scheduler integration
20
21
  - **OIMCollection**: Base collection with CRUD operations and event emission
21
22
 
@@ -27,14 +28,19 @@ This package exports all the core classes, interfaces, and types needed to build
27
28
 
28
29
  ### Storage & Indexing
29
30
  - **OIMCollectionStoreMapDriven**: Map-based storage backend
30
- - **OIMIndexManual**: Manual index management with custom comparison logic
31
+ - **OIMIndexManualSetBased**: Set-based manual index (returns `Set<TPk>`)
32
+ - **OIMIndexManualArrayBased**: Array-based manual index (returns `TPk[]`)
33
+ - **OIMIndexStoreMapDrivenSetBased**: Set-based index storage backend
34
+ - **OIMIndexStoreMapDrivenArrayBased**: Array-based index storage backend
31
35
  - **OIMMap2Keys**: Two-key mapping utilities for complex indexing
32
36
 
33
37
  ### Abstract Classes & Interfaces
34
38
  - **OIMCollectionStore**: Storage backend interface
35
39
  - **OIMEventQueueScheduler**: Event processing scheduler interface
36
- - **OIMIndex**: Base index interface
37
- - **OIMReactiveIndex**: Reactive index interface
40
+ - **OIMIndexSetBased**: Base Set-based index interface (returns `Set<TPk>`)
41
+ - **OIMIndexArrayBased**: Base Array-based index interface (returns `TPk[]`)
42
+ - **OIMReactiveIndexSetBased**: Reactive Set-based index interface
43
+ - **OIMReactiveIndexArrayBased**: Reactive Array-based index interface
38
44
 
39
45
  ### Types & Enums
40
46
  - **TOIM\***: Generic types for collections, indices, events, and schedulers
@@ -92,36 +98,72 @@ const multipleUsers = users.getManyByPks(['user1', 'user2']);
92
98
 
93
99
  ### Creating a Reactive Index
94
100
 
101
+ OIMDB provides two types of indexes optimized for different use cases:
102
+
103
+ #### SetBased Indexes (for incremental updates)
104
+
95
105
  ```typescript
96
- import { OIMReactiveIndexManual, OIMEventQueue } from '@oimdb/core';
106
+ import { OIMReactiveIndexManualSetBased, OIMEventQueue } from '@oimdb/core';
97
107
 
98
- // Create reactive index for user roles
108
+ // Create Set-based reactive index for user roles
99
109
  const queue = new OIMEventQueue({
100
110
  scheduler: OIMEventQueueSchedulerFactory.createMicrotask()
101
111
  });
102
112
 
103
- const userRoleIndex = new OIMReactiveIndexManual<string, string>(queue);
113
+ const userRoleIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
104
114
 
105
115
  // Subscribe to specific index key changes
106
116
  userRoleIndex.updateEventEmitter.subscribeOnKey('admin', (pks) => {
107
- console.log('Admin users changed:', pks);
117
+ console.log('Admin users changed:', pks); // pks is Set<string>
108
118
  });
109
119
 
110
120
  // Build the index manually
111
121
  userRoleIndex.setPks('admin', ['user1']);
112
122
  userRoleIndex.setPks('user', ['user2', 'user3']);
113
123
 
114
- // Add more users to existing roles
124
+ // Add more users to existing roles (efficient for Set-based)
115
125
  userRoleIndex.addPks('admin', ['user2']);
116
126
 
117
- // Query the index
118
- const adminUsers = userRoleIndex.index.getPksByKey('admin'); // ['user1', 'user2']
119
- const regularUsers = userRoleIndex.index.getPksByKey('user'); // ['user2', 'user3']
127
+ // Query the index - returns Set
128
+ const adminUsers = userRoleIndex.index.getPksByKey('admin'); // Set(['user1', 'user2'])
129
+ const regularUsers = userRoleIndex.index.getPksByKey('user'); // Set(['user2', 'user3'])
120
130
 
121
- // Remove users from roles
131
+ // Remove users from roles (efficient for Set-based)
122
132
  userRoleIndex.removePks('admin', ['user1']);
123
133
  ```
124
134
 
135
+ #### ArrayBased Indexes (for full replacements)
136
+
137
+ ```typescript
138
+ import { OIMReactiveIndexManualArrayBased, OIMEventQueue } from '@oimdb/core';
139
+
140
+ // Create Array-based reactive index for deck cards
141
+ const queue = new OIMEventQueue({
142
+ scheduler: OIMEventQueueSchedulerFactory.createMicrotask()
143
+ });
144
+
145
+ const cardsByDeckIndex = new OIMReactiveIndexManualArrayBased<string, string>(queue);
146
+
147
+ // Subscribe to specific index key changes
148
+ cardsByDeckIndex.updateEventEmitter.subscribeOnKey('deck1', (pks) => {
149
+ console.log('Deck cards changed:', pks); // pks is string[]
150
+ });
151
+
152
+ // Build the index manually - set full array
153
+ cardsByDeckIndex.setPks('deck1', ['card1', 'card2', 'card3']);
154
+
155
+ // Query the index - returns Array
156
+ const deckCards = cardsByDeckIndex.index.getPksByKey('deck1'); // ['card1', 'card2', 'card3']
157
+
158
+ // For Array-based indexes, prefer setPks for updates (addPks/removePks are available but less efficient)
159
+ cardsByDeckIndex.setPks('deck1', ['card1', 'card2', 'card4']); // Full replacement (recommended)
160
+ // cardsByDeckIndex.addPks('deck1', ['card5']); // Works but less efficient than SetBased
161
+ ```
162
+
163
+ **When to use which:**
164
+ - **SetBased**: Use when you frequently add/remove individual items (`addPks`/`removePks` are efficient) and order doesn't matter
165
+ - **ArrayBased**: Use when you typically replace the entire array (`setPks` is more efficient, no diff computation needed) or when you need to preserve element order/sorting
166
+
125
167
  ### Event Queue and Schedulers
126
168
 
127
169
  ```typescript
@@ -187,9 +229,9 @@ const queue = new OIMEventQueue({
187
229
  scheduler: OIMEventQueueSchedulerFactory.createMicrotask()
188
230
  });
189
231
 
190
- // Create indexes
191
- const teamIndex = new OIMReactiveIndexManual<string, string>(queue);
192
- const roleIndex = new OIMReactiveIndexManual<string, string>(queue);
232
+ // Create indexes (choose SetBased or ArrayBased based on your needs)
233
+ const teamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
234
+ const roleIndex = new OIMReactiveIndexManualArrayBased<string, string>(queue);
193
235
 
194
236
  // Create collection with indexes
195
237
  const users = new OIMRICollection(queue, {
@@ -334,9 +376,13 @@ OIMCollection (base)
334
376
  ├── OIMReactiveCollection (adds event emitter + coalescing)
335
377
  └── OIMRICollection (reactive collection + indexes)
336
378
 
337
- OIMIndex (base)
338
- ├── OIMIndexManual (manual index management)
339
- └── OIMReactiveIndexManual (reactive index with event emitter)
379
+ OIMIndexSetBased (base for Set-based)
380
+ ├── OIMIndexManualSetBased (manual Set-based index)
381
+ └── OIMReactiveIndexManualSetBased (reactive Set-based index with event emitter)
382
+
383
+ OIMIndexArrayBased (base for Array-based)
384
+ ├── OIMIndexManualArrayBased (manual Array-based index)
385
+ └── OIMReactiveIndexManualArrayBased (reactive Array-based index with event emitter)
340
386
  ```
341
387
 
342
388
  ## ⚡ Performance Characteristics
@@ -352,6 +398,21 @@ OIMIndex (base)
352
398
  - **Timeout**: Custom delay for batching strategies
353
399
  - **AnimationFrame**: 16ms, synced with 60fps rendering
354
400
 
401
+ ### Index Performance
402
+
403
+ **SetBased Indexes** (`OIMReactiveIndexManualSetBased`):
404
+ - **Returns**: `Set<TPk>` for efficient membership checks
405
+ - **Best for**: Frequent incremental updates using `addPks`/`removePks`
406
+ - **Performance**: O(1) add/remove operations, O(n) for `setPks` (requires Set creation)
407
+ - **Use case**: When you need to frequently add/remove individual items
408
+
409
+ **ArrayBased Indexes** (`OIMReactiveIndexManualArrayBased`):
410
+ - **Returns**: `TPk[]` for direct array access
411
+ - **Best for**: Full array replacements using `setPks`
412
+ - **Performance**: O(1) `setPks` operation (direct assignment, no diff computation)
413
+ - **Use case**: When you typically replace the entire array (e.g., deck cards, ordered lists) or when you need to preserve element order/sorting
414
+ - **Note**: While `addPks`/`removePks` are available, they are less efficient (O(n)) than for SetBased indexes. For ArrayBased indexes, prefer `setPks` for better performance.
415
+
355
416
  ## 🔗 Integration Patterns
356
417
 
357
418
  ### With React (@oimdb/react)
@@ -462,26 +523,54 @@ new OIMRICollection(queue: OIMEventQueue, opts: {
462
523
  - `indexes: TReactiveIndexMap` - Named reactive indexes preserving index-to-name mapping
463
524
  - *(inherits all OIMReactiveCollection properties)*
464
525
 
465
- #### `OIMReactiveIndexManual<TKey, TPk>`
466
- Reactive index with manual key-to-entity mapping and change notifications.
526
+ #### `OIMReactiveIndexManualSetBased<TKey, TPk>`
527
+ Reactive Set-based index with manual key-to-entity mapping and change notifications. Returns `Set<TPk>` for efficient membership checks.
528
+
529
+ **Constructor:**
530
+ ```typescript
531
+ new OIMReactiveIndexManualSetBased(queue: OIMEventQueue, opts?: {
532
+ index?: OIMIndexManualSetBased<TKey, TPk>
533
+ })
534
+ ```
535
+
536
+ **Properties:**
537
+ - `index: OIMIndexManualSetBased<TKey, TPk>` - Underlying Set-based index
538
+ - `updateEventEmitter: OIMUpdateEventEmitter<TKey>` - Key-specific subscriptions
539
+
540
+ **Methods:**
541
+ - `setPks(key: TKey, pks: readonly TPk[]): void` - Set primary keys for index key (replaces entire Set)
542
+ - `addPks(key: TKey, pks: readonly TPk[]): void` - Add primary keys to index key (efficient)
543
+ - `removePks(key: TKey, pks: readonly TPk[]): void` - Remove primary keys from index key (efficient)
544
+ - `clear(key?: TKey): void` - Clear all keys or specific key
545
+
546
+ **Query:**
547
+ - `index.getPksByKey(key: TKey): Set<TPk>` - Returns Set of primary keys
548
+
549
+ #### `OIMReactiveIndexManualArrayBased<TKey, TPk>`
550
+ Reactive Array-based index with manual key-to-entity mapping and change notifications. Returns `TPk[]` for direct array access.
467
551
 
468
552
  **Constructor:**
469
553
  ```typescript
470
- new OIMReactiveIndexManual(queue: OIMEventQueue, opts?: {
471
- index?: OIMIndexManual<TKey, TPk>
554
+ new OIMReactiveIndexManualArrayBased(queue: OIMEventQueue, opts?: {
555
+ index?: OIMIndexManualArrayBased<TKey, TPk>
472
556
  })
473
557
  ```
474
558
 
475
559
  **Properties:**
476
- - `index: OIMIndexManual<TKey, TPk>` - Underlying index
560
+ - `index: OIMIndexManualArrayBased<TKey, TPk>` - Underlying Array-based index
477
561
  - `updateEventEmitter: OIMUpdateEventEmitter<TKey>` - Key-specific subscriptions
478
562
 
479
563
  **Methods:**
480
- - `setPks(key: TKey, pks: readonly TPk[]): void` - Set primary keys for index key
481
- - `addPks(key: TKey, pks: readonly TPk[]): void` - Add primary keys to index key
482
- - `removePks(key: TKey, pks: readonly TPk[]): void` - Remove primary keys from index key
564
+ - `setPks(key: TKey, pks: readonly TPk[]): void` - Set primary keys for index key (direct assignment, no diff) - **Recommended for ArrayBased**
565
+ - `addPks(key: TKey, pks: readonly TPk[]): void` - Add primary keys to index key (O(n) operation, less efficient than SetBased)
566
+ - `removePks(key: TKey, pks: readonly TPk[]): void` - Remove primary keys from index key (O(n) operation, less efficient than SetBased)
483
567
  - `clear(key?: TKey): void` - Clear all keys or specific key
484
568
 
569
+ **Query:**
570
+ - `index.getPksByKey(key: TKey): TPk[]` - Returns Array of primary keys
571
+
572
+ **Note**: While `addPks`/`removePks` are available, they require array operations (Set creation, filtering) making them O(n) compared to O(1) for SetBased indexes. For ArrayBased indexes, prefer `setPks` for better performance when replacing the entire array.
573
+
485
574
  #### `OIMEventQueue`
486
575
  Event processing queue with configurable scheduling.
487
576
 
package/dist/index.cjs CHANGED
@@ -39,15 +39,23 @@ __export(index_exports, {
39
39
  OIMEventQueueSchedulerImmediate: () => OIMEventQueueSchedulerImmediate,
40
40
  OIMEventQueueSchedulerMicrotask: () => OIMEventQueueSchedulerMicrotask,
41
41
  OIMEventQueueSchedulerTimeout: () => OIMEventQueueSchedulerTimeout,
42
- OIMIndex: () => OIMIndex,
42
+ OIMIndexArrayBased: () => OIMIndexArrayBased,
43
43
  OIMIndexComparatorFactory: () => OIMIndexComparatorFactory,
44
- OIMIndexManual: () => OIMIndexManual,
44
+ OIMIndexManualArrayBased: () => OIMIndexManualArrayBased,
45
+ OIMIndexManualSetBased: () => OIMIndexManualSetBased,
46
+ OIMIndexSetBased: () => OIMIndexSetBased,
47
+ OIMIndexStoreArrayBased: () => OIMIndexStoreArrayBased,
48
+ OIMIndexStoreMapDrivenArrayBased: () => OIMIndexStoreMapDrivenArrayBased,
49
+ OIMIndexStoreMapDrivenSetBased: () => OIMIndexStoreMapDrivenSetBased,
50
+ OIMIndexStoreSetBased: () => OIMIndexStoreSetBased,
45
51
  OIMMap2Keys: () => OIMMap2Keys,
46
52
  OIMPkSelectorFactory: () => OIMPkSelectorFactory,
47
53
  OIMRICollection: () => OIMRICollection,
48
54
  OIMReactiveCollection: () => OIMReactiveCollection,
49
- OIMReactiveIndex: () => OIMReactiveIndex,
50
- OIMReactiveIndexManual: () => OIMReactiveIndexManual,
55
+ OIMReactiveIndexArrayBased: () => OIMReactiveIndexArrayBased,
56
+ OIMReactiveIndexManualArrayBased: () => OIMReactiveIndexManualArrayBased,
57
+ OIMReactiveIndexManualSetBased: () => OIMReactiveIndexManualSetBased,
58
+ OIMReactiveIndexSetBased: () => OIMReactiveIndexSetBased,
51
59
  OIMUpdateEventCoalescer: () => OIMUpdateEventCoalescer,
52
60
  OIMUpdateEventCoalescerCollection: () => OIMUpdateEventCoalescerCollection,
53
61
  OIMUpdateEventCoalescerIndex: () => OIMUpdateEventCoalescerIndex,
@@ -59,6 +67,14 @@ module.exports = __toCommonJS(index_exports);
59
67
  var OIMCollectionStore = class {
60
68
  };
61
69
 
70
+ // src/abstract/OIMIndexStoreSetBased.ts
71
+ var OIMIndexStoreSetBased = class {
72
+ };
73
+
74
+ // src/abstract/OIMIndexStoreArrayBased.ts
75
+ var OIMIndexStoreArrayBased = class {
76
+ };
77
+
62
78
  // src/core/OIMEventEmitter.ts
63
79
  var OIMEventEmitter = class {
64
80
  constructor() {
@@ -184,12 +200,8 @@ var EOIMIndexEventType = /* @__PURE__ */ ((EOIMIndexEventType2) => {
184
200
  return EOIMIndexEventType2;
185
201
  })(EOIMIndexEventType || {});
186
202
 
187
- // src/abstract/OIMIndexStore.ts
188
- var OIMIndexStore = class {
189
- };
190
-
191
- // src/core/OIMIndexStoreMapDriven.ts
192
- var OIMIndexStoreMapDriven = class extends OIMIndexStore {
203
+ // src/core/OIMIndexStoreMapDrivenSetBased.ts
204
+ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
193
205
  constructor() {
194
206
  super(...arguments);
195
207
  this.pks = /* @__PURE__ */ new Map();
@@ -232,12 +244,12 @@ var OIMIndexStoreMapDriven = class extends OIMIndexStore {
232
244
  }
233
245
  };
234
246
 
235
- // src/abstract/OIMIndex.ts
236
- var OIMIndex = class {
247
+ // src/abstract/OIMIndexSetBased.ts
248
+ var OIMIndexSetBased = class {
237
249
  constructor(options = {}) {
238
250
  this.emitter = new OIMEventEmitter();
239
251
  this.comparePks = options.comparePks;
240
- this.store = options.store ?? new OIMIndexStoreMapDriven();
252
+ this.store = options.store ?? new OIMIndexStoreMapDrivenSetBased();
241
253
  }
242
254
  /**
243
255
  * Get primary keys for multiple index keys
@@ -346,6 +358,155 @@ var OIMIndex = class {
346
358
  }
347
359
  };
348
360
 
361
+ // src/core/OIMIndexStoreMapDrivenArrayBased.ts
362
+ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
363
+ constructor() {
364
+ super(...arguments);
365
+ this.pks = /* @__PURE__ */ new Map();
366
+ }
367
+ setOneByKey(key, pks) {
368
+ this.pks.set(key, pks);
369
+ }
370
+ removeOneByKey(key) {
371
+ this.pks.delete(key);
372
+ }
373
+ removeManyByKeys(keys) {
374
+ for (const key of keys) {
375
+ this.pks.delete(key);
376
+ }
377
+ }
378
+ getOneByKey(key) {
379
+ return this.pks.get(key);
380
+ }
381
+ getManyByKeys(keys) {
382
+ const result = /* @__PURE__ */ new Map();
383
+ for (const key of keys) {
384
+ const pks = this.getOneByKey(key);
385
+ if (pks !== void 0) {
386
+ result.set(key, pks);
387
+ }
388
+ }
389
+ return result;
390
+ }
391
+ getAllKeys() {
392
+ return Array.from(this.pks.keys());
393
+ }
394
+ getAll() {
395
+ return new Map(this.pks);
396
+ }
397
+ countAll() {
398
+ return this.pks.size;
399
+ }
400
+ clear() {
401
+ this.pks.clear();
402
+ }
403
+ };
404
+
405
+ // src/abstract/OIMIndexArrayBased.ts
406
+ var OIMIndexArrayBased = class {
407
+ constructor(options = {}) {
408
+ this.emitter = new OIMEventEmitter();
409
+ this.comparePks = options.comparePks;
410
+ this.store = options.store ?? new OIMIndexStoreMapDrivenArrayBased();
411
+ }
412
+ /**
413
+ * Get primary keys for multiple index keys
414
+ */
415
+ getPksByKeys(keys) {
416
+ return this.store.getManyByKeys(keys);
417
+ }
418
+ /**
419
+ * Get primary keys for a specific index key
420
+ */
421
+ getPksByKey(key) {
422
+ const pksArray = this.store.getOneByKey(key);
423
+ return pksArray ? pksArray : [];
424
+ }
425
+ /**
426
+ * Check if an index key exists
427
+ */
428
+ hasKey(key) {
429
+ return this.store.getOneByKey(key) !== void 0;
430
+ }
431
+ /**
432
+ * Get all index keys
433
+ */
434
+ getKeys() {
435
+ return this.store.getAllKeys();
436
+ }
437
+ /**
438
+ * Get the number of primary keys for a specific index key
439
+ */
440
+ getKeySize(key) {
441
+ const pksArray = this.store.getOneByKey(key);
442
+ return pksArray ? pksArray.length : 0;
443
+ }
444
+ /**
445
+ * Get total number of index keys
446
+ */
447
+ get size() {
448
+ return this.store.countAll();
449
+ }
450
+ /**
451
+ * Check if the index is empty
452
+ */
453
+ get isEmpty() {
454
+ return this.store.countAll() === 0;
455
+ }
456
+ /**
457
+ * Get performance metrics for monitoring and debugging
458
+ */
459
+ getMetrics() {
460
+ let totalPks = 0;
461
+ let maxBucketSize = 0;
462
+ let minBucketSize = Infinity;
463
+ const allPks = this.store.getAll();
464
+ for (const pksArray of allPks.values()) {
465
+ totalPks += pksArray.length;
466
+ maxBucketSize = Math.max(maxBucketSize, pksArray.length);
467
+ minBucketSize = Math.min(minBucketSize, pksArray.length);
468
+ }
469
+ return {
470
+ totalKeys: allPks.size,
471
+ totalPks,
472
+ averagePksPerKey: allPks.size > 0 ? totalPks / allPks.size : 0,
473
+ maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
474
+ minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
475
+ };
476
+ }
477
+ /**
478
+ * Clean up event listeners when index is no longer needed
479
+ */
480
+ destroy() {
481
+ this.emitter.offAll();
482
+ this.store.clear();
483
+ }
484
+ /**
485
+ * Set primary keys for a specific index key with optional comparison.
486
+ * If comparator is provided and returns true (no changes), skip the update.
487
+ */
488
+ setPksWithComparison(key, newPks) {
489
+ if (this.comparePks) {
490
+ const existingPksArray = this.store.getOneByKey(key);
491
+ if (existingPksArray && existingPksArray.length === newPks.length) {
492
+ if (this.comparePks(existingPksArray, newPks)) {
493
+ return false;
494
+ }
495
+ } else if (!existingPksArray && newPks.length === 0) {
496
+ return false;
497
+ }
498
+ }
499
+ this.store.setOneByKey(key, newPks);
500
+ return true;
501
+ }
502
+ /**
503
+ * Emit update event with changed keys
504
+ */
505
+ emitUpdate(keys) {
506
+ this.emitter.emit(0 /* UPDATE */, { keys });
507
+ }
508
+ };
509
+
349
510
  // src/enum/EOIMUpdateEventCoalescerEventType.ts
350
511
  var EOIMUpdateEventCoalescerEventType = /* @__PURE__ */ ((EOIMUpdateEventCoalescerEventType2) => {
351
512
  EOIMUpdateEventCoalescerEventType2[EOIMUpdateEventCoalescerEventType2["HAS_CHANGES"] = 0] = "HAS_CHANGES";
@@ -472,8 +633,8 @@ var OIMUpdateEventEmitter = class {
472
633
  }
473
634
  };
474
635
 
475
- // src/core/OIMIndexManual.ts
476
- var OIMIndexManual = class extends OIMIndex {
636
+ // src/core/OIMIndexManualSetBased.ts
637
+ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
477
638
  constructor(options = {}) {
478
639
  super(options);
479
640
  }
@@ -565,8 +726,8 @@ var OIMUpdateEventCoalescer = class {
565
726
  constructor(index) {
566
727
  this.emitter = new OIMEventEmitter();
567
728
  this.hasEmittedChanges = false;
568
- this.updatedKeysIndex = index ?? new OIMIndexManual({
569
- store: new OIMIndexStoreMapDriven()
729
+ this.updatedKeysIndex = index ?? new OIMIndexManualSetBased({
730
+ store: new OIMIndexStoreMapDrivenSetBased()
570
731
  });
571
732
  if (!this.updatedKeysIndex.hasKey(OIMDBSettings.UPDATED_KEYS_INDEX_KEY)) {
572
733
  this.updatedKeysIndex.setPks(
@@ -636,8 +797,49 @@ var OIMUpdateEventCoalescerIndex = class extends OIMUpdateEventCoalescer {
636
797
  }
637
798
  };
638
799
 
639
- // src/abstract/OIMReactiveIndex.ts
640
- var OIMReactiveIndex = class {
800
+ // src/abstract/OIMReactiveIndexSetBased.ts
801
+ var OIMReactiveIndexSetBased = class {
802
+ constructor(queue, opts) {
803
+ this.index = opts?.index ?? this.createDefaultIndex();
804
+ this.coalescer = new OIMUpdateEventCoalescerIndex(
805
+ this.index.emitter
806
+ );
807
+ this.updateEventEmitter = new OIMUpdateEventEmitter({
808
+ coalescer: this.coalescer,
809
+ queue
810
+ });
811
+ }
812
+ getPksByKey(key) {
813
+ return this.index.getPksByKey(key);
814
+ }
815
+ getPksByKeys(keys) {
816
+ return this.index.getPksByKeys(keys);
817
+ }
818
+ hasKey(key) {
819
+ return this.index.hasKey(key);
820
+ }
821
+ getKeys() {
822
+ return this.index.getKeys();
823
+ }
824
+ getKeySize(key) {
825
+ return this.index.getKeySize(key);
826
+ }
827
+ get size() {
828
+ return this.index.size;
829
+ }
830
+ get isEmpty() {
831
+ return this.index.isEmpty;
832
+ }
833
+ getMetrics() {
834
+ return this.index.getMetrics();
835
+ }
836
+ destroy() {
837
+ this.index.destroy();
838
+ }
839
+ };
840
+
841
+ // src/abstract/OIMReactiveIndexArrayBased.ts
842
+ var OIMReactiveIndexArrayBased = class {
641
843
  constructor(queue, opts) {
642
844
  this.index = opts?.index ?? this.createDefaultIndex();
643
845
  this.coalescer = new OIMUpdateEventCoalescerIndex(
@@ -873,13 +1075,111 @@ var OIMReactiveCollection = class extends OIMCollection {
873
1075
  }
874
1076
  };
875
1077
 
876
- // src/core/OIMReactiveIndexManual.ts
877
- var OIMReactiveIndexManual = class extends OIMReactiveIndex {
1078
+ // src/core/OIMReactiveIndexManualSetBased.ts
1079
+ var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1080
+ constructor(queue, opts) {
1081
+ super(queue, opts);
1082
+ }
1083
+ createDefaultIndex() {
1084
+ return new OIMIndexManualSetBased();
1085
+ }
1086
+ setPks(key, pks) {
1087
+ this.index.setPks(key, pks);
1088
+ }
1089
+ addPks(key, pks) {
1090
+ this.index.addPks(key, pks);
1091
+ }
1092
+ removePks(key, pks) {
1093
+ this.index.removePks(key, pks);
1094
+ }
1095
+ clear(key) {
1096
+ this.index.clear(key);
1097
+ }
1098
+ };
1099
+
1100
+ // src/core/OIMIndexManualArrayBased.ts
1101
+ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1102
+ constructor(options = {}) {
1103
+ super(options);
1104
+ }
1105
+ /**
1106
+ * Set primary keys for a specific index key, replacing any existing values.
1107
+ * Uses optional comparator to skip updates if PKs haven't actually changed.
1108
+ */
1109
+ setPks(key, pks) {
1110
+ const hasChanges = this.setPksWithComparison(key, pks);
1111
+ if (hasChanges) {
1112
+ this.emitUpdate([key]);
1113
+ }
1114
+ }
1115
+ /**
1116
+ * Add primary keys to a specific index key
1117
+ */
1118
+ addPks(key, pks) {
1119
+ if (pks.length === 0) return;
1120
+ let pksArray = this.store.getOneByKey(key);
1121
+ if (!pksArray) {
1122
+ pksArray = [];
1123
+ this.store.setOneByKey(key, pksArray);
1124
+ }
1125
+ const pksSet = new Set(pksArray);
1126
+ let hasChanges = false;
1127
+ for (const pk of pks) {
1128
+ if (!pksSet.has(pk)) {
1129
+ pksSet.add(pk);
1130
+ hasChanges = true;
1131
+ }
1132
+ }
1133
+ if (hasChanges) {
1134
+ this.store.setOneByKey(key, Array.from(pksSet));
1135
+ this.emitUpdate([key]);
1136
+ }
1137
+ }
1138
+ /**
1139
+ * Remove primary keys from a specific index key
1140
+ */
1141
+ removePks(key, pks) {
1142
+ if (pks.length === 0) return;
1143
+ const pksArray = this.store.getOneByKey(key);
1144
+ if (!pksArray) return;
1145
+ const pksSet = new Set(pks);
1146
+ const filtered = pksArray.filter((pk) => !pksSet.has(pk));
1147
+ const hasChanges = filtered.length !== pksArray.length;
1148
+ if (hasChanges) {
1149
+ if (filtered.length === 0) {
1150
+ this.store.removeOneByKey(key);
1151
+ } else {
1152
+ this.store.setOneByKey(key, filtered);
1153
+ }
1154
+ this.emitUpdate([key]);
1155
+ }
1156
+ }
1157
+ /**
1158
+ * Clear all primary keys for a specific index key, or all keys if no key specified
1159
+ */
1160
+ clear(key) {
1161
+ if (key === void 0) {
1162
+ const allKeys = this.store.getAllKeys();
1163
+ if (allKeys.length > 0) {
1164
+ this.store.clear();
1165
+ this.emitUpdate(allKeys);
1166
+ }
1167
+ } else {
1168
+ if (this.store.getOneByKey(key) !== void 0) {
1169
+ this.store.removeOneByKey(key);
1170
+ this.emitUpdate([key]);
1171
+ }
1172
+ }
1173
+ }
1174
+ };
1175
+
1176
+ // src/core/OIMReactiveIndexManualArrayBased.ts
1177
+ var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
878
1178
  constructor(queue, opts) {
879
1179
  super(queue, opts);
880
1180
  }
881
1181
  createDefaultIndex() {
882
- return new OIMIndexManual();
1182
+ return new OIMIndexManualArrayBased();
883
1183
  }
884
1184
  setPks(key, pks) {
885
1185
  this.index.setPks(key, pks);
@@ -1287,15 +1587,23 @@ var OIMEventQueueSchedulerFactory = class {
1287
1587
  OIMEventQueueSchedulerImmediate,
1288
1588
  OIMEventQueueSchedulerMicrotask,
1289
1589
  OIMEventQueueSchedulerTimeout,
1290
- OIMIndex,
1590
+ OIMIndexArrayBased,
1291
1591
  OIMIndexComparatorFactory,
1292
- OIMIndexManual,
1592
+ OIMIndexManualArrayBased,
1593
+ OIMIndexManualSetBased,
1594
+ OIMIndexSetBased,
1595
+ OIMIndexStoreArrayBased,
1596
+ OIMIndexStoreMapDrivenArrayBased,
1597
+ OIMIndexStoreMapDrivenSetBased,
1598
+ OIMIndexStoreSetBased,
1293
1599
  OIMMap2Keys,
1294
1600
  OIMPkSelectorFactory,
1295
1601
  OIMRICollection,
1296
1602
  OIMReactiveCollection,
1297
- OIMReactiveIndex,
1298
- OIMReactiveIndexManual,
1603
+ OIMReactiveIndexArrayBased,
1604
+ OIMReactiveIndexManualArrayBased,
1605
+ OIMReactiveIndexManualSetBased,
1606
+ OIMReactiveIndexSetBased,
1299
1607
  OIMUpdateEventCoalescer,
1300
1608
  OIMUpdateEventCoalescerCollection,
1301
1609
  OIMUpdateEventCoalescerIndex,