@oimdb/core 3.4.0 → 3.5.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/dist/index.cjs CHANGED
@@ -83,6 +83,7 @@ __export(index_exports, {
83
83
  OIMObjectValueByKeySelector: () => OIMObjectValueByKeySelector,
84
84
  OIMObjectValuesByKeysSelector: () => OIMObjectValuesByKeysSelector,
85
85
  OIMOrderedListCommandStream: () => OIMOrderedListCommandStream,
86
+ OIMOrderedListMappedCommandStream: () => OIMOrderedListMappedCommandStream,
86
87
  OIMPkSelectorFactory: () => OIMPkSelectorFactory,
87
88
  OIMReactiveCollection: () => OIMReactiveCollection,
88
89
  OIMReactiveCollectionIndexManualArrayBased: () => OIMReactiveCollectionIndexManualArrayBased,
@@ -97,7 +98,8 @@ __export(index_exports, {
97
98
  createInPlaceEntityUpdater: () => createInPlaceEntityUpdater,
98
99
  createMergeEntityUpdater: () => createMergeEntityUpdater,
99
100
  createOIMCollectionIndexFactory: () => createOIMCollectionIndexFactory,
100
- createOIMCollectionKit: () => createOIMCollectionKit
101
+ createOIMCollectionKit: () => createOIMCollectionKit,
102
+ createOIMOrderedListMappedCommandStream: () => createOIMOrderedListMappedCommandStream
101
103
  });
102
104
  module.exports = __toCommonJS(index_exports);
103
105
 
@@ -1704,6 +1706,14 @@ var OIMOrderedListCommandStream = class {
1704
1706
  key
1705
1707
  );
1706
1708
  }
1709
+ /** {@link IOIMOrderedListCommandSource} — alias of {@link getSlotsByKey}. */
1710
+ getItemsByKey(key) {
1711
+ return this.getSlotsByKey(key);
1712
+ }
1713
+ /** {@link IOIMOrderedListCommandSource} — per-key command subscription. */
1714
+ subscribeCommands(key, handler) {
1715
+ return this.commandsEventEmitter.subscribeOnKey(key, handler);
1716
+ }
1707
1717
  getEntitiesByKey(key) {
1708
1718
  return this.index.getEntitiesByKey(key);
1709
1719
  }
@@ -3650,6 +3660,162 @@ var OIMCollectionChangedFields = class {
3650
3660
  }
3651
3661
  };
3652
3662
 
3663
+ // src/modules/wrapper/index/OIMOrderedListMappedCommandStream.ts
3664
+ var OIMOrderedListMappedCommandStream = class _OIMOrderedListMappedCommandStream {
3665
+ constructor(source, opts) {
3666
+ /** Per-key positional mirror of the mapped elements. */
3667
+ this.mirror = /* @__PURE__ */ new Map();
3668
+ /** Mapped commands produced for the current delivery, per key. */
3669
+ this.buffer = /* @__PURE__ */ new Map();
3670
+ this.subscribers = /* @__PURE__ */ new Map();
3671
+ this.sourceUnsubscribers = /* @__PURE__ */ new Map();
3672
+ this.source = source;
3673
+ this.create = opts.create;
3674
+ this.destroyItem = opts.destroy;
3675
+ }
3676
+ subscribeCommands(key, handler) {
3677
+ this.ensureWired(key);
3678
+ let subs = this.subscribers.get(key);
3679
+ if (!subs) {
3680
+ subs = /* @__PURE__ */ new Set();
3681
+ this.subscribers.set(key, subs);
3682
+ }
3683
+ subs.add(handler);
3684
+ return () => {
3685
+ const current = this.subscribers.get(key);
3686
+ if (!current) return;
3687
+ current.delete(handler);
3688
+ if (current.size === 0) this.subscribers.delete(key);
3689
+ };
3690
+ }
3691
+ consumeCommands(key) {
3692
+ const cmds = this.buffer.get(key);
3693
+ if (!cmds || cmds.length === 0) return [];
3694
+ return cmds.slice();
3695
+ }
3696
+ getItemsByKey(key) {
3697
+ this.ensureWired(key);
3698
+ return this.mirror.get(key) ?? [];
3699
+ }
3700
+ /** Map this source again, chaining the projection. */
3701
+ map(opts) {
3702
+ return new _OIMOrderedListMappedCommandStream(
3703
+ this,
3704
+ opts
3705
+ );
3706
+ }
3707
+ destroy() {
3708
+ this.sourceUnsubscribers.forEach((unsubscribe) => unsubscribe());
3709
+ this.sourceUnsubscribers.clear();
3710
+ if (this.destroyItem) {
3711
+ this.mirror.forEach(
3712
+ (items) => items.forEach((item) => this.destroyItem(item))
3713
+ );
3714
+ }
3715
+ this.mirror.clear();
3716
+ this.buffer.clear();
3717
+ this.subscribers.clear();
3718
+ }
3719
+ /** Build the initial mirror and subscribe to the source once per key. */
3720
+ ensureWired(key) {
3721
+ if (this.mirror.has(key)) return;
3722
+ const items = this.source.getItemsByKey(key);
3723
+ this.mirror.set(key, items.map((item) => this.create(item)));
3724
+ const unsubscribe = this.source.subscribeCommands(
3725
+ key,
3726
+ () => this.onSourceCommands(key)
3727
+ );
3728
+ this.sourceUnsubscribers.set(key, unsubscribe);
3729
+ }
3730
+ onSourceCommands(key) {
3731
+ const cmds = this.source.consumeCommands(key);
3732
+ if (cmds.length === 0) return;
3733
+ let mirror = this.mirror.get(key);
3734
+ if (!mirror) {
3735
+ mirror = [];
3736
+ this.mirror.set(key, mirror);
3737
+ }
3738
+ const out = [];
3739
+ for (let i = 0; i < cmds.length; i++) {
3740
+ const cmd = cmds[i];
3741
+ switch (cmd.type) {
3742
+ case "insert": {
3743
+ const mapped = this.create(cmd.item);
3744
+ mirror.splice(cmd.index, 0, mapped);
3745
+ out.push({
3746
+ type: "insert",
3747
+ index: cmd.index,
3748
+ item: mapped
3749
+ });
3750
+ break;
3751
+ }
3752
+ case "remove": {
3753
+ const count = cmd.count ?? 1;
3754
+ const removed = mirror.splice(cmd.index, count);
3755
+ if (this.destroyItem) {
3756
+ for (let r = 0; r < removed.length; r++) {
3757
+ this.destroyItem(removed[r]);
3758
+ }
3759
+ }
3760
+ out.push({ type: "remove", index: cmd.index, count });
3761
+ break;
3762
+ }
3763
+ case "move": {
3764
+ const count = cmd.count ?? 1;
3765
+ const segment = mirror.splice(cmd.from, count);
3766
+ mirror.splice(cmd.to, 0, ...segment);
3767
+ out.push({
3768
+ type: "move",
3769
+ from: cmd.from,
3770
+ to: cmd.to,
3771
+ count
3772
+ });
3773
+ break;
3774
+ }
3775
+ case "set": {
3776
+ const mapped = this.create(cmd.item);
3777
+ const replaced = mirror.splice(cmd.index, 1, mapped);
3778
+ if (this.destroyItem && replaced.length > 0) {
3779
+ this.destroyItem(replaced[0]);
3780
+ }
3781
+ out.push({ type: "set", index: cmd.index, item: mapped });
3782
+ break;
3783
+ }
3784
+ case "reset": {
3785
+ if (this.destroyItem) {
3786
+ for (let r = 0; r < mirror.length; r++) {
3787
+ this.destroyItem(mirror[r]);
3788
+ }
3789
+ }
3790
+ const mapped = cmd.items.map((item) => this.create(item));
3791
+ mirror = mapped;
3792
+ this.mirror.set(key, mapped);
3793
+ out.push({ type: "reset", items: mapped });
3794
+ break;
3795
+ }
3796
+ }
3797
+ }
3798
+ this.buffer.set(key, out);
3799
+ try {
3800
+ const subs = this.subscribers.get(key);
3801
+ if (subs && subs.size > 0) {
3802
+ const snapshot = Array.from(subs);
3803
+ for (let s = 0; s < snapshot.length; s++) {
3804
+ const handler = snapshot[s];
3805
+ if (subs.has(handler)) handler();
3806
+ }
3807
+ }
3808
+ } finally {
3809
+ this.buffer.delete(key);
3810
+ }
3811
+ }
3812
+ };
3813
+
3814
+ // src/modules/wrapper/index/createOIMOrderedListMappedCommandStream.ts
3815
+ function createOIMOrderedListMappedCommandStream(source, opts) {
3816
+ return new OIMOrderedListMappedCommandStream(source, opts);
3817
+ }
3818
+
3653
3819
  // src/core/event-queue-scheduler/OIMEventQueueSchedulerAnimationFrame.ts
