@oimdb/core 1.2.2 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -27
- package/dist/index.cjs +341 -29
- package/dist/index.d.cts +225 -38
- package/dist/index.d.ts +225 -38
- package/dist/index.js +329 -25
- 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() {
|
|
@@ -102,10 +118,14 @@ var OIMEventEmitter = class {
|
|
|
102
118
|
}
|
|
103
119
|
on(event, handler) {
|
|
104
120
|
const bucket = this.getOrCreateBucket(event);
|
|
105
|
-
if (bucket.indexByHandler.has(handler))
|
|
121
|
+
if (bucket.indexByHandler.has(handler))
|
|
122
|
+
return;
|
|
106
123
|
const index = bucket.handlers.length;
|
|
107
124
|
bucket.handlers.push(handler);
|
|
108
|
-
bucket.indexByHandler.set(
|
|
125
|
+
bucket.indexByHandler.set(
|
|
126
|
+
handler,
|
|
127
|
+
index
|
|
128
|
+
);
|
|
109
129
|
}
|
|
110
130
|
emit(event, payload) {
|
|
111
131
|
const bucket = this.buckets[event];
|
|
@@ -184,12 +204,8 @@ var EOIMIndexEventType = /* @__PURE__ */ ((EOIMIndexEventType2) => {
|
|
|
184
204
|
return EOIMIndexEventType2;
|
|
185
205
|
})(EOIMIndexEventType || {});
|
|
186
206
|
|
|
187
|
-
// src/
|
|
188
|
-
var
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
// src/core/OIMIndexStoreMapDriven.ts
|
|
192
|
-
var OIMIndexStoreMapDriven = class extends OIMIndexStore {
|
|
207
|
+
// src/core/OIMIndexStoreMapDrivenSetBased.ts
|
|
208
|
+
var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
|
|
193
209
|
constructor() {
|
|
194
210
|
super(...arguments);
|
|
195
211
|
this.pks = /* @__PURE__ */ new Map();
|
|
@@ -232,12 +248,12 @@ var OIMIndexStoreMapDriven = class extends OIMIndexStore {
|
|
|
232
248
|
}
|
|
233
249
|
};
|
|
234
250
|
|
|
235
|
-
// src/abstract/
|
|
236
|
-
var
|
|
251
|
+
// src/abstract/OIMIndexSetBased.ts
|
|
252
|
+
var OIMIndexSetBased = class {
|
|
237
253
|
constructor(options = {}) {
|
|
238
254
|
this.emitter = new OIMEventEmitter();
|
|
239
255
|
this.comparePks = options.comparePks;
|
|
240
|
-
this.store = options.store ?? new
|
|
256
|
+
this.store = options.store ?? new OIMIndexStoreMapDrivenSetBased();
|
|
241
257
|
}
|
|
242
258
|
/**
|
|
243
259
|
* Get primary keys for multiple index keys
|
|
@@ -346,6 +362,155 @@ var OIMIndex = class {
|
|
|
346
362
|
}
|
|
347
363
|
};
|
|
348
364
|
|
|
365
|
+
// src/core/OIMIndexStoreMapDrivenArrayBased.ts
|
|
366
|
+
var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
|
|
367
|
+
constructor() {
|
|
368
|
+
super(...arguments);
|
|
369
|
+
this.pks = /* @__PURE__ */ new Map();
|
|
370
|
+
}
|
|
371
|
+
setOneByKey(key, pks) {
|
|
372
|
+
this.pks.set(key, pks);
|
|
373
|
+
}
|
|
374
|
+
removeOneByKey(key) {
|
|
375
|
+
this.pks.delete(key);
|
|
376
|
+
}
|
|
377
|
+
removeManyByKeys(keys) {
|
|
378
|
+
for (const key of keys) {
|
|
379
|
+
this.pks.delete(key);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
getOneByKey(key) {
|
|
383
|
+
return this.pks.get(key);
|
|
384
|
+
}
|
|
385
|
+
getManyByKeys(keys) {
|
|
386
|
+
const result = /* @__PURE__ */ new Map();
|
|
387
|
+
for (const key of keys) {
|
|
388
|
+
const pks = this.getOneByKey(key);
|
|
389
|
+
if (pks !== void 0) {
|
|
390
|
+
result.set(key, pks);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return result;
|
|
394
|
+
}
|
|
395
|
+
getAllKeys() {
|
|
396
|
+
return Array.from(this.pks.keys());
|
|
397
|
+
}
|
|
398
|
+
getAll() {
|
|
399
|
+
return new Map(this.pks);
|
|
400
|
+
}
|
|
401
|
+
countAll() {
|
|
402
|
+
return this.pks.size;
|
|
403
|
+
}
|
|
404
|
+
clear() {
|
|
405
|
+
this.pks.clear();
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// src/abstract/OIMIndexArrayBased.ts
|
|
410
|
+
var OIMIndexArrayBased = class {
|
|
411
|
+
constructor(options = {}) {
|
|
412
|
+
this.emitter = new OIMEventEmitter();
|
|
413
|
+
this.comparePks = options.comparePks;
|
|
414
|
+
this.store = options.store ?? new OIMIndexStoreMapDrivenArrayBased();
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Get primary keys for multiple index keys
|
|
418
|
+
*/
|
|
419
|
+
getPksByKeys(keys) {
|
|
420
|
+
return this.store.getManyByKeys(keys);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get primary keys for a specific index key
|
|
424
|
+
*/
|
|
425
|
+
getPksByKey(key) {
|
|
426
|
+
const pksArray = this.store.getOneByKey(key);
|
|
427
|
+
return pksArray ? pksArray : [];
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Check if an index key exists
|
|
431
|
+
*/
|
|
432
|
+
hasKey(key) {
|
|
433
|
+
return this.store.getOneByKey(key) !== void 0;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Get all index keys
|
|
437
|
+
*/
|
|
438
|
+
getKeys() {
|
|
439
|
+
return this.store.getAllKeys();
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Get the number of primary keys for a specific index key
|
|
443
|
+
*/
|
|
444
|
+
getKeySize(key) {
|
|
445
|
+
const pksArray = this.store.getOneByKey(key);
|
|
446
|
+
return pksArray ? pksArray.length : 0;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Get total number of index keys
|
|
450
|
+
*/
|
|
451
|
+
get size() {
|
|
452
|
+
return this.store.countAll();
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Check if the index is empty
|
|
456
|
+
*/
|
|
457
|
+
get isEmpty() {
|
|
458
|
+
return this.store.countAll() === 0;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Get performance metrics for monitoring and debugging
|
|
462
|
+
*/
|
|
463
|
+
getMetrics() {
|
|
464
|
+
let totalPks = 0;
|
|
465
|
+
let maxBucketSize = 0;
|
|
466
|
+
let minBucketSize = Infinity;
|
|
467
|
+
const allPks = this.store.getAll();
|
|
468
|
+
for (const pksArray of allPks.values()) {
|
|
469
|
+
totalPks += pksArray.length;
|
|
470
|
+
maxBucketSize = Math.max(maxBucketSize, pksArray.length);
|
|
471
|
+
minBucketSize = Math.min(minBucketSize, pksArray.length);
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
totalKeys: allPks.size,
|
|
475
|
+
totalPks,
|
|
476
|
+
averagePksPerKey: allPks.size > 0 ? totalPks / allPks.size : 0,
|
|
477
|
+
maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
|
|
478
|
+
minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Clean up event listeners when index is no longer needed
|
|
483
|
+
*/
|
|
484
|
+
destroy() {
|
|
485
|
+
this.emitter.offAll();
|
|
486
|
+
this.store.clear();
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Set primary keys for a specific index key with optional comparison.
|
|
490
|
+
* If comparator is provided and returns true (no changes), skip the update.
|
|
491
|
+
*/
|
|
492
|
+
setPksWithComparison(key, newPks) {
|
|
493
|
+
if (this.comparePks) {
|
|
494
|
+
const existingPksArray = this.store.getOneByKey(key);
|
|
495
|
+
if (existingPksArray && existingPksArray.length === newPks.length) {
|
|
496
|
+
if (this.comparePks(existingPksArray, newPks)) {
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
} else if (!existingPksArray && newPks.length === 0) {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
this.store.setOneByKey(key, newPks);
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Emit update event with changed keys
|
|
508
|
+
*/
|
|
509
|
+
emitUpdate(keys) {
|
|
510
|
+
this.emitter.emit(0 /* UPDATE */, { keys });
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
349
514
|
// src/enum/EOIMUpdateEventCoalescerEventType.ts
|
|
350
515
|
var EOIMUpdateEventCoalescerEventType = /* @__PURE__ */ ((EOIMUpdateEventCoalescerEventType2) => {
|
|
351
516
|
EOIMUpdateEventCoalescerEventType2[EOIMUpdateEventCoalescerEventType2["HAS_CHANGES"] = 0] = "HAS_CHANGES";
|
|
@@ -472,8 +637,8 @@ var OIMUpdateEventEmitter = class {
|
|
|
472
637
|
}
|
|
473
638
|
};
|
|
474
639
|
|
|
475
|
-
// src/core/
|
|
476
|
-
var
|
|
640
|
+
// src/core/OIMIndexManualSetBased.ts
|
|
641
|
+
var OIMIndexManualSetBased = class extends OIMIndexSetBased {
|
|
477
642
|
constructor(options = {}) {
|
|
478
643
|
super(options);
|
|
479
644
|
}
|
|
@@ -565,8 +730,8 @@ var OIMUpdateEventCoalescer = class {
|
|
|
565
730
|
constructor(index) {
|
|
566
731
|
this.emitter = new OIMEventEmitter();
|
|
567
732
|
this.hasEmittedChanges = false;
|
|
568
|
-
this.updatedKeysIndex = index ?? new
|
|
569
|
-
store: new
|
|
733
|
+
this.updatedKeysIndex = index ?? new OIMIndexManualSetBased({
|
|
734
|
+
store: new OIMIndexStoreMapDrivenSetBased()
|
|
570
735
|
});
|
|
571
736
|
if (!this.updatedKeysIndex.hasKey(OIMDBSettings.UPDATED_KEYS_INDEX_KEY)) {
|
|
572
737
|
this.updatedKeysIndex.setPks(
|
|
@@ -636,8 +801,49 @@ var OIMUpdateEventCoalescerIndex = class extends OIMUpdateEventCoalescer {
|
|
|
636
801
|
}
|
|
637
802
|
};
|
|
638
803
|
|
|
639
|
-
// src/abstract/
|
|
640
|
-
var
|
|
804
|
+
// src/abstract/OIMReactiveIndexSetBased.ts
|
|
805
|
+
var OIMReactiveIndexSetBased = class {
|
|
806
|
+
constructor(queue, opts) {
|
|
807
|
+
this.index = opts?.index ?? this.createDefaultIndex();
|
|
808
|
+
this.coalescer = new OIMUpdateEventCoalescerIndex(
|
|
809
|
+
this.index.emitter
|
|
810
|
+
);
|
|
811
|
+
this.updateEventEmitter = new OIMUpdateEventEmitter({
|
|
812
|
+
coalescer: this.coalescer,
|
|
813
|
+
queue
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
getPksByKey(key) {
|
|
817
|
+
return this.index.getPksByKey(key);
|
|
818
|
+
}
|
|
819
|
+
getPksByKeys(keys) {
|
|
820
|
+
return this.index.getPksByKeys(keys);
|
|
821
|
+
}
|
|
822
|
+
hasKey(key) {
|
|
823
|
+
return this.index.hasKey(key);
|
|
824
|
+
}
|
|
825
|
+
getKeys() {
|
|
826
|
+
return this.index.getKeys();
|
|
827
|
+
}
|
|
828
|
+
getKeySize(key) {
|
|
829
|
+
return this.index.getKeySize(key);
|
|
830
|
+
}
|
|
831
|
+
get size() {
|
|
832
|
+
return this.index.size;
|
|
833
|
+
}
|
|
834
|
+
get isEmpty() {
|
|
835
|
+
return this.index.isEmpty;
|
|
836
|
+
}
|
|
837
|
+
getMetrics() {
|
|
838
|
+
return this.index.getMetrics();
|
|
839
|
+
}
|
|
840
|
+
destroy() {
|
|
841
|
+
this.index.destroy();
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
// src/abstract/OIMReactiveIndexArrayBased.ts
|
|
846
|
+
var OIMReactiveIndexArrayBased = class {
|
|
641
847
|
constructor(queue, opts) {
|
|
642
848
|
this.index = opts?.index ?? this.createDefaultIndex();
|
|
643
849
|
this.coalescer = new OIMUpdateEventCoalescerIndex(
|
|
@@ -873,13 +1079,111 @@ var OIMReactiveCollection = class extends OIMCollection {
|
|
|
873
1079
|
}
|
|
874
1080
|
};
|
|
875
1081
|
|
|
876
|
-
// src/core/
|
|
877
|
-
var
|
|
1082
|
+
// src/core/OIMReactiveIndexManualSetBased.ts
|
|
1083
|
+
var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
|
|
1084
|
+
constructor(queue, opts) {
|
|
1085
|
+
super(queue, opts);
|
|
1086
|
+
}
|
|
1087
|
+
createDefaultIndex() {
|
|
1088
|
+
return new OIMIndexManualSetBased();
|
|
1089
|
+
}
|
|
1090
|
+
setPks(key, pks) {
|
|
1091
|
+
this.index.setPks(key, pks);
|
|
1092
|
+
}
|
|
1093
|
+
addPks(key, pks) {
|
|
1094
|
+
this.index.addPks(key, pks);
|
|
1095
|
+
}
|
|
1096
|
+
removePks(key, pks) {
|
|
1097
|
+
this.index.removePks(key, pks);
|
|
1098
|
+
}
|
|
1099
|
+
clear(key) {
|
|
1100
|
+
this.index.clear(key);
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
// src/core/OIMIndexManualArrayBased.ts
|
|
1105
|
+
var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
|
|
1106
|
+
constructor(options = {}) {
|
|
1107
|
+
super(options);
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Set primary keys for a specific index key, replacing any existing values.
|
|
1111
|
+
* Uses optional comparator to skip updates if PKs haven't actually changed.
|
|
1112
|
+
*/
|
|
1113
|
+
setPks(key, pks) {
|
|
1114
|
+
const hasChanges = this.setPksWithComparison(key, pks);
|
|
1115
|
+
if (hasChanges) {
|
|
1116
|
+
this.emitUpdate([key]);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Add primary keys to a specific index key
|
|
1121
|
+
*/
|
|
1122
|
+
addPks(key, pks) {
|
|
1123
|
+
if (pks.length === 0) return;
|
|
1124
|
+
let pksArray = this.store.getOneByKey(key);
|
|
1125
|
+
if (!pksArray) {
|
|
1126
|
+
pksArray = [];
|
|
1127
|
+
this.store.setOneByKey(key, pksArray);
|
|
1128
|
+
}
|
|
1129
|
+
const pksSet = new Set(pksArray);
|
|
1130
|
+
let hasChanges = false;
|
|
1131
|
+
for (const pk of pks) {
|
|
1132
|
+
if (!pksSet.has(pk)) {
|
|
1133
|
+
pksSet.add(pk);
|
|
1134
|
+
hasChanges = true;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
if (hasChanges) {
|
|
1138
|
+
this.store.setOneByKey(key, Array.from(pksSet));
|
|
1139
|
+
this.emitUpdate([key]);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Remove primary keys from a specific index key
|
|
1144
|
+
*/
|
|
1145
|
+
removePks(key, pks) {
|
|
1146
|
+
if (pks.length === 0) return;
|
|
1147
|
+
const pksArray = this.store.getOneByKey(key);
|
|
1148
|
+
if (!pksArray) return;
|
|
1149
|
+
const pksSet = new Set(pks);
|
|
1150
|
+
const filtered = pksArray.filter((pk) => !pksSet.has(pk));
|
|
1151
|
+
const hasChanges = filtered.length !== pksArray.length;
|
|
1152
|
+
if (hasChanges) {
|
|
1153
|
+
if (filtered.length === 0) {
|
|
1154
|
+
this.store.removeOneByKey(key);
|
|
1155
|
+
} else {
|
|
1156
|
+
this.store.setOneByKey(key, filtered);
|
|
1157
|
+
}
|
|
1158
|
+
this.emitUpdate([key]);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Clear all primary keys for a specific index key, or all keys if no key specified
|
|
1163
|
+
*/
|
|
1164
|
+
clear(key) {
|
|
1165
|
+
if (key === void 0) {
|
|
1166
|
+
const allKeys = this.store.getAllKeys();
|
|
1167
|
+
if (allKeys.length > 0) {
|
|
1168
|
+
this.store.clear();
|
|
1169
|
+
this.emitUpdate(allKeys);
|
|
1170
|
+
}
|
|
1171
|
+
} else {
|
|
1172
|
+
if (this.store.getOneByKey(key) !== void 0) {
|
|
1173
|
+
this.store.removeOneByKey(key);
|
|
1174
|
+
this.emitUpdate([key]);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
|
|
1180
|
+
// src/core/OIMReactiveIndexManualArrayBased.ts
|
|
1181
|
+
var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
|
|
878
1182
|
constructor(queue, opts) {
|
|
879
1183
|
super(queue, opts);
|
|
880
1184
|
}
|
|
881
1185
|
createDefaultIndex() {
|
|
882
|
-
return new
|
|
1186
|
+
return new OIMIndexManualArrayBased();
|
|
883
1187
|
}
|
|
884
1188
|
setPks(key, pks) {
|
|
885
1189
|
this.index.setPks(key, pks);
|
|
@@ -899,7 +1203,7 @@ var OIMReactiveIndexManual = class extends OIMReactiveIndex {
|
|
|
899
1203
|
var OIMRICollection = class extends OIMReactiveCollection {
|
|
900
1204
|
constructor(queue, opts) {
|
|
901
1205
|
super(queue, opts.collectionOpts);
|
|
902
|
-
this.indexes = opts.indexes;
|
|
1206
|
+
this.indexes = opts.indexes || {};
|
|
903
1207
|
}
|
|
904
1208
|
};
|
|
905
1209
|
|
|
@@ -1287,15 +1591,23 @@ var OIMEventQueueSchedulerFactory = class {
|
|
|
1287
1591
|
OIMEventQueueSchedulerImmediate,
|
|
1288
1592
|
OIMEventQueueSchedulerMicrotask,
|
|
1289
1593
|
OIMEventQueueSchedulerTimeout,
|
|
1290
|
-
|
|
1594
|
+
OIMIndexArrayBased,
|
|
1291
1595
|
OIMIndexComparatorFactory,
|
|
1292
|
-
|
|
1596
|
+
OIMIndexManualArrayBased,
|
|
1597
|
+
OIMIndexManualSetBased,
|
|
1598
|
+
OIMIndexSetBased,
|
|
1599
|
+
OIMIndexStoreArrayBased,
|
|
1600
|
+
OIMIndexStoreMapDrivenArrayBased,
|
|
1601
|
+
OIMIndexStoreMapDrivenSetBased,
|
|
1602
|
+
OIMIndexStoreSetBased,
|
|
1293
1603
|
OIMMap2Keys,
|
|
1294
1604
|
OIMPkSelectorFactory,
|
|
1295
1605
|
OIMRICollection,
|
|
1296
1606
|
OIMReactiveCollection,
|
|
1297
|
-
|
|
1298
|
-
|
|
1607
|
+
OIMReactiveIndexArrayBased,
|
|
1608
|
+
OIMReactiveIndexManualArrayBased,
|
|
1609
|
+
OIMReactiveIndexManualSetBased,
|
|
1610
|
+
OIMReactiveIndexSetBased,
|
|
1299
1611
|
OIMUpdateEventCoalescer,
|
|
1300
1612
|
OIMUpdateEventCoalescerCollection,
|
|
1301
1613
|
OIMUpdateEventCoalescerIndex,
|