@oimdb/core 3.5.0 → 3.7.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
@@ -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://abaikov.github.io/oimdb/)**
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
- - **OIMCollectionRelations**: Helper for creating collection-bound indexes and ordered lists next to a collection
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 `createOIMCollectionRelations` 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.
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
- createOIMCollectionRelations,
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 = createOIMCollectionRelations(queue, users);
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 = createOIMCollectionRelations(queue, cards);
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 `OIMComputativeRuntime`, which is backed by the same queue. This keeps the public API simple and avoids a multi-phase flush model.
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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
- OIMComputativeRuntime,
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 OIMComputativeRuntime(queue);
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 - executes before next browser render
1121
- - **`timeout`**: Configurable delay for custom batching strategies
1122
- - **`animationFrame`**: Syncs with browser rendering (60fps)
1123
- - **`immediate`**: Fastest execution using platform-specific APIs
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 optimal batching:
1155
- - **Microtask**: ~1-5ms delay, ideal for UI updates
1156
- - **Immediate**: <1ms, fastest execution
1157
- - **Timeout**: Custom delay for batching strategies
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
- relations: OIMCollectionRelations<TEntity, TPk>;
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 relation objects, not stored inside the collection.
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 `OIMComputativeRuntime`.
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
- #### `OIMCollectionRelations<TEntity, TPk>`
1350
- Factory helper for collection-bound relations. It keeps `queue + collection` together for construction only; created indexes/lists still live next to the collection, not inside it.
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 OIMCollectionRelations(queue: OIMEventQueue, collection: OIMReactiveCollection<TEntity, TPk>)
1354
+ new OIMCollectionIndexFactory(queue: OIMEventQueue, collection: OIMReactiveCollection<TEntity, TPk>)
1355
1355
  ```
1356
1356
 
1357
1357
  **Factory:**
1358
1358
  ```typescript
1359
- createOIMCollectionRelations(queue, collection)
1359
+ createOIMCollectionIndexFactory(queue, collection)
1360
1360
  ```
1361
1361
 
1362
1362
  **Methods:**