@oimdb/core 3.3.0 → 3.4.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 +139 -61
- package/dist/index.d.cts +83 -23
- package/dist/index.d.ts +83 -23
- package/dist/index.js +138 -61
- 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
|
@@ -95,6 +95,7 @@ __export(index_exports, {
|
|
|
95
95
|
OIMReactiveObject: () => OIMReactiveObject,
|
|
96
96
|
OIMSelector: () => OIMSelector,
|
|
97
97
|
createInPlaceEntityUpdater: () => createInPlaceEntityUpdater,
|
|
98
|
+
createMergeEntityUpdater: () => createMergeEntityUpdater,
|
|
98
99
|
createOIMCollectionIndexFactory: () => createOIMCollectionIndexFactory,
|
|
99
100
|
createOIMCollectionKit: () => createOIMCollectionKit
|
|
100
101
|
});
|
|
@@ -967,12 +968,15 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
|
|
|
967
968
|
}
|
|
968
969
|
};
|
|
969
970
|
|
|
971
|
+
// src/core/createMergeEntityUpdater.ts
|
|
972
|
+
function createMergeEntityUpdater() {
|
|
973
|
+
return (draft, prev) => ({ ...prev, ...draft });
|
|
974
|
+
}
|
|
975
|
+
|
|
970
976
|
// src/core/OIMEntityUpdaterFactory.ts
|
|
971
977
|
var OIMEntityUpdaterFactory = class {
|
|
972
978
|
createMergeEntityUpdater() {
|
|
973
|
-
return (
|
|
974
|
-
return { ...prev, ...draft };
|
|
975
|
-
};
|
|
979
|
+
return createMergeEntityUpdater();
|
|
976
980
|
}
|
|
977
981
|
};
|
|
978
982
|
|
|
@@ -1291,6 +1295,22 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1291
1295
|
this.emitUpdate([key]);
|
|
1292
1296
|
return removed;
|
|
1293
1297
|
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Remove up to `count` consecutive slots starting at `index`.
|
|
1300
|
+
* Returns the number actually removed (clamped to bounds; 0 if nothing).
|
|
1301
|
+
*/
|
|
1302
|
+
removeRange(key, index, count) {
|
|
1303
|
+
const list = this.store.getOneByKey(key);
|
|
1304
|
+
if (!list) return 0;
|
|
1305
|
+
if (index < 0 || index >= list.length || count <= 0) return 0;
|
|
1306
|
+
const actual = Math.min(count, list.length - index);
|
|
1307
|
+
list.splice(index, actual);
|
|
1308
|
+
if (list.length === 0) {
|
|
1309
|
+
this.store.removeOneByKey(key);
|
|
1310
|
+
}
|
|
1311
|
+
this.emitUpdate([key]);
|
|
1312
|
+
return actual;
|
|
1313
|
+
}
|
|
1294
1314
|
move(key, fromIndex, toIndex) {
|
|
1295
1315
|
const list = this.store.getOneByKey(key);
|
|
1296
1316
|
if (!list) return void 0;
|
|
@@ -1303,6 +1323,32 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1303
1323
|
this.emitUpdate([key]);
|
|
1304
1324
|
return slot;
|
|
1305
1325
|
}
|
|
1326
|
+
/** Replace the slot at `index` in place. Returns the safe index, or -1. */
|
|
1327
|
+
setSlotAt(key, index, slot) {
|
|
1328
|
+
const list = this.store.getOneByKey(key);
|
|
1329
|
+
if (!list) return -1;
|
|
1330
|
+
if (index < 0 || index >= list.length) return -1;
|
|
1331
|
+
list[index] = slot;
|
|
1332
|
+
this.emitUpdate([key]);
|
|
1333
|
+
return index;
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Move up to `count` consecutive slots from `from` to `to`. `to` is in the
|
|
1337
|
+
* coordinate space *after* the block is extracted (same as single `move`).
|
|
1338
|
+
* Returns the number actually moved (0 if a no-op / out of bounds).
|
|
1339
|
+
*/
|
|
1340
|
+
moveRange(key, from, to, count) {
|
|
1341
|
+
const list = this.store.getOneByKey(key);
|
|
1342
|
+
if (!list) return 0;
|
|
1343
|
+
if (from < 0 || from >= list.length || count <= 0) return 0;
|
|
1344
|
+
const actual = Math.min(count, list.length - from);
|
|
1345
|
+
const insertAt = Math.max(0, Math.min(to, list.length - actual));
|
|
1346
|
+
if (insertAt === from) return 0;
|
|
1347
|
+
const block = list.splice(from, actual);
|
|
1348
|
+
list.splice(insertAt, 0, ...block);
|
|
1349
|
+
this.emitUpdate([key]);
|
|
1350
|
+
return actual;
|
|
1351
|
+
}
|
|
1306
1352
|
resetSlots(key, slots) {
|
|
1307
1353
|
if (slots.length === 0) {
|
|
1308
1354
|
if (this.store.getOneByKey(key) !== void 0) {
|
|
@@ -1513,15 +1559,15 @@ var OIMUpdateEventEmitter = class {
|
|
|
1513
1559
|
const flushingKeys = this.updatedKeys;
|
|
1514
1560
|
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
1515
1561
|
if (this.keyHandlers.size === 0 || flushingKeys.size === 0) return;
|
|
1516
|
-
if (flushingKeys.size
|
|
1517
|
-
|
|
1518
|
-
|
|
1562
|
+
if (flushingKeys.size === this.keyHandlers.size) {
|
|
1563
|
+
this.keyHandlers.forEach((handlers, key) => {
|
|
1564
|
+
if (!flushingKeys.has(key)) return;
|
|
1519
1565
|
if (!handlers || handlers.size === 0) return;
|
|
1520
1566
|
this.notifyHandlers(handlers);
|
|
1521
1567
|
});
|
|
1522
1568
|
} else {
|
|
1523
|
-
|
|
1524
|
-
|
|
1569
|
+
flushingKeys.forEach((key) => {
|
|
1570
|
+
const handlers = this.keyHandlers.get(key);
|
|
1525
1571
|
if (!handlers || handlers.size === 0) return;
|
|
1526
1572
|
this.notifyHandlers(handlers);
|
|
1527
1573
|
});
|
|
@@ -1633,7 +1679,7 @@ var OIMOrderedListCommandStream = class {
|
|
|
1633
1679
|
if (payload.keys.length === 0) return;
|
|
1634
1680
|
for (let i = 0; i < payload.keys.length; i++) {
|
|
1635
1681
|
const key = payload.keys[i];
|
|
1636
|
-
this.
|
|
1682
|
+
this.appendResetCommand(key, this.getSlotsByKey(key));
|
|
1637
1683
|
}
|
|
1638
1684
|
};
|
|
1639
1685
|
this.commandsEventEmitter = new OIMUpdateEventEmitter(
|
|
@@ -1670,12 +1716,7 @@ var OIMOrderedListCommandStream = class {
|
|
|
1670
1716
|
pushSlot(key, slot) {
|
|
1671
1717
|
this.withWrite(() => {
|
|
1672
1718
|
const index = this.index.pushSlot(key, slot);
|
|
1673
|
-
this.appendCommand(key, {
|
|
1674
|
-
type: "insert",
|
|
1675
|
-
pk: slot.pk,
|
|
1676
|
-
slot,
|
|
1677
|
-
index
|
|
1678
|
-
});
|
|
1719
|
+
this.appendCommand(key, { type: "insert", index, item: slot });
|
|
1679
1720
|
});
|
|
1680
1721
|
}
|
|
1681
1722
|
insertSlotAt(key, index, slot) {
|
|
@@ -1683,9 +1724,20 @@ var OIMOrderedListCommandStream = class {
|
|
|
1683
1724
|
const safeIndex = this.index.insertSlotAt(key, index, slot);
|
|
1684
1725
|
this.appendCommand(key, {
|
|
1685
1726
|
type: "insert",
|
|
1686
|
-
|
|
1687
|
-
slot
|
|
1688
|
-
|
|
1727
|
+
index: safeIndex,
|
|
1728
|
+
item: slot
|
|
1729
|
+
});
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
/** Replace the slot at `index` in place. */
|
|
1733
|
+
setSlotAt(key, index, slot) {
|
|
1734
|
+
this.withWrite(() => {
|
|
1735
|
+
const safeIndex = this.index.setSlotAt(key, index, slot);
|
|
1736
|
+
if (safeIndex < 0) return;
|
|
1737
|
+
this.appendCommand(key, {
|
|
1738
|
+
type: "set",
|
|
1739
|
+
index: safeIndex,
|
|
1740
|
+
item: slot
|
|
1689
1741
|
});
|
|
1690
1742
|
});
|
|
1691
1743
|
}
|
|
@@ -1693,12 +1745,15 @@ var OIMOrderedListCommandStream = class {
|
|
|
1693
1745
|
this.withWrite(() => {
|
|
1694
1746
|
const slot = this.index.removeAt(key, index);
|
|
1695
1747
|
if (slot === void 0) return;
|
|
1696
|
-
this.appendCommand(key, {
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1748
|
+
this.appendCommand(key, { type: "remove", index });
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
/** Remove `count` consecutive elements from `index`; emits `remove` with `count`. */
|
|
1752
|
+
removeRange(key, index, count) {
|
|
1753
|
+
this.withWrite(() => {
|
|
1754
|
+
const removed = this.index.removeRange(key, index, count);
|
|
1755
|
+
if (removed <= 0) return;
|
|
1756
|
+
this.appendCommand(key, { type: "remove", index, count: removed });
|
|
1702
1757
|
});
|
|
1703
1758
|
}
|
|
1704
1759
|
move(key, fromIndex, toIndex) {
|
|
@@ -1707,17 +1762,26 @@ var OIMOrderedListCommandStream = class {
|
|
|
1707
1762
|
if (slot === void 0) return;
|
|
1708
1763
|
this.appendCommand(key, {
|
|
1709
1764
|
type: "move",
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
fromIndex,
|
|
1713
|
-
toIndex
|
|
1765
|
+
from: fromIndex,
|
|
1766
|
+
to: toIndex
|
|
1714
1767
|
});
|
|
1715
1768
|
});
|
|
1716
1769
|
}
|
|
1770
|
+
/**
|
|
1771
|
+
* Move `count` consecutive elements from `from` to `to`; emits `move` with
|
|
1772
|
+
* `count`. `to` is in the post-extraction coordinate space (as single `move`).
|
|
1773
|
+
*/
|
|
1774
|
+
moveRange(key, from, to, count) {
|
|
1775
|
+
this.withWrite(() => {
|
|
1776
|
+
const moved = this.index.moveRange(key, from, to, count);
|
|
1777
|
+
if (moved <= 0) return;
|
|
1778
|
+
this.appendCommand(key, { type: "move", from, to, count: moved });
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1717
1781
|
setSlots(key, slots) {
|
|
1718
1782
|
this.withWrite(() => {
|
|
1719
1783
|
this.index.resetSlots(key, slots);
|
|
1720
|
-
this.
|
|
1784
|
+
this.appendResetCommand(key, slots);
|
|
1721
1785
|
});
|
|
1722
1786
|
}
|
|
1723
1787
|
getBufferedCommands(key) {
|
|
@@ -1761,26 +1825,21 @@ var OIMOrderedListCommandStream = class {
|
|
|
1761
1825
|
cmds = [];
|
|
1762
1826
|
this.commandsByKey.set(key, cmds);
|
|
1763
1827
|
}
|
|
1764
|
-
if (cmd.type === "
|
|
1828
|
+
if (cmd.type === "reset") {
|
|
1765
1829
|
cmds.length = 0;
|
|
1766
1830
|
cmds.push(cmd);
|
|
1767
|
-
} else if (cmds.length > 0 && cmds[0].type === "
|
|
1768
|
-
|
|
1769
|
-
cmds[0] = this.createSetCommand(slots);
|
|
1831
|
+
} else if (cmds.length > 0 && cmds[0].type === "reset") {
|
|
1832
|
+
cmds[0] = this.createResetCommand(this.getSlotsByKey(key));
|
|
1770
1833
|
} else {
|
|
1771
1834
|
cmds.push(cmd);
|
|
1772
1835
|
}
|
|
1773
1836
|
this.commandsEventEmitter.markUpdatedKeys([key]);
|
|
1774
1837
|
}
|
|
1775
|
-
|
|
1776
|
-
this.appendCommand(key, this.
|
|
1838
|
+
appendResetCommand(key, slots) {
|
|
1839
|
+
this.appendCommand(key, this.createResetCommand(slots));
|
|
1777
1840
|
}
|
|
1778
|
-
|
|
1779
|
-
return {
|
|
1780
|
-
type: "set",
|
|
1781
|
-
pks: slots.map((slot) => slot.pk),
|
|
1782
|
-
slots
|
|
1783
|
-
};
|
|
1841
|
+
createResetCommand(slots) {
|
|
1842
|
+
return { type: "reset", items: slots };
|
|
1784
1843
|
}
|
|
1785
1844
|
};
|
|
1786
1845
|
|
|
@@ -1797,12 +1856,7 @@ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandS
|
|
|
1797
1856
|
this.withWrite(() => {
|
|
1798
1857
|
const slot = this.resolvePk(pk);
|
|
1799
1858
|
const index = this.index.pushSlot(key, slot);
|
|
1800
|
-
this.appendCommand(key, {
|
|
1801
|
-
type: "insert",
|
|
1802
|
-
pk,
|
|
1803
|
-
slot,
|
|
1804
|
-
index
|
|
1805
|
-
});
|
|
1859
|
+
this.appendCommand(key, { type: "insert", index, item: slot });
|
|
1806
1860
|
});
|
|
1807
1861
|
}
|
|
1808
1862
|
insertAt(key, index, pk) {
|
|
@@ -1811,9 +1865,21 @@ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandS
|
|
|
1811
1865
|
const safeIndex = this.index.insertSlotAt(key, index, slot);
|
|
1812
1866
|
this.appendCommand(key, {
|
|
1813
1867
|
type: "insert",
|
|
1814
|
-
|
|
1815
|
-
slot
|
|
1816
|
-
|
|
1868
|
+
index: safeIndex,
|
|
1869
|
+
item: slot
|
|
1870
|
+
});
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
/** Replace the element at `index` with the slot for `pk`, in place. */
|
|
1874
|
+
setAt(key, index, pk) {
|
|
1875
|
+
this.withWrite(() => {
|
|
1876
|
+
const slot = this.resolvePk(pk);
|
|
1877
|
+
const safeIndex = this.index.setSlotAt(key, index, slot);
|
|
1878
|
+
if (safeIndex < 0) return;
|
|
1879
|
+
this.appendCommand(key, {
|
|
1880
|
+
type: "set",
|
|
1881
|
+
index: safeIndex,
|
|
1882
|
+
item: slot
|
|
1817
1883
|
});
|
|
1818
1884
|
});
|
|
1819
1885
|
}
|
|
@@ -1821,7 +1887,7 @@ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandS
|
|
|
1821
1887
|
this.withWrite(() => {
|
|
1822
1888
|
const slots = pks.map((pk) => this.resolvePk(pk));
|
|
1823
1889
|
this.index.resetSlots(key, slots);
|
|
1824
|
-
this.
|
|
1890
|
+
this.appendResetCommand(key, slots);
|
|
1825
1891
|
});
|
|
1826
1892
|
}
|
|
1827
1893
|
getSlotsByKey(key) {
|
|
@@ -3400,20 +3466,27 @@ var OIMCollectionChangedFields = class {
|
|
|
3400
3466
|
constructor(queue, collection, opts) {
|
|
3401
3467
|
this.isInWrite = false;
|
|
3402
3468
|
this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
|
|
3469
|
+
// PKs written through this wrapper during the current flush. Their fields are
|
|
3470
|
+
// already tracked accurately via trackPatch, so the flush-time external-write
|
|
3471
|
+
// handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
|
|
3472
|
+
this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
|
|
3403
3473
|
this.changedFieldsByPk = /* @__PURE__ */ new Map();
|
|
3404
3474
|
this.changedPksByField = /* @__PURE__ */ new Map();
|
|
3405
3475
|
this.onAfterFlush = () => {
|
|
3406
3476
|
this.clear();
|
|
3477
|
+
this.writtenThroughThisFlush.clear();
|
|
3407
3478
|
};
|
|
3408
3479
|
this.onCollectionUpdate = (payload) => {
|
|
3409
3480
|
if (this.isInWrite) return;
|
|
3410
3481
|
if (payload.pks.length === 0) return;
|
|
3411
3482
|
for (let i = 0; i < payload.pks.length; i++) {
|
|
3412
3483
|
const pk = payload.pks[i];
|
|
3484
|
+
if (this.writtenThroughThisFlush.has(pk)) continue;
|
|
3413
3485
|
this.trackExternalMutationByPk(pk);
|
|
3414
3486
|
}
|
|
3415
3487
|
};
|
|
3416
3488
|
this.collection = collection;
|
|
3489
|
+
this.detectExternalMutations = opts?.detectExternalMutations !== false;
|
|
3417
3490
|
this.selectPk = opts?.selectPk ?? (() => {
|
|
3418
3491
|
throw new Error(
|
|
3419
3492
|
`[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
|
|
@@ -3428,15 +3501,17 @@ var OIMCollectionChangedFields = class {
|
|
|
3428
3501
|
0 /* AFTER_FLUSH */,
|
|
3429
3502
|
this.onAfterFlush
|
|
3430
3503
|
);
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
this.
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3504
|
+
if (this.detectExternalMutations) {
|
|
3505
|
+
const maybeReactive = collection;
|
|
3506
|
+
this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
|
|
3507
|
+
this.onCollectionUpdate({
|
|
3508
|
+
pks
|
|
3509
|
+
});
|
|
3510
|
+
}) : collection.emitter.on(
|
|
3511
|
+
0 /* UPDATE */,
|
|
3512
|
+
this.onCollectionUpdate
|
|
3513
|
+
);
|
|
3514
|
+
}
|
|
3440
3515
|
}
|
|
3441
3516
|
upsertOneByPk(pk, patch) {
|
|
3442
3517
|
const slot = this.withWrite(() => {
|
|
@@ -3492,7 +3567,7 @@ var OIMCollectionChangedFields = class {
|
|
|
3492
3567
|
this.changedPksEventEmitter.destroy();
|
|
3493
3568
|
this.changedFieldsEventEmitter.destroy();
|
|
3494
3569
|
this.unsubscribeAfterFlush();
|
|
3495
|
-
this.unsubscribeFromCollectionEmitter();
|
|
3570
|
+
this.unsubscribeFromCollectionEmitter?.();
|
|
3496
3571
|
this.clear();
|
|
3497
3572
|
}
|
|
3498
3573
|
withWrite(fn) {
|
|
@@ -3534,6 +3609,8 @@ var OIMCollectionChangedFields = class {
|
|
|
3534
3609
|
return changed;
|
|
3535
3610
|
}
|
|
3536
3611
|
updateEntitySnapshot(pk) {
|
|
3612
|
+
if (!this.detectExternalMutations) return;
|
|
3613
|
+
this.writtenThroughThisFlush.add(pk);
|
|
3537
3614
|
const entity = this.collection.getOneByPk(pk);
|
|
3538
3615
|
if (!entity) {
|
|
3539
3616
|
this.lastEntitySnapshotByPk.delete(pk);
|
|
@@ -4218,6 +4295,7 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
4218
4295
|
OIMReactiveObject,
|
|
4219
4296
|
OIMSelector,
|
|
4220
4297
|
createInPlaceEntityUpdater,
|
|
4298
|
+
createMergeEntityUpdater,
|
|
4221
4299
|
createOIMCollectionIndexFactory,
|
|
4222
4300
|
createOIMCollectionKit
|
|
4223
4301
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -627,7 +627,20 @@ declare class OIMIndexManualOrderedArrayBased<TKey extends TOIMPk, TItemKey exte
|
|
|
627
627
|
pushSlot(key: TKey, slot: TOIMAnyEntitySlot<TItemKey>): number;
|
|
628
628
|
insertSlotAt(key: TKey, index: number, slot: TOIMAnyEntitySlot<TItemKey>): number;
|
|
629
629
|
removeAt(key: TKey, index: number): TOIMAnyEntitySlot<TItemKey> | undefined;
|
|
630
|
+
/**
|
|
631
|
+
* Remove up to `count` consecutive slots starting at `index`.
|
|
632
|
+
* Returns the number actually removed (clamped to bounds; 0 if nothing).
|
|
633
|
+
*/
|
|
634
|
+
removeRange(key: TKey, index: number, count: number): number;
|
|
630
635
|
move(key: TKey, fromIndex: number, toIndex: number): TOIMAnyEntitySlot<TItemKey> | undefined;
|
|
636
|
+
/** Replace the slot at `index` in place. Returns the safe index, or -1. */
|
|
637
|
+
setSlotAt(key: TKey, index: number, slot: TOIMAnyEntitySlot<TItemKey>): number;
|
|
638
|
+
/**
|
|
639
|
+
* Move up to `count` consecutive slots from `from` to `to`. `to` is in the
|
|
640
|
+
* coordinate space *after* the block is extracted (same as single `move`).
|
|
641
|
+
* Returns the number actually moved (0 if a no-op / out of bounds).
|
|
642
|
+
*/
|
|
643
|
+
moveRange(key: TKey, from: number, to: number, count: number): number;
|
|
631
644
|
resetSlots(key: TKey, slots: readonly TOIMAnyEntitySlot<TItemKey>[]): void;
|
|
632
645
|
private getOrCreateList;
|
|
633
646
|
}
|
|
@@ -712,36 +725,46 @@ declare class OIMUpdateEventEmitter<TKey extends TOIMPk> {
|
|
|
712
725
|
unsubscribeFromKeys(keys: readonly TKey[], handler: TOIMEventHandler<void>): void;
|
|
713
726
|
}
|
|
714
727
|
|
|
715
|
-
|
|
728
|
+
/**
|
|
729
|
+
* Incremental command for an ordered per-key list, addressed by position.
|
|
730
|
+
*
|
|
731
|
+
* `item` is whatever the stream stores per element — the command stream uses the
|
|
732
|
+
* entity slot (`TOIMEntitySlot`), so a consumer reads `item.item` for the entity.
|
|
733
|
+
*
|
|
734
|
+
* - `insert` — a single element appears at `index`.
|
|
735
|
+
* - `remove` — `count` elements (default 1, may be > 1) disappear from `index`.
|
|
736
|
+
* - `move` — `count` elements (default 1) move from `from` to `to`.
|
|
737
|
+
* - `set` — the element at `index` is replaced in place.
|
|
738
|
+
* - `reset` — the whole list for the key is replaced by `items`.
|
|
739
|
+
*/
|
|
740
|
+
type TOIMOrderedListCommand<TItem> = {
|
|
716
741
|
type: 'insert';
|
|
717
|
-
pk: TPk;
|
|
718
|
-
slot: TOIMEntitySlot<TEntity, TPk>;
|
|
719
742
|
index: number;
|
|
743
|
+
item: TItem;
|
|
720
744
|
} | {
|
|
721
745
|
type: 'remove';
|
|
722
|
-
pk: TPk;
|
|
723
|
-
slot: TOIMEntitySlot<TEntity, TPk>;
|
|
724
746
|
index: number;
|
|
747
|
+
count?: number;
|
|
725
748
|
} | {
|
|
726
749
|
type: 'move';
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
toIndex: number;
|
|
750
|
+
from: number;
|
|
751
|
+
to: number;
|
|
752
|
+
count?: number;
|
|
731
753
|
} | {
|
|
732
|
-
/**
|
|
733
|
-
* Replace the whole list for this key (preferred name).
|
|
734
|
-
*/
|
|
735
754
|
type: 'set';
|
|
736
|
-
|
|
737
|
-
|
|
755
|
+
index: number;
|
|
756
|
+
item: TItem;
|
|
757
|
+
} | {
|
|
758
|
+
type: 'reset';
|
|
759
|
+
items: readonly TItem[];
|
|
738
760
|
};
|
|
739
761
|
|
|
740
762
|
/**
|
|
741
763
|
* Slot-first ordered-list command stream.
|
|
742
764
|
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
765
|
+
* Emits position-addressed {@link TOIMOrderedListCommand}s whose `item` is the
|
|
766
|
+
* entity slot. Use this when you already have slots and want incremental
|
|
767
|
+
* commands for an imperative renderer. For PK writes bound to a collection, use
|
|
745
768
|
* `OIMCollectionOrderedListCommandStream`.
|
|
746
769
|
*/
|
|
747
770
|
declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMPk, TEntity extends object = object> {
|
|
@@ -760,19 +783,28 @@ declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMP
|
|
|
760
783
|
getKeys(): readonly TKey[];
|
|
761
784
|
pushSlot(key: TKey, slot: TOIMEntitySlot<TEntity, TPk>): void;
|
|
762
785
|
insertSlotAt(key: TKey, index: number, slot: TOIMEntitySlot<TEntity, TPk>): void;
|
|
786
|
+
/** Replace the slot at `index` in place. */
|
|
787
|
+
setSlotAt(key: TKey, index: number, slot: TOIMEntitySlot<TEntity, TPk>): void;
|
|
763
788
|
removeAt(key: TKey, index: number): void;
|
|
789
|
+
/** Remove `count` consecutive elements from `index`; emits `remove` with `count`. */
|
|
790
|
+
removeRange(key: TKey, index: number, count: number): void;
|
|
764
791
|
move(key: TKey, fromIndex: number, toIndex: number): void;
|
|
792
|
+
/**
|
|
793
|
+
* Move `count` consecutive elements from `from` to `to`; emits `move` with
|
|
794
|
+
* `count`. `to` is in the post-extraction coordinate space (as single `move`).
|
|
795
|
+
*/
|
|
796
|
+
moveRange(key: TKey, from: number, to: number, count: number): void;
|
|
765
797
|
setSlots(key: TKey, slots: readonly TOIMEntitySlot<TEntity, TPk>[]): void;
|
|
766
|
-
getBufferedCommands(key: TKey): readonly TOIMOrderedListCommand<
|
|
767
|
-
consumeCommands(key: TKey): TOIMOrderedListCommand<
|
|
798
|
+
getBufferedCommands(key: TKey): readonly TOIMOrderedListCommand<TOIMEntitySlot<TEntity, TPk>>[];
|
|
799
|
+
consumeCommands(key: TKey): TOIMOrderedListCommand<TOIMEntitySlot<TEntity, TPk>>[];
|
|
768
800
|
clear(key?: TKey): void;
|
|
769
801
|
destroy(): void;
|
|
770
802
|
private readonly onAfterFlush;
|
|
771
803
|
private readonly onIndexUpdate;
|
|
772
804
|
protected withWrite(fn: () => void): void;
|
|
773
|
-
protected appendCommand(key: TKey, cmd: TOIMOrderedListCommand<
|
|
774
|
-
protected
|
|
775
|
-
protected
|
|
805
|
+
protected appendCommand(key: TKey, cmd: TOIMOrderedListCommand<TOIMEntitySlot<TEntity, TPk>>): void;
|
|
806
|
+
protected appendResetCommand(key: TKey, slots: readonly TOIMEntitySlot<TEntity, TPk>[]): void;
|
|
807
|
+
protected createResetCommand(slots: readonly TOIMEntitySlot<TEntity, TPk>[]): TOIMOrderedListCommand<TOIMEntitySlot<TEntity, TPk>>;
|
|
776
808
|
}
|
|
777
809
|
|
|
778
810
|
/**
|
|
@@ -785,6 +817,8 @@ declare class OIMCollectionOrderedListCommandStream<TKey extends TOIMPk, TPk ext
|
|
|
785
817
|
constructor(queue: OIMEventQueue, opts: TOIMCollectionOrderedListCommandStreamOptions<TKey, TPk, TEntity>);
|
|
786
818
|
push(key: TKey, pk: TPk): void;
|
|
787
819
|
insertAt(key: TKey, index: number, pk: TPk): void;
|
|
820
|
+
/** Replace the element at `index` with the slot for `pk`, in place. */
|
|
821
|
+
setAt(key: TKey, index: number, pk: TPk): void;
|
|
788
822
|
set(key: TKey, pks: readonly TPk[]): void;
|
|
789
823
|
getSlotsByKey(key: TKey): readonly TOIMEntitySlot<TEntity, TPk>[];
|
|
790
824
|
getEntitiesByKey(key: TKey): (TEntity | undefined)[];
|
|
@@ -1149,6 +1183,18 @@ declare class OIMEntityUpdaterFactory<TEntity extends object> {
|
|
|
1149
1183
|
*/
|
|
1150
1184
|
declare function createInPlaceEntityUpdater<TEntity extends object>(): TOIMEntityUpdater<TEntity>;
|
|
1151
1185
|
|
|
1186
|
+
/**
|
|
1187
|
+
* Default entity updater: shallow-merges the patch into a NEW object
|
|
1188
|
+
* (`{ ...prev, ...draft }`). Each update produces a fresh entity reference, which
|
|
1189
|
+
* reference-comparison bindings (React's `Object.is` / `useSyncExternalStore`,
|
|
1190
|
+
* `React.memo`, prev/next diffing, time-travel) need to detect a change.
|
|
1191
|
+
*
|
|
1192
|
+
* The new-object allocation is the biggest per-update data-layer cost; for
|
|
1193
|
+
* update-heavy, subscription-driven readers use {@link createInPlaceEntityUpdater}
|
|
1194
|
+
* instead.
|
|
1195
|
+
*/
|
|
1196
|
+
declare function createMergeEntityUpdater<TEntity extends object>(): TOIMEntityUpdater<TEntity>;
|
|
1197
|
+
|
|
1152
1198
|
/**
|
|
1153
1199
|
* Factory for creating index comparators.
|
|
1154
1200
|
* Provides common comparison strategies for primary key arrays.
|
|
@@ -1297,9 +1343,11 @@ declare class OIMCollectionChangedFields<TEntity extends object, TPk extends TOI
|
|
|
1297
1343
|
readonly collection: OIMCollection<TEntity, TPk>;
|
|
1298
1344
|
private readonly selectPk;
|
|
1299
1345
|
private readonly unsubscribeAfterFlush;
|
|
1300
|
-
private readonly unsubscribeFromCollectionEmitter
|
|
1346
|
+
private readonly unsubscribeFromCollectionEmitter?;
|
|
1347
|
+
private readonly detectExternalMutations;
|
|
1301
1348
|
private isInWrite;
|
|
1302
1349
|
private readonly lastEntitySnapshotByPk;
|
|
1350
|
+
private readonly writtenThroughThisFlush;
|
|
1303
1351
|
/**
|
|
1304
1352
|
* Emits updates scoped by PK. Subscribe, then read state via `getChangedFieldsByPk(...)`
|
|
1305
1353
|
* or drain via `consumeChangedFieldsByPk(...)`.
|
|
@@ -1318,6 +1366,18 @@ declare class OIMCollectionChangedFields<TEntity extends object, TPk extends TOI
|
|
|
1318
1366
|
* If not provided, only `upsertOneByPk(...)` should be used.
|
|
1319
1367
|
*/
|
|
1320
1368
|
selectPk?: (entity: TEntity | Partial<TEntity>) => TPk;
|
|
1369
|
+
/**
|
|
1370
|
+
* Detect writes that bypass this wrapper (direct collection
|
|
1371
|
+
* mutations) by diffing entity snapshots. Default `true`.
|
|
1372
|
+
*
|
|
1373
|
+
* Set to `false` when you always write through this wrapper: it
|
|
1374
|
+
* drops the per-write snapshot clone and the per-PK snapshot map
|
|
1375
|
+
* (~2x entity memory), since those exist only to power external
|
|
1376
|
+
* detection. The shallow diff also can't see deep in-place
|
|
1377
|
+
* mutations anyway — so if that's your model, opting out costs you
|
|
1378
|
+
* nothing it could have caught.
|
|
1379
|
+
*/
|
|
1380
|
+
detectExternalMutations?: boolean;
|
|
1321
1381
|
});
|
|
1322
1382
|
upsertOneByPk(pk: TPk, patch: Partial<TEntity>): TOIMEntitySlot<TEntity, TPk>;
|
|
1323
1383
|
upsertOne(entity: TEntity | Partial<TEntity>): TOIMEntitySlot<TEntity, TPk>;
|
|
@@ -1690,4 +1750,4 @@ declare class OIMEntitiesByIndexKeyArrayBasedSelector<TEntity extends object, TP
|
|
|
1690
1750
|
protected areEqual(prev: readonly (TEntity | undefined)[], next: readonly (TEntity | undefined)[]): boolean;
|
|
1691
1751
|
}
|
|
1692
1752
|
|
|
1693
|
-
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, createOIMCollectionIndexFactory, createOIMCollectionKit };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -627,7 +627,20 @@ declare class OIMIndexManualOrderedArrayBased<TKey extends TOIMPk, TItemKey exte
|
|
|
627
627
|
pushSlot(key: TKey, slot: TOIMAnyEntitySlot<TItemKey>): number;
|
|
628
628
|
insertSlotAt(key: TKey, index: number, slot: TOIMAnyEntitySlot<TItemKey>): number;
|
|
629
629
|
removeAt(key: TKey, index: number): TOIMAnyEntitySlot<TItemKey> | undefined;
|
|
630
|
+
/**
|
|
631
|
+
* Remove up to `count` consecutive slots starting at `index`.
|
|
632
|
+
* Returns the number actually removed (clamped to bounds; 0 if nothing).
|
|
633
|
+
*/
|
|
634
|
+
removeRange(key: TKey, index: number, count: number): number;
|
|
630
635
|
move(key: TKey, fromIndex: number, toIndex: number): TOIMAnyEntitySlot<TItemKey> | undefined;
|
|
636
|
+
/** Replace the slot at `index` in place. Returns the safe index, or -1. */
|
|
637
|
+
setSlotAt(key: TKey, index: number, slot: TOIMAnyEntitySlot<TItemKey>): number;
|
|
638
|
+
/**
|
|
639
|
+
* Move up to `count` consecutive slots from `from` to `to`. `to` is in the
|
|
640
|
+
* coordinate space *after* the block is extracted (same as single `move`).
|
|
641
|
+
* Returns the number actually moved (0 if a no-op / out of bounds).
|
|
642
|
+
*/
|
|
643
|
+
moveRange(key: TKey, from: number, to: number, count: number): number;
|
|
631
644
|
resetSlots(key: TKey, slots: readonly TOIMAnyEntitySlot<TItemKey>[]): void;
|
|
632
645
|
private getOrCreateList;
|
|
633
646
|
}
|
|
@@ -712,36 +725,46 @@ declare class OIMUpdateEventEmitter<TKey extends TOIMPk> {
|
|
|
712
725
|
unsubscribeFromKeys(keys: readonly TKey[], handler: TOIMEventHandler<void>): void;
|
|
713
726
|
}
|
|
714
727
|
|
|
715
|
-
|
|
728
|
+
/**
|
|
729
|
+
* Incremental command for an ordered per-key list, addressed by position.
|
|
730
|
+
*
|
|
731
|
+
* `item` is whatever the stream stores per element — the command stream uses the
|
|
732
|
+
* entity slot (`TOIMEntitySlot`), so a consumer reads `item.item` for the entity.
|
|
733
|
+
*
|
|
734
|
+
* - `insert` — a single element appears at `index`.
|
|
735
|
+
* - `remove` — `count` elements (default 1, may be > 1) disappear from `index`.
|
|
736
|
+
* - `move` — `count` elements (default 1) move from `from` to `to`.
|
|
737
|
+
* - `set` — the element at `index` is replaced in place.
|
|
738
|
+
* - `reset` — the whole list for the key is replaced by `items`.
|
|
739
|
+
*/
|
|
740
|
+
type TOIMOrderedListCommand<TItem> = {
|
|
716
741
|
type: 'insert';
|
|
717
|
-
pk: TPk;
|
|
718
|
-
slot: TOIMEntitySlot<TEntity, TPk>;
|
|
719
742
|
index: number;
|
|
743
|
+
item: TItem;
|
|
720
744
|
} | {
|
|
721
745
|
type: 'remove';
|
|
722
|
-
pk: TPk;
|
|
723
|
-
slot: TOIMEntitySlot<TEntity, TPk>;
|
|
724
746
|
index: number;
|
|
747
|
+
count?: number;
|
|
725
748
|
} | {
|
|
726
749
|
type: 'move';
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
toIndex: number;
|
|
750
|
+
from: number;
|
|
751
|
+
to: number;
|
|
752
|
+
count?: number;
|
|
731
753
|
} | {
|
|
732
|
-
/**
|
|
733
|
-
* Replace the whole list for this key (preferred name).
|
|
734
|
-
*/
|
|
735
754
|
type: 'set';
|
|
736
|
-
|
|
737
|
-
|
|
755
|
+
index: number;
|
|
756
|
+
item: TItem;
|
|
757
|
+
} | {
|
|
758
|
+
type: 'reset';
|
|
759
|
+
items: readonly TItem[];
|
|
738
760
|
};
|
|
739
761
|
|
|
740
762
|
/**
|
|
741
763
|
* Slot-first ordered-list command stream.
|
|
742
764
|
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
765
|
+
* Emits position-addressed {@link TOIMOrderedListCommand}s whose `item` is the
|
|
766
|
+
* entity slot. Use this when you already have slots and want incremental
|
|
767
|
+
* commands for an imperative renderer. For PK writes bound to a collection, use
|
|
745
768
|
* `OIMCollectionOrderedListCommandStream`.
|
|
746
769
|
*/
|
|
747
770
|
declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMPk, TEntity extends object = object> {
|
|
@@ -760,19 +783,28 @@ declare class OIMOrderedListCommandStream<TKey extends TOIMPk, TPk extends TOIMP
|
|
|
760
783
|
getKeys(): readonly TKey[];
|
|
761
784
|
pushSlot(key: TKey, slot: TOIMEntitySlot<TEntity, TPk>): void;
|
|
762
785
|
insertSlotAt(key: TKey, index: number, slot: TOIMEntitySlot<TEntity, TPk>): void;
|
|
786
|
+
/** Replace the slot at `index` in place. */
|
|
787
|
+
setSlotAt(key: TKey, index: number, slot: TOIMEntitySlot<TEntity, TPk>): void;
|
|
763
788
|
removeAt(key: TKey, index: number): void;
|
|
789
|
+
/** Remove `count` consecutive elements from `index`; emits `remove` with `count`. */
|
|
790
|
+
removeRange(key: TKey, index: number, count: number): void;
|
|
764
791
|
move(key: TKey, fromIndex: number, toIndex: number): void;
|
|
792
|
+
/**
|
|
793
|
+
* Move `count` consecutive elements from `from` to `to`; emits `move` with
|
|
794
|
+
* `count`. `to` is in the post-extraction coordinate space (as single `move`).
|
|
795
|
+
*/
|
|
796
|
+
moveRange(key: TKey, from: number, to: number, count: number): void;
|
|
765
797
|
setSlots(key: TKey, slots: readonly TOIMEntitySlot<TEntity, TPk>[]): void;
|
|
766
|
-
getBufferedCommands(key: TKey): readonly TOIMOrderedListCommand<
|
|
767
|
-
consumeCommands(key: TKey): TOIMOrderedListCommand<
|
|
798
|
+
getBufferedCommands(key: TKey): readonly TOIMOrderedListCommand<TOIMEntitySlot<TEntity, TPk>>[];
|
|
799
|
+
consumeCommands(key: TKey): TOIMOrderedListCommand<TOIMEntitySlot<TEntity, TPk>>[];
|
|
768
800
|
clear(key?: TKey): void;
|
|
769
801
|
destroy(): void;
|
|
770
802
|
private readonly onAfterFlush;
|
|
771
803
|
private readonly onIndexUpdate;
|
|
772
804
|
protected withWrite(fn: () => void): void;
|
|
773
|
-
protected appendCommand(key: TKey, cmd: TOIMOrderedListCommand<
|
|
774
|
-
protected
|
|
775
|
-
protected
|
|
805
|
+
protected appendCommand(key: TKey, cmd: TOIMOrderedListCommand<TOIMEntitySlot<TEntity, TPk>>): void;
|
|
806
|
+
protected appendResetCommand(key: TKey, slots: readonly TOIMEntitySlot<TEntity, TPk>[]): void;
|
|
807
|
+
protected createResetCommand(slots: readonly TOIMEntitySlot<TEntity, TPk>[]): TOIMOrderedListCommand<TOIMEntitySlot<TEntity, TPk>>;
|
|
776
808
|
}
|
|
777
809
|
|
|
778
810
|
/**
|
|
@@ -785,6 +817,8 @@ declare class OIMCollectionOrderedListCommandStream<TKey extends TOIMPk, TPk ext
|
|
|
785
817
|
constructor(queue: OIMEventQueue, opts: TOIMCollectionOrderedListCommandStreamOptions<TKey, TPk, TEntity>);
|
|
786
818
|
push(key: TKey, pk: TPk): void;
|
|
787
819
|
insertAt(key: TKey, index: number, pk: TPk): void;
|
|
820
|
+
/** Replace the element at `index` with the slot for `pk`, in place. */
|
|
821
|
+
setAt(key: TKey, index: number, pk: TPk): void;
|
|
788
822
|
set(key: TKey, pks: readonly TPk[]): void;
|
|
789
823
|
getSlotsByKey(key: TKey): readonly TOIMEntitySlot<TEntity, TPk>[];
|
|
790
824
|
getEntitiesByKey(key: TKey): (TEntity | undefined)[];
|
|
@@ -1149,6 +1183,18 @@ declare class OIMEntityUpdaterFactory<TEntity extends object> {
|
|
|
1149
1183
|
*/
|
|
1150
1184
|
declare function createInPlaceEntityUpdater<TEntity extends object>(): TOIMEntityUpdater<TEntity>;
|
|
1151
1185
|
|
|
1186
|
+
/**
|
|
1187
|
+
* Default entity updater: shallow-merges the patch into a NEW object
|
|
1188
|
+
* (`{ ...prev, ...draft }`). Each update produces a fresh entity reference, which
|
|
1189
|
+
* reference-comparison bindings (React's `Object.is` / `useSyncExternalStore`,
|
|
1190
|
+
* `React.memo`, prev/next diffing, time-travel) need to detect a change.
|
|
1191
|
+
*
|
|
1192
|
+
* The new-object allocation is the biggest per-update data-layer cost; for
|
|
1193
|
+
* update-heavy, subscription-driven readers use {@link createInPlaceEntityUpdater}
|
|
1194
|
+
* instead.
|
|
1195
|
+
*/
|
|
1196
|
+
declare function createMergeEntityUpdater<TEntity extends object>(): TOIMEntityUpdater<TEntity>;
|
|
1197
|
+
|
|
1152
1198
|
/**
|
|
1153
1199
|
* Factory for creating index comparators.
|
|
1154
1200
|
* Provides common comparison strategies for primary key arrays.
|
|
@@ -1297,9 +1343,11 @@ declare class OIMCollectionChangedFields<TEntity extends object, TPk extends TOI
|
|
|
1297
1343
|
readonly collection: OIMCollection<TEntity, TPk>;
|
|
1298
1344
|
private readonly selectPk;
|
|
1299
1345
|
private readonly unsubscribeAfterFlush;
|
|
1300
|
-
private readonly unsubscribeFromCollectionEmitter
|
|
1346
|
+
private readonly unsubscribeFromCollectionEmitter?;
|
|
1347
|
+
private readonly detectExternalMutations;
|
|
1301
1348
|
private isInWrite;
|
|
1302
1349
|
private readonly lastEntitySnapshotByPk;
|
|
1350
|
+
private readonly writtenThroughThisFlush;
|
|
1303
1351
|
/**
|
|
1304
1352
|
* Emits updates scoped by PK. Subscribe, then read state via `getChangedFieldsByPk(...)`
|
|
1305
1353
|
* or drain via `consumeChangedFieldsByPk(...)`.
|
|
@@ -1318,6 +1366,18 @@ declare class OIMCollectionChangedFields<TEntity extends object, TPk extends TOI
|
|
|
1318
1366
|
* If not provided, only `upsertOneByPk(...)` should be used.
|
|
1319
1367
|
*/
|
|
1320
1368
|
selectPk?: (entity: TEntity | Partial<TEntity>) => TPk;
|
|
1369
|
+
/**
|
|
1370
|
+
* Detect writes that bypass this wrapper (direct collection
|
|
1371
|
+
* mutations) by diffing entity snapshots. Default `true`.
|
|
1372
|
+
*
|
|
1373
|
+
* Set to `false` when you always write through this wrapper: it
|
|
1374
|
+
* drops the per-write snapshot clone and the per-PK snapshot map
|
|
1375
|
+
* (~2x entity memory), since those exist only to power external
|
|
1376
|
+
* detection. The shallow diff also can't see deep in-place
|
|
1377
|
+
* mutations anyway — so if that's your model, opting out costs you
|
|
1378
|
+
* nothing it could have caught.
|
|
1379
|
+
*/
|
|
1380
|
+
detectExternalMutations?: boolean;
|
|
1321
1381
|
});
|
|
1322
1382
|
upsertOneByPk(pk: TPk, patch: Partial<TEntity>): TOIMEntitySlot<TEntity, TPk>;
|
|
1323
1383
|
upsertOne(entity: TEntity | Partial<TEntity>): TOIMEntitySlot<TEntity, TPk>;
|
|
@@ -1690,4 +1750,4 @@ declare class OIMEntitiesByIndexKeyArrayBasedSelector<TEntity extends object, TP
|
|
|
1690
1750
|
protected areEqual(prev: readonly (TEntity | undefined)[], next: readonly (TEntity | undefined)[]): boolean;
|
|
1691
1751
|
}
|
|
1692
1752
|
|
|
1693
|
-
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, createOIMCollectionIndexFactory, createOIMCollectionKit };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -865,12 +865,15 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
|
|
|
865
865
|
}
|
|
866
866
|
};
|
|
867
867
|
|
|
868
|
+
// src/core/createMergeEntityUpdater.ts
|
|
869
|
+
function createMergeEntityUpdater() {
|
|
870
|
+
return (draft, prev) => ({ ...prev, ...draft });
|
|
871
|
+
}
|
|
872
|
+
|
|
868
873
|
// src/core/OIMEntityUpdaterFactory.ts
|
|
869
874
|
var OIMEntityUpdaterFactory = class {
|
|
870
875
|
createMergeEntityUpdater() {
|
|
871
|
-
return (
|
|
872
|
-
return { ...prev, ...draft };
|
|
873
|
-
};
|
|
876
|
+
return createMergeEntityUpdater();
|
|
874
877
|
}
|
|
875
878
|
};
|
|
876
879
|
|
|
@@ -1189,6 +1192,22 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1189
1192
|
this.emitUpdate([key]);
|
|
1190
1193
|
return removed;
|
|
1191
1194
|
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Remove up to `count` consecutive slots starting at `index`.
|
|
1197
|
+
* Returns the number actually removed (clamped to bounds; 0 if nothing).
|
|
1198
|
+
*/
|
|
1199
|
+
removeRange(key, index, count) {
|
|
1200
|
+
const list = this.store.getOneByKey(key);
|
|
1201
|
+
if (!list) return 0;
|
|
1202
|
+
if (index < 0 || index >= list.length || count <= 0) return 0;
|
|
1203
|
+
const actual = Math.min(count, list.length - index);
|
|
1204
|
+
list.splice(index, actual);
|
|
1205
|
+
if (list.length === 0) {
|
|
1206
|
+
this.store.removeOneByKey(key);
|
|
1207
|
+
}
|
|
1208
|
+
this.emitUpdate([key]);
|
|
1209
|
+
return actual;
|
|
1210
|
+
}
|
|
1192
1211
|
move(key, fromIndex, toIndex) {
|
|
1193
1212
|
const list = this.store.getOneByKey(key);
|
|
1194
1213
|
if (!list) return void 0;
|
|
@@ -1201,6 +1220,32 @@ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
|
|
|
1201
1220
|
this.emitUpdate([key]);
|
|
1202
1221
|
return slot;
|
|
1203
1222
|
}
|
|
1223
|
+
/** Replace the slot at `index` in place. Returns the safe index, or -1. */
|
|
1224
|
+
setSlotAt(key, index, slot) {
|
|
1225
|
+
const list = this.store.getOneByKey(key);
|
|
1226
|
+
if (!list) return -1;
|
|
1227
|
+
if (index < 0 || index >= list.length) return -1;
|
|
1228
|
+
list[index] = slot;
|
|
1229
|
+
this.emitUpdate([key]);
|
|
1230
|
+
return index;
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Move up to `count` consecutive slots from `from` to `to`. `to` is in the
|
|
1234
|
+
* coordinate space *after* the block is extracted (same as single `move`).
|
|
1235
|
+
* Returns the number actually moved (0 if a no-op / out of bounds).
|
|
1236
|
+
*/
|
|
1237
|
+
moveRange(key, from, to, count) {
|
|
1238
|
+
const list = this.store.getOneByKey(key);
|
|
1239
|
+
if (!list) return 0;
|
|
1240
|
+
if (from < 0 || from >= list.length || count <= 0) return 0;
|
|
1241
|
+
const actual = Math.min(count, list.length - from);
|
|
1242
|
+
const insertAt = Math.max(0, Math.min(to, list.length - actual));
|
|
1243
|
+
if (insertAt === from) return 0;
|
|
1244
|
+
const block = list.splice(from, actual);
|
|
1245
|
+
list.splice(insertAt, 0, ...block);
|
|
1246
|
+
this.emitUpdate([key]);
|
|
1247
|
+
return actual;
|
|
1248
|
+
}
|
|
1204
1249
|
resetSlots(key, slots) {
|
|
1205
1250
|
if (slots.length === 0) {
|
|
1206
1251
|
if (this.store.getOneByKey(key) !== void 0) {
|
|
@@ -1411,15 +1456,15 @@ var OIMUpdateEventEmitter = class {
|
|
|
1411
1456
|
const flushingKeys = this.updatedKeys;
|
|
1412
1457
|
this.updatedKeys = /* @__PURE__ */ new Set();
|
|
1413
1458
|
if (this.keyHandlers.size === 0 || flushingKeys.size === 0) return;
|
|
1414
|
-
if (flushingKeys.size
|
|
1415
|
-
|
|
1416
|
-
|
|
1459
|
+
if (flushingKeys.size === this.keyHandlers.size) {
|
|
1460
|
+
this.keyHandlers.forEach((handlers, key) => {
|
|
1461
|
+
if (!flushingKeys.has(key)) return;
|
|
1417
1462
|
if (!handlers || handlers.size === 0) return;
|
|
1418
1463
|
this.notifyHandlers(handlers);
|
|
1419
1464
|
});
|
|
1420
1465
|
} else {
|
|
1421
|
-
|
|
1422
|
-
|
|
1466
|
+
flushingKeys.forEach((key) => {
|
|
1467
|
+
const handlers = this.keyHandlers.get(key);
|
|
1423
1468
|
if (!handlers || handlers.size === 0) return;
|
|
1424
1469
|
this.notifyHandlers(handlers);
|
|
1425
1470
|
});
|
|
@@ -1531,7 +1576,7 @@ var OIMOrderedListCommandStream = class {
|
|
|
1531
1576
|
if (payload.keys.length === 0) return;
|
|
1532
1577
|
for (let i = 0; i < payload.keys.length; i++) {
|
|
1533
1578
|
const key = payload.keys[i];
|
|
1534
|
-
this.
|
|
1579
|
+
this.appendResetCommand(key, this.getSlotsByKey(key));
|
|
1535
1580
|
}
|
|
1536
1581
|
};
|
|
1537
1582
|
this.commandsEventEmitter = new OIMUpdateEventEmitter(
|
|
@@ -1568,12 +1613,7 @@ var OIMOrderedListCommandStream = class {
|
|
|
1568
1613
|
pushSlot(key, slot) {
|
|
1569
1614
|
this.withWrite(() => {
|
|
1570
1615
|
const index = this.index.pushSlot(key, slot);
|
|
1571
|
-
this.appendCommand(key, {
|
|
1572
|
-
type: "insert",
|
|
1573
|
-
pk: slot.pk,
|
|
1574
|
-
slot,
|
|
1575
|
-
index
|
|
1576
|
-
});
|
|
1616
|
+
this.appendCommand(key, { type: "insert", index, item: slot });
|
|
1577
1617
|
});
|
|
1578
1618
|
}
|
|
1579
1619
|
insertSlotAt(key, index, slot) {
|
|
@@ -1581,9 +1621,20 @@ var OIMOrderedListCommandStream = class {
|
|
|
1581
1621
|
const safeIndex = this.index.insertSlotAt(key, index, slot);
|
|
1582
1622
|
this.appendCommand(key, {
|
|
1583
1623
|
type: "insert",
|
|
1584
|
-
|
|
1585
|
-
slot
|
|
1586
|
-
|
|
1624
|
+
index: safeIndex,
|
|
1625
|
+
item: slot
|
|
1626
|
+
});
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
/** Replace the slot at `index` in place. */
|
|
1630
|
+
setSlotAt(key, index, slot) {
|
|
1631
|
+
this.withWrite(() => {
|
|
1632
|
+
const safeIndex = this.index.setSlotAt(key, index, slot);
|
|
1633
|
+
if (safeIndex < 0) return;
|
|
1634
|
+
this.appendCommand(key, {
|
|
1635
|
+
type: "set",
|
|
1636
|
+
index: safeIndex,
|
|
1637
|
+
item: slot
|
|
1587
1638
|
});
|
|
1588
1639
|
});
|
|
1589
1640
|
}
|
|
@@ -1591,12 +1642,15 @@ var OIMOrderedListCommandStream = class {
|
|
|
1591
1642
|
this.withWrite(() => {
|
|
1592
1643
|
const slot = this.index.removeAt(key, index);
|
|
1593
1644
|
if (slot === void 0) return;
|
|
1594
|
-
this.appendCommand(key, {
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1645
|
+
this.appendCommand(key, { type: "remove", index });
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
/** Remove `count` consecutive elements from `index`; emits `remove` with `count`. */
|
|
1649
|
+
removeRange(key, index, count) {
|
|
1650
|
+
this.withWrite(() => {
|
|
1651
|
+
const removed = this.index.removeRange(key, index, count);
|
|
1652
|
+
if (removed <= 0) return;
|
|
1653
|
+
this.appendCommand(key, { type: "remove", index, count: removed });
|
|
1600
1654
|
});
|
|
1601
1655
|
}
|
|
1602
1656
|
move(key, fromIndex, toIndex) {
|
|
@@ -1605,17 +1659,26 @@ var OIMOrderedListCommandStream = class {
|
|
|
1605
1659
|
if (slot === void 0) return;
|
|
1606
1660
|
this.appendCommand(key, {
|
|
1607
1661
|
type: "move",
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
fromIndex,
|
|
1611
|
-
toIndex
|
|
1662
|
+
from: fromIndex,
|
|
1663
|
+
to: toIndex
|
|
1612
1664
|
});
|
|
1613
1665
|
});
|
|
1614
1666
|
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Move `count` consecutive elements from `from` to `to`; emits `move` with
|
|
1669
|
+
* `count`. `to` is in the post-extraction coordinate space (as single `move`).
|
|
1670
|
+
*/
|
|
1671
|
+
moveRange(key, from, to, count) {
|
|
1672
|
+
this.withWrite(() => {
|
|
1673
|
+
const moved = this.index.moveRange(key, from, to, count);
|
|
1674
|
+
if (moved <= 0) return;
|
|
1675
|
+
this.appendCommand(key, { type: "move", from, to, count: moved });
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1615
1678
|
setSlots(key, slots) {
|
|
1616
1679
|
this.withWrite(() => {
|
|
1617
1680
|
this.index.resetSlots(key, slots);
|
|
1618
|
-
this.
|
|
1681
|
+
this.appendResetCommand(key, slots);
|
|
1619
1682
|
});
|
|
1620
1683
|
}
|
|
1621
1684
|
getBufferedCommands(key) {
|
|
@@ -1659,26 +1722,21 @@ var OIMOrderedListCommandStream = class {
|
|
|
1659
1722
|
cmds = [];
|
|
1660
1723
|
this.commandsByKey.set(key, cmds);
|
|
1661
1724
|
}
|
|
1662
|
-
if (cmd.type === "
|
|
1725
|
+
if (cmd.type === "reset") {
|
|
1663
1726
|
cmds.length = 0;
|
|
1664
1727
|
cmds.push(cmd);
|
|
1665
|
-
} else if (cmds.length > 0 && cmds[0].type === "
|
|
1666
|
-
|
|
1667
|
-
cmds[0] = this.createSetCommand(slots);
|
|
1728
|
+
} else if (cmds.length > 0 && cmds[0].type === "reset") {
|
|
1729
|
+
cmds[0] = this.createResetCommand(this.getSlotsByKey(key));
|
|
1668
1730
|
} else {
|
|
1669
1731
|
cmds.push(cmd);
|
|
1670
1732
|
}
|
|
1671
1733
|
this.commandsEventEmitter.markUpdatedKeys([key]);
|
|
1672
1734
|
}
|
|
1673
|
-
|
|
1674
|
-
this.appendCommand(key, this.
|
|
1735
|
+
appendResetCommand(key, slots) {
|
|
1736
|
+
this.appendCommand(key, this.createResetCommand(slots));
|
|
1675
1737
|
}
|
|
1676
|
-
|
|
1677
|
-
return {
|
|
1678
|
-
type: "set",
|
|
1679
|
-
pks: slots.map((slot) => slot.pk),
|
|
1680
|
-
slots
|
|
1681
|
-
};
|
|
1738
|
+
createResetCommand(slots) {
|
|
1739
|
+
return { type: "reset", items: slots };
|
|
1682
1740
|
}
|
|
1683
1741
|
};
|
|
1684
1742
|
|
|
@@ -1695,12 +1753,7 @@ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandS
|
|
|
1695
1753
|
this.withWrite(() => {
|
|
1696
1754
|
const slot = this.resolvePk(pk);
|
|
1697
1755
|
const index = this.index.pushSlot(key, slot);
|
|
1698
|
-
this.appendCommand(key, {
|
|
1699
|
-
type: "insert",
|
|
1700
|
-
pk,
|
|
1701
|
-
slot,
|
|
1702
|
-
index
|
|
1703
|
-
});
|
|
1756
|
+
this.appendCommand(key, { type: "insert", index, item: slot });
|
|
1704
1757
|
});
|
|
1705
1758
|
}
|
|
1706
1759
|
insertAt(key, index, pk) {
|
|
@@ -1709,9 +1762,21 @@ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandS
|
|
|
1709
1762
|
const safeIndex = this.index.insertSlotAt(key, index, slot);
|
|
1710
1763
|
this.appendCommand(key, {
|
|
1711
1764
|
type: "insert",
|
|
1712
|
-
|
|
1713
|
-
slot
|
|
1714
|
-
|
|
1765
|
+
index: safeIndex,
|
|
1766
|
+
item: slot
|
|
1767
|
+
});
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
/** Replace the element at `index` with the slot for `pk`, in place. */
|
|
1771
|
+
setAt(key, index, pk) {
|
|
1772
|
+
this.withWrite(() => {
|
|
1773
|
+
const slot = this.resolvePk(pk);
|
|
1774
|
+
const safeIndex = this.index.setSlotAt(key, index, slot);
|
|
1775
|
+
if (safeIndex < 0) return;
|
|
1776
|
+
this.appendCommand(key, {
|
|
1777
|
+
type: "set",
|
|
1778
|
+
index: safeIndex,
|
|
1779
|
+
item: slot
|
|
1715
1780
|
});
|
|
1716
1781
|
});
|
|
1717
1782
|
}
|
|
@@ -1719,7 +1784,7 @@ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandS
|
|
|
1719
1784
|
this.withWrite(() => {
|
|
1720
1785
|
const slots = pks.map((pk) => this.resolvePk(pk));
|
|
1721
1786
|
this.index.resetSlots(key, slots);
|
|
1722
|
-
this.
|
|
1787
|
+
this.appendResetCommand(key, slots);
|
|
1723
1788
|
});
|
|
1724
1789
|
}
|
|
1725
1790
|
getSlotsByKey(key) {
|
|
@@ -3298,20 +3363,27 @@ var OIMCollectionChangedFields = class {
|
|
|
3298
3363
|
constructor(queue, collection, opts) {
|
|
3299
3364
|
this.isInWrite = false;
|
|
3300
3365
|
this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
|
|
3366
|
+
// PKs written through this wrapper during the current flush. Their fields are
|
|
3367
|
+
// already tracked accurately via trackPatch, so the flush-time external-write
|
|
3368
|
+
// handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
|
|
3369
|
+
this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
|
|
3301
3370
|
this.changedFieldsByPk = /* @__PURE__ */ new Map();
|
|
3302
3371
|
this.changedPksByField = /* @__PURE__ */ new Map();
|
|
3303
3372
|
this.onAfterFlush = () => {
|
|
3304
3373
|
this.clear();
|
|
3374
|
+
this.writtenThroughThisFlush.clear();
|
|
3305
3375
|
};
|
|
3306
3376
|
this.onCollectionUpdate = (payload) => {
|
|
3307
3377
|
if (this.isInWrite) return;
|
|
3308
3378
|
if (payload.pks.length === 0) return;
|
|
3309
3379
|
for (let i = 0; i < payload.pks.length; i++) {
|
|
3310
3380
|
const pk = payload.pks[i];
|
|
3381
|
+
if (this.writtenThroughThisFlush.has(pk)) continue;
|
|
3311
3382
|
this.trackExternalMutationByPk(pk);
|
|
3312
3383
|
}
|
|
3313
3384
|
};
|
|
3314
3385
|
this.collection = collection;
|
|
3386
|
+
this.detectExternalMutations = opts?.detectExternalMutations !== false;
|
|
3315
3387
|
this.selectPk = opts?.selectPk ?? (() => {
|
|
3316
3388
|
throw new Error(
|
|
3317
3389
|
`[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
|
|
@@ -3326,15 +3398,17 @@ var OIMCollectionChangedFields = class {
|
|
|
3326
3398
|
0 /* AFTER_FLUSH */,
|
|
3327
3399
|
this.onAfterFlush
|
|
3328
3400
|
);
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
this.
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3401
|
+
if (this.detectExternalMutations) {
|
|
3402
|
+
const maybeReactive = collection;
|
|
3403
|
+
this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
|
|
3404
|
+
this.onCollectionUpdate({
|
|
3405
|
+
pks
|
|
3406
|
+
});
|
|
3407
|
+
}) : collection.emitter.on(
|
|
3408
|
+
0 /* UPDATE */,
|
|
3409
|
+
this.onCollectionUpdate
|
|
3410
|
+
);
|
|
3411
|
+
}
|
|
3338
3412
|
}
|
|
3339
3413
|
upsertOneByPk(pk, patch) {
|
|
3340
3414
|
const slot = this.withWrite(() => {
|
|
@@ -3390,7 +3464,7 @@ var OIMCollectionChangedFields = class {
|
|
|
3390
3464
|
this.changedPksEventEmitter.destroy();
|
|
3391
3465
|
this.changedFieldsEventEmitter.destroy();
|
|
3392
3466
|
this.unsubscribeAfterFlush();
|
|
3393
|
-
this.unsubscribeFromCollectionEmitter();
|
|
3467
|
+
this.unsubscribeFromCollectionEmitter?.();
|
|
3394
3468
|
this.clear();
|
|
3395
3469
|
}
|
|
3396
3470
|
withWrite(fn) {
|
|
@@ -3432,6 +3506,8 @@ var OIMCollectionChangedFields = class {
|
|
|
3432
3506
|
return changed;
|
|
3433
3507
|
}
|
|
3434
3508
|
updateEntitySnapshot(pk) {
|
|
3509
|
+
if (!this.detectExternalMutations) return;
|
|
3510
|
+
this.writtenThroughThisFlush.add(pk);
|
|
3435
3511
|
const entity = this.collection.getOneByPk(pk);
|
|
3436
3512
|
if (!entity) {
|
|
3437
3513
|
this.lastEntitySnapshotByPk.delete(pk);
|
|
@@ -4115,6 +4191,7 @@ export {
|
|
|
4115
4191
|
OIMReactiveObject,
|
|
4116
4192
|
OIMSelector,
|
|
4117
4193
|
createInPlaceEntityUpdater,
|
|
4194
|
+
createMergeEntityUpdater,
|
|
4118
4195
|
createOIMCollectionIndexFactory,
|
|
4119
4196
|
createOIMCollectionKit
|
|
4120
4197
|
};
|