@oimdb/core 3.7.0 → 3.9.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
@@ -185,6 +185,25 @@ const multipleUsers = users.getManyByPks(['user1', 'user2']);
185
185
  const userSlot = users.getSlotByPk('user1');
186
186
  ```
187
187
 
188
+ #### Composite primary key
189
+
190
+ Instead of a primitive PK, a collection can be keyed by a **composite key path** — an arbitrary-length tuple of primitive segments. Pass a trie-backed store:
191
+
192
+ ```typescript
193
+ import { OIMReactiveCollection, OIMCollectionStoreTrieDriven, TOIMKeyPath } from '@oimdb/core';
194
+
195
+ const memberships = new OIMReactiveCollection<Membership, TOIMKeyPath>(queue, {
196
+ selectPk: (m) => [m.userId, m.projectId],
197
+ store: new OIMCollectionStoreTrieDriven<Membership>(),
198
+ });
199
+
200
+ memberships.getOneByPk([1, 10]); // matched by content — a fresh array works
201
+ memberships.subscribeOnKey([1, 10], render);
202
+ memberships.removeOneByPk([1, 10]);
203
+ ```
204
+
205
+ Key paths match **by content** (the store interns each logical PK to one canonical `slot.pk`). Indexes work over such a collection too (`indexFactory.setBasedIndex()` indexes composite PKs). Primitive-PK collections keep the native-`Map` store untouched. At serialization boundaries a composite PK needs an `IOIMPkCodec` (`OIMPkCodecKeyPath`) — `@oimdb/persist`/`@oimdb/snapshot-manager` need none; `@oimdb/redux-adapter` takes it.
206
+
188
207
  ### Creating a Reactive Index
189
208
 
190
209
  OIMDB provides two types of indexes optimized for different use cases:
@@ -634,6 +653,24 @@ users.upsertOne({ id: 'user1', role: 'admin' });
634
653
  // This prevents unnecessary re-renders and improves performance
635
654
  ```
636
655
 
656
+ ### Disposing everything at once — `OIMDisposeScope`
657
+
658
+ Teardown is LIFO: dispose dependents before what they depend on (indexes / streams / selectors → collection → queue). Instead of tracking that order by hand, register into an `OIMDisposeScope` as you build and tear it all down in one call:
659
+
660
+ ```typescript
661
+ import { OIMDisposeScope, OIMEventQueue, OIMReactiveCollection } from '@oimdb/core';
662
+
663
+ const scope = new OIMDisposeScope();
664
+ const queue = scope.add(new OIMEventQueue());
665
+ const users = scope.add(new OIMReactiveCollection(queue, { selectPk: u => u.id }));
666
+ const byTeam = scope.add(usersIndexFactory.setBasedIndex());
667
+ scope.add(selector.watch(render)); // bare unsubscribe fn works too
668
+
669
+ scope.destroy(); // disposes byTeam → users → queue (reverse order), no bookkeeping
670
+ ```
671
+
672
+ `add(x)` returns `x` for inline capture. Accepts both `{ destroy(): void }` objects and bare `() => void` unsubscribe functions (selectors, per-key subscriptions and scheduler tasks expose only the latter). Idempotent; every item is disposed even if one throws (first error rethrown after); `child()` nests a sub-scope. Factory: `createOIMDisposeScope()`. Related exports: `IOIMDisposable`, `TOIMDisposable`.
673
+
637
674
  ### Effects, Computed, and the Event Lifecycle
638
675
 
639
676
  OIMDB uses a single-pass flush boundary: `queue.flush()` executes the current batch of pending work.
@@ -1361,6 +1398,8 @@ createOIMCollectionIndexFactory(queue, collection)
1361
1398
 
1362
1399
  **Methods:**
1363
1400
  - `setBasedIndex<TKey>()` - Create `OIMReactiveCollectionIndexManualSetBased<TKey, TPk, TEntity>`
1401
+ - `compositeSetIndex()` - Create `OIMReactiveCollectionIndexCompositeTrieSetBased<TPk, TEntity>` — set-based index keyed by a composite **key path** (`TOIMKeyPath`, an arbitrary-length tuple of primitive segments, e.g. `setPks([projectId, role], pks)`). Matched by content in O(arity), no string concatenation. Primitive-keyed indexes keep their native-`Map` fast path.
1402
+ - `compositeArrayIndex()` - Create `OIMReactiveCollectionIndexCompositeTrieArrayBased<TPk, TEntity>` — ordered counterpart of `compositeSetIndex`, buckets are ordered slot arrays per key path.
1364
1403
  - `derivedSetIndex<TKey>(selectIndexKeys)` - Create `OIMDerivedCollectionIndexSetBased<TKey, TPk, TEntity>`
1365
1404
  - `arrayBasedIndex<TKey>()` - Create `OIMReactiveCollectionIndexManualArrayBased<TKey, TPk, TEntity>`
1366
1405
  - `derivedArrayIndex<TKey>(selectIndexKeys, { orderBy?, compareEntities? })` - Create `OIMDerivedCollectionIndexArrayBased<TKey, TPk, TEntity>`
@@ -1404,7 +1443,7 @@ new OIMReactiveCollectionIndexManualSetBased(queue: OIMEventQueue, opts: {
1404
1443
  store?: OIMIndexStoreSetBased<TKey, TPk>;
1405
1444
  };
1406
1445
  } | {
1407
- resolveSlot: TOIMEntitySlotResolver<TPk>;
1446
+ getSlot: TOIMEntitySlotGetter<TPk>;
1408
1447
  indexOptions?: {
1409
1448
  comparePks?: TOIMIndexComparator<TPk>;
1410
1449
  store?: OIMIndexStoreSetBased<TKey, TPk>;
@@ -1412,7 +1451,7 @@ new OIMReactiveCollectionIndexManualSetBased(queue: OIMEventQueue, opts: {
1412
1451
  })
1413
1452
  ```
1414
1453
 
1415
- Pass exactly one binding: `collection` for normal collection-bound indexes, or `resolveSlot` for custom slot resolution.
1454
+ Pass exactly one binding: `collection` for normal collection-bound indexes, or `getSlot` for custom slot lookup.
1416
1455
 
1417
1456
  **Methods:**
1418
1457
  - `setPks(key: TKey, pks: readonly TPk[]): void` - Set primary keys for index key
@@ -1502,7 +1541,7 @@ new OIMReactiveCollectionIndexManualArrayBased(queue: OIMEventQueue, opts: {
1502
1541
  store?: OIMIndexStoreArrayBased<TKey, TPk>;
1503
1542
  };
1504
1543
  } | {
1505
- resolveSlot: TOIMEntitySlotResolver<TPk>;
1544
+ getSlot: TOIMEntitySlotGetter<TPk>;
1506
1545
  indexOptions?: {
1507
1546
  comparePks?: TOIMIndexComparator<TPk>;
1508
1547
  store?: OIMIndexStoreArrayBased<TKey, TPk>;
@@ -1510,7 +1549,7 @@ new OIMReactiveCollectionIndexManualArrayBased(queue: OIMEventQueue, opts: {
1510
1549
  })
1511
1550
  ```
1512
1551
 
1513
- Pass exactly one binding: `collection` for normal collection-bound indexes, or `resolveSlot` for custom slot resolution.
1552
+ Pass exactly one binding: `collection` for normal collection-bound indexes, or `getSlot` for custom slot lookup.
1514
1553
 
1515
1554
  **Methods:**
1516
1555
  - `setPks(key: TKey, pks: readonly TPk[]): void` - Set primary keys for index key