3654
3820
  var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler {
3655
3821
  constructor() {
@@ -4283,6 +4449,7 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
4283
4449
  OIMObjectValueByKeySelector,
4284
4450
  OIMObjectValuesByKeysSelector,
4285
4451
  OIMOrderedListCommandStream,
4452
+ OIMOrderedListMappedCommandStream,
4286
4453
  OIMPkSelectorFactory,
4287
4454
  OIMReactiveCollection,
4288
4455
  OIMReactiveCollectionIndexManualArrayBased,
@@ -4297,5 +4464,6 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
4297
4464
  createInPlaceEntityUpdater,
4298
4465
  createMergeEntityUpdater,
4299
4466
  createOIMCollectionIndexFactory,
4300
- createOIMCollectionKit
4467
+ createOIMCollectionKit,
4468
+ createOIMOrderedListMappedCommandStream
4301
4469
  });
package/dist/index.d.cts CHANGED
@@ -759,6 +759,32 @@ type TOIMOrderedListCommand<TItem> = {
759
759
  items: readonly TItem[];
760
760
  };
761
761
 
762
+ /**
763
+ * Read/consume side of an ordered-list command stream, generic over the element
764
+ * type it carries.
765
+ *
766
+ * The concrete stream stores entity slots (`TItem = TOIMEntitySlot<...>`); a
767
+ * mapped source ({@link OIMOrderedListMappedCommandStream}) carries whatever the
768
+ * mapping produces. A consumer that depends on this interface works unchanged
769
+ * against either — raw or mapped.
770
+ *
771
+ * Consumption is pull-based and synchronous: the notification carries no
772
+ * payload, so `consumeCommands(key)` must be called inside the `handler`, before
773
+ * the buffer for that key is cleared.
774
+ */
775
+ interface IOIMOrderedListCommandSource<TKey extends TOIMPk, TItem> {
776
+ /**
777
+ * Subscribe to per-key command notifications. The handler receives no
778
+ * payload — pull the commands with {@link consumeCommands} synchronously.
779
+ * Returns an unsubscribe function.
780
+ */
781
+ subscribeCommands(key: TKey, handler: () => void): () => void;
782
+ /** Drain the commands buffered for `key` this delivery (a copy). */
783
+ consumeCommands(key: TKey): TOIMOrderedListCommand<TItem>[];
784
+ /** Current ordered items for `key` — the initial state before any command. */
785
+ getItemsByKey(key: TKey): readonly TItem[];
786
+ }
787
+
762
788
  /**
763
789
  * Slot-first ordered-list command stream.
764
790
  *
@@ -766,8 +792,12 @@ type TOIMOrderedListCommand<TItem> = {
766
792
  * entity slot. Use this when you already have slots and want incremental
767
793
  * commands for an imperative renderer. For PK writes bound to a collection, use
768
794
  * `OIMCollectionOrderedListCommandStream`.
795
+ *
796
+ * Implements {@link IOIMOrderedListCommandSource} (with the slot as the element)
797
+ * so it can be projected element-wise via
798
+ * `createOIMOrderedListMappedCommandStream`.
769
799
  */
