@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 +116 -27
- package/dist/index.cjs +334 -26
- package/dist/index.d.cts +222 -36
- package/dist/index.d.ts +222 -36
- package/dist/index.js +322 -22
- package/package.json +13 -1
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
|
-
- **
|
|
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
|
-
- **
|
|
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
|
-
- **
|
|
37
|
-
- **
|
|
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 {
|
|
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
|
|
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
|
|
192
|
-
const roleIndex = new
|
|
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
|
-
|
|
338
|
-
├──
|
|
339
|
-
└──
|
|
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
|
-
#### `
|
|
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
|
|
471
|
-
index?:
|
|
554
|
+
new OIMReactiveIndexManualArrayBased(queue: OIMEventQueue, opts?: {
|
|
555
|
+
index?: OIMIndexManualArrayBased<TKey, TPk>
|
|
472
556
|
})
|
|
473
557
|
```
|
|
474
558
|
|
|
475
559
|
**Properties:**
|
|
476
|
-
- `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
|
-
|
|
42
|
+
OIMIndexArrayBased: () => OIMIndexArrayBased,
|
|
43
43
|
OIMIndexComparatorFactory: () => OIMIndexComparatorFactory,
|
|
44
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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/
|
|
188
|
-
var
|
|
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/
|
|
236
|
-
var
|
|
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
|
|
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/
|
|
476
|
-
var
|
|
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
|
|
569
|
-
store: new
|
|
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/
|
|
640
|
-
var
|
|
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/
|
|
877
|
-
var
|
|
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
|
|
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
|
-
|
|
1590
|
+
OIMIndexArrayBased,
|
|
1291
1591
|
OIMIndexComparatorFactory,
|
|
1292
|
-
|
|
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
|
-
|
|
1298
|
-
|
|
1603
|
+
OIMReactiveIndexArrayBased,
|
|
1604
|
+
OIMReactiveIndexManualArrayBased,
|
|
1605
|
+
OIMReactiveIndexManualSetBased,
|
|
1606
|
+
OIMReactiveIndexSetBased,
|
|
1299
1607
|
OIMUpdateEventCoalescer,
|
|
1300
1608
|
OIMUpdateEventCoalescerCollection,
|
|
1301
1609
|
OIMUpdateEventCoalescerIndex,
|