@oimdb/core 3.9.0 → 3.11.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 +18 -1
- package/dist/index.cjs +761 -458
- package/dist/index.d.cts +341 -125
- package/dist/index.d.ts +341 -125
- package/dist/index.js +755 -457
- package/llms.txt +82 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -225,6 +225,25 @@ var OIMEventQueueScheduler = class {
|
|
|
225
225
|
flush() {
|
|
226
226
|
this.emitter.emit(0 /* FLUSH */, void 0);
|
|
227
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Detach a Node timer handle from the event loop, returning it unchanged.
|
|
230
|
+
*
|
|
231
|
+
* A pending flush must never be a reason for a process to stay alive: if
|
|
232
|
+
* nothing else is running there is no UI left to update, and the flush is
|
|
233
|
+
* moot. Without this a scheduled-but-not-yet-run flush keeps Node awake —
|
|
234
|
+
* which is why a test suite that leaves a queue scheduled reports
|
|
235
|
+
* "a worker process has failed to exit gracefully".
|
|
236
|
+
*
|
|
237
|
+
* In the browser `setTimeout` returns a number, which has no `unref`, so the
|
|
238
|
+
* call is guarded and the handle passes straight through.
|
|
239
|
+
*/
|
|
240
|
+
unref(handle) {
|
|
241
|
+
const maybeUnrefable = handle;
|
|
242
|
+
if (typeof maybeUnrefable?.unref === "function") {
|
|
243
|
+
maybeUnrefable.unref();
|
|
244
|
+
}
|
|
245
|
+
return handle;
|
|
246
|
+
}
|
|
228
247
|
};
|
|
229
248
|
|
|
230
249
|
// src/core/OIMKeyedBucketSetBased.ts
|
|
@@ -559,19 +578,28 @@ var OIMCarrierKeyedEmitter = class {
|
|
|
559
578
|
// doubles as membership: a carrier is in this array iff `carrier.dirty`.
|
|
560
579
|
this.dirtyCarriers = [];
|
|
561
580
|
this.isFlushEnqueued = false;
|
|
581
|
+
// Number of live subscriptions whose single handler spans 2+ distinct
|
|
582
|
+
// carriers (a `subscribeOnKeys` over distinct keys). While this is > 0 a
|
|
583
|
+
// flush coalesces delivery so such a handler fires at most ONCE per flush,
|
|
584
|
+
// not once per changed carrier — the callback carries no key, so extra
|
|
585
|
+
// fires are pure waste. At 0 the hot single-subscriber path pays nothing:
|
|
586
|
+
// no per-flush Set, no membership checks.
|
|
587
|
+
this.multiCarrierSubscriptions = 0;
|
|
562
588
|
this.onFlush = () => {
|
|
563
589
|
this.isFlushEnqueued = false;
|
|
564
590
|
if (this.dirtyCarriers.length === 0) return;
|
|
565
591
|
const flushing = this.dirtyCarriers;
|
|
566
592
|
this.dirtyCarriers = [];
|
|
593
|
+
const delivered = this.multiCarrierSubscriptions > 0 ? this.deliveredPool ?? (this.deliveredPool = /* @__PURE__ */ new Set()) : void 0;
|
|
567
594
|
let i = 0;
|
|
568
595
|
try {
|
|
569
596
|
for (; i < flushing.length; i++) {
|
|
570
597
|
flushing[i].dirty = false;
|
|
571
|
-
this.notify(flushing[i]);
|
|
598
|
+
this.notify(flushing[i], delivered);
|
|
572
599
|
}
|
|
573
600
|
} finally {
|
|
574
601
|
for (; i < flushing.length; i++) flushing[i].dirty = false;
|
|
602
|
+
delivered?.clear();
|
|
575
603
|
}
|
|
576
604
|
};
|
|
577
605
|
this.queue = queue;
|
|
@@ -589,7 +617,13 @@ var OIMCarrierKeyedEmitter = class {
|
|
|
589
617
|
this.addHandler(carrier, handler);
|
|
590
618
|
carriers.push(carrier);
|
|
591
619
|
}
|
|
620
|
+
const spansMultipleCarriers = carriers.length > 1;
|
|
621
|
+
if (spansMultipleCarriers) this.multiCarrierSubscriptions++;
|
|
622
|
+
let unsubscribed = false;
|
|
592
623
|
return () => {
|
|
624
|
+
if (unsubscribed) return;
|
|
625
|
+
unsubscribed = true;
|
|
626
|
+
if (spansMultipleCarriers) this.multiCarrierSubscriptions--;
|
|
593
627
|
for (let i = 0; i < carriers.length; i++) {
|
|
594
628
|
this.removeHandler(carriers[i], handler);
|
|
595
629
|
}
|
|
@@ -705,19 +739,31 @@ var OIMCarrierKeyedEmitter = class {
|
|
|
705
739
|
this.isFlushEnqueued = true;
|
|
706
740
|
this.queue.enqueue(this.onFlush);
|
|
707
741
|
}
|
|
708
|
-
notify(carrier) {
|
|
742
|
+
notify(carrier, delivered) {
|
|
709
743
|
const subscribers = carrier.subscribers;
|
|
710
744
|
if (!subscribers || subscribers.size === 0) return;
|
|
711
745
|
if (subscribers.size === 1) {
|
|
712
746
|
const only = subscribers.values().next().value;
|
|
713
|
-
if (only)
|
|
747
|
+
if (!only) return;
|
|
748
|
+
if (delivered) {
|
|
749
|
+
const size = delivered.size;
|
|
750
|
+
delivered.add(only);
|
|
751
|
+
if (delivered.size === size) return;
|
|
752
|
+
}
|
|
753
|
+
only();
|
|
714
754
|
return;
|
|
715
755
|
}
|
|
716
756
|
const snapshot = [];
|
|
717
757
|
subscribers.forEach((h) => snapshot.push(h));
|
|
718
758
|
for (let i = 0; i < snapshot.length; i++) {
|
|
719
759
|
const handler = snapshot[i];
|
|
720
|
-
if (subscribers.has(handler))
|
|
760
|
+
if (!subscribers.has(handler)) continue;
|
|
761
|
+
if (delivered) {
|
|
762
|
+
const size = delivered.size;
|
|
763
|
+
delivered.add(handler);
|
|
764
|
+
if (delivered.size === size) continue;
|
|
765
|
+
}
|
|
766
|
+
handler();
|
|
721
767
|
}
|
|
722
768
|
}
|
|
723
769
|
};
|
|
@@ -1339,8 +1385,11 @@ var OIMCollection = class {
|
|
|
1339
1385
|
this.emitter.emit(0 /* UPDATE */, { pks });
|
|
1340
1386
|
}
|
|
1341
1387
|
clear() {
|
|
1388
|
+
const pks = this.store.getAllPks();
|
|
1342
1389
|
this.store.clear();
|
|
1343
|
-
|
|
1390
|
+
if (pks.length > 0) {
|
|
1391
|
+
this.emitter.emit(0 /* UPDATE */, { pks });
|
|
1392
|
+
}
|
|
1344
1393
|
}
|
|
1345
1394
|
countAll() {
|
|
1346
1395
|
return this.store.countAll();
|
|
@@ -1382,14 +1431,12 @@ var OIMReactiveCollection = class extends OIMCollection {
|
|
|
1382
1431
|
// Only ever holds `slot.pk` (one canonical reference per logical key), so a
|
|
1383
1432
|
// native `Set` dedups correctly by reference even for composite pks.
|
|
1384
1433
|
this.pendingAnyUpdatePks = /* @__PURE__ */ new Set();
|
|
1385
|
-
this.isAnyUpdateClearPending = false;
|
|
1386
1434
|
this.isAnyUpdateScheduled = false;
|
|
1387
1435
|
this.runAnyUpdate = () => {
|
|
1388
1436
|
if (!this.isAnyUpdateScheduled) return;
|
|
1389
1437
|
this.isAnyUpdateScheduled = false;
|
|
1390
|
-
const pks =
|
|
1438
|
+
const pks = Array.from(this.pendingAnyUpdatePks.values());
|
|
1391
1439
|
this.pendingAnyUpdatePks.clear();
|
|
1392
|
-
this.isAnyUpdateClearPending = false;
|
|
1393
1440
|
const snapshot = Array.from(this.anyUpdateHandlers);
|
|
1394
1441
|
for (let i = 0; i < snapshot.length; i++) snapshot[i](pks);
|
|
1395
1442
|
};
|
|
@@ -1429,30 +1476,43 @@ var OIMReactiveCollection = class extends OIMCollection {
|
|
|
1429
1476
|
this.anyUpdateHandlers.delete(handler);
|
|
1430
1477
|
};
|
|
1431
1478
|
}
|
|
1432
|
-
//
|
|
1433
|
-
//
|
|
1434
|
-
//
|
|
1435
|
-
//
|
|
1436
|
-
|
|
1479
|
+
// Two notification channels coexist on a reactive collection; they serve
|
|
1480
|
+
// different audiences and NEITHER is obsolete:
|
|
1481
|
+
// • the keyed `updateEmitter` — per-key/per-carrier pings ("did MY key
|
|
1482
|
+
// change?", payload-less `void`), for leaf subscribers like React hooks
|
|
1483
|
+
// that declare the keys they care about up front;
|
|
1484
|
+
// • this `emitter` UPDATE event — the aggregate/batch channel that carries
|
|
1485
|
+
// the full list of changed pks in one SYNCHRONOUS callback, for
|
|
1486
|
+
// consumers that do NOT know their keys ahead of time and must re-derive
|
|
1487
|
+
// over the batch: derived indexes/collections, changed-fields, persist,
|
|
1488
|
+
// snapshot-manager. `pks` always enumerates exactly what changed — even
|
|
1489
|
+
// clear() lists every removed key, so an empty array never occurs.
|
|
1490
|
+
// It is not a per-key emitter with fewer features — it is the opposite
|
|
1491
|
+
// shape (enumerated batch, not "my key"), which is why it stays.
|
|
1492
|
+
//
|
|
1493
|
+
// Emit on the batch channel only when something is actually subscribed —
|
|
1494
|
+
// otherwise every upsert/remove allocates a throwaway `{ pks }` object (and
|
|
1495
|
+
// array) that nobody reads.
|
|
1496
|
+
notifyBatch(pk) {
|
|
1437
1497
|
if (this.emitter.hasHandlers(0 /* UPDATE */)) {
|
|
1438
1498
|
this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
|
|
1439
1499
|
}
|
|
1440
1500
|
}
|
|
1441
|
-
|
|
1501
|
+
notifyBatchMany(pks) {
|
|
1442
1502
|
if (this.emitter.hasHandlers(0 /* UPDATE */)) {
|
|
1443
1503
|
this.emitter.emit(0 /* UPDATE */, { pks });
|
|
1444
1504
|
}
|
|
1445
1505
|
}
|
|
1446
1506
|
upsertOneByPk(pk, entity) {
|
|
1447
1507
|
const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
|
|
1448
|
-
this.
|
|
1508
|
+
this.notifyBatch(pk);
|
|
1449
1509
|
this.updateEmitter.markUpdatedCarrier(slot);
|
|
1450
1510
|
this.trackAnyUpdatePk(pk);
|
|
1451
1511
|
return slot;
|
|
1452
1512
|
}
|
|
1453
1513
|
upsertOne(entity) {
|
|
1454
1514
|
const slot = this.upsertOneWithoutNotifications(entity);
|
|
1455
|
-
this.
|
|
1515
|
+
this.notifyBatch(slot.pk);
|
|
1456
1516
|
this.updateEmitter.markUpdatedCarrier(slot);
|
|
1457
1517
|
this.trackAnyUpdatePk(slot.pk);
|
|
1458
1518
|
return slot;
|
|
@@ -1463,7 +1523,7 @@ var OIMReactiveCollection = class extends OIMCollection {
|
|
|
1463
1523
|
(entity) => this.upsertOneWithoutNotifications(entity)
|
|
1464
1524
|
);
|
|
1465
1525
|
const pks = slots.map((slot) => slot.pk);
|
|
1466
|
-
this.
|
|
1526
|
+
this.notifyBatchMany(pks);
|
|
1467
1527
|
for (let i = 0; i < slots.length; i++) {
|
|
1468
1528
|
this.updateEmitter.markUpdatedCarrier(slots[i]);
|
|
1469
1529
|
}
|
|
@@ -1473,7 +1533,7 @@ var OIMReactiveCollection = class extends OIMCollection {
|
|
|
1473
1533
|
removeOne(entity) {
|
|
1474
1534
|
const pk = this.selectPk(entity);
|
|
1475
1535
|
this.store.removeOneByPk(pk);
|
|
1476
|
-
this.
|
|
1536
|
+
this.notifyBatch(pk);
|
|
1477
1537
|
this.updateEmitter.markUpdatedKey(pk);
|
|
1478
1538
|
this.trackAnyUpdatePk(pk);
|
|
1479
1539
|
}
|
|
@@ -1481,30 +1541,32 @@ var OIMReactiveCollection = class extends OIMCollection {
|
|
|
1481
1541
|
if (entities.length === 0) return;
|
|
1482
1542
|
const pks = entities.map(this.selectPk);
|
|
1483
1543
|
this.store.removeManyByPks(pks);
|
|
1484
|
-
this.
|
|
1544
|
+
this.notifyBatchMany(pks);
|
|
1485
1545
|
this.updateEmitter.markUpdatedKeys(pks);
|
|
1486
1546
|
this.trackAnyUpdatePks(pks);
|
|
1487
1547
|
}
|
|
1488
1548
|
removeOneByPk(pk) {
|
|
1489
1549
|
this.store.removeOneByPk(pk);
|
|
1490
|
-
this.
|
|
1550
|
+
this.notifyBatch(pk);
|
|
1491
1551
|
this.updateEmitter.markUpdatedKey(pk);
|
|
1492
1552
|
this.trackAnyUpdatePk(pk);
|
|
1493
1553
|
}
|
|
1494
1554
|
removeManyByPks(pks) {
|
|
1495
1555
|
if (pks.length === 0) return;
|
|
1496
1556
|
this.store.removeManyByPks(pks);
|
|
1497
|
-
this.
|
|
1557
|
+
this.notifyBatchMany(pks);
|
|
1498
1558
|
this.updateEmitter.markUpdatedKeys(pks);
|
|
1499
1559
|
this.trackAnyUpdatePks(pks);
|
|
1500
1560
|
}
|
|
1501
1561
|
clear() {
|
|
1562
|
+
const needsPks = this.emitter.hasHandlers(0 /* UPDATE */) || this.anyUpdateHandlers.size > 0;
|
|
1563
|
+
const pks = needsPks ? this.store.getAllPks() : void 0;
|
|
1502
1564
|
this.store.clear();
|
|
1503
|
-
if (
|
|
1504
|
-
this.
|
|
1565
|
+
if (pks && pks.length > 0) {
|
|
1566
|
+
this.notifyBatchMany(pks);
|
|
1567
|
+
this.trackAnyUpdatePks(pks);
|
|
1505
1568
|
}
|
|
1506
1569
|
this.updateEmitter.markAllUpdated();
|
|
1507
|
-
this.trackAnyUpdateClear();
|
|
1508
1570
|
}
|
|
1509
1571
|
trackAnyUpdatePk(pk) {
|
|
1510
1572
|
if (this.anyUpdateHandlers.size === 0) return;
|
|
@@ -1513,14 +1575,9 @@ var OIMReactiveCollection = class extends OIMCollection {
|
|
|
1513
1575
|
}
|
|
1514
1576
|
trackAnyUpdatePks(pks) {
|
|
1515
1577
|
if (this.anyUpdateHandlers.size === 0) return;
|
|
1516
|
-
for (let i = 0; i < pks.length; i++)
|
|
1578
|
+
for (let i = 0; i < pks.length; i++) {
|
|
1517
1579
|
this.pendingAnyUpdatePks.add(pks[i]);
|
|
1518
|
-
|
|
1519
|
-
}
|
|
1520
|
-
trackAnyUpdateClear() {
|
|
1521
|
-
if (this.anyUpdateHandlers.size === 0) return;
|
|
1522
|
-
this.isAnyUpdateClearPending = true;
|
|
1523
|
-
this.pendingAnyUpdatePks.clear();
|
|
1580
|
+
}
|
|
1524
1581
|
this.ensureAnyUpdateEnqueued();
|
|
1525
1582
|
}
|
|
1526
1583
|
ensureAnyUpdateEnqueued() {
|
|
@@ -1699,6 +1756,7 @@ var OIMCollectionIndexManualOrderedArrayBased = class extends OIMIndexManualOrde
|
|
|
1699
1756
|
var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
|
|
1700
1757
|
EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
|
|
1701
1758
|
EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
|
|
1759
|
+
EOIMEventQueueEventType2[EOIMEventQueueEventType2["FLUSH_ERROR"] = 2] = "FLUSH_ERROR";
|
|
1702
1760
|
return EOIMEventQueueEventType2;
|
|
1703
1761
|
})(EOIMEventQueueEventType || {});
|
|
1704
1762
|
|
|
@@ -1952,8 +2010,13 @@ var OIMUpdateEventEmitter = class {
|
|
|
1952
2010
|
var OIMOrderedListCommandBuffer = class {
|
|
1953
2011
|
constructor(queue) {
|
|
1954
2012
|
this.commandsByKey = /* @__PURE__ */ new Map();
|
|
2013
|
+
// Keys whose buffered leading `reset` is stale: an edit after the reset
|
|
2014
|
+
// should be folded into it, but we defer the (O(list-size)) rebuild to the
|
|
2015
|
+
// next read instead of redoing it on every subsequent edit in the batch.
|
|
2016
|
+
this.staleResetKeys = /* @__PURE__ */ new Set();
|
|
1955
2017
|
this.onAfterFlush = () => {
|
|
1956
2018
|
this.commandsByKey.clear();
|
|
2019
|
+
this.staleResetKeys.clear();
|
|
1957
2020
|
};
|
|
1958
2021
|
this.commandsEventEmitter = new OIMUpdateEventEmitter(
|
|
1959
2022
|
queue,
|
|
@@ -1968,9 +2031,11 @@ var OIMOrderedListCommandBuffer = class {
|
|
|
1968
2031
|
return this.commandsEventEmitter.subscribeOnKey(key, handler);
|
|
1969
2032
|
}
|
|
1970
2033
|
getBufferedCommands(key) {
|
|
2034
|
+
this.materializeStaleReset(key);
|
|
1971
2035
|
return this.commandsByKey.get(key) ?? [];
|
|
1972
2036
|
}
|
|
1973
2037
|
consumeCommands(key) {
|
|
2038
|
+
this.materializeStaleReset(key);
|
|
1974
2039
|
const cmds = this.commandsByKey.get(key);
|
|
1975
2040
|
if (!cmds || cmds.length === 0) return [];
|
|
1976
2041
|
return cmds.slice();
|
|
@@ -1979,6 +2044,20 @@ var OIMOrderedListCommandBuffer = class {
|
|
|
1979
2044
|
this.commandsEventEmitter.destroy();
|
|
1980
2045
|
this.unsubscribeAfterFlush();
|
|
1981
2046
|
this.commandsByKey.clear();
|
|
2047
|
+
this.staleResetKeys.clear();
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* Rebuild a deferred leading `reset` from the current order, once, at read
|
|
2051
|
+
* time. All the post-reset edits that were folded in are already reflected
|
|
2052
|
+
* in the driver's backing order, so a single rebuild here is equivalent to
|
|
2053
|
+
* having recomputed the reset on each of those edits.
|
|
2054
|
+
*/
|
|
2055
|
+
materializeStaleReset(key) {
|
|
2056
|
+
if (!this.staleResetKeys.delete(key)) return;
|
|
2057
|
+
const cmds = this.commandsByKey.get(key);
|
|
2058
|
+
if (cmds && cmds.length > 0 && cmds[0].type === "reset") {
|
|
2059
|
+
cmds[0] = this.createResetCommand(this.getItemsByKey(key));
|
|
2060
|
+
}
|
|
1982
2061
|
}
|
|
1983
2062
|
appendCommand(key, cmd) {
|
|
1984
2063
|
let cmds = this.commandsByKey.get(key);
|
|
@@ -1989,8 +2068,9 @@ var OIMOrderedListCommandBuffer = class {
|
|
|
1989
2068
|
if (cmd.type === "reset") {
|
|
1990
2069
|
cmds.length = 0;
|
|
1991
2070
|
cmds.push(cmd);
|
|
2071
|
+
this.staleResetKeys.delete(key);
|
|
1992
2072
|
} else if (cmds.length > 0 && cmds[0].type === "reset") {
|
|
1993
|
-
|
|
2073
|
+
this.staleResetKeys.add(key);
|
|
1994
2074
|
} else {
|
|
1995
2075
|
cmds.push(cmd);
|
|
1996
2076
|
}
|
|
@@ -3032,10 +3112,6 @@ var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndex
|
|
|
3032
3112
|
});
|
|
3033
3113
|
this.keysByPk = /* @__PURE__ */ new Map();
|
|
3034
3114
|
this.onCollectionUpdate = (pks) => {
|
|
3035
|
-
if (pks.length === 0) {
|
|
3036
|
-
this.rebuildFromCollection();
|
|
3037
|
-
return;
|
|
3038
|
-
}
|
|
3039
3115
|
for (const pk of pks) {
|
|
3040
3116
|
this.syncPk(pk);
|
|
3041
3117
|
}
|
|
@@ -3137,10 +3213,6 @@ var OIMDerivedCollectionIndexArrayBased = class extends OIMReactiveCollectionInd
|
|
|
3137
3213
|
// touching an unrelated field can skip re-sorting when the value is unchanged.
|
|
3138
3214
|
this.orderValueByPk = /* @__PURE__ */ new Map();
|
|
3139
3215
|
this.onCollectionUpdate = (pks) => {
|
|
3140
|
-
if (pks.length === 0) {
|
|
3141
|
-
this.rebuildFromCollection();
|
|
3142
|
-
return;
|
|
3143
|
-
}
|
|
3144
3216
|
const ordered = !!(this.compareEntities || this.orderBy);
|
|
3145
3217
|
const keysToSort = ordered ? /* @__PURE__ */ new Set() : null;
|
|
3146
3218
|
for (const pk of pks) {
|
|
@@ -3557,10 +3629,6 @@ var OIMDerivedCollectionGlobalIndexArrayBased = class extends OIMReactiveCollect
|
|
|
3557
3629
|
// an unrelated field can skip re-sorting when the value is unchanged.
|
|
3558
3630
|
this.orderValueByPk = /* @__PURE__ */ new Map();
|
|
3559
3631
|
this.onCollectionUpdate = (pks) => {
|
|
3560
|
-
if (pks.length === 0) {
|
|
3561
|
-
this.rebuildFromCollection();
|
|
3562
|
-
return;
|
|
3563
|
-
}
|
|
3564
3632
|
const ordered = !!(this.compareEntities || this.orderBy);
|
|
3565
3633
|
let needsSort = false;
|
|
3566
3634
|
for (const pk of pks) {
|
|
@@ -3658,10 +3726,6 @@ var OIMDerivedCollectionGlobalIndexSetBased = class extends OIMReactiveCollectio
|
|
|
3658
3726
|
super(queue, { collection, indexOptions: opts.indexOptions });
|
|
3659
3727
|
this.presentPks = /* @__PURE__ */ new Set();
|
|
3660
3728
|
this.onCollectionUpdate = (pks) => {
|
|
3661
|
-
if (pks.length === 0) {
|
|
3662
|
-
this.rebuildFromCollection();
|
|
3663
|
-
return;
|
|
3664
|
-
}
|
|
3665
3729
|
for (const pk of pks) {
|
|
3666
3730
|
const prev = this.presentPks.has(pk);
|
|
3667
3731
|
const slot = this.collection.getSlotByPk(pk);
|
|
@@ -3837,6 +3901,55 @@ function createOIMCollectionIndexFactory(queue, collection) {
|
|
|
3837
3901
|
return new OIMCollectionIndexFactory(queue, collection);
|
|
3838
3902
|
}
|
|
3839
3903
|
|
|
3904
|
+
// src/core/OIMDerivedCollection.ts
|
|
3905
|
+
var OIMDerivedCollection = class extends OIMReactiveCollection {
|
|
3906
|
+
constructor(queue, source, derive, opts) {
|
|
3907
|
+
super(queue, {
|
|
3908
|
+
store: opts?.store,
|
|
3909
|
+
// Each sync recomputes the WHOLE derived entity → replace, not merge.
|
|
3910
|
+
updateEntity: (draft) => draft
|
|
3911
|
+
});
|
|
3912
|
+
this.onSourceUpdate = (pks) => {
|
|
3913
|
+
for (let i = 0; i < pks.length; i++) this.syncPk(pks[i]);
|
|
3914
|
+
};
|
|
3915
|
+
this.source = source;
|
|
3916
|
+
this.derive = derive;
|
|
3917
|
+
this.compareDerived = opts?.compare ?? Object.is;
|
|
3918
|
+
this.unsubscribeFromSource = source.emitter.on(
|
|
3919
|
+
0 /* UPDATE */,
|
|
3920
|
+
(payload) => this.onSourceUpdate(payload.pks)
|
|
3921
|
+
);
|
|
3922
|
+
if (opts?.buildInitial !== false) {
|
|
3923
|
+
this.rebuildFromSource();
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
rebuildFromSource() {
|
|
3927
|
+
for (const pk of this.getAllPks()) {
|
|
3928
|
+
if (this.source.getOneByPk(pk) === void 0) {
|
|
3929
|
+
this.removeOneByPk(pk);
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
for (const slot of this.source.getAllSlots()) {
|
|
3933
|
+
if (slot.item !== void 0) this.syncPk(slot.pk);
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
destroy() {
|
|
3937
|
+
this.unsubscribeFromSource();
|
|
3938
|
+
super.destroy();
|
|
3939
|
+
}
|
|
3940
|
+
syncPk(pk) {
|
|
3941
|
+
const sourceEntity = this.source.getOneByPk(pk);
|
|
3942
|
+
if (sourceEntity === void 0) {
|
|
3943
|
+
if (this.getOneByPk(pk) !== void 0) this.removeOneByPk(pk);
|
|
3944
|
+
return;
|
|
3945
|
+
}
|
|
3946
|
+
const next = this.derive(sourceEntity, pk);
|
|
3947
|
+
const prev = this.getOneByPk(pk);
|
|
3948
|
+
if (prev !== void 0 && this.compareDerived(prev, next)) return;
|
|
3949
|
+
this.upsertOneByPk(pk, next);
|
|
3950
|
+
}
|
|
3951
|
+
};
|
|
3952
|
+
|
|
3840
3953
|
// src/core/OIMObjectStoreRecordDriven.ts
|
|
3841
3954
|
var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
|
|
3842
3955
|
constructor(initial) {
|
|
@@ -3909,8 +4022,11 @@ var OIMObject = class {
|
|
|
3909
4022
|
return this.store.getAll();
|
|
3910
4023
|
}
|
|
3911
4024
|
clear() {
|
|
4025
|
+
const keys = this.store.keys();
|
|
3912
4026
|
this.store.clear();
|
|
3913
|
-
|
|
4027
|
+
if (keys.length > 0) {
|
|
4028
|
+
this.onUpdatedKeys(keys);
|
|
4029
|
+
}
|
|
3914
4030
|
}
|
|
3915
4031
|
/**
|
|
3916
4032
|
* Write fan-out hooks. Split single-key vs multi-key so a reactive subclass
|
|
@@ -4213,8 +4329,9 @@ var OIMComparatorFactory = class {
|
|
|
4213
4329
|
if (ka.length !== kb.length) return true;
|
|
4214
4330
|
for (let i = 0; i < ka.length; i++) {
|
|
4215
4331
|
const k = ka[i];
|
|
4216
|
-
if (b[k] !== a[k])
|
|
4332
|
+
if (b[k] !== a[k]) {
|
|
4217
4333
|
return true;
|
|
4334
|
+
}
|
|
4218
4335
|
}
|
|
4219
4336
|
return false;
|
|
4220
4337
|
};
|
|
@@ -4236,6 +4353,7 @@ var OIMEventQueue = class {
|
|
|
4236
4353
|
this.tasksSpare = /* @__PURE__ */ new Set();
|
|
4237
4354
|
this.isFlushing = false;
|
|
4238
4355
|
this.scheduler = options.scheduler;
|
|
4356
|
+
this.onError = options.onError;
|
|
4239
4357
|
if (this.scheduler) {
|
|
4240
4358
|
this.flushBound = () => this.flush();
|
|
4241
4359
|
this.scheduler.on(
|
|
@@ -4263,10 +4381,14 @@ var OIMEventQueue = class {
|
|
|
4263
4381
|
return this.isFlushing;
|
|
4264
4382
|
}
|
|
4265
4383
|
/**
|
|
4266
|
-
* Execute
|
|
4384
|
+
* Execute the tasks pending at the moment the flush starts — a single pass,
|
|
4385
|
+
* not a drain-to-empty.
|
|
4267
4386
|
*
|
|
4268
|
-
*
|
|
4269
|
-
*
|
|
4387
|
+
* The pending set is swapped out for an empty spare up front and the flush
|
|
4388
|
+
* iterates that captured set. Tasks enqueued during the flush land in the
|
|
4389
|
+
* fresh set and run on the NEXT flush, not this one. (Contrast
|
|
4390
|
+
* `OIMComputeRuntime.flush()`, which re-reads its bucket per level and so
|
|
4391
|
+
* does drain same-level tasks scheduled mid-flush.)
|
|
4270
4392
|
*/
|
|
4271
4393
|
flush() {
|
|
4272
4394
|
this.scheduler?.cancel();
|
|
@@ -4275,12 +4397,34 @@ var OIMEventQueue = class {
|
|
|
4275
4397
|
const flushing = this.tasks;
|
|
4276
4398
|
this.tasks = this.tasksSpare;
|
|
4277
4399
|
this.flushing = flushing;
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4400
|
+
let errors;
|
|
4401
|
+
try {
|
|
4402
|
+
for (const task of flushing) {
|
|
4403
|
+
try {
|
|
4404
|
+
task();
|
|
4405
|
+
} catch (error) {
|
|
4406
|
+
(errors ?? (errors = [])).push(error);
|
|
4407
|
+
this.emitter.emit(2 /* FLUSH_ERROR */, {
|
|
4408
|
+
error
|
|
4409
|
+
});
|
|
4410
|
+
this.onError?.(error);
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
} finally {
|
|
4414
|
+
flushing.clear();
|
|
4415
|
+
this.tasksSpare = flushing;
|
|
4416
|
+
this.flushing = void 0;
|
|
4417
|
+
this.isFlushing = false;
|
|
4418
|
+
}
|
|
4283
4419
|
this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
|
|
4420
|
+
if (errors && this.onError === void 0) {
|
|
4421
|
+
if (errors.length === 1) throw errors[0];
|
|
4422
|
+
const AggregateErrorCtor = globalThis.AggregateError;
|
|
4423
|
+
throw AggregateErrorCtor ? new AggregateErrorCtor(
|
|
4424
|
+
errors,
|
|
4425
|
+
"Errors thrown during queue flush"
|
|
4426
|
+
) : errors[0];
|
|
4427
|
+
}
|
|
4284
4428
|
}
|
|
4285
4429
|
/**
|
|
4286
4430
|
* Get the current number of queued functions.
|
|
@@ -4518,20 +4662,92 @@ var OIMPkCodecKeyPath = class {
|
|
|
4518
4662
|
}
|
|
4519
4663
|
};
|
|
4520
4664
|
|
|
4665
|
+
// src/modules/effect/core/OIMEffectDependencyComputed.ts
|
|
4666
|
+
var OIMEffectDependencyComputed = class {
|
|
4667
|
+
constructor(source) {
|
|
4668
|
+
this.source = source;
|
|
4669
|
+
}
|
|
4670
|
+
subscribe(onUpdate) {
|
|
4671
|
+
return this.source.emitter.on("update", onUpdate);
|
|
4672
|
+
}
|
|
4673
|
+
};
|
|
4674
|
+
|
|
4521
4675
|
// src/modules/compute/core/OIMComputeRuntime.ts
|
|
4522
4676
|
var OIMComputeRuntime = class {
|
|
4523
4677
|
constructor(queue) {
|
|
4524
4678
|
this.queue = queue;
|
|
4525
4679
|
this.isFlushScheduled = false;
|
|
4526
4680
|
this.isFlushing = false;
|
|
4681
|
+
// Active level range (min > max means nothing pending).
|
|
4527
4682
|
this.minLevel = Infinity;
|
|
4528
4683
|
this.maxLevel = -Infinity;
|
|
4529
|
-
this.dirtyByLevel =
|
|
4684
|
+
this.dirtyByLevel = [];
|
|
4530
4685
|
this.afterFlushTasks = /* @__PURE__ */ new Set();
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4686
|
+
// Topological depth per computed node — the engine is the ONLY owner of
|
|
4687
|
+
// levels; nodes never expose their own. A node's dependents look up its depth
|
|
4688
|
+
// here (by the computed reference their dependency wraps). Written once at
|
|
4689
|
+
// construction, never on the hot flush path.
|
|
4690
|
+
this.levelByComputed = /* @__PURE__ */ new WeakMap();
|
|
4691
|
+
this._isDestroyed = false;
|
|
4692
|
+
this.unsubscribeFromQueue = this.queue.emitter.on(
|
|
4693
|
+
0 /* AFTER_FLUSH */,
|
|
4694
|
+
() => {
|
|
4695
|
+
if (!this.isFlushScheduled) return;
|
|
4696
|
+
this.flush();
|
|
4697
|
+
}
|
|
4698
|
+
);
|
|
4699
|
+
}
|
|
4700
|
+
/**
|
|
4701
|
+
* Whether {@link destroy} has run. A destroyed runtime is detached from its
|
|
4702
|
+
* queue and must not be reused — `getOIMComputeRuntime` reads this to hand
|
|
4703
|
+
* out a fresh runtime instead of a dead one.
|
|
4704
|
+
*/
|
|
4705
|
+
get isDestroyed() {
|
|
4706
|
+
return this._isDestroyed;
|
|
4707
|
+
}
|
|
4708
|
+
/**
|
|
4709
|
+
* Detach from the queue and drop everything pending.
|
|
4710
|
+
*
|
|
4711
|
+
* Without this the AFTER_FLUSH subscription lives as long as the queue does,
|
|
4712
|
+
* even once every computed / effect on this runtime is gone — harmless (the
|
|
4713
|
+
* handler early-returns) but impossible to unwind. Destroying does NOT
|
|
4714
|
+
* destroy the nodes scheduled on it; destroy them first, then the runtime.
|
|
4715
|
+
* Idempotent.
|
|
4716
|
+
*/
|
|
4717
|
+
destroy() {
|
|
4718
|
+
if (this._isDestroyed) return;
|
|
4719
|
+
this._isDestroyed = true;
|
|
4720
|
+
this.unsubscribeFromQueue();
|
|
4721
|
+
for (let i = 0; i < this.dirtyByLevel.length; i++) {
|
|
4722
|
+
this.dirtyByLevel[i]?.clear();
|
|
4723
|
+
}
|
|
4724
|
+
this.afterFlushTasks.clear();
|
|
4725
|
+
this.minLevel = Infinity;
|
|
4726
|
+
this.maxLevel = -Infinity;
|
|
4727
|
+
this.isFlushScheduled = false;
|
|
4728
|
+
}
|
|
4729
|
+
/**
|
|
4730
|
+
* Topological depth for a node with these dependencies: one above the deepest
|
|
4731
|
+
* computed dependency (source-only → 0). The engine owns this — callers pass
|
|
4732
|
+
* their dependencies and get back a level, they never compute it themselves.
|
|
4733
|
+
*/
|
|
4734
|
+
computeLevel(deps) {
|
|
4735
|
+
let max = -1;
|
|
4736
|
+
for (const dep of deps) {
|
|
4737
|
+
if (dep instanceof OIMEffectDependencyComputed) {
|
|
4738
|
+
const depLevel = this.levelByComputed.get(dep.source) ?? 0;
|
|
4739
|
+
if (depLevel > max) max = depLevel;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
return max + 1;
|
|
4743
|
+
}
|
|
4744
|
+
/** Record a computed's depth so its dependents can look it up. */
|
|
4745
|
+
registerLevel(computed, level) {
|
|
4746
|
+
this.levelByComputed.set(computed, level);
|
|
4747
|
+
}
|
|
4748
|
+
/** The recorded topological depth of a computed (0 if unknown). Introspection. */
|
|
4749
|
+
getLevel(computed) {
|
|
4750
|
+
return this.levelByComputed.get(computed) ?? 0;
|
|
4535
4751
|
}
|
|
4536
4752
|
/**
|
|
4537
4753
|
* Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
|
|
@@ -4543,10 +4759,10 @@ var OIMComputeRuntime = class {
|
|
|
4543
4759
|
if (!isActive) return;
|
|
4544
4760
|
task();
|
|
4545
4761
|
};
|
|
4546
|
-
let bucket = this.dirtyByLevel
|
|
4547
|
-
if (
|
|
4762
|
+
let bucket = this.dirtyByLevel[level];
|
|
4763
|
+
if (bucket === void 0) {
|
|
4548
4764
|
bucket = /* @__PURE__ */ new Set();
|
|
4549
|
-
this.dirtyByLevel
|
|
4765
|
+
this.dirtyByLevel[level] = bucket;
|
|
4550
4766
|
}
|
|
4551
4767
|
bucket.add(wrapped);
|
|
4552
4768
|
if (level < this.minLevel) this.minLevel = level;
|
|
@@ -4578,7 +4794,7 @@ var OIMComputeRuntime = class {
|
|
|
4578
4794
|
}
|
|
4579
4795
|
flush() {
|
|
4580
4796
|
this.isFlushScheduled = false;
|
|
4581
|
-
if (this.
|
|
4797
|
+
if (this.minLevel > this.maxLevel && this.afterFlushTasks.size === 0)
|
|
4582
4798
|
return;
|
|
4583
4799
|
this.isFlushing = true;
|
|
4584
4800
|
try {
|
|
@@ -4587,15 +4803,15 @@ var OIMComputeRuntime = class {
|
|
|
4587
4803
|
this.afterFlushTasks.clear();
|
|
4588
4804
|
for (let i = 0; i < snapshot.length; i++) snapshot[i]();
|
|
4589
4805
|
}
|
|
4590
|
-
if (this.
|
|
4806
|
+
if (this.minLevel > this.maxLevel) return;
|
|
4591
4807
|
let level = this.minLevel;
|
|
4592
4808
|
let max = this.maxLevel;
|
|
4593
4809
|
while (level <= max) {
|
|
4594
4810
|
for (; ; ) {
|
|
4595
|
-
const bucket = this.dirtyByLevel
|
|
4596
|
-
if (
|
|
4597
|
-
this.dirtyByLevel.delete(level);
|
|
4811
|
+
const bucket = this.dirtyByLevel[level];
|
|
4812
|
+
if (bucket === void 0 || bucket.size === 0) break;
|
|
4598
4813
|
const snapshot = Array.from(bucket);
|
|
4814
|
+
bucket.clear();
|
|
4599
4815
|
for (let i = 0; i < snapshot.length; i++) snapshot[i]();
|
|
4600
4816
|
}
|
|
4601
4817
|
if (this.maxLevel > max) max = this.maxLevel;
|
|
@@ -4603,24 +4819,38 @@ var OIMComputeRuntime = class {
|
|
|
4603
4819
|
}
|
|
4604
4820
|
} finally {
|
|
4605
4821
|
this.isFlushing = false;
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
this.isFlushScheduled = true;
|
|
4822
|
+
this.recomputePendingRange();
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
recomputePendingRange() {
|
|
4826
|
+
let nextMin = Infinity;
|
|
4827
|
+
let nextMax = -Infinity;
|
|
4828
|
+
const buckets = this.dirtyByLevel;
|
|
4829
|
+
for (let i = 0; i < buckets.length; i++) {
|
|
4830
|
+
const bucket = buckets[i];
|
|
4831
|
+
if (bucket !== void 0 && bucket.size > 0) {
|
|
4832
|
+
if (i < nextMin) nextMin = i;
|
|
4833
|
+
if (i > nextMax) nextMax = i;
|
|
4619
4834
|
}
|
|
4620
4835
|
}
|
|
4836
|
+
this.minLevel = nextMin;
|
|
4837
|
+
this.maxLevel = nextMax;
|
|
4838
|
+
if (nextMin <= nextMax) this.isFlushScheduled = true;
|
|
4621
4839
|
}
|
|
4622
4840
|
};
|
|
4623
4841
|
|
|
4842
|
+
// src/dx/oimComputeRuntimeByQueue.ts
|
|
4843
|
+
var oimComputeRuntimeByQueue = /* @__PURE__ */ new WeakMap();
|
|
4844
|
+
|
|
4845
|
+
// src/dx/getOIMComputeRuntime.ts
|
|
4846
|
+
function getOIMComputeRuntime(queue) {
|
|
4847
|
+
const runtime = oimComputeRuntimeByQueue.get(queue);
|
|
4848
|
+
if (runtime !== void 0 && !runtime.isDestroyed) return runtime;
|
|
4849
|
+
const nextRuntime = new OIMComputeRuntime(queue);
|
|
4850
|
+
oimComputeRuntimeByQueue.set(queue, nextRuntime);
|
|
4851
|
+
return nextRuntime;
|
|
4852
|
+
}
|
|
4853
|
+
|
|
4624
4854
|
// src/modules/selector/core/OIMSelector.ts
|
|
4625
4855
|
var OIMSelector = class {
|
|
4626
4856
|
constructor(runtime, sourceDeps, computedDeps = []) {
|
|
@@ -4881,8 +5111,9 @@ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
|
|
|
4881
5111
|
areEqual(prev, next) {
|
|
4882
5112
|
if (prev === next) return true;
|
|
4883
5113
|
if (prev.length !== next.length) return false;
|
|
4884
|
-
for (let i = 0; i < prev.length; i++)
|
|
5114
|
+
for (let i = 0; i < prev.length; i++) {
|
|
4885
5115
|
if (prev[i] !== next[i]) return false;
|
|
5116
|
+
}
|
|
4886
5117
|
return true;
|
|
4887
5118
|
}
|
|
4888
5119
|
};
|
|
@@ -4952,8 +5183,9 @@ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
|
|
|
4952
5183
|
areEqual(prev, next) {
|
|
4953
5184
|
if (prev === next) return true;
|
|
4954
5185
|
if (prev.length !== next.length) return false;
|
|
4955
|
-
for (let i = 0; i < prev.length; i++)
|
|
5186
|
+
for (let i = 0; i < prev.length; i++) {
|
|
4956
5187
|
if (prev[i] !== next[i]) return false;
|
|
5188
|
+
}
|
|
4957
5189
|
return true;
|
|
4958
5190
|
}
|
|
4959
5191
|
};
|
|
@@ -5017,8 +5249,9 @@ var OIMEntitiesByGlobalIndexArrayBasedSelector = class extends OIMSelector {
|
|
|
5017
5249
|
areEqual(prev, next) {
|
|
5018
5250
|
if (prev === next) return true;
|
|
5019
5251
|
if (prev.length !== next.length) return false;
|
|
5020
|
-
for (let i = 0; i < prev.length; i++)
|
|
5252
|
+
for (let i = 0; i < prev.length; i++) {
|
|
5021
5253
|
if (prev[i] !== next[i]) return false;
|
|
5254
|
+
}
|
|
5022
5255
|
return true;
|
|
5023
5256
|
}
|
|
5024
5257
|
};
|
|
@@ -5083,8 +5316,9 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
|
|
|
5083
5316
|
areEqual(prev, next) {
|
|
5084
5317
|
if (prev === next) return true;
|
|
5085
5318
|
if (prev.length !== next.length) return false;
|
|
5086
|
-
for (let i = 0; i < prev.length; i++)
|
|
5319
|
+
for (let i = 0; i < prev.length; i++) {
|
|
5087
5320
|
if (prev[i] !== next[i]) return false;
|
|
5321
|
+
}
|
|
5088
5322
|
return true;
|
|
5089
5323
|
}
|
|
5090
5324
|
};
|
|
@@ -5093,7 +5327,7 @@ var OIMEntitiesByGlobalIndexSetBasedSelector = class extends OIMSelector {
|
|
|
5093
5327
|
var OIMCollectionSelectors = class {
|
|
5094
5328
|
constructor(queue, collection) {
|
|
5095
5329
|
this.collection = collection;
|
|
5096
|
-
this.runtime =
|
|
5330
|
+
this.runtime = getOIMComputeRuntime(queue);
|
|
5097
5331
|
}
|
|
5098
5332
|
byPk(pk) {
|
|
5099
5333
|
return new OIMCollectionByPkSelector(this.runtime, this.collection, pk);
|
|
@@ -5160,127 +5394,408 @@ var OIMCollectionSelectors = class {
|
|
|
5160
5394
|
}
|
|
5161
5395
|
};
|
|
5162
5396
|
|
|
5163
|
-
// src/
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
const scope = new OIMDisposeScope();
|
|
5169
|
-
scope.add(collection);
|
|
5170
|
-
return {
|
|
5171
|
-
queue,
|
|
5172
|
-
collection,
|
|
5173
|
-
indexFactory,
|
|
5174
|
-
select,
|
|
5175
|
-
scope,
|
|
5176
|
-
destroy: () => scope.destroy()
|
|
5177
|
-
};
|
|
5178
|
-
}
|
|
5179
|
-
|
|
5180
|
-
// src/modules/wrapper/collection/OIMCollectionChangedFields.ts
|
|
5181
|
-
var OIMCollectionChangedFields = class {
|
|
5182
|
-
constructor(queue, collection, opts) {
|
|
5183
|
-
this.isInWrite = false;
|
|
5184
|
-
this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
|
|
5185
|
-
// PKs written through this wrapper during the current flush. Their fields are
|
|
5186
|
-
// already tracked accurately via trackPatch, so the flush-time external-write
|
|
5187
|
-
// handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
|
|
5188
|
-
this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
|
|
5189
|
-
this.changedFieldsByPk = /* @__PURE__ */ new Map();
|
|
5190
|
-
this.changedPksByField = /* @__PURE__ */ new Map();
|
|
5191
|
-
this.onAfterFlush = () => {
|
|
5192
|
-
this.clear();
|
|
5193
|
-
this.writtenThroughThisFlush.clear();
|
|
5194
|
-
};
|
|
5195
|
-
this.onCollectionUpdate = (payload) => {
|
|
5196
|
-
if (this.isInWrite) return;
|
|
5197
|
-
if (payload.pks.length === 0) return;
|
|
5198
|
-
for (let i = 0; i < payload.pks.length; i++) {
|
|
5199
|
-
const pk = payload.pks[i];
|
|
5200
|
-
if (this.writtenThroughThisFlush.has(pk)) continue;
|
|
5201
|
-
this.trackExternalMutationByPk(pk);
|
|
5202
|
-
}
|
|
5203
|
-
};
|
|
5204
|
-
this.collection = collection;
|
|
5205
|
-
this.detectExternalMutations = opts?.detectExternalMutations !== false;
|
|
5206
|
-
this.selectPk = opts?.selectPk ?? (() => {
|
|
5207
|
-
throw new Error(
|
|
5208
|
-
`[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
|
|
5209
|
-
);
|
|
5210
|
-
});
|
|
5211
|
-
this.changedPksEventEmitter = new OIMUpdateEventEmitter(
|
|
5212
|
-
queue,
|
|
5213
|
-
"after_flush"
|
|
5214
|
-
);
|
|
5215
|
-
this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
|
|
5216
|
-
this.unsubscribeAfterFlush = queue.emitter.on(
|
|
5217
|
-
0 /* AFTER_FLUSH */,
|
|
5218
|
-
this.onAfterFlush
|
|
5219
|
-
);
|
|
5220
|
-
if (this.detectExternalMutations) {
|
|
5221
|
-
const maybeReactive = collection;
|
|
5222
|
-
this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
|
|
5223
|
-
this.onCollectionUpdate({
|
|
5224
|
-
pks
|
|
5225
|
-
});
|
|
5226
|
-
}) : collection.emitter.on(
|
|
5227
|
-
0 /* UPDATE */,
|
|
5228
|
-
this.onCollectionUpdate
|
|
5229
|
-
);
|
|
5230
|
-
}
|
|
5231
|
-
}
|
|
5232
|
-
upsertOneByPk(pk, patch) {
|
|
5233
|
-
const slot = this.withWrite(() => {
|
|
5234
|
-
this.trackPatch(pk, patch);
|
|
5235
|
-
return this.collection.upsertOneByPk(pk, patch);
|
|
5236
|
-
});
|
|
5237
|
-
this.updateEntitySnapshot(pk);
|
|
5238
|
-
return slot;
|
|
5397
|
+
// src/modules/effect/core/OIMEffectDependencyKeyed.ts
|
|
5398
|
+
var OIMEffectDependencyKeyed = class {
|
|
5399
|
+
constructor(source, key) {
|
|
5400
|
+
this.source = source;
|
|
5401
|
+
this.key = key;
|
|
5239
5402
|
}
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
const pk = this.selectPk(entity);
|
|
5243
|
-
this.trackPatch(pk, entity);
|
|
5244
|
-
return this.collection.upsertOne(entity);
|
|
5245
|
-
});
|
|
5246
|
-
this.updateEntitySnapshot(slot.pk);
|
|
5247
|
-
return slot;
|
|
5403
|
+
subscribe(onUpdate) {
|
|
5404
|
+
return this.source.subscribeOnKey(this.key, onUpdate);
|
|
5248
5405
|
}
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
this.trackPatch(pk, entity);
|
|
5256
|
-
}
|
|
5257
|
-
return this.collection.upsertMany(entities);
|
|
5258
|
-
});
|
|
5259
|
-
for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
|
|
5260
|
-
return slots;
|
|
5406
|
+
};
|
|
5407
|
+
|
|
5408
|
+
// src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
|
|
5409
|
+
var OIMEffectDependencyKeyedCollection = class {
|
|
5410
|
+
constructor(collection, pk) {
|
|
5411
|
+
this.dep = new OIMEffectDependencyKeyed(collection, pk);
|
|
5261
5412
|
}
|
|
5262
|
-
|
|
5263
|
-
return this.
|
|
5413
|
+
get source() {
|
|
5414
|
+
return this.dep.source;
|
|
5264
5415
|
}
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
if (!existing) return /* @__PURE__ */ new Set();
|
|
5268
|
-
return new Set(existing);
|
|
5416
|
+
subscribe(onUpdate) {
|
|
5417
|
+
return this.dep.subscribe(onUpdate);
|
|
5269
5418
|
}
|
|
5270
|
-
|
|
5271
|
-
|
|
5419
|
+
};
|
|
5420
|
+
|
|
5421
|
+
// src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
|
|
5422
|
+
var OIMEffectDependencyKeyedIndex = class {
|
|
5423
|
+
constructor(index, key) {
|
|
5424
|
+
this.dep = new OIMEffectDependencyKeyed(index, key);
|
|
5272
5425
|
}
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
if (!existing) return /* @__PURE__ */ new Set();
|
|
5276
|
-
return new Set(existing);
|
|
5426
|
+
get source() {
|
|
5427
|
+
return this.dep.source;
|
|
5277
5428
|
}
|
|
5278
|
-
|
|
5279
|
-
this.
|
|
5280
|
-
this.changedPksByField.clear();
|
|
5429
|
+
subscribe(onUpdate) {
|
|
5430
|
+
return this.dep.subscribe(onUpdate);
|
|
5281
5431
|
}
|
|
5282
|
-
|
|
5283
|
-
|
|
5432
|
+
};
|
|
5433
|
+
|
|
5434
|
+
// src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
|
|
5435
|
+
var OIMEffectDependencyKeyedObject = class {
|
|
5436
|
+
constructor(object, key) {
|
|
5437
|
+
this.dep = new OIMEffectDependencyKeyed(object, key);
|
|
5438
|
+
}
|
|
5439
|
+
get source() {
|
|
5440
|
+
return this.dep.source;
|
|
5441
|
+
}
|
|
5442
|
+
subscribe(onUpdate) {
|
|
5443
|
+
return this.dep.subscribe(onUpdate);
|
|
5444
|
+
}
|
|
5445
|
+
};
|
|
5446
|
+
|
|
5447
|
+
// src/dx/on.ts
|
|
5448
|
+
var on = {
|
|
5449
|
+
/** Depend on one PK of a collection. */
|
|
5450
|
+
collection: (collection, pk) => new OIMEffectDependencyKeyedCollection(collection, pk),
|
|
5451
|
+
/** Depend on one key of an index. */
|
|
5452
|
+
index: (index, key) => new OIMEffectDependencyKeyedIndex(index, key),
|
|
5453
|
+
/** Depend on one key of a reactive object. */
|
|
5454
|
+
object: (object, key) => new OIMEffectDependencyKeyedObject(object, key),
|
|
5455
|
+
/** Depend on another computed. */
|
|
5456
|
+
computed: (computed) => new OIMEffectDependencyComputed(computed)
|
|
5457
|
+
};
|
|
5458
|
+
|
|
5459
|
+
// src/modules/effect/core/OIMEffect.ts
|
|
5460
|
+
var OIMEffect = class {
|
|
5461
|
+
constructor(runtime, opts, level) {
|
|
5462
|
+
this.sourceDeps = [];
|
|
5463
|
+
this.computedDeps = [];
|
|
5464
|
+
this.sourceUnsubscribers = [];
|
|
5465
|
+
this.computedUnsubscribers = [];
|
|
5466
|
+
this.isResubscribeEnqueued = false;
|
|
5467
|
+
this.isTemporarilyUnsubscribedForFlush = false;
|
|
5468
|
+
this.isScheduled = false;
|
|
5469
|
+
this.runTask = () => {
|
|
5470
|
+
this.isScheduled = false;
|
|
5471
|
+
this.dequeueScheduled = void 0;
|
|
5472
|
+
this.runOnce();
|
|
5473
|
+
};
|
|
5474
|
+
this.onSourceInvalidate = () => {
|
|
5475
|
+
this.onUpdate?.();
|
|
5476
|
+
if (!this.isTemporarilyUnsubscribedForFlush) {
|
|
5477
|
+
this.isTemporarilyUnsubscribedForFlush = true;
|
|
5478
|
+
this.unsubscribeFromSourceDeps();
|
|
5479
|
+
this.ensureResubscribeAfterFlush();
|
|
5480
|
+
}
|
|
5481
|
+
if (this.isScheduled) return;
|
|
5482
|
+
this.isScheduled = true;
|
|
5483
|
+
this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
|
|
5484
|
+
};
|
|
5485
|
+
this.onComputedInvalidate = () => {
|
|
5486
|
+
this.onUpdate?.();
|
|
5487
|
+
if (this.isScheduled) return;
|
|
5488
|
+
this.isScheduled = true;
|
|
5489
|
+
this.dequeueScheduled = this.runtime.schedule(this.runTask, this.level);
|
|
5490
|
+
};
|
|
5491
|
+
this.onAfterFlush = () => {
|
|
5492
|
+
if (!this.isResubscribeEnqueued) return;
|
|
5493
|
+
this.isResubscribeEnqueued = false;
|
|
5494
|
+
this.dequeueResubscribe = void 0;
|
|
5495
|
+
this.isTemporarilyUnsubscribedForFlush = false;
|
|
5496
|
+
if (this.sourceUnsubscribers.length === 0) {
|
|
5497
|
+
for (const dep of this.sourceDeps) {
|
|
5498
|
+
this.sourceUnsubscribers.push(
|
|
5499
|
+
dep.subscribe(this.onSourceInvalidate)
|
|
5500
|
+
);
|
|
5501
|
+
}
|
|
5502
|
+
}
|
|
5503
|
+
};
|
|
5504
|
+
this.runtime = runtime;
|
|
5505
|
+
this.onUpdate = opts.onUpdate;
|
|
5506
|
+
this.run = opts.run;
|
|
5507
|
+
this.deps = opts.deps ?? [];
|
|
5508
|
+
for (const dep of this.deps) {
|
|
5509
|
+
if (dep instanceof OIMEffectDependencyComputed) {
|
|
5510
|
+
this.computedDeps.push(dep);
|
|
5511
|
+
} else {
|
|
5512
|
+
this.sourceDeps.push(dep);
|
|
5513
|
+
}
|
|
5514
|
+
}
|
|
5515
|
+
this.level = level ?? this.runtime.computeLevel(this.deps);
|
|
5516
|
+
this.subscribeToDeps();
|
|
5517
|
+
}
|
|
5518
|
+
destroy() {
|
|
5519
|
+
if (this.dequeueScheduled) {
|
|
5520
|
+
this.dequeueScheduled();
|
|
5521
|
+
this.dequeueScheduled = void 0;
|
|
5522
|
+
}
|
|
5523
|
+
this.unsubscribeFromAllDeps();
|
|
5524
|
+
if (this.dequeueResubscribe) {
|
|
5525
|
+
this.dequeueResubscribe();
|
|
5526
|
+
this.dequeueResubscribe = void 0;
|
|
5527
|
+
}
|
|
5528
|
+
this.isResubscribeEnqueued = false;
|
|
5529
|
+
this.isTemporarilyUnsubscribedForFlush = false;
|
|
5530
|
+
}
|
|
5531
|
+
runOnce() {
|
|
5532
|
+
this.run();
|
|
5533
|
+
}
|
|
5534
|
+
subscribeToDeps() {
|
|
5535
|
+
if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
|
|
5536
|
+
return;
|
|
5537
|
+
for (const dep of this.sourceDeps) {
|
|
5538
|
+
this.sourceUnsubscribers.push(
|
|
5539
|
+
dep.subscribe(this.onSourceInvalidate)
|
|
5540
|
+
);
|
|
5541
|
+
}
|
|
5542
|
+
for (const dep of this.computedDeps) {
|
|
5543
|
+
this.computedUnsubscribers.push(
|
|
5544
|
+
dep.subscribe(this.onComputedInvalidate)
|
|
5545
|
+
);
|
|
5546
|
+
}
|
|
5547
|
+
}
|
|
5548
|
+
unsubscribeFromSourceDeps() {
|
|
5549
|
+
if (this.sourceUnsubscribers.length === 0) return;
|
|
5550
|
+
for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
|
|
5551
|
+
this.sourceUnsubscribers = [];
|
|
5552
|
+
}
|
|
5553
|
+
unsubscribeFromAllDeps() {
|
|
5554
|
+
this.unsubscribeFromSourceDeps();
|
|
5555
|
+
if (this.computedUnsubscribers.length === 0) return;
|
|
5556
|
+
for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
|
|
5557
|
+
this.computedUnsubscribers = [];
|
|
5558
|
+
}
|
|
5559
|
+
ensureResubscribeAfterFlush() {
|
|
5560
|
+
if (this.isResubscribeEnqueued) return;
|
|
5561
|
+
this.isResubscribeEnqueued = true;
|
|
5562
|
+
this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
|
|
5563
|
+
this.onAfterFlush
|
|
5564
|
+
);
|
|
5565
|
+
}
|
|
5566
|
+
};
|
|
5567
|
+
|
|
5568
|
+
// src/modules/computed/enums/EOIMComputedEventType.ts
|
|
5569
|
+
var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
|
|
5570
|
+
EOIMComputedEventType2["UPDATE"] = "update";
|
|
5571
|
+
return EOIMComputedEventType2;
|
|
5572
|
+
})(EOIMComputedEventType || {});
|
|
5573
|
+
|
|
5574
|
+
// src/modules/computed/core/OIMComputed.ts
|
|
5575
|
+
var OIMComputed = class {
|
|
5576
|
+
constructor(runtime, opts) {
|
|
5577
|
+
this.emitter = new OIMEventEmitter();
|
|
5578
|
+
this.hasValue = false;
|
|
5579
|
+
this.isDirty = true;
|
|
5580
|
+
this.runtime = runtime;
|
|
5581
|
+
this.compute = opts.compute;
|
|
5582
|
+
this.compare = opts.compare ?? Object.is;
|
|
5583
|
+
this._deps = opts.deps ?? [];
|
|
5584
|
+
this.updateEventEmitter = new OIMUpdateEventEmitter(
|
|
5585
|
+
this.runtime.queue,
|
|
5586
|
+
"immediate"
|
|
5587
|
+
);
|
|
5588
|
+
const level = this.runtime.computeLevel(this._deps);
|
|
5589
|
+
this.runtime.registerLevel(this, level);
|
|
5590
|
+
this.effect = new OIMEffect(
|
|
5591
|
+
this.runtime,
|
|
5592
|
+
{
|
|
5593
|
+
deps: this._deps,
|
|
5594
|
+
onUpdate: () => {
|
|
5595
|
+
this.isDirty = true;
|
|
5596
|
+
},
|
|
5597
|
+
run: () => {
|
|
5598
|
+
this.recomputeAndEmitIfChanged();
|
|
5599
|
+
}
|
|
5600
|
+
},
|
|
5601
|
+
level
|
|
5602
|
+
);
|
|
5603
|
+
}
|
|
5604
|
+
get needsRecompute() {
|
|
5605
|
+
return this.isDirty;
|
|
5606
|
+
}
|
|
5607
|
+
get isReady() {
|
|
5608
|
+
return this.hasValue;
|
|
5609
|
+
}
|
|
5610
|
+
get deps() {
|
|
5611
|
+
return this._deps;
|
|
5612
|
+
}
|
|
5613
|
+
get() {
|
|
5614
|
+
this.recomputeAndEmitIfChanged();
|
|
5615
|
+
return this.value;
|
|
5616
|
+
}
|
|
5617
|
+
getIfReady() {
|
|
5618
|
+
return this.hasValue ? this.value : void 0;
|
|
5619
|
+
}
|
|
5620
|
+
destroy() {
|
|
5621
|
+
this.effect.destroy();
|
|
5622
|
+
this.updateEventEmitter.destroy();
|
|
5623
|
+
this.emitter.offAll();
|
|
5624
|
+
}
|
|
5625
|
+
recomputeAndEmitIfChanged() {
|
|
5626
|
+
if (!this.isDirty) return;
|
|
5627
|
+
const prev = this.value;
|
|
5628
|
+
const hadPrev = this.hasValue;
|
|
5629
|
+
const next = this.compute();
|
|
5630
|
+
this.value = next;
|
|
5631
|
+
this.hasValue = true;
|
|
5632
|
+
this.isDirty = false;
|
|
5633
|
+
const changed = !hadPrev || !this.compare(prev, next);
|
|
5634
|
+
if (!changed) return;
|
|
5635
|
+
this.emitter.emit("update" /* UPDATE */, {
|
|
5636
|
+
keys: ["value"]
|
|
5637
|
+
});
|
|
5638
|
+
this.updateEventEmitter.markUpdatedKey("value");
|
|
5639
|
+
}
|
|
5640
|
+
};
|
|
5641
|
+
|
|
5642
|
+
// src/dx/OIMCollectionKit.ts
|
|
5643
|
+
function createOIMCollectionKit(queue, opts) {
|
|
5644
|
+
const collection = new OIMReactiveCollection(queue, opts);
|
|
5645
|
+
const indexFactory = new OIMCollectionIndexFactory(queue, collection);
|
|
5646
|
+
const select = new OIMCollectionSelectors(queue, collection);
|
|
5647
|
+
const runtime = getOIMComputeRuntime(queue);
|
|
5648
|
+
const scope = new OIMDisposeScope();
|
|
5649
|
+
scope.add(collection);
|
|
5650
|
+
return {
|
|
5651
|
+
queue,
|
|
5652
|
+
collection,
|
|
5653
|
+
indexFactory,
|
|
5654
|
+
select,
|
|
5655
|
+
on,
|
|
5656
|
+
computed: (deps, compute, options) => {
|
|
5657
|
+
const computed = new OIMComputed(runtime, {
|
|
5658
|
+
deps,
|
|
5659
|
+
compute,
|
|
5660
|
+
compare: options?.compare
|
|
5661
|
+
});
|
|
5662
|
+
scope.add(computed);
|
|
5663
|
+
return computed;
|
|
5664
|
+
},
|
|
5665
|
+
effect: (deps, run, options) => {
|
|
5666
|
+
const effect = new OIMEffect(runtime, {
|
|
5667
|
+
deps,
|
|
5668
|
+
run,
|
|
5669
|
+
onUpdate: options?.onUpdate
|
|
5670
|
+
});
|
|
5671
|
+
scope.add(effect);
|
|
5672
|
+
return effect;
|
|
5673
|
+
},
|
|
5674
|
+
scope,
|
|
5675
|
+
destroy: () => scope.destroy()
|
|
5676
|
+
};
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
// src/dx/peekOIMComputeRuntime.ts
|
|
5680
|
+
function peekOIMComputeRuntime(queue) {
|
|
5681
|
+
const runtime = oimComputeRuntimeByQueue.get(queue);
|
|
5682
|
+
return runtime === void 0 || runtime.isDestroyed ? void 0 : runtime;
|
|
5683
|
+
}
|
|
5684
|
+
|
|
5685
|
+
// src/dx/setOIMComputeRuntime.ts
|
|
5686
|
+
function setOIMComputeRuntime(queue, runtime) {
|
|
5687
|
+
if (runtime.queue !== queue) {
|
|
5688
|
+
throw new Error(
|
|
5689
|
+
"setOIMComputeRuntime: the runtime is attached to a different queue. A runtime drains on its own queue and would never run here."
|
|
5690
|
+
);
|
|
5691
|
+
}
|
|
5692
|
+
oimComputeRuntimeByQueue.set(queue, runtime);
|
|
5693
|
+
}
|
|
5694
|
+
|
|
5695
|
+
// src/modules/wrapper/collection/OIMCollectionChangedFields.ts
|
|
5696
|
+
var OIMCollectionChangedFields = class {
|
|
5697
|
+
constructor(queue, collection, opts) {
|
|
5698
|
+
this.isInWrite = false;
|
|
5699
|
+
this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
|
|
5700
|
+
// PKs written through this wrapper during the current flush. Their fields are
|
|
5701
|
+
// already tracked accurately via trackPatch, so the flush-time external-write
|
|
5702
|
+
// handler skips them — no redundant diff + snapshot re-clone. Cleared per flush.
|
|
5703
|
+
this.writtenThroughThisFlush = /* @__PURE__ */ new Set();
|
|
5704
|
+
this.changedFieldsByPk = /* @__PURE__ */ new Map();
|
|
5705
|
+
this.changedPksByField = /* @__PURE__ */ new Map();
|
|
5706
|
+
this.onAfterFlush = () => {
|
|
5707
|
+
this.clear();
|
|
5708
|
+
this.writtenThroughThisFlush.clear();
|
|
5709
|
+
};
|
|
5710
|
+
this.onCollectionUpdate = (payload) => {
|
|
5711
|
+
if (this.isInWrite) return;
|
|
5712
|
+
if (payload.pks.length === 0) return;
|
|
5713
|
+
for (let i = 0; i < payload.pks.length; i++) {
|
|
5714
|
+
const pk = payload.pks[i];
|
|
5715
|
+
if (this.writtenThroughThisFlush.has(pk)) continue;
|
|
5716
|
+
this.trackExternalMutationByPk(pk);
|
|
5717
|
+
}
|
|
5718
|
+
};
|
|
5719
|
+
this.collection = collection;
|
|
5720
|
+
this.detectExternalMutations = opts?.detectExternalMutations !== false;
|
|
5721
|
+
this.selectPk = opts?.selectPk ?? (() => {
|
|
5722
|
+
throw new Error(
|
|
5723
|
+
`[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
|
|
5724
|
+
);
|
|
5725
|
+
});
|
|
5726
|
+
this.changedPksEventEmitter = new OIMUpdateEventEmitter(
|
|
5727
|
+
queue,
|
|
5728
|
+
"after_flush"
|
|
5729
|
+
);
|
|
5730
|
+
this.changedFieldsEventEmitter = new OIMUpdateEventEmitter(queue, "after_flush");
|
|
5731
|
+
this.unsubscribeAfterFlush = queue.emitter.on(
|
|
5732
|
+
0 /* AFTER_FLUSH */,
|
|
5733
|
+
this.onAfterFlush
|
|
5734
|
+
);
|
|
5735
|
+
if (this.detectExternalMutations) {
|
|
5736
|
+
const maybeReactive = collection;
|
|
5737
|
+
this.unsubscribeFromCollectionEmitter = maybeReactive.subscribeOnAnyUpdate ? maybeReactive.subscribeOnAnyUpdate((pks) => {
|
|
5738
|
+
this.onCollectionUpdate({
|
|
5739
|
+
pks
|
|
5740
|
+
});
|
|
5741
|
+
}) : collection.emitter.on(
|
|
5742
|
+
0 /* UPDATE */,
|
|
5743
|
+
this.onCollectionUpdate
|
|
5744
|
+
);
|
|
5745
|
+
}
|
|
5746
|
+
}
|
|
5747
|
+
upsertOneByPk(pk, patch) {
|
|
5748
|
+
const slot = this.withWrite(() => {
|
|
5749
|
+
this.trackPatch(pk, patch);
|
|
5750
|
+
return this.collection.upsertOneByPk(pk, patch);
|
|
5751
|
+
});
|
|
5752
|
+
this.updateEntitySnapshot(pk);
|
|
5753
|
+
return slot;
|
|
5754
|
+
}
|
|
5755
|
+
upsertOne(entity) {
|
|
5756
|
+
const slot = this.withWrite(() => {
|
|
5757
|
+
const pk = this.selectPk(entity);
|
|
5758
|
+
this.trackPatch(pk, entity);
|
|
5759
|
+
return this.collection.upsertOne(entity);
|
|
5760
|
+
});
|
|
5761
|
+
this.updateEntitySnapshot(slot.pk);
|
|
5762
|
+
return slot;
|
|
5763
|
+
}
|
|
5764
|
+
upsertMany(entities) {
|
|
5765
|
+
const pks = [];
|
|
5766
|
+
const slots = this.withWrite(() => {
|
|
5767
|
+
for (const entity of entities) {
|
|
5768
|
+
const pk = this.selectPk(entity);
|
|
5769
|
+
pks.push(pk);
|
|
5770
|
+
this.trackPatch(pk, entity);
|
|
5771
|
+
}
|
|
5772
|
+
return this.collection.upsertMany(entities);
|
|
5773
|
+
});
|
|
5774
|
+
for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
|
|
5775
|
+
return slots;
|
|
5776
|
+
}
|
|
5777
|
+
getChangedFieldsByPk(pk) {
|
|
5778
|
+
return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
|
|
5779
|
+
}
|
|
5780
|
+
consumeChangedFieldsByPk(pk) {
|
|
5781
|
+
const existing = this.changedFieldsByPk.get(pk);
|
|
5782
|
+
if (!existing) return /* @__PURE__ */ new Set();
|
|
5783
|
+
return new Set(existing);
|
|
5784
|
+
}
|
|
5785
|
+
getChangedPksByField(field) {
|
|
5786
|
+
return this.changedPksByField.get(field) ?? /* @__PURE__ */ new Set();
|
|
5787
|
+
}
|
|
5788
|
+
consumeChangedPksByField(field) {
|
|
5789
|
+
const existing = this.changedPksByField.get(field);
|
|
5790
|
+
if (!existing) return /* @__PURE__ */ new Set();
|
|
5791
|
+
return new Set(existing);
|
|
5792
|
+
}
|
|
5793
|
+
clear() {
|
|
5794
|
+
this.changedFieldsByPk.clear();
|
|
5795
|
+
this.changedPksByField.clear();
|
|
5796
|
+
}
|
|
5797
|
+
destroy() {
|
|
5798
|
+
this.changedPksEventEmitter.destroy();
|
|
5284
5799
|
this.changedFieldsEventEmitter.destroy();
|
|
5285
5800
|
this.unsubscribeAfterFlush();
|
|
5286
5801
|
this.unsubscribeFromCollectionEmitter?.();
|
|
@@ -5319,8 +5834,9 @@ var OIMCollectionChangedFields = class {
|
|
|
5319
5834
|
Object.keys(nextObj).forEach((k) => keys.add(k));
|
|
5320
5835
|
const changed = [];
|
|
5321
5836
|
keys.forEach((k) => {
|
|
5322
|
-
if (prevObj[k] !== nextObj[k])
|
|
5837
|
+
if (prevObj[k] !== nextObj[k]) {
|
|
5323
5838
|
changed.push(k);
|
|
5839
|
+
}
|
|
5324
5840
|
});
|
|
5325
5841
|
return changed;
|
|
5326
5842
|
}
|
|
@@ -5486,10 +6002,15 @@ var OIMOrderedListMappedCommandStream = class _OIMOrderedListMappedCommandStream
|
|
|
5486
6002
|
try {
|
|
5487
6003
|
const subs = this.subscribers.get(key);
|
|
5488
6004
|
if (subs && subs.size > 0) {
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
6005
|
+
if (subs.size === 1) {
|
|
6006
|
+
const only = subs.values().next().value;
|
|
6007
|
+
if (only) only();
|
|
6008
|
+
} else {
|
|
6009
|
+
const snapshot = Array.from(subs);
|
|
6010
|
+
for (let s = 0; s < snapshot.length; s++) {
|
|
6011
|
+
const handler = snapshot[s];
|
|
6012
|
+
if (subs.has(handler)) handler();
|
|
6013
|
+
}
|
|
5493
6014
|
}
|
|
5494
6015
|
}
|
|
5495
6016
|
} finally {
|
|
@@ -5783,10 +6304,13 @@ var OIMEventQueueSchedulerAnimationFrame = class extends OIMEventQueueScheduler
|
|
|
5783
6304
|
this.flush();
|
|
5784
6305
|
});
|
|
5785
6306
|
} else {
|
|
5786
|
-
this.frameId =
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
6307
|
+
this.frameId = this.unref(
|
|
6308
|
+
setTimeout(() => {
|
|
6309
|
+
this.frameId = void 0;
|
|
6310
|
+
this.flush();
|
|
6311
|
+
}, 16)
|
|
6312
|
+
// ~60fps
|
|
6313
|
+
);
|
|
5790
6314
|
}
|
|
5791
6315
|
}
|
|
5792
6316
|
cancel() {
|
|
@@ -5832,10 +6356,12 @@ var OIMEventQueueSchedulerTimeout = class extends OIMEventQueueScheduler {
|
|
|
5832
6356
|
}
|
|
5833
6357
|
schedule() {
|
|
5834
6358
|
if (this.timeoutId !== void 0) return;
|
|
5835
|
-
this.timeoutId =
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
6359
|
+
this.timeoutId = this.unref(
|
|
6360
|
+
setTimeout(() => {
|
|
6361
|
+
this.timeoutId = void 0;
|
|
6362
|
+
this.flush();
|
|
6363
|
+
}, this.delay)
|
|
6364
|
+
);
|
|
5839
6365
|
}
|
|
5840
6366
|
cancel() {
|
|
5841
6367
|
if (this.timeoutId === void 0) return;
|
|
@@ -5869,13 +6395,13 @@ var OIMEventQueueSchedulerImmediate = class extends OIMEventQueueScheduler {
|
|
|
5869
6395
|
this.flush();
|
|
5870
6396
|
};
|
|
5871
6397
|
if (this.useSetImmediate) {
|
|
5872
|
-
this.immediateId = setImmediate(callback);
|
|
6398
|
+
this.immediateId = this.unref(setImmediate(callback));
|
|
5873
6399
|
} else if (this.useMessageChannel && this.messageChannel) {
|
|
5874
6400
|
this.immediateId = 1;
|
|
5875
6401
|
this.pendingCallback = callback;
|
|
5876
6402
|
this.messageChannel.port1.postMessage(null);
|
|
5877
6403
|
} else {
|
|
5878
|
-
this.immediateId = setTimeout(callback, 0);
|
|
6404
|
+
this.immediateId = this.unref(setTimeout(callback, 0));
|
|
5879
6405
|
}
|
|
5880
6406
|
}
|
|
5881
6407
|
cancel() {
|
|
@@ -5968,242 +6494,6 @@ var OIMDBSettings = class {
|
|
|
5968
6494
|
OIMDBSettings.UPDATED_KEYS_INDEX_KEY = "__updatedKeys__";
|
|
5969
6495
|
OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
|
|
5970
6496
|
|
|
5971
|
-
// src/modules/effect/core/OIMEffectDependencyComputed.ts
|
|
5972
|
-
var OIMEffectDependencyComputed = class {
|
|
5973
|
-
constructor(source) {
|
|
5974
|
-
this.source = source;
|
|
5975
|
-
}
|
|
5976
|
-
subscribe(onUpdate) {
|
|
5977
|
-
return this.source.emitter.on("update", onUpdate);
|
|
5978
|
-
}
|
|
5979
|
-
};
|
|
5980
|
-
|
|
5981
|
-
// src/modules/effect/core/OIMEffect.ts
|
|
5982
|
-
var OIMEffect = class {
|
|
5983
|
-
constructor(runtime, opts) {
|
|
5984
|
-
this.sourceDeps = [];
|
|
5985
|
-
this.computedDeps = [];
|
|
5986
|
-
this.sourceUnsubscribers = [];
|
|
5987
|
-
this.computedUnsubscribers = [];
|
|
5988
|
-
this.isResubscribeEnqueued = false;
|
|
5989
|
-
this.isTemporarilyUnsubscribedForFlush = false;
|
|
5990
|
-
this.isScheduled = false;
|
|
5991
|
-
this.runTask = () => {
|
|
5992
|
-
this.isScheduled = false;
|
|
5993
|
-
this.dequeueScheduled = void 0;
|
|
5994
|
-
this.runOnce();
|
|
5995
|
-
};
|
|
5996
|
-
this.onSourceInvalidate = () => {
|
|
5997
|
-
this.onUpdate?.();
|
|
5998
|
-
if (!this.isTemporarilyUnsubscribedForFlush) {
|
|
5999
|
-
this.isTemporarilyUnsubscribedForFlush = true;
|
|
6000
|
-
this.unsubscribeFromSourceDeps();
|
|
6001
|
-
this.ensureResubscribeAfterFlush();
|
|
6002
|
-
}
|
|
6003
|
-
if (this.isScheduled) return;
|
|
6004
|
-
this.isScheduled = true;
|
|
6005
|
-
this.dequeueScheduled = this.runtime.schedule(this.runTask);
|
|
6006
|
-
};
|
|
6007
|
-
this.onComputedInvalidate = () => {
|
|
6008
|
-
this.onUpdate?.();
|
|
6009
|
-
if (this.isScheduled) return;
|
|
6010
|
-
this.isScheduled = true;
|
|
6011
|
-
this.dequeueScheduled = this.runtime.schedule(this.runTask);
|
|
6012
|
-
};
|
|
6013
|
-
this.onAfterFlush = () => {
|
|
6014
|
-
if (!this.isResubscribeEnqueued) return;
|
|
6015
|
-
this.isResubscribeEnqueued = false;
|
|
6016
|
-
this.dequeueResubscribe = void 0;
|
|
6017
|
-
this.isTemporarilyUnsubscribedForFlush = false;
|
|
6018
|
-
if (this.sourceUnsubscribers.length === 0) {
|
|
6019
|
-
for (const dep of this.sourceDeps) {
|
|
6020
|
-
this.sourceUnsubscribers.push(
|
|
6021
|
-
dep.subscribe(this.onSourceInvalidate)
|
|
6022
|
-
);
|
|
6023
|
-
}
|
|
6024
|
-
}
|
|
6025
|
-
};
|
|
6026
|
-
this.runtime = runtime;
|
|
6027
|
-
this.onUpdate = opts.onUpdate;
|
|
6028
|
-
this.run = opts.run;
|
|
6029
|
-
this.deps = opts.deps ?? [];
|
|
6030
|
-
for (const dep of this.deps) {
|
|
6031
|
-
if (dep instanceof OIMEffectDependencyComputed) {
|
|
6032
|
-
this.computedDeps.push(dep);
|
|
6033
|
-
} else {
|
|
6034
|
-
this.sourceDeps.push(dep);
|
|
6035
|
-
}
|
|
6036
|
-
}
|
|
6037
|
-
this.subscribeToDeps();
|
|
6038
|
-
}
|
|
6039
|
-
destroy() {
|
|
6040
|
-
if (this.dequeueScheduled) {
|
|
6041
|
-
this.dequeueScheduled();
|
|
6042
|
-
this.dequeueScheduled = void 0;
|
|
6043
|
-
}
|
|
6044
|
-
this.unsubscribeFromAllDeps();
|
|
6045
|
-
if (this.dequeueResubscribe) {
|
|
6046
|
-
this.dequeueResubscribe();
|
|
6047
|
-
this.dequeueResubscribe = void 0;
|
|
6048
|
-
}
|
|
6049
|
-
this.isResubscribeEnqueued = false;
|
|
6050
|
-
this.isTemporarilyUnsubscribedForFlush = false;
|
|
6051
|
-
}
|
|
6052
|
-
runOnce() {
|
|
6053
|
-
this.run();
|
|
6054
|
-
}
|
|
6055
|
-
subscribeToDeps() {
|
|
6056
|
-
if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
|
|
6057
|
-
return;
|
|
6058
|
-
for (const dep of this.sourceDeps) {
|
|
6059
|
-
this.sourceUnsubscribers.push(
|
|
6060
|
-
dep.subscribe(this.onSourceInvalidate)
|
|
6061
|
-
);
|
|
6062
|
-
}
|
|
6063
|
-
for (const dep of this.computedDeps) {
|
|
6064
|
-
this.computedUnsubscribers.push(
|
|
6065
|
-
dep.subscribe(this.onComputedInvalidate)
|
|
6066
|
-
);
|
|
6067
|
-
}
|
|
6068
|
-
}
|
|
6069
|
-
unsubscribeFromSourceDeps() {
|
|
6070
|
-
if (this.sourceUnsubscribers.length === 0) return;
|
|
6071
|
-
for (const unsubscribe of this.sourceUnsubscribers) unsubscribe();
|
|
6072
|
-
this.sourceUnsubscribers = [];
|
|
6073
|
-
}
|
|
6074
|
-
unsubscribeFromAllDeps() {
|
|
6075
|
-
this.unsubscribeFromSourceDeps();
|
|
6076
|
-
if (this.computedUnsubscribers.length === 0) return;
|
|
6077
|
-
for (const unsubscribe of this.computedUnsubscribers) unsubscribe();
|
|
6078
|
-
this.computedUnsubscribers = [];
|
|
6079
|
-
}
|
|
6080
|
-
ensureResubscribeAfterFlush() {
|
|
6081
|
-
if (this.isResubscribeEnqueued) return;
|
|
6082
|
-
this.isResubscribeEnqueued = true;
|
|
6083
|
-
this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
|
|
6084
|
-
this.onAfterFlush
|
|
6085
|
-
);
|
|
6086
|
-
}
|
|
6087
|
-
};
|
|
6088
|
-
|
|
6089
|
-
// src/modules/computed/enums/EOIMComputedEventType.ts
|
|
6090
|
-
var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
|
|
6091
|
-
EOIMComputedEventType2["UPDATE"] = "update";
|
|
6092
|
-
return EOIMComputedEventType2;
|
|
6093
|
-
})(EOIMComputedEventType || {});
|
|
6094
|
-
|
|
6095
|
-
// src/modules/computed/core/OIMComputed.ts
|
|
6096
|
-
var OIMComputed = class {
|
|
6097
|
-
constructor(runtime, opts) {
|
|
6098
|
-
this.emitter = new OIMEventEmitter();
|
|
6099
|
-
this.hasValue = false;
|
|
6100
|
-
this.isDirty = true;
|
|
6101
|
-
this.runtime = runtime;
|
|
6102
|
-
this.compute = opts.compute;
|
|
6103
|
-
this.compare = opts.compare ?? Object.is;
|
|
6104
|
-
this._deps = opts.deps ?? [];
|
|
6105
|
-
this.updateEventEmitter = new OIMUpdateEventEmitter(
|
|
6106
|
-
this.runtime.queue,
|
|
6107
|
-
"immediate"
|
|
6108
|
-
);
|
|
6109
|
-
this.effect = new OIMEffect(this.runtime, {
|
|
6110
|
-
deps: this._deps,
|
|
6111
|
-
onUpdate: () => {
|
|
6112
|
-
this.isDirty = true;
|
|
6113
|
-
},
|
|
6114
|
-
run: () => {
|
|
6115
|
-
this.recomputeAndEmitIfChanged();
|
|
6116
|
-
}
|
|
6117
|
-
});
|
|
6118
|
-
}
|
|
6119
|
-
get needsRecompute() {
|
|
6120
|
-
return this.isDirty;
|
|
6121
|
-
}
|
|
6122
|
-
get isReady() {
|
|
6123
|
-
return this.hasValue;
|
|
6124
|
-
}
|
|
6125
|
-
get deps() {
|
|
6126
|
-
return this._deps;
|
|
6127
|
-
}
|
|
6128
|
-
get() {
|
|
6129
|
-
this.recomputeAndEmitIfChanged();
|
|
6130
|
-
return this.value;
|
|
6131
|
-
}
|
|
6132
|
-
getIfReady() {
|
|
6133
|
-
return this.hasValue ? this.value : void 0;
|
|
6134
|
-
}
|
|
6135
|
-
destroy() {
|
|
6136
|
-
this.effect.destroy();
|
|
6137
|
-
this.updateEventEmitter.destroy();
|
|
6138
|
-
this.emitter.offAll();
|
|
6139
|
-
}
|
|
6140
|
-
recomputeAndEmitIfChanged() {
|
|
6141
|
-
if (!this.isDirty) return;
|
|
6142
|
-
const prev = this.value;
|
|
6143
|
-
const hadPrev = this.hasValue;
|
|
6144
|
-
const next = this.compute();
|
|
6145
|
-
this.value = next;
|
|
6146
|
-
this.hasValue = true;
|
|
6147
|
-
this.isDirty = false;
|
|
6148
|
-
const changed = !hadPrev || !this.compare(prev, next);
|
|
6149
|
-
if (!changed) return;
|
|
6150
|
-
this.emitter.emit("update" /* UPDATE */, {
|
|
6151
|
-
keys: ["value"]
|
|
6152
|
-
});
|
|
6153
|
-
this.updateEventEmitter.markUpdatedKey("value");
|
|
6154
|
-
}
|
|
6155
|
-
};
|
|
6156
|
-
|
|
6157
|
-
// src/modules/effect/core/OIMEffectDependencyKeyed.ts
|
|
6158
|
-
var OIMEffectDependencyKeyed = class {
|
|
6159
|
-
constructor(source, key) {
|
|
6160
|
-
this.source = source;
|
|
6161
|
-
this.key = key;
|
|
6162
|
-
}
|
|
6163
|
-
subscribe(onUpdate) {
|
|
6164
|
-
return this.source.subscribeOnKey(this.key, onUpdate);
|
|
6165
|
-
}
|
|
6166
|
-
};
|
|
6167
|
-
|
|
6168
|
-
// src/modules/effect/core/OIMEffectDependencyKeyedObject.ts
|
|
6169
|
-
var OIMEffectDependencyKeyedObject = class {
|
|
6170
|
-
constructor(object, key) {
|
|
6171
|
-
this.dep = new OIMEffectDependencyKeyed(object, key);
|
|
6172
|
-
}
|
|
6173
|
-
get source() {
|
|
6174
|
-
return this.dep.source;
|
|
6175
|
-
}
|
|
6176
|
-
subscribe(onUpdate) {
|
|
6177
|
-
return this.dep.subscribe(onUpdate);
|
|
6178
|
-
}
|
|
6179
|
-
};
|
|
6180
|
-
|
|
6181
|
-
// src/modules/effect/core/OIMEffectDependencyKeyedCollection.ts
|
|
6182
|
-
var OIMEffectDependencyKeyedCollection = class {
|
|
6183
|
-
constructor(collection, pk) {
|
|
6184
|
-
this.dep = new OIMEffectDependencyKeyed(collection, pk);
|
|
6185
|
-
}
|
|
6186
|
-
get source() {
|
|
6187
|
-
return this.dep.source;
|
|
6188
|
-
}
|
|
6189
|
-
subscribe(onUpdate) {
|
|
6190
|
-
return this.dep.subscribe(onUpdate);
|
|
6191
|
-
}
|
|
6192
|
-
};
|
|
6193
|
-
|
|
6194
|
-
// src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
|
|
6195
|
-
var OIMEffectDependencyKeyedIndex = class {
|
|
6196
|
-
constructor(index, key) {
|
|
6197
|
-
this.dep = new OIMEffectDependencyKeyed(index, key);
|
|
6198
|
-
}
|
|
6199
|
-
get source() {
|
|
6200
|
-
return this.dep.source;
|
|
6201
|
-
}
|
|
6202
|
-
subscribe(onUpdate) {
|
|
6203
|
-
return this.dep.subscribe(onUpdate);
|
|
6204
|
-
}
|
|
6205
|
-
};
|
|
6206
|
-
|
|
6207
6497
|
// src/modules/selector/core/OIMObjectValueByKeySelector.ts
|
|
6208
6498
|
var OIMObjectValueByKeySelector = class extends OIMSelector {
|
|
6209
6499
|
constructor(runtime, obj, key) {
|
|
@@ -6281,8 +6571,9 @@ var OIMIndexPksByKeyArrayBasedSelector = class extends OIMSelector {
|
|
|
6281
6571
|
areEqual(prev, next) {
|
|
6282
6572
|
if (prev === next) return true;
|
|
6283
6573
|
if (prev.length !== next.length) return false;
|
|
6284
|
-
for (let i = 0; i < prev.length; i++)
|
|
6574
|
+
for (let i = 0; i < prev.length; i++) {
|
|
6285
6575
|
if (prev[i] !== next[i]) return false;
|
|
6576
|
+
}
|
|
6286
6577
|
return true;
|
|
6287
6578
|
}
|
|
6288
6579
|
};
|
|
@@ -6305,8 +6596,9 @@ var OIMIndexPksByKeysSetBasedSelector = class extends OIMSelector {
|
|
|
6305
6596
|
areEqual(prev, next) {
|
|
6306
6597
|
if (prev === next) return true;
|
|
6307
6598
|
if (prev.length !== next.length) return false;
|
|
6308
|
-
for (let i = 0; i < prev.length; i++)
|
|
6599
|
+
for (let i = 0; i < prev.length; i++) {
|
|
6309
6600
|
if (prev[i] !== next[i]) return false;
|
|
6601
|
+
}
|
|
6310
6602
|
return true;
|
|
6311
6603
|
}
|
|
6312
6604
|
};
|
|
@@ -6329,8 +6621,9 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
|
|
|
6329
6621
|
areEqual(prev, next) {
|
|
6330
6622
|
if (prev === next) return true;
|
|
6331
6623
|
if (prev.length !== next.length) return false;
|
|
6332
|
-
for (let i = 0; i < prev.length; i++)
|
|
6624
|
+
for (let i = 0; i < prev.length; i++) {
|
|
6333
6625
|
if (prev[i] !== next[i]) return false;
|
|
6626
|
+
}
|
|
6334
6627
|
return true;
|
|
6335
6628
|
}
|
|
6336
6629
|
};
|
|
@@ -6359,6 +6652,7 @@ export {
|
|
|
6359
6652
|
OIMComputeRuntime,
|
|
6360
6653
|
OIMComputed,
|
|
6361
6654
|
OIMDBSettings,
|
|
6655
|
+
OIMDerivedCollection,
|
|
6362
6656
|
OIMDerivedCollectionGlobalIndexArrayBased,
|
|
6363
6657
|
OIMDerivedCollectionGlobalIndexSetBased,
|
|
6364
6658
|
OIMDerivedCollectionIndexArrayBased,
|
|
@@ -6454,5 +6748,9 @@ export {
|
|
|
6454
6748
|
createOIMIndexSlotMap,
|
|
6455
6749
|
createOIMOrderedListCommandStreamDiffDriven,
|
|
6456
6750
|
createOIMOrderedListMappedCommandStream,
|
|
6457
|
-
diffOrderedListByPk
|
|
6751
|
+
diffOrderedListByPk,
|
|
6752
|
+
getOIMComputeRuntime,
|
|
6753
|
+
on,
|
|
6754
|
+
peekOIMComputeRuntime,
|
|
6755
|
+
setOIMComputeRuntime
|
|
6458
6756
|
};
|