770
- declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMPk, TEntity extends object = object> {
800
+ declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMPk, TEntity extends object = object> implements IOIMOrderedListCommandSource<TKey, TOIMEntitySlot<TEntity, TPk>> {
771
801
  readonly commandsEventEmitter: OIMUpdateEventEmitter<TKey>;
772
802
  private readonly unsubscribeAfterFlush;
773
803
  private readonly unsubscribeFromIndexEmitter;
@@ -778,6 +808,10 @@ declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMP
778
808
  constructor(queue: OIMEventQueue, index?: OIMIndexManualOrderedArrayBased<TKey, TPk>);
779
809
  getPksByKey(key: TKey): readonly TPk[];
780
810
  getSlotsByKey(key: TKey): readonly TOIMEntitySlot<TEntity, TPk>[];
811
+ /** {@link IOIMOrderedListCommandSource} — alias of {@link getSlotsByKey}. */
812
+ getItemsByKey(key: TKey): readonly TOIMEntitySlot<TEntity, TPk>[];
813
+ /** {@link IOIMOrderedListCommandSource} — per-key command subscription. */
814
+ subscribeCommands(key: TKey, handler: () => void): () => void;
781
815
  getEntitiesByKey(key: TKey): (TEntity | undefined)[];
782
816
  hasKey(key: TKey): boolean;
783
817
  getKeys(): readonly TKey[];
@@ -1398,6 +1432,80 @@ declare class OIMCollectionChangedFields<TEntity extends object, TPk extends TOI
1398
1432
  private trackPatch;
1399
1433
  }
1400
1434
 
1435
+ /**
1436
+ * Options for {@link OIMOrderedListMappedCommandStream} — how a source element
1437
+ * (`TIn`) is projected to a downstream element (`TOut`) and torn down.
1438
+ *
1439
+ * `create` runs once per element when it enters the list (an `insert`, a `set`
1440
+ * replacement, or list build / `reset`). `destroy` runs when it leaves (a
1441
+ * `remove`, the replaced half of a `set`, every element on `reset`, and on
1442
+ * stream `destroy()`). A `move` reuses the same `TOut` — it is never recreated.
1443
+ */
1444
+ type TOIMOrderedListMapOptions<TIn, TOut> = {
1445
+ create: (item: TIn) => TOut;
1446
+ destroy?: (mapped: TOut) => void;
1447
+ };
1448
+
1449
+ /**
1450
+ * Projects an ordered-list command source element-wise, re-emitting the same
1451
+ * position-addressed commands with `item` replaced by the mapped value.
1452
+ *
1453
+ * It rides the source's batching: there is no queue and no independent
1454
+ * scheduling. On each source notification it pulls the source commands,
1455
+ * translates them (running `create` / `destroy` and updating a positional
1456
+ * mirror of the mapped elements), and notifies its own subscribers
1457
+ * synchronously — so the source's `after_flush` semantics carry through.
1458
+ *
1459
+ * Identity is positional: a `move` reorders the *same* mapped references in the
1460
+ * mirror, so the mapped element is preserved across moves and never recreated.
1461
+ * `create` runs on `insert` / `set` / `reset` / initial build; `destroy` on
1462
+ * `remove` / the replaced half of `set` / every element on `reset` / `destroy()`.
1463
+ *
1464
+ * Wiring to the source for a key is lazy (on first `subscribeCommands` or
1465
+ * `getItemsByKey`) and lives until `destroy()`; subscriber churn does not
1466
+ * create or tear down mapped elements.
1467
+ *
1468
+ * The output is itself an {@link IOIMOrderedListCommandSource}, so maps chain:
1469
+ * `source.map(...)` → `.map(...)`.
1470
+ */
1471
+ declare class OIMOrderedListMappedCommandStream<TKey extends TOIMPk, TOut, TIn> implements IOIMOrderedListCommandSource<TKey, TOut> {
1472
+ private readonly source;
1473
+ private readonly create;
1474
+ private readonly destroyItem?;
1475
+ /** Per-key positional mirror of the mapped elements. */
1476
+ private readonly mirror;
1477
+ /** Mapped commands produced for the current delivery, per key. */
1478
+ private readonly buffer;
1479
+ private readonly subscribers;
1480
+ private readonly sourceUnsubscribers;
1481
+ constructor(source: IOIMOrderedListCommandSource<TKey, TIn>, opts: TOIMOrderedListMapOptions<TIn, TOut>);
1482
+ subscribeCommands(key: TKey, handler: () => void): () => void;
1483
+ consumeCommands(key: TKey): TOIMOrderedListCommand<TOut>[];
1484
+ getItemsByKey(key: TKey): readonly TOut[];
1485
+ /** Map this source again, chaining the projection. */
1486
+ map<TNext>(opts: TOIMOrderedListMapOptions<TOut, TNext>): OIMOrderedListMappedCommandStream<TKey, TNext, TOut>;
1487
+ destroy(): void;
1488
+ /** Build the initial mirror and subscribe to the source once per key. */
1489
+ private ensureWired;
1490
+ private onSourceCommands;
1491
+ }
1492
+
1493
+ /**
1494
+ * Map an ordered-list command source element-wise. The result is itself a
1495
+ * source, so it can be mapped again (or via its `.map()` method).
1496
+ *
1497
+ * ```ts
1498
+ * const nodes = createOIMOrderedListMappedCommandStream(stream, {
1499
+ * create: (slot) => engine.makeNode(slot.item),
1500
+ * destroy: (node) => engine.dropNode(node),
1501
+ * });
1502
+ * nodes.subscribeCommands(key, () =>
1503
+ * engine.apply(nodes.consumeCommands(key))
1504
+ * );
1505
+ * ```
1506
+ */
1507
+ declare function createOIMOrderedListMappedCommandStream<TKey extends TOIMPk, TIn, TOut>(source: IOIMOrderedListCommandSource<TKey, TIn>, opts: TOIMOrderedListMapOptions<TIn, TOut>): OIMOrderedListMappedCommandStream<TKey, TOut, TIn>;
1508
+
1401
1509
  /**
1402
1510
  * Animation frame-based scheduler that executes flushes synchronized with the browser's repaint cycle.
1403
1511
  * Ideal for UI-related updates that need to be synchronized with rendering.
@@ -1750,4 +1858,4 @@ declare class OIMEntitiesByIndexKeyArrayBasedSelector<TEntity extends object, TP
1750
1858
  protected areEqual(prev: readonly (TEntity | undefined)[], next: readonly (TEntity | undefined)[]): boolean;
1751
1859
  }
1752
1860
 
1753
- export { EOIMCollectionEventType, EOIMComputedEventType, EOIMEventQueueEventType, EOIMEventQueueSchedulerEventType, EOIMIndexEventType, EOIMObjectEventType, type IOIMCarrierResolver, type IOIMComputedEventSource, type IOIMEffectDependency, type IOIMEventQueueSchedulerEvents, type IOIMIndexStore, type IOIMKeyedUpdateEmitter, type IOIMSubscribable, OIMCarrierKeyedEmitter, OIMCollection, OIMCollectionByPkSelector, OIMCollectionByPksSelector, OIMCollectionChangedFields, OIMCollectionIndexFactory, OIMCollectionIndexManualOrderedArrayBased, OIMCollectionOrderedListCommandStream, OIMCollectionSelectors, OIMCollectionStore, OIMCollectionStoreMapDriven, OIMComparatorFactory, OIMComputeRuntime, OIMComputed, OIMDBSettings, OIMDerivedCollectionIndexArrayBased, OIMDerivedCollectionIndexSetBased, OIMEffect, OIMEffectDependencyComputed, OIMEffectDependencyKeyed, OIMEffectDependencyKeyedCollection, OIMEffectDependencyKeyedIndex, OIMEffectDependencyKeyedObject, OIMEntitiesByIndexKeyArrayBasedSelector, OIMEntitiesByIndexKeySetBasedSelector, OIMEntityUpdaterFactory, OIMEventEmitter, OIMEventQueue, OIMEventQueueScheduler, OIMEventQueueSchedulerAnimationFrame, OIMEventQueueSchedulerFactory, OIMEventQueueSchedulerImmediate, OIMEventQueueSchedulerMicrotask, OIMEventQueueSchedulerTimeout, OIMIndex, OIMIndexArrayBased, OIMIndexComparatorFactory, OIMIndexManualArrayBased, OIMIndexManualOrderedArrayBased, OIMIndexManualSetBased, OIMIndexPksByKeyArrayBasedSelector, OIMIndexPksByKeySetBasedSelector, OIMIndexPksByKeysArrayBasedSelector, OIMIndexPksByKeysSetBasedSelector, OIMIndexSetBased, OIMIndexStoreArrayBased, OIMIndexStoreMapDrivenArrayBased, OIMIndexStoreMapDrivenSetBased, OIMIndexStoreSetBased, OIMMap2Keys, OIMObject, OIMObjectStore, OIMObjectStoreMapDriven, OIMObjectStoreRecordDriven, OIMObjectValueByKeySelector, OIMObjectValuesByKeysSelector, OIMOrderedListCommandStream, OIMPkSelectorFactory, OIMReactiveCollection, OIMReactiveCollectionIndexManualArrayBased, OIMReactiveCollectionIndexManualSetBased, OIMReactiveIndex, OIMReactiveIndexArrayBased, OIMReactiveIndexManualArrayBased, OIMReactiveIndexManualSetBased, OIMReactiveIndexSetBased, OIMReactiveObject, OIMSelector, type TOIMAnyEntitySlot, type TOIMCollectionEntitiesSelector, type TOIMCollectionEntitySelector, type TOIMCollectionIndexArrayBasedOptions, type TOIMCollectionIndexSetBasedOptions, type TOIMCollectionKit, type TOIMCollectionOptions, type TOIMCollectionOrderedIndexOptions, type TOIMCollectionOrderedListCommandStreamOptions, type TOIMCollectionUpdatePayload, type TOIMComparator, type TOIMComputedOptions, type TOIMComputedUpdatePayload, type TOIMDerivedCollectionIndexArrayBasedOptions, type TOIMDerivedCollectionIndexSetBasedOptions, type TOIMDerivedEntityComparator, type TOIMDerivedEntityOrderSelector, type TOIMDerivedEntityOrderValue, type TOIMDerivedIndexKeySelector, type TOIMEffectOptions, type TOIMEntitySlot, type TOIMEntitySlotResolver, type TOIMEntityUpdater, type TOIMEventHandler, type TOIMEventQueueOptions, type TOIMIndexComparator, type TOIMIndexKey, type TOIMIndexOptionsArrayBased, type TOIMIndexOptionsSetBased, type TOIMIndexPksUpdatePayload, type TOIMIndexUpdatePayload, type TOIMObjectOptions, type TOIMOrderedListCommand, type TOIMPk, type TOIMPkSelector, type TOIMReactiveCollectionFactoryOptions, type TOIMRelationsArrayIndexOptions, type TOIMRelationsDerivedArrayIndexOptions, type TOIMRelationsDerivedSetIndexOptions, type TOIMRelationsOrderedListOptions, type TOIMRelationsSetIndexOptions, type TOIMSchedulerOptions, type TOIMSchedulerType, type TOIMUpdatePayload, createInPlaceEntityUpdater, createMergeEntityUpdater, createOIMCollectionIndexFactory, createOIMCollectionKit };
1861
+ export { EOIMCollectionEventType, EOIMComputedEventType, EOIMEventQueueEventType, EOIMEventQueueSchedulerEventType, EOIMIndexEventType, EOIMObjectEventType, type IOIMCarrierResolver, type IOIMComputedEventSource, type IOIMEffectDependency, type IOIMEventQueueSchedulerEvents, type IOIMIndexStore, type IOIMKeyedUpdateEmitter, type IOIMOrderedListCommandSource, type IOIMSubscribable, OIMCarrierKeyedEmitter, OIMCollection, OIMCollectionByPkSelector, OIMCollectionByPksSelector, OIMCollectionChangedFields, OIMCollectionIndexFactory, OIMCollectionIndexManualOrderedArrayBased, OIMCollectionOrderedListCommandStream, OIMCollectionSelectors, OIMCollectionStore, OIMCollectionStoreMapDriven, OIMComparatorFactory, OIMComputeRuntime, OIMComputed, OIMDBSettings, OIMDerivedCollectionIndexArrayBased, OIMDerivedCollectionIndexSetBased, OIMEffect, OIMEffectDependencyComputed, OIMEffectDependencyKeyed, OIMEffectDependencyKeyedCollection, OIMEffectDependencyKeyedIndex, OIMEffectDependencyKeyedObject, OIMEntitiesByIndexKeyArrayBasedSelector, OIMEntitiesByIndexKeySetBasedSelector, OIMEntityUpdaterFactory, OIMEventEmitter, OIMEventQueue, OIMEventQueueScheduler, OIMEventQueueSchedulerAnimationFrame, OIMEventQueueSchedulerFactory, OIMEventQueueSchedulerImmediate, OIMEventQueueSchedulerMicrotask, OIMEventQueueSchedulerTimeout, OIMIndex, OIMIndexArrayBased, OIMIndexComparatorFactory, OIMIndexManualArrayBased, OIMIndexManualOrderedArrayBased, OIMIndexManualSetBased, OIMIndexPksByKeyArrayBasedSelector, OIMIndexPksByKeySetBasedSelector, OIMIndexPksByKeysArrayBasedSelector, OIMIndexPksByKeysSetBasedSelector, OIMIndexSetBased, OIMIndexStoreArrayBased, OIMIndexStoreMapDrivenArrayBased, OIMIndexStoreMapDrivenSetBased, OIMIndexStoreSetBased, OIMMap2Keys, OIMObject, OIMObjectStore, OIMObjectStoreMapDriven, OIMObjectStoreRecordDriven, OIMObjectValueByKeySelector, OIMObjectValuesByKeysSelector, OIMOrderedListCommandStream, OIMOrderedListMappedCommandStream, OIMPkSelectorFactory, OIMReactiveCollection, OIMReactiveCollectionIndexManualArrayBased, OIMReactiveCollectionIndexManualSetBased, OIMReactiveIndex, OIMReactiveIndexArrayBased, OIMReactiveIndexManualArrayBased, OIMReactiveIndexManualSetBased, OIMReactiveIndexSetBased, OIMReactiveObject, OIMSelector, type TOIMAnyEntitySlot, type TOIMCollectionEntitiesSelector, type TOIMCollectionEntitySelector, type TOIMCollectionIndexArrayBasedOptions, type TOIMCollectionIndexSetBasedOptions, type TOIMCollectionKit, type TOIMCollectionOptions, type TOIMCollectionOrderedIndexOptions, type TOIMCollectionOrderedListCommandStreamOptions, type TOIMCollectionUpdatePayload, type TOIMComparator, type TOIMComputedOptions, type TOIMComputedUpdatePayload, type TOIMDerivedCollectionIndexArrayBasedOptions, type TOIMDerivedCollectionIndexSetBasedOptions, type TOIMDerivedEntityComparator, type TOIMDerivedEntityOrderSelector, type TOIMDerivedEntityOrderValue, type TOIMDerivedIndexKeySelector, type TOIMEffectOptions, type TOIMEntitySlot, type TOIMEntitySlotResolver, type TOIMEntityUpdater, type TOIMEventHandler, type TOIMEventQueueOptions, type TOIMIndexComparator, type TOIMIndexKey, type TOIMIndexOptionsArrayBased, type TOIMIndexOptionsSetBased, type TOIMIndexPksUpdatePayload, type TOIMIndexUpdatePayload, type TOIMObjectOptions, type TOIMOrderedListCommand, type TOIMOrderedListMapOptions, type TOIMPk, type TOIMPkSelector, type TOIMReactiveCollectionFactoryOptions, type TOIMRelationsArrayIndexOptions, type TOIMRelationsDerivedArrayIndexOptions, type TOIMRelationsDerivedSetIndexOptions, type TOIMRelationsOrderedListOptions, type TOIMRelationsSetIndexOptions, type TOIMSchedulerOptions, type TOIMSchedulerType, type TOIMUpdatePayload, createInPlaceEntityUpdater, createMergeEntityUpdater, createOIMCollectionIndexFactory, createOIMCollectionKit, createOIMOrderedListMappedCommandStream };
package/dist/index.d.ts CHANGED
@@ -759,6 +759,32 @@ type TOIMOrderedListCommand<TItem> = {
759
759
  items: readonly TItem[];
760
760
  };
761
761
 
762
+ /**
763
+ * Read/consume side of an ordered-list command stream, generic over the element
764
+ * type it carries.
765
+ *
766
+ * The concrete stream stores entity slots (`TItem = TOIMEntitySlot<...>`); a
767
+ * mapped source ({@link OIMOrderedListMappedCommandStream}) carries whatever the
768
+ * mapping produces. A consumer that depends on this interface works unchanged
769
+ * against either — raw or mapped.
770
+ *
771
+ * Consumption is pull-based and synchronous: the notification carries no
772
+ * payload, so `consumeCommands(key)` must be called inside the `handler`, before
773
+ * the buffer for that key is cleared.
774
+ */
775
+ interface IOIMOrderedListCommandSource<TKey extends TOIMPk, TItem> {
776
+ /**
777
+ * Subscribe to per-key command notifications. The handler receives no
778
+ * payload — pull the commands with {@link consumeCommands} synchronously.
779
+ * Returns an unsubscribe function.
780
+ */
781
+ subscribeCommands(key: TKey, handler: () => void): () => void;
782
+ /** Drain the commands buffered for `key` this delivery (a copy). */
783
+ consumeCommands(key: TKey): TOIMOrderedListCommand<TItem>[];
784
+ /** Current ordered items for `key` — the initial state before any command. */
785
+ getItemsByKey(key: TKey): readonly TItem[];
786
+ }
787
+
762
788
  /**
763
789
  * Slot-first ordered-list command stream.
764
790
  *
@@ -766,8 +792,12 @@ type TOIMOrderedListCommand<TItem> = {
766
792
  * entity slot. Use this when you already have slots and want incremental
767
793
  * commands for an imperative renderer. For PK writes bound to a collection, use
768
794
  * `OIMCollectionOrderedListCommandStream`.
795
+ *
796
+ * Implements {@link IOIMOrderedListCommandSource} (with the slot as the element)
797
+ * so it can be projected element-wise via
798
+ * `createOIMOrderedListMappedCommandStream`.
769
799
  */
770
- declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMPk, TEntity extends object = object> {
800
+ declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMPk, TEntity extends object = object> implements IOIMOrderedListCommandSource<TKey, TOIMEntitySlot<TEntity, TPk>> {
771
801
  readonly commandsEventEmitter: OIMUpdateEventEmitter<TKey>;
772
802
  private readonly unsubscribeAfterFlush;
773
803
  private readonly unsubscribeFromIndexEmitter;
@@ -778,6 +808,10 @@ declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMP
778
808
  constructor(queue: OIMEventQueue, index?: OIMIndexManualOrderedArrayBased<TKey, TPk>);
779
809
  getPksByKey(key: TKey): readonly TPk[];
780
810
  getSlotsByKey(key: TKey): readonly TOIMEntitySlot<TEntity, TPk>[];
811
+ /** {@link IOIMOrderedListCommandSource} — alias of {@link getSlotsByKey}. */
812
+ getItemsByKey(key: TKey): readonly TOIMEntitySlot<TEntity, TPk>[];
813
+ /** {@link IOIMOrderedListCommandSource} — per-key command subscription. */
814
+ subscribeCommands(key: TKey, handler: () => void): () => void;
781
815
  getEntitiesByKey(key: TKey): (TEntity | undefined)[];
782
816
  hasKey(key: TKey): boolean;
783
817
  getKeys(): readonly TKey[];
@@ -1398,6 +1432,80 @@ declare class OIMCollectionChangedFields<TEntity extends object, TPk extends TOI
1398
1432
  private trackPatch;
1399
1433
  }
1400
1434
 
1435
+ /**
1436
+ * Options for {@link OIMOrderedListMappedCommandStream} — how a source element
1437
+ * (`TIn`) is projected to a downstream element (`TOut`) and torn down.
1438
+ *
1439
+ * `create` runs once per element when it enters the list (an `insert`, a `set`
1440
+ * replacement, or list build / `reset`). `destroy` runs when it leaves (a
1441
+ * `remove`, the replaced half of a `set`, every element on `reset`, and on
1442
+ * stream `destroy()`). A `move` reuses the same `TOut` — it is never recreated.
1443
+ */
1444
+ type TOIMOrderedListMapOptions<TIn, TOut> = {
1445
+ create: (item: TIn) => TOut;
1446
+ destroy?: (mapped: TOut) => void;
1447
+ };
1448
+
1449
+ /**
1450
+ * Projects an ordered-list command source element-wise, re-emitting the same
1451
+ * position-addressed commands with `item` replaced by the mapped value.
1452
+ *
1453
+ * It rides the source's batching: there is no queue and no independent
1454
+ * scheduling. On each source notification it pulls the source commands,
1455
+ * translates them (running `create` / `destroy` and updating a positional
1456
+ * mirror of the mapped elements), and notifies its own subscribers
1457
+ * synchronously — so the source's `after_flush` semantics carry through.
1458
+ *
1459
+ * Identity is positional: a `move` reorders the *same* mapped references in the
1460
+ * mirror, so the mapped element is preserved across moves and never recreated.
1461
+ * `create` runs on `insert` / `set` / `reset` / initial build; `destroy` on
1462
+ * `remove` / the replaced half of `set` / every element on `reset` / `destroy()`.
1463
+ *
1464
+ * Wiring to the source for a key is lazy (on first `subscribeCommands` or
1465
+ * `getItemsByKey`) and lives until `destroy()`; subscriber churn does not
1466
+ * create or tear down mapped elements.
1467
+ *
1468
+ * The output is itself an {@link IOIMOrderedListCommandSource}, so maps chain:
1469
+ * `source.map(...)` → `.map(...)`.
1470
+ */
1471
+ declare class OIMOrderedListMappedCommandStream<TKey extends TOIMPk, TOut, TIn> implements IOIMOrderedListCommandSource<TKey, TOut> {
1472
+ private readonly source;
1473
+ private readonly create;
1474
+ private readonly destroyItem?;
1475
+ /** Per-key positional mirror of the mapped elements. */
1476
+ private readonly mirror;
1477
+ /** Mapped commands produced for the current delivery, per key. */
1478
+ private readonly buffer;
1479
+ private readonly subscribers;
1480
+ private readonly sourceUnsubscribers;
1481
+ constructor(source: IOIMOrderedListCommandSource<TKey, TIn>, opts: TOIMOrderedListMapOptions<TIn, TOut>);
1482
+ subscribeCommands(key: TKey, handler: () => void): () => void;
1483
+ consumeCommands(key: TKey): TOIMOrderedListCommand<TOut>[];
1484
+ getItemsByKey(key: TKey): readonly TOut[];
1485
+ /** Map this source again, chaining the projection. */
1486
+ map<TNext>(opts: TOIMOrderedListMapOptions<TOut, TNext>): OIMOrderedListMappedCommandStream<TKey, TNext, TOut>;
1487
+ destroy(): void;
1488
+ /** Build the initial mirror and subscribe to the source once per key. */
1489
+ private ensureWired;
1490
+ private onSourceCommands;
1491
+ }
1492
+
1493
+ /**
1494
+ * Map an ordered-list command source element-wise. The result is itself a
1495
+ * source, so it can be mapped again (or via its `.map()` method).
1496
+ *
1497
+ * ```ts
1498
+ * const nodes = createOIMOrderedListMappedCommandStream(stream, {
1499
+ * create: (slot) => engine.makeNode(slot.item),
1500
+ * destroy: (node) => engine.dropNode(node),
1501
+ * });
1502
+ * nodes.subscribeCommands(key, () =>
1503
+ * engine.apply(nodes.consumeCommands(key))
1504
+ * );
1505
+ * ```
1506
+ */
1507
+ declare function createOIMOrderedListMappedCommandStream<TKey extends TOIMPk, TIn, TOut>(source: IOIMOrderedListCommandSource<TKey, TIn>, opts: TOIMOrderedListMapOptions<TIn, TOut>): OIMOrderedListMappedCommandStream<TKey, TOut, TIn>;
1508
+
1401
1509
  /**
1402
1510
  * Animation frame-based scheduler that executes flushes synchronized with the browser's repaint cycle.
1403
1511
  * Ideal for UI-related updates that need to be synchronized with rendering.
@@ -1750,4 +1858,4 @@ declare class OIMEntitiesByIndexKeyArrayBasedSelector<TEntity extends object, TP
1750
1858
  protected areEqual(prev: readonly (TEntity | undefined)[], next: readonly (TEntity | undefined)[]): boolean;
1751
1859
  }
1752
1860
 
1753
- export { EOIMCollectionEventType, EOIMComputedEventType, EOIMEventQueueEventType, EOIMEventQueueSchedulerEventType, EOIMIndexEventType, EOIMObjectEventType, type IOIMCarrierResolver, type IOIMComputedEventSource, type IOIMEffectDependency, type IOIMEventQueueSchedulerEvents, type IOIMIndexStore, type IOIMKeyedUpdateEmitter, type IOIMSubscribable, OIMCarrierKeyedEmitter, OIMCollection, OIMCollectionByPkSelector, OIMCollectionByPksSelector, OIMCollectionChangedFields, OIMCollectionIndexFactory, OIMCollectionIndexManualOrderedArrayBased, OIMCollectionOrderedListCommandStream, OIMCollectionSelectors, OIMCollectionStore, OIMCollectionStoreMapDriven, OIMComparatorFactory, OIMComputeRuntime, OIMComputed, OIMDBSettings, OIMDerivedCollectionIndexArrayBased, OIMDerivedCollectionIndexSetBased, OIMEffect, OIMEffectDependencyComputed, OIMEffectDependencyKeyed, OIMEffectDependencyKeyedCollection, OIMEffectDependencyKeyedIndex, OIMEffectDependencyKeyedObject, OIMEntitiesByIndexKeyArrayBasedSelector, OIMEntitiesByIndexKeySetBasedSelector, OIMEntityUpdaterFactory, OIMEventEmitter, OIMEventQueue, OIMEventQueueScheduler, OIMEventQueueSchedulerAnimationFrame, OIMEventQueueSchedulerFactory, OIMEventQueueSchedulerImmediate, OIMEventQueueSchedulerMicrotask, OIMEventQueueSchedulerTimeout, OIMIndex, OIMIndexArrayBased, OIMIndexComparatorFactory, OIMIndexManualArrayBased, OIMIndexManualOrderedArrayBased, OIMIndexManualSetBased, OIMIndexPksByKeyArrayBasedSelector, OIMIndexPksByKeySetBasedSelector, OIMIndexPksByKeysArrayBasedSelector, OIMIndexPksByKeysSetBasedSelector, OIMIndexSetBased, OIMIndexStoreArrayBased, OIMIndexStoreMapDrivenArrayBased, OIMIndexStoreMapDrivenSetBased, OIMIndexStoreSetBased, OIMMap2Keys, OIMObject, OIMObjectStore, OIMObjectStoreMapDriven, OIMObjectStoreRecordDriven, OIMObjectValueByKeySelector, OIMObjectValuesByKeysSelector, OIMOrderedListCommandStream, OIMPkSelectorFactory, OIMReactiveCollection, OIMReactiveCollectionIndexManualArrayBased, OIMReactiveCollectionIndexManualSetBased, OIMReactiveIndex, OIMReactiveIndexArrayBased, OIMReactiveIndexManualArrayBased, OIMReactiveIndexManualSetBased, OIMReactiveIndexSetBased, OIMReactiveObject, OIMSelector, type TOIMAnyEntitySlot, type TOIMCollectionEntitiesSelector, type TOIMCollectionEntitySelector, type TOIMCollectionIndexArrayBasedOptions, type TOIMCollectionIndexSetBasedOptions, type TOIMCollectionKit, type TOIMCollectionOptions, type TOIMCollectionOrderedIndexOptions, type TOIMCollectionOrderedListCommandStreamOptions, type TOIMCollectionUpdatePayload, type TOIMComparator, type TOIMComputedOptions, type TOIMComputedUpdatePayload, type TOIMDerivedCollectionIndexArrayBasedOptions, type TOIMDerivedCollectionIndexSetBasedOptions, type TOIMDerivedEntityComparator, type TOIMDerivedEntityOrderSelector, type TOIMDerivedEntityOrderValue, type TOIMDerivedIndexKeySelector, type TOIMEffectOptions, type TOIMEntitySlot, type TOIMEntitySlotResolver, type TOIMEntityUpdater, type TOIMEventHandler, type TOIMEventQueueOptions, type TOIMIndexComparator, type TOIMIndexKey, type TOIMIndexOptionsArrayBased, type TOIMIndexOptionsSetBased, type TOIMIndexPksUpdatePayload, type TOIMIndexUpdatePayload, type TOIMObjectOptions, type TOIMOrderedListCommand, type TOIMPk, type TOIMPkSelector, type TOIMReactiveCollectionFactoryOptions, type TOIMRelationsArrayIndexOptions, type TOIMRelationsDerivedArrayIndexOptions, type TOIMRelationsDerivedSetIndexOptions, type TOIMRelationsOrderedListOptions, type TOIMRelationsSetIndexOptions, type TOIMSchedulerOptions, type TOIMSchedulerType, type TOIMUpdatePayload, createInPlaceEntityUpdater, createMergeEntityUpdater, createOIMCollectionIndexFactory, createOIMCollectionKit };
1861
+ export { EOIMCollectionEventType, EOIMComputedEventType, EOIMEventQueueEventType, EOIMEventQueueSchedulerEventType, EOIMIndexEventType, EOIMObjectEventType, type IOIMCarrierResolver, type IOIMComputedEventSource, type IOIMEffectDependency, type IOIMEventQueueSchedulerEvents, type IOIMIndexStore, type IOIMKeyedUpdateEmitter, type IOIMOrderedListCommandSource, type IOIMSubscribable, OIMCarrierKeyedEmitter, OIMCollection, OIMCollectionByPkSelector, OIMCollectionByPksSelector, OIMCollectionChangedFields, OIMCollectionIndexFactory, OIMCollectionIndexManualOrderedArrayBased, OIMCollectionOrderedListCommandStream, OIMCollectionSelectors, OIMCollectionStore, OIMCollectionStoreMapDriven, OIMComparatorFactory, OIMComputeRuntime, OIMComputed, OIMDBSettings, OIMDerivedCollectionIndexArrayBased, OIMDerivedCollectionIndexSetBased, OIMEffect, OIMEffectDependencyComputed, OIMEffectDependencyKeyed, OIMEffectDependencyKeyedCollection, OIMEffectDependencyKeyedIndex, OIMEffectDependencyKeyedObject, OIMEntitiesByIndexKeyArrayBasedSelector, OIMEntitiesByIndexKeySetBasedSelector, OIMEntityUpdaterFactory, OIMEventEmitter, OIMEventQueue, OIMEventQueueScheduler, OIMEventQueueSchedulerAnimationFrame, OIMEventQueueSchedulerFactory, OIMEventQueueSchedulerImmediate, OIMEventQueueSchedulerMicrotask, OIMEventQueueSchedulerTimeout, OIMIndex, OIMIndexArrayBased, OIMIndexComparatorFactory, OIMIndexManualArrayBased, OIMIndexManualOrderedArrayBased, OIMIndexManualSetBased, OIMIndexPksByKeyArrayBasedSelector, OIMIndexPksByKeySetBasedSelector, OIMIndexPksByKeysArrayBasedSelector, OIMIndexPksByKeysSetBasedSelector, OIMIndexSetBased, OIMIndexStoreArrayBased, OIMIndexStoreMapDrivenArrayBased, OIMIndexStoreMapDrivenSetBased, OIMIndexStoreSetBased, OIMMap2Keys, OIMObject, OIMObjectStore, OIMObjectStoreMapDriven, OIMObjectStoreRecordDriven, OIMObjectValueByKeySelector, OIMObjectValuesByKeysSelector, OIMOrderedListCommandStream, OIMOrderedListMappedCommandStream, OIMPkSelectorFactory, OIMReactiveCollection, OIMReactiveCollectionIndexManualArrayBased, OIMReactiveCollectionIndexManualSetBased, OIMReactiveIndex, OIMReactiveIndexArrayBased, OIMReactiveIndexManualArrayBased, OIMReactiveIndexManualSetBased, OIMReactiveIndexSetBased, OIMReactiveObject, OIMSelector, type TOIMAnyEntitySlot, type TOIMCollectionEntitiesSelector, type TOIMCollectionEntitySelector, type TOIMCollectionIndexArrayBasedOptions, type TOIMCollectionIndexSetBasedOptions, type TOIMCollectionKit, type TOIMCollectionOptions, type TOIMCollectionOrderedIndexOptions, type TOIMCollectionOrderedListCommandStreamOptions, type TOIMCollectionUpdatePayload, type TOIMComparator, type TOIMComputedOptions, type TOIMComputedUpdatePayload, type TOIMDerivedCollectionIndexArrayBasedOptions, type TOIMDerivedCollectionIndexSetBasedOptions, type TOIMDerivedEntityComparator, type TOIMDerivedEntityOrderSelector, type TOIMDerivedEntityOrderValue, type TOIMDerivedIndexKeySelector, type TOIMEffectOptions, type TOIMEntitySlot, type TOIMEntitySlotResolver, type TOIMEntityUpdater, type TOIMEventHandler, type TOIMEventQueueOptions, type TOIMIndexComparator, type TOIMIndexKey, type TOIMIndexOptionsArrayBased, type TOIMIndexOptionsSetBased, type TOIMIndexPksUpdatePayload, type TOIMIndexUpdatePayload, type TOIMObjectOptions, type TOIMOrderedListCommand, type TOIMOrderedListMapOptions, type TOIMPk, type TOIMPkSelector, type TOIMReactiveCollectionFactoryOptions, type TOIMRelationsArrayIndexOptions, type TOIMRelationsDerivedArrayIndexOptions, type TOIMRelationsDerivedSetIndexOptions, type TOIMRelationsOrderedListOptions, type TOIMRelationsSetIndexOptions, type TOIMSchedulerOptions, type TOIMSchedulerType, type TOIMUpdatePayload, createInPlaceEntityUpdater, createMergeEntityUpdater, createOIMCollectionIndexFactory, createOIMCollectionKit, createOIMOrderedListMappedCommandStream };
package/dist/index.js CHANGED
@@ -1601,6 +1601,14 @@ var OIMOrderedListCommandStream = class {
1601
1601
  key
1602
1602
  );
1603
1603
  }
1604
+ /** {@link IOIMOrderedListCommandSource} — alias of {@link getSlotsByKey}. */
1605
+ getItemsByKey(key) {
1606
+ return this.getSlotsByKey(key);
1607
+ }
1608
+ /** {@link IOIMOrderedListCommandSource} — per-key command subscription. */
1609
+ subscribeCommands(key, handler) {
1610
+ return this.commandsEventEmitter.subscribeOnKey(key, handler);
1611
+ }
1604
1612
  getEntitiesByKey(key) {
1605
1613
  return this.index.getEntitiesByKey(key);
1606
1614
  }
@@ -3547,6 +3555,162 @@ var OIMCollectionChangedFields = class {
3547
3555
  }
3548
3556
  };
3549
3557
 
3558
+ // src/modules/wrapper/index/OIMOrderedListMappedCommandStream.ts
3559
+ var OIMOrderedListMappedCommandStream = class _OIMOrderedListMappedCommandStream {
3560
+ constructor(source, opts) {
3561
+ /** Per-key positional mirror of the mapped elements. */
3562
+ this.mirror = /* @__PURE__ */ new Map();
3563
+ /** Mapped commands produced for the current delivery, per key. */
3564
+ this.buffer = /* @__PURE__ */ new Map();
3565
+ this.subscribers = /* @__PURE__ */ new Map();
3566
+ this.sourceUnsubscribers = /* @__PURE__ */ new Map();
3567
+ this.source = source;
3568
+ this.create = opts.create;
3569
+ this.destroyItem = opts.destroy;
3570
+ }
3571
+ subscribeCommands(key, handler) {
3572
+ this.ensureWired(key);
3573
+ let subs = this.subscribers.get(key);
3574
+ if (!subs) {
3575
+ subs = /* @__PURE__ */ new Set();
3576
+ this.subscribers.set(key, subs);
3577
+ }
3578
+ subs.add(handler);
3579
+ return () => {
3580
+ const current = this.subscribers.get(key);
3581
+ if (!current) return;
3582
+ current.delete(handler);
3583
+ if (current.size === 0) this.subscribers.delete(key);
3584
+ };
3585
+ }
3586
+ consumeCommands(key) {
3587
+ const cmds = this.buffer.get(key);
3588
+ if (!cmds || cmds.length === 0) return [];
3589
+ return cmds.slice();
3590
+ }
3591
+ getItemsByKey(key) {
3592
+ this.ensureWired(key);
3593
+ return this.mirror.get(key) ?? [];
3594
+ }
3595
+ /** Map this source again, chaining the projection. */
3596
+ map(opts) {
3597
+ return new _OIMOrderedListMappedCommandStream(
3598
+ this,
3599
+ opts
3600
+ );
3601
+ }
3602
+ destroy() {
3603
+ this.sourceUnsubscribers.forEach((unsubscribe) => unsubscribe());
3604
+ this.sourceUnsubscribers.clear();
3605
+ if (this.destroyItem) {
3606
+ this.mirror.forEach(
3607
+ (items) => items.forEach((item) => this.destroyItem(item))
3608
+ );
3609
+ }
3610
+ this.mirror.clear();
3611
+ this.buffer.clear();
3612
+ this.subscribers.clear();
3613
+ }
3614
+ /** Build the initial mirror and subscribe to the source once per key. */
3615
+ ensureWired(key) {
3616
+ if (this.mirror.has(key)) return;
3617
+ const items = this.source.getItemsByKey(key);
3618
+ this.mirror.set(key, items.map((item) => this.create(item)));
3619
+ const unsubscribe = this.source.subscribeCommands(
3620
+ key,
3621
+ () => this.onSourceCommands(key)
3622
+ );
3623
+ this.sourceUnsubscribers.set(key, unsubscribe);
3624
+ }
3625
+ onSourceCommands(key) {
3626
+ const cmds = this.source.consumeCommands(key);
3627
+ if (cmds.length === 0) return;
3628
+ let mirror = this.mirror.get(key);
3629
+ if (!mirror) {
3630
+ mirror = [];
3631
+ this.mirror.set(key, mirror);
3632
+ }
3633
+ const out = [];
3634
+ for (let i = 0; i < cmds.length; i++) {
3635
+ const cmd = cmds[i];
3636
+ switch (cmd.type) {
3637
+ case "insert": {
3638
+ const mapped = this.create(cmd.item);
3639
+ mirror.splice(cmd.index, 0, mapped);
3640
+ out.push({
3641
+ type: "insert",
3642
+ index: cmd.index,
3643
+ item: mapped
3644
+ });
3645
+ break;
3646
+ }
3647
+ case "remove": {
3648
+ const count = cmd.count ?? 1;
3649
+ const removed = mirror.splice(cmd.index, count);
3650
+ if (this.destroyItem) {
3651
+ for (let r = 0; r < removed.length; r++) {
3652
+ this.destroyItem(removed[r]);
3653
+ }
3654
+ }
3655
+ out.push({ type: "remove", index: cmd.index, count });
3656
+ break;
3657
+ }
3658
+ case "move": {
3659
+ const count = cmd.count ?? 1;
3660
+ const segment = mirror.splice(cmd.from, count);
3661
+ mirror.splice(cmd.to, 0, ...segment);
3662
+ out.push({
3663
+ type: "move",
3664
+ from: cmd.from,
3665
+ to: cmd.to,
3666
+ count
3667
+ });
3668
+ break;
3669
+ }
3670
+ case "set": {
3671
+ const mapped = this.create(cmd.item);
3672
+ const replaced = mirror.splice(cmd.index, 1, mapped);
3673
+ if (this.destroyItem && replaced.length > 0) {
3674
+ this.destroyItem(replaced[0]);
3675
+ }
3676
+ out.push({ type: "set", index: cmd.index, item: mapped });
3677
+ break;
3678
+ }
3679
+ case "reset": {
3680
+ if (this.destroyItem) {
3681
+ for (let r = 0; r < mirror.length; r++) {
3682
+ this.destroyItem(mirror[r]);
3683
+ }
3684
+ }
3685
+ const mapped = cmd.items.map((item) => this.create(item));
3686
+ mirror = mapped;
3687
+ this.mirror.set(key, mapped);
3688
+ out.push({ type: "reset", items: mapped });
3689
+ break;
3690
+ }
3691
+ }
3692
+ }
3693
+ this.buffer.set(key, out);
3694
+ try {
3695
+ const subs = this.subscribers.get(key);
3696
+ if (subs && subs.size > 0) {
3697
+ const snapshot = Array.from(subs);
3698
+ for (let s = 0; s < snapshot.length; s++) {
3699
+ const handler = snapshot[s];
3700
+ if (subs.has(handler)) handler();
3701
+ }
3702
+ }
3703
+ } finally {
3704
+ this.buffer.delete(key);
3705
+ }
3706
+ }
3707
+ };
3708
+
3709
+ // src/modules/wrapper/index/createOIMOrderedListMappedCommandStream.ts
3710
+ function createOIMOrderedListMappedCommandStream(source, opts) {
3711
+ return new OIMOrderedListMappedCommandStream(source, opts);
3712
+ }
3713
+
3550
3714
  // src/core/event-queue-scheduler/OIMEventQueueSchedulerAnimationFrame.ts
3551
3715
  var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler {
3552
3716
  constructor() {
@@ -4179,6 +4343,7 @@ export {
4179
4343
  OIMObjectValueByKeySelector,
4180
4344
  OIMObjectValuesByKeysSelector,
4181
4345
  OIMOrderedListCommandStream,
4346
+ OIMOrderedListMappedCommandStream,
4182
4347
  OIMPkSelectorFactory,
4183
4348
  OIMReactiveCollection,
4184
4349
  OIMReactiveCollectionIndexManualArrayBased,
@@ -4193,5 +4358,6 @@ export {
4193
4358
  createInPlaceEntityUpdater,
4194
4359
  createMergeEntityUpdater,
4195
4360
  createOIMCollectionIndexFactory,
4196
- createOIMCollectionKit
4361
+ createOIMCollectionKit,
4362
+ createOIMOrderedListMappedCommandStream
4197
4363
  };
package/llms.txt CHANGED
@@ -84,6 +84,39 @@ Membership writes are incremental (touch only the changed pks):
84
84
  - `setPks` / `addPks` resolve each pk → slot (O(1) lookup); if you already hold slots (`collection.upsertMany` returns them) use `setSlots` / `appendSlots` to skip the lookup.
85
85
  - `getEntitiesByKey(key)` returns `(TEntity | undefined)[]` — positional holes for not-yet-loaded pks, aligned with `getPksByKey` (not dropped), so you can render a loading state.
86
86
 
87
+ ### Ordered list command stream
88
+
89
+ For an ordered per-key list that emits **position-addressed commands** (insert / remove / move / set / reset) instead of a new array to diff — for imperative renderers that apply edits in place. `OIMCollectionOrderedListCommandStream` (pk writes, via `indexFactory.orderedList()`) or `OIMOrderedListCommandStream` (slot writes). Commands are not in the event payload — subscribe, then pull synchronously.
90
+
91
+ ```ts
92
+ import { createOIMOrderedListMappedCommandStream } from '@oimdb/core';
93
+
94
+ stream.push('main', 'u1'); // append by pk; also insertAt/setAt/removeAt/removeRange/moveRange/set
95
+ stream.subscribeCommands('main', () => {
96
+ for (const cmd of stream.consumeCommands('main')) {
97
+ // { type:'insert', index, item } | { type:'remove', index, count? }
98
+ // | { type:'move', from, to, count? } | { type:'set', index, item }
99
+ // | { type:'reset', items } — item is the entity slot; entity is item.item
100
+ }
101
+ });
102
+ stream.getEntitiesByKey('main'); // (TEntity | undefined)[] — holes preserved
103
+ ```
104
+
105
+ `count` defaults to 1 (read `count ?? 1`). A whole-list change collapses to a single `reset`. Reads: `getPksByKey` / `getSlotsByKey` / `getEntitiesByKey`.
106
+
107
+ **Mapping the list to your own elements** — `createOIMOrderedListMappedCommandStream(source, { create, destroy? })` projects element-wise: the same commands with `item` replaced by `create(slot)`'s result (a DOM node, view model, …). Identity is positional — a `move` reuses the **same** mapped instance (never recreated); `create` runs on insert/set/reset/initial build, `destroy` on remove / replaced-set / reset / `destroy()`. It needs no queue (rides the source's batching) and is itself an `IOIMOrderedListCommandSource`, so it consumes like the raw stream and chains via `.map(...)`. New exports: `IOIMOrderedListCommandSource`, `TOIMOrderedListMapOptions`, `OIMOrderedListMappedCommandStream`, `createOIMOrderedListMappedCommandStream`.
108
+
109
+ ```ts
110
+ const nodes = createOIMOrderedListMappedCommandStream(stream, {
111
+ create: (slot) => engine.makeNode(slot.item),
112
+ destroy: (node) => engine.dropNode(node),
113
+ });
114
+ nodes.subscribeCommands('main', () => engine.apply(nodes.consumeCommands('main'))); // cmd.item is your node
115
+ nodes.getItemsByKey('main'); // current mapped elements — initial render
116
+ ```
117
+
118
+ > Content updates (entity fields changed) are not list commands — they arrive via the collection's per-pk subscription. The stream carries structure only.
119
+
87
120
  ### DX Layer — Collection Model
88
121
 
89
122
  `createOIMCollectionKit` wires queue + collection + index factory + selectors together.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oimdb/core",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "Core in-memory data library with type-safe indices, reactive subscriptions, and event processing",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",