@oimdb/core 3.4.0 → 3.6.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 +46 -46
- package/dist/index.cjs +1424 -146
- package/dist/index.d.cts +741 -29
- package/dist/index.d.ts +741 -29
- package/dist/index.js +1397 -145
- package/llms.txt +82 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Core in-memory data library providing reactive collections, intelligent indexing, and configurable event processing.
|
|
4
4
|
|
|
5
|
-
📖 **[Full documentation](https://
|
|
5
|
+
📖 **[Full documentation](https://oimdb.org/)**
|
|
6
6
|
|
|
7
7
|
## For AI Assistants
|
|
8
8
|
|
|
@@ -40,7 +40,7 @@ This package exports all the core classes, interfaces, and types needed to build
|
|
|
40
40
|
|
|
41
41
|
### Core Classes
|
|
42
42
|
- **OIMReactiveCollection**: Reactive entity storage with automatic change notifications
|
|
43
|
-
- **
|
|
43
|
+
- **OIMCollectionIndexFactory**: Helper for creating collection-bound indexes and ordered lists next to a collection
|
|
44
44
|
- **OIMReactiveIndexManualSetBased**: Reactive index with Set-based storage (efficient for incremental updates)
|
|
45
45
|
- **OIMReactiveIndexManualArrayBased**: Reactive index with Array-based storage (efficient for full replacements)
|
|
46
46
|
- **OIMReactiveCollectionIndexManualSetBased**: Collection-bound Set-based index with safe PK-oriented writes
|
|
@@ -452,11 +452,11 @@ indexes.usersByRole.setPks('admin', ['u1']);
|
|
|
452
452
|
|
|
453
453
|
### Collection Relations Helper
|
|
454
454
|
|
|
455
|
-
Use `
|
|
455
|
+
Use `createOIMCollectionIndexFactory` when you want the same clean model with less constructor noise. It does not store indexes inside the collection; it only keeps the shared `queue + collection` binding for related structures that live next to the collection.
|
|
456
456
|
|
|
457
457
|
```typescript
|
|
458
458
|
import {
|
|
459
|
-
|
|
459
|
+
createOIMCollectionIndexFactory,
|
|
460
460
|
OIMReactiveCollection,
|
|
461
461
|
OIMEventQueue
|
|
462
462
|
} from '@oimdb/core';
|
|
@@ -477,11 +477,11 @@ const queue = new OIMEventQueue();
|
|
|
477
477
|
const users = new OIMReactiveCollection<User, string>(queue, {
|
|
478
478
|
selectPk: user => user.id,
|
|
479
479
|
});
|
|
480
|
-
const userRelations =
|
|
480
|
+
const userRelations = createOIMCollectionIndexFactory(queue, users);
|
|
481
481
|
const cards = new OIMReactiveCollection<Card, string>(queue, {
|
|
482
482
|
selectPk: card => card.id,
|
|
483
483
|
});
|
|
484
|
-
const cardRelations =
|
|
484
|
+
const cardRelations = createOIMCollectionIndexFactory(queue, cards);
|
|
485
485
|
|
|
486
486
|
// Derived indexes update themselves from collection writes.
|
|
487
487
|
const usersByTeam = userRelations.derivedSetIndex(user => user.teamId);
|
|
@@ -638,7 +638,7 @@ users.upsertOne({ id: 'user1', role: 'admin' });
|
|
|
638
638
|
|
|
639
639
|
OIMDB uses a single-pass flush boundary: `queue.flush()` executes the current batch of pending work.
|
|
640
640
|
|
|
641
|
-
Effects and computed values are scheduled through `
|
|
641
|
+
Effects and computed values are scheduled through `OIMComputeRuntime`, which is backed by the same queue. This keeps the public API simple and avoids a multi-phase flush model.
|
|
642
642
|
|
|
643
643
|
#### What is an Effect?
|
|
644
644
|
|
|
@@ -649,7 +649,7 @@ Effects and computed values are scheduled through `OIMComputativeRuntime`, which
|
|
|
649
649
|
```typescript
|
|
650
650
|
import {
|
|
651
651
|
OIMEffect,
|
|
652
|
-
|
|
652
|
+
OIMComputeRuntime,
|
|
653
653
|
OIMEventQueue,
|
|
654
654
|
OIMReactiveObject,
|
|
655
655
|
OIMEffectDependencyKeyedObject,
|
|
@@ -658,7 +658,7 @@ import {
|
|
|
658
658
|
type TKey = 'a';
|
|
659
659
|
|
|
660
660
|
const queue = new OIMEventQueue();
|
|
661
|
-
const runtime = new
|
|
661
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
662
662
|
const obj = new OIMReactiveObject<TKey, number>(queue);
|
|
663
663
|
|
|
664
664
|
const effect = new OIMEffect(runtime, {
|
|
@@ -681,7 +681,7 @@ queue.destroy();
|
|
|
681
681
|
```typescript
|
|
682
682
|
import {
|
|
683
683
|
OIMEffect,
|
|
684
|
-
|
|
684
|
+
OIMComputeRuntime,
|
|
685
685
|
OIMEventQueue,
|
|
686
686
|
OIMReactiveCollection,
|
|
687
687
|
OIMEffectDependencyKeyedCollection,
|
|
@@ -693,7 +693,7 @@ interface User {
|
|
|
693
693
|
}
|
|
694
694
|
|
|
695
695
|
const queue = new OIMEventQueue();
|
|
696
|
-
const runtime = new
|
|
696
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
697
697
|
const users = new OIMReactiveCollection<User, string>(queue, {
|
|
698
698
|
selectPk: (u) => u.id,
|
|
699
699
|
});
|
|
@@ -719,7 +719,7 @@ queue.destroy();
|
|
|
719
719
|
```typescript
|
|
720
720
|
import {
|
|
721
721
|
OIMEffect,
|
|
722
|
-
|
|
722
|
+
OIMComputeRuntime,
|
|
723
723
|
OIMEventQueue,
|
|
724
724
|
OIMReactiveCollection,
|
|
725
725
|
OIMReactiveCollectionIndexManualSetBased,
|
|
@@ -732,7 +732,7 @@ interface User {
|
|
|
732
732
|
}
|
|
733
733
|
|
|
734
734
|
const queue = new OIMEventQueue();
|
|
735
|
-
const runtime = new
|
|
735
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
736
736
|
const users = new OIMReactiveCollection<User, string>(queue, {
|
|
737
737
|
selectPk: (u) => u.id,
|
|
738
738
|
});
|
|
@@ -767,7 +767,7 @@ queue.destroy();
|
|
|
767
767
|
```typescript
|
|
768
768
|
import {
|
|
769
769
|
OIMEffect,
|
|
770
|
-
|
|
770
|
+
OIMComputeRuntime,
|
|
771
771
|
OIMEventQueue,
|
|
772
772
|
OIMReactiveObject,
|
|
773
773
|
OIMReactiveCollection,
|
|
@@ -776,7 +776,7 @@ import {
|
|
|
776
776
|
} from '@oimdb/core';
|
|
777
777
|
|
|
778
778
|
const queue = new OIMEventQueue();
|
|
779
|
-
const runtime = new
|
|
779
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
780
780
|
const settings = new OIMReactiveObject<'theme' | 'lang', string>(queue);
|
|
781
781
|
const users = new OIMReactiveCollection<User, string>(queue, {
|
|
782
782
|
selectPk: (u) => u.id,
|
|
@@ -814,14 +814,14 @@ queue.destroy();
|
|
|
814
814
|
import {
|
|
815
815
|
OIMComputed,
|
|
816
816
|
OIMEventQueue,
|
|
817
|
-
|
|
817
|
+
OIMComputeRuntime,
|
|
818
818
|
OIMReactiveObject,
|
|
819
819
|
OIMEffectDependencyKeyedObject,
|
|
820
820
|
} from '@oimdb/core';
|
|
821
821
|
|
|
822
822
|
type TKey = 'a';
|
|
823
823
|
const queue = new OIMEventQueue();
|
|
824
|
-
const runtime = new
|
|
824
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
825
825
|
const obj = new OIMReactiveObject<TKey, number>(queue);
|
|
826
826
|
|
|
827
827
|
const doubled = new OIMComputed<number>(runtime, {
|
|
@@ -854,7 +854,7 @@ queue.destroy();
|
|
|
854
854
|
import {
|
|
855
855
|
OIMComputed,
|
|
856
856
|
OIMEventQueue,
|
|
857
|
-
|
|
857
|
+
OIMComputeRuntime,
|
|
858
858
|
OIMReactiveCollection,
|
|
859
859
|
OIMReactiveCollectionIndexManualSetBased,
|
|
860
860
|
OIMEffectDependencyKeyedCollection,
|
|
@@ -868,7 +868,7 @@ interface User {
|
|
|
868
868
|
}
|
|
869
869
|
|
|
870
870
|
const queue = new OIMEventQueue();
|
|
871
|
-
const runtime = new
|
|
871
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
872
872
|
const users = new OIMReactiveCollection<User, string>(queue, {
|
|
873
873
|
selectPk: (u) => u.id,
|
|
874
874
|
});
|
|
@@ -925,7 +925,7 @@ For computed-to-computed dependencies you can use `OIMEffectDependencyComputed`.
|
|
|
925
925
|
import {
|
|
926
926
|
OIMComputed,
|
|
927
927
|
OIMEffect,
|
|
928
|
-
|
|
928
|
+
OIMComputeRuntime,
|
|
929
929
|
OIMEffectDependencyComputed,
|
|
930
930
|
OIMEventQueue,
|
|
931
931
|
OIMReactiveObject,
|
|
@@ -934,7 +934,7 @@ import {
|
|
|
934
934
|
|
|
935
935
|
type TKey = 'a';
|
|
936
936
|
const queue = new OIMEventQueue();
|
|
937
|
-
const runtime = new
|
|
937
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
938
938
|
const obj = new OIMReactiveObject<TKey, number>(queue);
|
|
939
939
|
|
|
940
940
|
const A = new OIMComputed<number>(runtime, {
|
|
@@ -972,7 +972,7 @@ Selectors provide a convenient way to watch and react to changes in collections,
|
|
|
972
972
|
import {
|
|
973
973
|
OIMCollectionByPkSelector,
|
|
974
974
|
OIMCollectionByPksSelector,
|
|
975
|
-
|
|
975
|
+
OIMComputeRuntime,
|
|
976
976
|
OIMEventQueue,
|
|
977
977
|
OIMReactiveCollection,
|
|
978
978
|
} from '@oimdb/core';
|
|
@@ -983,7 +983,7 @@ interface User {
|
|
|
983
983
|
}
|
|
984
984
|
|
|
985
985
|
const queue = new OIMEventQueue();
|
|
986
|
-
const runtime = new
|
|
986
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
987
987
|
const users = new OIMReactiveCollection<User, string>(queue, {
|
|
988
988
|
selectPk: (u) => u.id,
|
|
989
989
|
});
|
|
@@ -1020,14 +1020,14 @@ queue.destroy();
|
|
|
1020
1020
|
```typescript
|
|
1021
1021
|
import {
|
|
1022
1022
|
OIMEntitiesByIndexKeySetBasedSelector,
|
|
1023
|
-
|
|
1023
|
+
OIMComputeRuntime,
|
|
1024
1024
|
OIMEventQueue,
|
|
1025
1025
|
OIMReactiveCollection,
|
|
1026
1026
|
OIMReactiveCollectionIndexManualSetBased,
|
|
1027
1027
|
} from '@oimdb/core';
|
|
1028
1028
|
|
|
1029
1029
|
const queue = new OIMEventQueue();
|
|
1030
|
-
const runtime = new
|
|
1030
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
1031
1031
|
const users = new OIMReactiveCollection<User, string>(queue, {
|
|
1032
1032
|
selectPk: (u) => u.id,
|
|
1033
1033
|
});
|
|
@@ -1071,13 +1071,13 @@ queue.destroy();
|
|
|
1071
1071
|
import {
|
|
1072
1072
|
OIMObjectValueByKeySelector,
|
|
1073
1073
|
OIMObjectValuesByKeysSelector,
|
|
1074
|
-
|
|
1074
|
+
OIMComputeRuntime,
|
|
1075
1075
|
OIMEventQueue,
|
|
1076
1076
|
OIMReactiveObject,
|
|
1077
1077
|
} from '@oimdb/core';
|
|
1078
1078
|
|
|
1079
1079
|
const queue = new OIMEventQueue();
|
|
1080
|
-
const runtime = new
|
|
1080
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
1081
1081
|
const settings = new OIMReactiveObject<'theme' | 'lang', string>(queue);
|
|
1082
1082
|
|
|
1083
1083
|
// Watch single key
|
|
@@ -1117,10 +1117,13 @@ queue.destroy();
|
|
|
1117
1117
|
|
|
1118
1118
|
Choose the right scheduler for your use case:
|
|
1119
1119
|
|
|
1120
|
-
- **`microtask`**: Most common -
|
|
1121
|
-
- **`timeout`**: Configurable delay for custom batching strategies
|
|
1122
|
-
- **`animationFrame`**: Syncs with browser rendering (60fps)
|
|
1123
|
-
- **`immediate`**:
|
|
1120
|
+
- **`microtask`**: Most common - drains right after the current synchronous code, before the next macrotask and before paint (lowest delivery latency)
|
|
1121
|
+
- **`timeout`**: Configurable macrotask delay for custom batching strategies
|
|
1122
|
+
- **`animationFrame`**: Syncs with browser rendering (~60fps)
|
|
1123
|
+
- **`immediate`**: Macrotask — `setImmediate` in Node, falling back to `MessageChannel` then `setTimeout(0)` in browsers; runs *after* the microtask queue, so higher latency than `microtask`
|
|
1124
|
+
- **`sync`** (`createSync()`): flushes synchronously, in the same call stack, on every write — **no batching**. Debug/diagnostic only
|
|
1125
|
+
|
|
1126
|
+
The async schedulers coalesce writes into one flush per tick. For synchronous delivery in normal code, call `queue.flush()` directly (e.g. in tests). Use the `sync` scheduler only to diagnose coalescing-related bugs: swap it in at the root and see whether the bug survives without batching (it exposes intermediate un-coalesced states and can overflow the stack on a cyclic graph).
|
|
1124
1127
|
|
|
1125
1128
|
### Reactive Collection Hierarchy
|
|
1126
1129
|
|
|
@@ -1151,11 +1154,11 @@ OIMCollectionOrderedListCommandStream (collection-bound ordered-list command str
|
|
|
1151
1154
|
- **Indices**: O(1) index lookups with lazy evaluation
|
|
1152
1155
|
- **Event System**: Smart coalescing prevents redundant notifications
|
|
1153
1156
|
- **Memory**: Efficient key-based subscriptions, no global listeners
|
|
1154
|
-
- **Schedulers**: Configurable timing for
|
|
1155
|
-
- **Microtask**:
|
|
1156
|
-
- **Immediate**:
|
|
1157
|
-
- **Timeout**:
|
|
1158
|
-
- **AnimationFrame**: 16ms, synced with 60fps rendering
|
|
1157
|
+
- **Schedulers**: Configurable timing for batching:
|
|
1158
|
+
- **Microtask**: drains after the current synchronous stack, before macrotasks/paint (lowest latency)
|
|
1159
|
+
- **Immediate**: macrotask — `setImmediate` (Node), falling back to `MessageChannel` then `setTimeout(0)` (browser); runs after the microtask queue, not synchronous
|
|
1160
|
+
- **Timeout**: custom macrotask delay for batching strategies
|
|
1161
|
+
- **AnimationFrame**: ~16ms, synced with ~60fps rendering
|
|
1159
1162
|
|
|
1160
1163
|
### Index Performance
|
|
1161
1164
|
|
|
@@ -1294,9 +1297,6 @@ unsubscribe();
|
|
|
1294
1297
|
|
|
1295
1298
|
### DX Factories
|
|
1296
1299
|
|
|
1297
|
-
#### `createOIMReactiveCollection<TEntity, TPk>(queue, opts?)`
|
|
1298
|
-
Creates an `OIMReactiveCollection<TEntity, TPk>` with less constructor noise.
|
|
1299
|
-
|
|
1300
1300
|
#### `createOIMCollectionKit<TEntity, TPk>(queue, opts?)`
|
|
1301
1301
|
Creates a small facade:
|
|
1302
1302
|
|
|
@@ -1304,15 +1304,15 @@ Creates a small facade:
|
|
|
1304
1304
|
type TOIMCollectionKit<TEntity, TPk> = {
|
|
1305
1305
|
queue: OIMEventQueue;
|
|
1306
1306
|
collection: OIMReactiveCollection<TEntity, TPk>;
|
|
1307
|
-
|
|
1307
|
+
indexFactory: OIMCollectionIndexFactory<TEntity, TPk>;
|
|
1308
1308
|
select: OIMCollectionSelectors<TEntity, TPk>;
|
|
1309
1309
|
};
|
|
1310
1310
|
```
|
|
1311
1311
|
|
|
1312
|
-
This is a DX entrypoint only: indexes and lists are still created as separate
|
|
1312
|
+
This is a DX entrypoint only: indexes and lists are still created as separate structures via `indexFactory`, not stored inside the collection.
|
|
1313
1313
|
|
|
1314
1314
|
#### `OIMCollectionSelectors<TEntity, TPk>`
|
|
1315
|
-
DX facade for reactive read selectors backed by one `
|
|
1315
|
+
DX facade for reactive read selectors backed by one `OIMComputeRuntime`.
|
|
1316
1316
|
|
|
1317
1317
|
**Methods:**
|
|
1318
1318
|
- `byPk(pk)` - Create `OIMCollectionByPkSelector<TEntity, TPk>`
|
|
@@ -1346,17 +1346,17 @@ new OIMReactiveCollection(queue: OIMEventQueue, opts?: TOIMCollectionOptions<TEn
|
|
|
1346
1346
|
- `getSlotByPk(pk: TPk): OIMEntitySlot<TEntity, TPk> | undefined` - Get the canonical slot for a primary key
|
|
1347
1347
|
- `getSlotsByPks(pks: readonly TPk[]): OIMEntitySlot<TEntity, TPk>[]` - Get existing canonical slots for primary keys
|
|
1348
1348
|
|
|
1349
|
-
#### `
|
|
1350
|
-
Factory helper for collection-bound
|
|
1349
|
+
#### `OIMCollectionIndexFactory<TEntity, TPk>`
|
|
1350
|
+
Factory helper for collection-bound indexes and ordered lists. It keeps `queue + collection` together for construction only; created indexes/lists still live next to the collection, not inside it.
|
|
1351
1351
|
|
|
1352
1352
|
**Constructor:**
|
|
1353
1353
|
```typescript
|
|
1354
|
-
new
|
|
1354
|
+
new OIMCollectionIndexFactory(queue: OIMEventQueue, collection: OIMReactiveCollection<TEntity, TPk>)
|
|
1355
1355
|
```
|
|
1356
1356
|
|
|
1357
1357
|
**Factory:**
|
|
1358
1358
|
```typescript
|
|
1359
|
-
|
|
1359
|
+
createOIMCollectionIndexFactory(queue, collection)
|
|
1360
1360
|
```
|
|
1361
1361
|
|
|
1362
1362
|
**Methods:**
|