@oimdb/core 3.3.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/README.md +30 -13
- package/dist/index.cjs +309 -63
- package/dist/index.d.cts +192 -24
- package/dist/index.d.ts +192 -24
- package/dist/index.js +305 -62
- package/llms.txt +33 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -318,16 +318,20 @@ cardsByDeck.commandsEventEmitter.subscribeOnKey('deck1', () => {
|
|
|
318
318
|
for (const command of commands) {
|
|
319
319
|
switch (command.type) {
|
|
320
320
|
case 'insert':
|
|
321
|
-
// Insert command.
|
|
321
|
+
// Insert command.item (the slot; entity is command.item.item)
|
|
322
|
+
// at command.index
|
|
322
323
|
break;
|
|
323
324
|
case 'remove':
|
|
324
|
-
// Remove command.
|
|
325
|
+
// Remove command.count ?? 1 elements starting at command.index
|
|
325
326
|
break;
|
|
326
327
|
case 'move':
|
|
327
|
-
// Move command.
|
|
328
|
+
// Move command.count ?? 1 elements from command.from to command.to
|
|
328
329
|
break;
|
|
329
330
|
case 'set':
|
|
330
|
-
// Replace the
|
|
331
|
+
// Replace the single element at command.index with command.item
|
|
332
|
+
break;
|
|
333
|
+
case 'reset':
|
|
334
|
+
// Replace the whole list with command.items (slots)
|
|
331
335
|
break;
|
|
332
336
|
}
|
|
333
337
|
}
|
|
@@ -1546,7 +1550,16 @@ Collection-bound ordered Array-based index. Use this when ordered PK writes shou
|
|
|
1546
1550
|
- `getEntitiesByKey(key: TKey): (TEntity | undefined)[]` - Returns entities from canonical slots (holes are `undefined`)
|
|
1547
1551
|
|
|
1548
1552
|
#### `OIMOrderedListCommandStream<TKey, TPk, TEntity>`
|
|
1549
|
-
Slot-first ordered per-key list with a command stream for imperative consumers. It stores data in an `OIMIndexManualOrderedArrayBased` and emits `TOIMOrderedListCommand
|
|
1553
|
+
Slot-first ordered per-key list with a command stream for imperative consumers. It stores data in an `OIMIndexManualOrderedArrayBased` and emits position-addressed `TOIMOrderedListCommand<Slot>` batches through `commandsEventEmitter`. Each command's `item` is the entity slot (read the entity via `item.item`):
|
|
1554
|
+
|
|
1555
|
+
```typescript
|
|
1556
|
+
type TOIMOrderedListCommand<TItem> =
|
|
1557
|
+
| { type: 'insert'; index: number; item: TItem }
|
|
1558
|
+
| { type: 'remove'; index: number; count?: number } // count may be > 1
|
|
1559
|
+
| { type: 'move'; from: number; to: number; count?: number }
|
|
1560
|
+
| { type: 'set'; index: number; item: TItem } // replace one element
|
|
1561
|
+
| { type: 'reset'; items: readonly TItem[] }; // replace whole list
|
|
1562
|
+
```
|
|
1550
1563
|
|
|
1551
1564
|
**Constructor:**
|
|
1552
1565
|
```typescript
|
|
@@ -1560,14 +1573,17 @@ new OIMOrderedListCommandStream(
|
|
|
1560
1573
|
- `index: OIMIndexManualOrderedArrayBased<TKey, TPk>` - Underlying ordered index and source of truth
|
|
1561
1574
|
- `commandsEventEmitter: OIMUpdateEventEmitter<TKey>` - Key-specific command notifications delivered after queue flush
|
|
1562
1575
|
|
|
1563
|
-
**Methods:**
|
|
1564
|
-
- `setSlots(key: TKey, slots: readonly
|
|
1565
|
-
- `pushSlot(key: TKey, slot:
|
|
1566
|
-
- `insertSlotAt(key: TKey, index: number, slot:
|
|
1576
|
+
**Methods:** (`Slot` = `TOIMEntitySlot<TEntity, TPk>`)
|
|
1577
|
+
- `setSlots(key: TKey, slots: readonly Slot[]): void` - Replace the whole ordered list and emit a `reset` command
|
|
1578
|
+
- `pushSlot(key: TKey, slot: Slot): void` - Append a slot and emit an `insert` command
|
|
1579
|
+
- `insertSlotAt(key: TKey, index: number, slot: Slot): void` - Insert a slot and emit an `insert` command
|
|
1580
|
+
- `setSlotAt(key: TKey, index: number, slot: Slot): void` - Replace the slot at `index` in place and emit a `set` command
|
|
1567
1581
|
- `removeAt(key: TKey, index: number): void` - Remove by index and emit a `remove` command
|
|
1582
|
+
- `removeRange(key: TKey, index: number, count: number): void` - Remove `count` consecutive elements from `index` and emit a `remove` command with `count`
|
|
1568
1583
|
- `move(key: TKey, fromIndex: number, toIndex: number): void` - Move within the list and emit a `move` command
|
|
1569
|
-
- `
|
|
1570
|
-
- `
|
|
1584
|
+
- `moveRange(key: TKey, from: number, to: number, count: number): void` - Move `count` consecutive elements and emit a `move` command with `count` (`to` is post-extraction space)
|
|
1585
|
+
- `consumeCommands(key: TKey): TOIMOrderedListCommand<Slot>[]` - Read buffered commands for a key inside the notification handler
|
|
1586
|
+
- `getBufferedCommands(key: TKey): readonly TOIMOrderedListCommand<Slot>[]` - Peek at buffered commands without clearing them
|
|
1571
1587
|
- `getPksByKey(key: TKey): readonly TPk[]` - Read the current ordered list
|
|
1572
1588
|
- `getSlotsByKey(key: TKey): readonly TOIMEntitySlot<TEntity, TPk>[]` - Read current ordered slots
|
|
1573
1589
|
- `getEntitiesByKey(key: TKey): (TEntity | undefined)[]` - Read current ordered entities (holes are `undefined`)
|
|
@@ -1575,10 +1591,11 @@ new OIMOrderedListCommandStream(
|
|
|
1575
1591
|
- `destroy(): void` - Dispose subscriptions and clear state
|
|
1576
1592
|
|
|
1577
1593
|
#### `OIMCollectionOrderedListCommandStream<TKey, TPk, TEntity>`
|
|
1578
|
-
Collection-bound ordered-list command stream. Public writes use PKs
|
|
1594
|
+
Collection-bound ordered-list command stream. Public writes use PKs that resolve to canonical collection slots; emitted commands carry the slot as `item` (entity via `item.item`).
|
|
1579
1595
|
|
|
1580
1596
|
**Methods:**
|
|
1581
|
-
- `set(key: TKey, pks: readonly TPk[]): void` - Replace the whole ordered list from PKs
|
|
1597
|
+
- `set(key: TKey, pks: readonly TPk[]): void` - Replace the whole ordered list from PKs (emits `reset`)
|
|
1598
|
+
- `setAt(key: TKey, index: number, pk: TPk): void` - Replace the element at `index` with the slot for `pk` (emits `set`)
|
|
1582
1599
|
- `push(key: TKey, pk: TPk): void` - Append a PK
|
|
1583
1600
|
- `insertAt(key: TKey, index: number, pk: TPk): void` - Insert a PK
|
|
1584
1601
|
- all read/command methods from `OIMOrderedListCommandStream`
|
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,
|
|
@@ -95,8 +96,10 @@ __export(index_exports, {
|
|
|
95
96
|
OIMReactiveObject: () => OIMReactiveObject,
|
|
96
97
|
OIMSelector: () => OIMSelector,
|
|
97
98
|
createInPlaceEntityUpdater: () => createInPlaceEntityUpdater,
|
|
99
|
+
createMergeEntityUpdater: () => createMergeEntityUpdater,
|
|
98
100
|
createOIMCollectionIndexFactory: () => createOIMCollectionIndexFactory,
|
|
99
|
-
createOIMCollectionKit: () => createOIMCollectionKit
|
|
101
|
+
createOIMCollectionKit: () => createOIMCollectionKit,
|
|
102
|
+
createOIMOrderedListMappedCommandStream: () => createOIMOrderedListMappedCommandStream
|
|
100
103
|
});
|
|
101
104
|
module.exports = __toCommonJS(index_exports);
|
|
102
105
|
|
|
@@ -967,12 +970,15 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
|
|
|
967
970
|
}
|
|
968
971
|
};
|
|
969
972
|
|
|
973
|
+
// src/core/createMergeEntityUpdater.ts
|
|
974
|
+
function createMergeEntityUpdater() {
|
|
975
|
+
return (draft, prev) => ({ ...prev, ...draft });
|
|
976
|
+
}
|
|
977
|
+
|
|
970
978
|
// src/core/OIMEntityUpdaterFactory.ts
|
|
971
979
|
var OIMEntityUpdaterFactory = class {
|
|
972
980
|
createMergeEntityUpdater() {
|
|
973
|
-
return (
|
|
974
|
-
return { ...prev, ...draft };
|
|
975
|
-
};
|
|
981
|
+
return createMergeEntityUpdater();
|
|
976
982
|
}
|
|
977
983
|
};
|
|
978
984
|
|
|
@@ -1291,6 +1297,22 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1291
1297
|
this.emitUpdate([key]);
|
|
1292
1298
|
return removed;
|
|
1293
1299
|
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Remove up to `count` consecutive slots starting at `index`.
|
|
1302
|
+
* Returns the number actually removed (clamped to bounds; 0 if nothing).
|
|
1303
|
+
*/
|
|
1304
|
+
removeRange(key, index, count) {
|
|
1305
|
+
const list = this.store.getOneByKey(key);
|
|
1306
|
+
if (!list) return 0;
|
|
1307
|
+
if (index < 0 || index >= list.length || count <= 0) return 0;
|
|
1308
|
+
const actual = Math.min(count, list.length - index);
|
|
1309
|
+
list.splice(index, actual);
|
|
1310
|
+
if (list.length === 0) {
|
|
1311
|
+
this.store.removeOneByKey(key);
|
|
1312
|
+
}
|
|
1313
|
+
this.emitUpdate([key]);
|
|
1314
|
+
return actual;
|
|
1315
|
+
}
|
|
1294
1316
|
move(key, fromIndex, toIndex) {
|
|
1295
1317
|
const list = this.store.getOneByKey(key);
|
|
1296
1318
|
if (!list) return void 0;
|
|
@@ -1303,6 +1325,32 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1303
1325
|
this.emitUpdate([key]);
|
|
1304
1326
|
return slot;
|
|
1305
1327
|
}
|
|
1328
|
+
/** Replace the slot at `index` in place. Returns the safe index, or -1. */
|
|
1329
|
+
setSlotAt(key, index, slot) {
|
|
1330
|
+
const list = this.store.getOneByKey(key);
|
|
1331
|
+
if (!list) return -1;
|
|
1332
|
+
if (index < 0 || index >= list.length) return -1;
|
|
1333
|
+
list[index] = slot;
|
|
1334
|
+
this.emitUpdate([key]);
|
|
1335
|
+
return index;
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Move up to `count` consecutive slots from `from` to `to`. `to` is in the
|
|
1339
|
+
* coordinate space *after* the block is extracted (same as single `move`).
|
|
1340
|
+
* Returns the number actually moved (0 if a no-op / out of bounds).
|
|
1341
|
+
*/
|
|
1342
|
+
moveRange(key, from, to, count) {
|
|
1343
|
+
const list = this.store.getOneByKey(key);
|
|
1344
|
+
if (!list) return 0;
|
|
1345
|
+
if (from < 0 || from >= list.length || count <= 0) return 0;
|
|
1346
|
+
const actual = Math.min(count, list.length - from);
|
|
1347
|
+
const insertAt = Math.max(0, Math.min(to, list.length - actual));
|
|
1348
|
+
if (insertAt === from) return 0;
|
|
1349
|
+
const block = list.splice(from, actual);
|
|
1350
|
+
list.splice(insertAt, 0, ...block);
|
|
1351
|
+
this.emitUpdate([key]);
|
|
1352
|
+
return actual;
|
|
1353
|
+
}
|
|
1306
1354
|
resetSlots(key, slots) {
|
|
1307
1355
|
if (slots.length === 0) {
|
|
1308
1356
|
if (this.store.getOneByKey(key) !== void 0) {
|
|
@@ -1513,15 +1561,15 @@ var OIMUpdateEventEmitter = class {
|
|
|
1513
1561
|
const flushingKeys = this.updatedKeys;
|
|
1514
1562
|
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
1515
1563
|
if (this.keyHandlers.size === 0 || flushingKeys.size === 0) return;
|
|
1516
|
-
if (flushingKeys.size
|
|
1517
|
-
|
|
1518
|
-
|
|
1564
|
+
if (flushingKeys.size === this.keyHandlers.size) {
|
|
1565
|
+
this.keyHandlers.forEach((handlers, key) => {
|
|
1566
|
+
if (!flushingKeys.has(key)) return;
|
|
1519
1567
|
if (!handlers || handlers.size === 0) return;
|
|
1520
1568
|
this.notifyHandlers(handlers);
|
|
1521
1569
|
});
|
|
1522
1570
|
} else {
|
|
1523
|
-
|
|
1524
|
-
|
|
1571
|
+
flushingKeys.forEach((key) => {
|
|
1572
|
+
const handlers = this.keyHandlers.get(key);
|
|
1525
1573
|
if (!handlers || handlers.size === 0) return;
|
|
1526
1574
|
this.notifyHandlers(handlers);
|
|
1527
1575
|
});
|
|
@@ -1633,7 +1681,7 @@ var OIMOrderedListCommandStream = class {
|
|
|
1633
1681
|
if (payload.keys.length === 0) return;
|
|
1634
1682
|
for (let i = 0; i < payload.keys.length; i++) {
|
|
1635
1683
|
const key = payload.keys[i];
|
|
1636
|
-
this.
|
|
1684
|
+
this.appendResetCommand(key, this.getSlotsByKey(key));
|
|
1637
1685
|
}
|
|
1638
1686
|
};
|
|
1639
1687
|
this.commandsEventEmitter = new OIMUpdateEventEmitter(
|
|
@@ -1658,6 +1706,14 @@ var OIMOrderedListCommandStream = class {
|
|
|
1658
1706
|
key
|
|
1659
1707
|
);
|
|
1660
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
|
+
}
|
|
1661
1717
|
getEntitiesByKey(key) {
|
|
1662
1718
|
return this.index.getEntitiesByKey(key);
|
|
1663
1719
|
}
|
|
@@ -1670,12 +1726,7 @@ var OIMOrderedListCommandStream = class {
|
|
|
1670
1726
|
pushSlot(key, slot) {
|
|
1671
1727
|
this.withWrite(() => {
|
|
1672
1728
|
const index = this.index.pushSlot(key, slot);
|
|
1673
|
-
this.appendCommand(key, {
|
|
1674
|
-
type: "insert",
|
|
1675
|
-
pk: slot.pk,
|
|
1676
|
-
slot,
|
|
1677
|
-
index
|
|
1678
|
-
});
|
|
1729
|
+
this.appendCommand(key, { type: "insert", index, item: slot });
|
|
1679
1730
|
});
|
|
1680
1731
|
}
|
|
1681
1732
|
insertSlotAt(key, index, slot) {
|
|
@@ -1683,9 +1734,20 @@ var OIMOrderedListCommandStream = class {
|
|
|
1683
1734
|
const safeIndex = this.index.insertSlotAt(key, index, slot);
|
|
1684
1735
|
this.appendCommand(key, {
|
|
1685
1736
|
type: "insert",
|
|
1686
|
-
|
|
1687
|
-
slot
|
|
1688
|
-
|
|
1737
|
+
index: safeIndex,
|
|
1738
|
+
item: slot
|
|
1739
|
+
});
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
/** Replace the slot at `index` in place. */
|
|
1743
|
+
setSlotAt(key, index, slot) {
|
|
1744
|
+
this.withWrite(() => {
|
|
1745
|
+
const safeIndex = this.index.setSlotAt(key, index, slot);
|
|
1746
|
+
if (safeIndex < 0) return;
|
|
1747
|
+
this.appendCommand(key, {
|
|
1748
|
+
type: "set",
|
|
1749
|
+
index: safeIndex,
|
|
1750
|
+
item: slot
|
|
1689
1751
|
});
|
|
1690
1752
|
});
|
|
1691
1753
|
}
|
|
@@ -1693,12 +1755,15 @@ var OIMOrderedListCommandStream = class {
|
|
|
1693
1755
|
this.withWrite(() => {
|
|
1694
1756
|
const slot = this.index.removeAt(key, index);
|
|
1695
1757
|
if (slot === void 0) return;
|
|
1696
|
-
this.appendCommand(key, {
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1758
|
+
this.appendCommand(key, { type: "remove", index });
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
/** Remove `count` consecutive elements from `index`; emits `remove` with `count`. */
|
|
1762
|
+
removeRange(key, index, count) {
|
|
1763
|
+
this.withWrite(() => {
|
|
1764
|
+
const removed = this.index.removeRange(key, index, count);
|
|
1765
|
+
if (removed <= 0) return;
|
|
1766
|
+
this.appendCommand(key, { type: "remove", index, count: removed });
|
|
1702
1767
|
});
|
|
1703
1768
|
}
|
|
1704
1769
|
move(key, fromIndex, toIndex) {
|
|
@@ -1707,17 +1772,26 @@ var OIMOrderedListCommandStream = class {
|
|
|
1707
1772
|
if (slot === void 0) return;
|
|
1708
1773
|
this.appendCommand(key, {
|
|
1709
1774
|
type: "move",
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
fromIndex,
|
|
1713
|
-
toIndex
|
|
1775
|
+
from: fromIndex,
|
|
1776
|
+
to: toIndex
|
|
1714
1777
|
});
|
|
1715
1778
|
});
|
|
1716
1779
|
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Move `count` consecutive elements from `from` to `to`; emits `move` with
|
|
1782
|
+
* `count`. `to` is in the post-extraction coordinate space (as single `move`).
|
|
1783
|
+
*/
|
|
1784
|
+
moveRange(key, from, to, count) {
|
|
1785
|
+
this.withWrite(() => {
|
|
1786
|
+
const moved = this.index.moveRange(key, from, to, count);
|
|
1787
|
+
if (moved <= 0) return;
|
|
1788
|
+
this.appendCommand(key, { type: "move", from, to, count: moved });
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1717
1791
|
setSlots(key, slots) {
|
|
1718
1792
|
this.withWrite(() => {
|
|
1719
1793
|
this.index.resetSlots(key, slots);
|
|
1720
|
-
this.
|
|
1794
|
+
this.appendResetCommand(key, slots);
|
|
1721
1795
|
});
|
|
1722
1796
|
}
|
|
1723
1797
|
getBufferedCommands(key) {
|
|
@@ -1761,26 +1835,21 @@ var OIMOrderedListCommandStream = class {
|
|
|
1761
1835
|
cmds = [];
|
|
1762
1836
|
this.commandsByKey.set(key, cmds);
|
|
1763
1837
|
}
|
|
1764
|
-
if (cmd.type === "
|
|
1838
|
+
if (cmd.type === "reset") {
|
|
1765
1839
|
cmds.length = 0;
|
|
1766
1840
|
cmds.push(cmd);
|
|
1767
|
-
} else if (cmds.length > 0 && cmds[0].type === "
|
|
1768
|
-
|
|
1769
|
-
cmds[0] = this.createSetCommand(slots);
|
|
1841
|
+
} else if (cmds.length > 0 && cmds[0].type === "reset") {
|
|
1842
|
+
cmds[0] = this.createResetCommand(this.getSlotsByKey(key));
|
|
1770
1843
|
} else {
|
|
1771
1844
|
cmds.push(cmd);
|
|
1772
1845
|
}
|
|
1773
1846
|
this.commandsEventEmitter.markUpdatedKeys([key]);
|
|
1774
1847
|
}
|
|
1775
|
-
|
|
1776
|
-
this.appendCommand(key, this.
|
|
1848
|
+
appendResetCommand(key, slots) {
|
|
1849
|
+
this.appendCommand(key, this.createResetCommand(slots));
|
|
1777
1850
|
}
|
|
1778
|
-
|
|
1779
|
-
return {
|
|
1780
|
-
type: "set",
|
|
1781
|
-
pks: slots.map((slot) => slot.pk),
|
|
1782
|
-
slots
|
|
1783
|
-
};
|
|
1851
|
+
createResetCommand(slots) {
|
|
1852
|
+
return { type: "reset", items: slots };
|
|
1784
1853
|
}
|
|
1785
1854
|
};
|
|
1786
1855
|
|
|
@@ -1797,12 +1866,7 @@ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandS
|
|
|
1797
1866
|
this.withWrite(() => {
|
|
1798
1867
|
const slot = this.resolvePk(pk);
|
|
1799
1868
|
const index = this.index.pushSlot(key, slot);
|
|
1800
|
-
this.appendCommand(key, {
|
|
1801
|
-
type: "insert",
|
|
1802
|
-
pk,
|
|
1803
|
-
slot,
|
|
1804
|
-
index
|
|
1805
|
-
});
|
|
1869
|
+
this.appendCommand(key, { type: "insert", index, item: slot });
|
|
1806
1870
|
});
|
|
1807
1871
|
}
|
|
1808
1872
|
insertAt(key, index, pk) {
|
|
@@ -1811,9 +1875,21 @@ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandS
|
|
|
1811
1875
|
const safeIndex = this.index.insertSlotAt(key, index, slot);
|
|
1812
1876
|
this.appendCommand(key, {
|
|
1813
1877
|
type: "insert",
|
|
1814
|
-
|
|
1815
|
-
slot
|
|
1816
|
-
|
|
1878
|
+
index: safeIndex,
|
|
1879
|
+
item: slot
|
|
1880
|
+
});
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
/** Replace the element at `index` with the slot for `pk`, in place. */
|
|
1884
|
+
setAt(key, index, pk) {
|
|
1885
|
+
this.withWrite(() => {
|
|
1886
|
+
const slot = this.resolvePk(pk);
|
|
1887
|
+
const safeIndex = this.index.setSlotAt(key, index, slot);
|
|
1888
|
+
if (safeIndex < 0) return;
|
|
1889
|
+
this.appendCommand(key, {
|
|
1890
|
+
type: "set",
|
|
1891
|
+
index: safeIndex,
|
|
1892
|
+
item: slot
|
|
1817
1893
|
});
|
|
1818
1894
|
});
|
|
1819
1895
|
}
|
|
@@ -1821,7 +1897,7 @@ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandS
|
|
|
1821
1897
|
this.withWrite(() => {
|
|
1822
1898
|
const slots = pks.map((pk) => this.resolvePk(pk));
|
|
1823
1899
|
this.index.resetSlots(key, slots);
|
|
1824
|
-
this.
|
|
1900
|
+
this.appendResetCommand(key, slots);
|
|
1825
1901
|
});
|
|
1826
1902
|
}
|
|
1827
1903
|
getSlotsByKey(key) {
|
|
@@ -3400,20 +3476,27 @@ var OIMCollectionChangedFields = class {
|
|
|
3400
3476
|
constructor(queue, collection, opts) {
|
|
3401
3477
|
this.isInWrite = false;
|
|
3402
3478
|
this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
|
|
3479
|
+
// PKs written through this wrapper during the current flush. Their fields are
|
|
3480
|
+
// already tracked accurately via trackPatch, so the flush-time external-write
|
|
3481
|
+
// handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
|
|
3482
|
+
this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
|
|
3403
3483
|
this.changedFieldsByPk = /* @__PURE__ */ new Map();
|
|
3404
3484
|
this.changedPksByField = /* @__PURE__ */ new Map();
|
|
3405
3485
|
this.onAfterFlush = () => {
|
|
3406
3486
|
this.clear();
|
|
3487
|
+
this.writtenThroughThisFlush.clear();
|
|
3407
3488
|
};
|
|
3408
3489
|
this.onCollectionUpdate = (payload) => {
|
|
3409
3490
|
if (this.isInWrite) return;
|
|
3410
3491
|
if (payload.pks.length === 0) return;
|
|
3411
3492
|
for (let i = 0; i < payload.pks.length; i++) {
|
|
3412
3493
|
const pk = payload.pks[i];
|
|
3494
|
+
if (this.writtenThroughThisFlush.has(pk)) continue;
|
|
3413
3495
|
this.trackExternalMutationByPk(pk);
|
|
3414
3496
|
}
|
|
3415
3497
|
};
|
|
3416
3498
|
this.collection = collection;
|
|
3499
|
+
this.detectExternalMutations = opts?.detectExternalMutations !== false;
|
|
3417
3500
|
this.selectPk = opts?.selectPk ?? (() => {
|
|
3418
3501
|
throw new Error(
|
|
3419
3502
|
`[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
|
|
@@ -3428,15 +3511,17 @@ var OIMCollectionChangedFields = class {
|
|
|
3428
3511
|
0 /* AFTER_FLUSH */,
|
|
3429
3512
|
this.onAfterFlush
|
|
3430
3513
|
);
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
this.
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3514
|
+
if (this.detectExternalMutations) {
|
|
3515
|
+
const maybeReactive = collection;
|
|
3516
|
+
this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
|
|
3517
|
+
this.onCollectionUpdate({
|
|
3518
|
+
pks
|
|
3519
|
+
});
|
|
3520
|
+
}) : collection.emitter.on(
|
|
3521
|
+
0 /* UPDATE */,
|
|
3522
|
+
this.onCollectionUpdate
|
|
3523
|
+
);
|
|
3524
|
+
}
|
|
3440
3525
|
}
|
|
3441
3526
|
upsertOneByPk(pk, patch) {
|
|
3442
3527
|
const slot = this.withWrite(() => {
|
|
@@ -3492,7 +3577,7 @@ var OIMCollectionChangedFields = class {
|
|
|
3492
3577
|
this.changedPksEventEmitter.destroy();
|
|
3493
3578
|
this.changedFieldsEventEmitter.destroy();
|
|
3494
3579
|
this.unsubscribeAfterFlush();
|
|
3495
|
-
this.unsubscribeFromCollectionEmitter();
|
|
3580
|
+
this.unsubscribeFromCollectionEmitter?.();
|
|
3496
3581
|
this.clear();
|
|
3497
3582
|
}
|
|
3498
3583
|
withWrite(fn) {
|
|
@@ -3534,6 +3619,8 @@ var OIMCollectionChangedFields = class {
|
|
|
3534
3619
|
return changed;
|
|
3535
3620
|
}
|
|
3536
3621
|
updateEntitySnapshot(pk) {
|
|
3622
|
+
if (!this.detectExternalMutations) return;
|
|
3623
|
+
this.writtenThroughThisFlush.add(pk);
|
|
3537
3624
|
const entity = this.collection.getOneByPk(pk);
|
|
3538
3625
|
if (!entity) {
|
|
3539
3626
|
this.lastEntitySnapshotByPk.delete(pk);
|
|
@@ -3573,6 +3660,162 @@ var OIMCollectionChangedFields = class {
|
|
|
3573
3660
|
}
|
|
3574
3661
|
};
|
|
3575
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
|
+
|
|
3576
3819
|
// src/core/event-queue-scheduler/OIMEventQueueSchedulerAnimationFrame.ts
|
|
3577
3820
|
var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler {
|
|
3578
3821
|
constructor() {
|
|
@@ -4206,6 +4449,7 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
4206
4449
|
OIMObjectValueByKeySelector,
|
|
4207
4450
|
OIMObjectValuesByKeysSelector,
|
|
4208
4451
|
OIMOrderedListCommandStream,
|
|
4452
|
+
OIMOrderedListMappedCommandStream,
|
|
4209
4453
|
OIMPkSelectorFactory,
|
|
4210
4454
|
OIMReactiveCollection,
|
|
4211
4455
|
OIMReactiveCollectionIndexManualArrayBased,
|
|
@@ -4218,6 +4462,8 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
4218
4462
|
OIMReactiveObject,
|
|
4219
4463
|
OIMSelector,
|
|
4220
4464
|
createInPlaceEntityUpdater,
|
|
4465
|
+
createMergeEntityUpdater,
|
|
4221
4466
|
createOIMCollectionIndexFactory,
|
|
4222
|
-
createOIMCollectionKit
|
|
4467
|
+
createOIMCollectionKit,
|
|
4468
|
+
createOIMOrderedListMappedCommandStream
|
|
4223
4469
|
});
|