doxum 0.1.16 → 0.1.17
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 +1 -1
- package/dist/advanced.cjs +47 -63
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +7 -10
- package/dist/advanced.d.ts +7 -10
- package/dist/advanced.js +47 -63
- package/dist/advanced.js.map +1 -1
- package/dist/definition-BdLGD8Rq.d.ts +101 -0
- package/dist/definition-Bf8Z7xtX.d.cts +101 -0
- package/dist/{definition-c6XQXzvK.js → definition-DUo1lfQ8.js} +1 -2
- package/dist/definition-DUo1lfQ8.js.map +1 -0
- package/dist/{definition-Z6TagMup.cjs → definition-De6rhTlZ.cjs} +1 -2
- package/dist/definition-De6rhTlZ.cjs.map +1 -0
- package/dist/index.cjs +808 -796
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +809 -797
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +5 -10
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +4 -6
- package/dist/react.d.ts +4 -6
- package/dist/react.js +6 -10
- package/dist/react.js.map +1 -1
- package/package.json +3 -2
- package/skills/doxum-runtime/SKILL.md +2 -1
- package/skills/doxum-runtime/references/patterns.en.md +4 -4
- package/skills/doxum-runtime/references/patterns.zh-CN.md +2 -2
- package/skills/doxum-runtime/references/projections.en.md +6 -3
- package/skills/doxum-runtime/references/projections.zh-CN.md +6 -3
- package/dist/definition-C__VJqA4.d.cts +0 -136
- package/dist/definition-Dc1Udkop.d.ts +0 -136
- package/dist/definition-Z6TagMup.cjs.map +0 -1
- package/dist/definition-c6XQXzvK.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -3,7 +3,7 @@ const require_issue = require("./issue-DhrNdQNg.cjs");
|
|
|
3
3
|
const require_scope = require("./scope-Ezs-Y3TY.cjs");
|
|
4
4
|
const require_notification = require("./notification-CDSUfiip.cjs");
|
|
5
5
|
const require_driver = require("./driver-BCyfqxsl.cjs");
|
|
6
|
-
const require_definition = require("./definition-
|
|
6
|
+
const require_definition = require("./definition-De6rhTlZ.cjs");
|
|
7
7
|
//#region core/src/schema.ts
|
|
8
8
|
const node = (value) => Object.freeze(value);
|
|
9
9
|
const field = (validator) => node({
|
|
@@ -1206,7 +1206,7 @@ const createScheduler = (onError) => {
|
|
|
1206
1206
|
const blocked = node.sources.find((source) => source.fault || source.disposed);
|
|
1207
1207
|
if (blocked) node.fault = errorFor(node, blocked.fault ?? new ProjectionDisposedError(), "blocked");
|
|
1208
1208
|
else {
|
|
1209
|
-
const build =
|
|
1209
|
+
const build = wasFaulted || node.sources.some((source) => source.reset());
|
|
1210
1210
|
try {
|
|
1211
1211
|
changed = node.evaluate(build);
|
|
1212
1212
|
const failure = node.sources.find((source) => source.fault)?.fault;
|
|
@@ -1227,8 +1227,6 @@ const createScheduler = (onError) => {
|
|
|
1227
1227
|
}
|
|
1228
1228
|
}
|
|
1229
1229
|
}
|
|
1230
|
-
node.forced = false;
|
|
1231
|
-
node.statusChanged = wasFaulted !== (node.fault !== void 0);
|
|
1232
1230
|
completed.push(node);
|
|
1233
1231
|
if (changed || wasFaulted || node.fault) {
|
|
1234
1232
|
require_issue.profile.projection("publishedNodes");
|
|
@@ -1368,23 +1366,6 @@ const createScheduler = (onError) => {
|
|
|
1368
1366
|
phase = "idle";
|
|
1369
1367
|
}
|
|
1370
1368
|
},
|
|
1371
|
-
rebuild(node) {
|
|
1372
|
-
assertIdle();
|
|
1373
|
-
if (node.disposed) throw new ProjectionDisposedError();
|
|
1374
|
-
node.forced = true;
|
|
1375
|
-
enqueue(node);
|
|
1376
|
-
run();
|
|
1377
|
-
},
|
|
1378
|
-
disposeNode(node) {
|
|
1379
|
-
if (node.disposed) return;
|
|
1380
|
-
assertIdle();
|
|
1381
|
-
if (node.consumers.size) throw new Error("Cannot dispose a projection node with downstream consumers.");
|
|
1382
|
-
node.disposed = true;
|
|
1383
|
-
node.sources.forEach((source) => source.consumers.delete(node));
|
|
1384
|
-
nodes.delete(node);
|
|
1385
|
-
node.release();
|
|
1386
|
-
for (const [handle, record] of records) if (record === node) records.delete(handle);
|
|
1387
|
-
},
|
|
1388
1369
|
batch,
|
|
1389
1370
|
batchContext: () => activeBatch,
|
|
1390
1371
|
dispose() {
|
|
@@ -1394,521 +1375,49 @@ const createScheduler = (onError) => {
|
|
|
1394
1375
|
const failures = [];
|
|
1395
1376
|
cleanups.forEach((cleanup) => {
|
|
1396
1377
|
try {
|
|
1397
|
-
cleanup();
|
|
1398
|
-
} catch (error) {
|
|
1399
|
-
failures.push(error);
|
|
1400
|
-
}
|
|
1401
|
-
});
|
|
1402
|
-
cleanups.clear();
|
|
1403
|
-
nodes.forEach((node) => {
|
|
1404
|
-
node.disposed = true;
|
|
1405
|
-
node.release();
|
|
1406
|
-
node.consumers.clear();
|
|
1407
|
-
});
|
|
1408
|
-
records.forEach((source) => {
|
|
1409
|
-
source.disposed = true;
|
|
1410
|
-
source.consumers.clear();
|
|
1411
|
-
source.clear();
|
|
1412
|
-
});
|
|
1413
|
-
nodes.clear();
|
|
1414
|
-
records.clear();
|
|
1415
|
-
pending.clear();
|
|
1416
|
-
heap.length = 0;
|
|
1417
|
-
completed.length = 0;
|
|
1418
|
-
queued.clear();
|
|
1419
|
-
emissions.clear();
|
|
1420
|
-
guards.clear();
|
|
1421
|
-
errors.length = 0;
|
|
1422
|
-
reportingFailures = [];
|
|
1423
|
-
if (failures.length) throw new AggregateError(failures, "Projection cleanup failed.");
|
|
1424
|
-
},
|
|
1425
|
-
debug: () => ({
|
|
1426
|
-
nodes: nodes.size,
|
|
1427
|
-
sources: records.size - nodes.size,
|
|
1428
|
-
subscriptions: cleanups.size,
|
|
1429
|
-
pending: pending.size + heap.length
|
|
1430
|
-
})
|
|
1431
|
-
};
|
|
1432
|
-
};
|
|
1433
|
-
const assertSynchronous = (value) => {
|
|
1434
|
-
if (value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function") throw new TypeError("Projection callbacks must be synchronous.");
|
|
1435
|
-
};
|
|
1436
|
-
const assertScope = (active) => {
|
|
1437
|
-
if (!active()) throw new Error("Projection reader or writer is no longer active.");
|
|
1438
|
-
};
|
|
1439
|
-
//#endregion
|
|
1440
|
-
//#region core/src/projection/node.ts
|
|
1441
|
-
const createNode = (scheduler, spec, behavior) => {
|
|
1442
|
-
scheduler.assertIdle();
|
|
1443
|
-
const entries = Object.entries(spec.sources).map(([key, handle]) => [key, scheduler.source(handle)]);
|
|
1444
|
-
const order = scheduler.order();
|
|
1445
|
-
const node = {
|
|
1446
|
-
...behavior,
|
|
1447
|
-
publish: () => {
|
|
1448
|
-
if (node.fault) behavior.clear();
|
|
1449
|
-
else behavior.publish();
|
|
1450
|
-
},
|
|
1451
|
-
order,
|
|
1452
|
-
name: spec.name ? `${spec.name} (${order})` : `projection-${order}`,
|
|
1453
|
-
sources: entries.map(([, source]) => source),
|
|
1454
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
1455
|
-
disposed: false,
|
|
1456
|
-
fault: void 0,
|
|
1457
|
-
forced: false,
|
|
1458
|
-
statusChanged: false,
|
|
1459
|
-
evaluate: (build) => {
|
|
1460
|
-
let active = true;
|
|
1461
|
-
try {
|
|
1462
|
-
const scopeActive = () => active;
|
|
1463
|
-
const inputs = Object.fromEntries(entries.map(([key, source]) => [key, source.context(scopeActive)]));
|
|
1464
|
-
return behavior.evaluate(inputs, build, scopeActive);
|
|
1465
|
-
} finally {
|
|
1466
|
-
active = false;
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
};
|
|
1470
|
-
scheduler.initialize(() => {
|
|
1471
|
-
const invalid = node.sources.find((source) => source.fault || source.disposed);
|
|
1472
|
-
if (invalid) throw invalid.fault ?? new ProjectionDisposedError();
|
|
1473
|
-
node.evaluate(true);
|
|
1474
|
-
node.publish();
|
|
1475
|
-
node.clear();
|
|
1476
|
-
});
|
|
1477
|
-
const check = () => {
|
|
1478
|
-
if (node.disposed || !scheduler.active) throw new ProjectionDisposedError();
|
|
1479
|
-
if (node.fault) throw node.fault;
|
|
1480
|
-
};
|
|
1481
|
-
return {
|
|
1482
|
-
node,
|
|
1483
|
-
check,
|
|
1484
|
-
install(handle) {
|
|
1485
|
-
scheduler.register(handle, node);
|
|
1486
|
-
scheduler.addNode(node);
|
|
1487
|
-
},
|
|
1488
|
-
rebuild: () => scheduler.rebuild(node),
|
|
1489
|
-
dispose: () => scheduler.disposeNode(node)
|
|
1490
|
-
};
|
|
1491
|
-
};
|
|
1492
|
-
//#endregion
|
|
1493
|
-
//#region core/src/projection/collection.ts
|
|
1494
|
-
const collectionHandles = /* @__PURE__ */ new WeakSet();
|
|
1495
|
-
const readonlySet = (values) => {
|
|
1496
|
-
const set = new Set(values);
|
|
1497
|
-
const view = Object.freeze({
|
|
1498
|
-
get size() {
|
|
1499
|
-
return set.size;
|
|
1500
|
-
},
|
|
1501
|
-
has: (value) => set.has(value),
|
|
1502
|
-
entries: () => set.entries(),
|
|
1503
|
-
keys: () => set.keys(),
|
|
1504
|
-
values: () => set.values(),
|
|
1505
|
-
[Symbol.iterator]: () => set.values(),
|
|
1506
|
-
forEach: (callback, thisArg) => set.forEach((value) => callback.call(thisArg, value, value, view))
|
|
1507
|
-
});
|
|
1508
|
-
return view;
|
|
1509
|
-
};
|
|
1510
|
-
const createCollection = (scheduler, spec) => {
|
|
1511
|
-
const values = /* @__PURE__ */ new Map();
|
|
1512
|
-
let ids = Object.freeze([]);
|
|
1513
|
-
let staged = /* @__PURE__ */ new Map();
|
|
1514
|
-
let nextIds = ids;
|
|
1515
|
-
let change;
|
|
1516
|
-
let instance;
|
|
1517
|
-
let initialized = false;
|
|
1518
|
-
let reset = false;
|
|
1519
|
-
let revision = 0;
|
|
1520
|
-
let idsRevision = 0;
|
|
1521
|
-
let batch;
|
|
1522
|
-
let cause;
|
|
1523
|
-
let publishedTransitions = Object.freeze([]);
|
|
1524
|
-
let all;
|
|
1525
|
-
const items = /* @__PURE__ */ new Map();
|
|
1526
|
-
const subscribedItems = /* @__PURE__ */ new Set();
|
|
1527
|
-
const idsListeners = /* @__PURE__ */ new Set();
|
|
1528
|
-
const allListeners = /* @__PURE__ */ new Set();
|
|
1529
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
1530
|
-
const changedKeys = /* @__PURE__ */ new Set();
|
|
1531
|
-
let orderChanged = false;
|
|
1532
|
-
const current = Object.freeze({
|
|
1533
|
-
get: (key) => {
|
|
1534
|
-
owner.check();
|
|
1535
|
-
return values.get(key);
|
|
1536
|
-
},
|
|
1537
|
-
has: (key) => {
|
|
1538
|
-
owner.check();
|
|
1539
|
-
return values.has(key);
|
|
1540
|
-
},
|
|
1541
|
-
ids: () => {
|
|
1542
|
-
owner.check();
|
|
1543
|
-
return ids;
|
|
1544
|
-
}
|
|
1545
|
-
});
|
|
1546
|
-
const hasNext = (key) => staged.has(key) ? staged.get(key).present : values.has(key);
|
|
1547
|
-
const getNext = (key) => {
|
|
1548
|
-
const entry = staged.get(key);
|
|
1549
|
-
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
1550
|
-
};
|
|
1551
|
-
const owner = createNode(scheduler, spec, {
|
|
1552
|
-
evaluate: (sources, build, active) => {
|
|
1553
|
-
const metadata = Object.values(sources).find((value) => value && typeof value === "object" && ("batch" in value && value.batch !== void 0 || "cause" in value && value.cause !== void 0));
|
|
1554
|
-
batch = scheduler.batchContext() ?? metadata?.batch;
|
|
1555
|
-
cause = metadata?.cause ?? batch?.cause;
|
|
1556
|
-
staged = /* @__PURE__ */ new Map();
|
|
1557
|
-
nextIds = ids;
|
|
1558
|
-
change = void 0;
|
|
1559
|
-
changedKeys.clear();
|
|
1560
|
-
let explicitOrder;
|
|
1561
|
-
let cleared = false;
|
|
1562
|
-
const nextHas = (key) => staged.has(key) ? staged.get(key).present : !cleared && values.has(key);
|
|
1563
|
-
const nextGet = (key) => {
|
|
1564
|
-
const entry = staged.get(key);
|
|
1565
|
-
return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : values.get(key);
|
|
1566
|
-
};
|
|
1567
|
-
const deriveIds = () => {
|
|
1568
|
-
const result = cleared ? [] : ids.filter(nextHas);
|
|
1569
|
-
for (const [key, entry] of staged) if (entry.present && (cleared || !values.has(key))) result.push(key);
|
|
1570
|
-
return result;
|
|
1571
|
-
};
|
|
1572
|
-
const read = (next) => ({
|
|
1573
|
-
get: (key) => {
|
|
1574
|
-
assertScope(active);
|
|
1575
|
-
return next ? nextGet(key) : values.get(key);
|
|
1576
|
-
},
|
|
1577
|
-
has: (key) => {
|
|
1578
|
-
assertScope(active);
|
|
1579
|
-
return next ? nextHas(key) : values.has(key);
|
|
1580
|
-
},
|
|
1581
|
-
ids: () => {
|
|
1582
|
-
assertScope(active);
|
|
1583
|
-
return next ? Object.freeze(explicitOrder ? explicitOrder.slice() : deriveIds()) : ids;
|
|
1584
|
-
}
|
|
1585
|
-
});
|
|
1586
|
-
const writer = {
|
|
1587
|
-
set: (key, value) => {
|
|
1588
|
-
assertScope(active);
|
|
1589
|
-
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
1590
|
-
staged.set(key, {
|
|
1591
|
-
present: true,
|
|
1592
|
-
value
|
|
1593
|
-
});
|
|
1594
|
-
},
|
|
1595
|
-
remove: (key) => {
|
|
1596
|
-
assertScope(active);
|
|
1597
|
-
staged.set(key, { present: false });
|
|
1598
|
-
},
|
|
1599
|
-
order: (order) => {
|
|
1600
|
-
assertScope(active);
|
|
1601
|
-
explicitOrder = order.slice();
|
|
1602
|
-
},
|
|
1603
|
-
replace: (entries) => {
|
|
1604
|
-
assertScope(active);
|
|
1605
|
-
staged.clear();
|
|
1606
|
-
cleared = true;
|
|
1607
|
-
const order = [];
|
|
1608
|
-
for (const [key, value] of entries) {
|
|
1609
|
-
if (staged.has(key)) throw new TypeError("Duplicate projection key.");
|
|
1610
|
-
writer.set(key, value);
|
|
1611
|
-
order.push(key);
|
|
1612
|
-
}
|
|
1613
|
-
explicitOrder = order;
|
|
1614
|
-
}
|
|
1615
|
-
};
|
|
1616
|
-
const input = {
|
|
1617
|
-
sources,
|
|
1618
|
-
previous: read(false),
|
|
1619
|
-
next: read(true),
|
|
1620
|
-
writer
|
|
1621
|
-
};
|
|
1622
|
-
if (build || !instance) {
|
|
1623
|
-
require_issue.profile.materialized.rebuilt();
|
|
1624
|
-
instance = void 0;
|
|
1625
|
-
cleared = true;
|
|
1626
|
-
const built = spec.build(input);
|
|
1627
|
-
assertSynchronous(built);
|
|
1628
|
-
instance = built;
|
|
1629
|
-
} else {
|
|
1630
|
-
require_issue.profile.materialized.updated();
|
|
1631
|
-
const result = instance.update(input);
|
|
1632
|
-
assertSynchronous(result);
|
|
1633
|
-
if (result?.kind === "rebuild") {
|
|
1634
|
-
staged.clear();
|
|
1635
|
-
cleared = true;
|
|
1636
|
-
explicitOrder = void 0;
|
|
1637
|
-
instance = void 0;
|
|
1638
|
-
const built = spec.build(input);
|
|
1639
|
-
assertSynchronous(built);
|
|
1640
|
-
instance = built;
|
|
1641
|
-
build = true;
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
if (cleared) {
|
|
1645
|
-
for (const key of values.keys()) if (!staged.has(key)) staged.set(key, { present: false });
|
|
1646
|
-
}
|
|
1647
|
-
const added = /* @__PURE__ */ new Set();
|
|
1648
|
-
const removed = /* @__PURE__ */ new Set();
|
|
1649
|
-
const updated = /* @__PURE__ */ new Set();
|
|
1650
|
-
require_issue.profile.projection("touchedKeys", staged.size);
|
|
1651
|
-
for (const [key, entry] of staged) {
|
|
1652
|
-
const existed = values.has(key);
|
|
1653
|
-
if (entry.present) if (!existed) added.add(key);
|
|
1654
|
-
else if (!(spec.isEqual ?? Object.is)(values.get(key), entry.value)) updated.add(key);
|
|
1655
|
-
else staged.delete(key);
|
|
1656
|
-
else if (existed) removed.add(key);
|
|
1657
|
-
else staged.delete(key);
|
|
1658
|
-
}
|
|
1659
|
-
if (explicitOrder || added.size || removed.size) {
|
|
1660
|
-
const order = explicitOrder ?? [...ids.filter((key) => !removed.has(key)), ...added];
|
|
1661
|
-
if (new Set(order).size !== order.length || order.length !== values.size + added.size - removed.size || order.some((key) => !hasNext(key))) throw new TypeError("Projection order must contain every key exactly once.");
|
|
1662
|
-
nextIds = ids.length === order.length && ids.every((key, i) => key === order[i]) ? ids : Object.freeze(order.slice());
|
|
1663
|
-
}
|
|
1664
|
-
orderChanged = nextIds !== ids;
|
|
1665
|
-
if (added.size || removed.size || updated.size || orderChanged) {
|
|
1666
|
-
let commonOrderChanged = false;
|
|
1667
|
-
if (orderChanged) {
|
|
1668
|
-
const before = ids.filter((key) => !removed.has(key));
|
|
1669
|
-
const after = nextIds.filter((key) => !added.has(key));
|
|
1670
|
-
commonOrderChanged = before.some((key, i) => after[i] !== key);
|
|
1671
|
-
}
|
|
1672
|
-
change = Object.freeze({
|
|
1673
|
-
kind: "incremental",
|
|
1674
|
-
added: readonlySet(added),
|
|
1675
|
-
removed: readonlySet(removed),
|
|
1676
|
-
updated: readonlySet(updated),
|
|
1677
|
-
orderChanged: commonOrderChanged
|
|
1678
|
-
});
|
|
1679
|
-
[
|
|
1680
|
-
...added,
|
|
1681
|
-
...removed,
|
|
1682
|
-
...updated
|
|
1683
|
-
].forEach((key) => changedKeys.add(key));
|
|
1684
|
-
require_issue.profile.projection("changedKeys", changedKeys.size);
|
|
1685
|
-
}
|
|
1686
|
-
const transitions = [];
|
|
1687
|
-
for (const key of changedKeys) {
|
|
1688
|
-
const beforePresent = values.has(key);
|
|
1689
|
-
const afterPresent = hasNext(key);
|
|
1690
|
-
if (beforePresent === afterPresent) {
|
|
1691
|
-
if (!beforePresent) continue;
|
|
1692
|
-
if ((spec.isEqual ?? Object.is)(values.get(key), getNext(key))) continue;
|
|
1693
|
-
}
|
|
1694
|
-
if (!beforePresent) transitions.push({
|
|
1695
|
-
key,
|
|
1696
|
-
kind: "added",
|
|
1697
|
-
before: void 0,
|
|
1698
|
-
after: getNext(key)
|
|
1699
|
-
});
|
|
1700
|
-
else if (!afterPresent) transitions.push({
|
|
1701
|
-
key,
|
|
1702
|
-
kind: "removed",
|
|
1703
|
-
before: values.get(key),
|
|
1704
|
-
after: void 0
|
|
1705
|
-
});
|
|
1706
|
-
else transitions.push({
|
|
1707
|
-
key,
|
|
1708
|
-
kind: "updated",
|
|
1709
|
-
before: values.get(key),
|
|
1710
|
-
after: getNext(key)
|
|
1711
|
-
});
|
|
1712
|
-
}
|
|
1713
|
-
publishedTransitions = Object.freeze(transitions);
|
|
1714
|
-
reset = build;
|
|
1715
|
-
return change !== void 0;
|
|
1716
|
-
},
|
|
1717
|
-
context: (active) => {
|
|
1718
|
-
const transitions = (keys) => {
|
|
1719
|
-
assertScope(active);
|
|
1720
|
-
const selected = keys ? new Set(keys) : void 0;
|
|
1721
|
-
return Object.freeze(publishedTransitions.filter((transition) => !selected || selected.has(transition.key)));
|
|
1722
|
-
};
|
|
1723
|
-
const input = {
|
|
1724
|
-
get: (key) => {
|
|
1725
|
-
assertScope(active);
|
|
1726
|
-
return getNext(key);
|
|
1727
|
-
},
|
|
1728
|
-
has: (key) => {
|
|
1729
|
-
assertScope(active);
|
|
1730
|
-
return hasNext(key);
|
|
1731
|
-
},
|
|
1732
|
-
ids: () => {
|
|
1733
|
-
assertScope(active);
|
|
1734
|
-
return nextIds;
|
|
1735
|
-
},
|
|
1736
|
-
previous: Object.freeze({
|
|
1737
|
-
get: (key) => {
|
|
1738
|
-
assertScope(active);
|
|
1739
|
-
return values.get(key);
|
|
1740
|
-
},
|
|
1741
|
-
has: (key) => {
|
|
1742
|
-
assertScope(active);
|
|
1743
|
-
return values.has(key);
|
|
1744
|
-
},
|
|
1745
|
-
ids: () => {
|
|
1746
|
-
assertScope(active);
|
|
1747
|
-
return ids;
|
|
1748
|
-
}
|
|
1749
|
-
}),
|
|
1750
|
-
change,
|
|
1751
|
-
revision: revision + (change ? 1 : 0),
|
|
1752
|
-
reset,
|
|
1753
|
-
transitions,
|
|
1754
|
-
batch,
|
|
1755
|
-
cause
|
|
1756
|
-
};
|
|
1757
|
-
return Object.freeze(input);
|
|
1758
|
-
},
|
|
1759
|
-
revision: () => revision,
|
|
1760
|
-
reset: () => reset,
|
|
1761
|
-
publish: () => {
|
|
1762
|
-
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
1763
|
-
else values.delete(key);
|
|
1764
|
-
if (change) {
|
|
1765
|
-
if (initialized) revision++;
|
|
1766
|
-
if (orderChanged && initialized) idsRevision++;
|
|
1767
|
-
all = void 0;
|
|
1768
|
-
changedKeys.forEach((key) => {
|
|
1769
|
-
const item = items.get(key);
|
|
1770
|
-
if (item && initialized) item.revision++;
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
ids = nextIds;
|
|
1774
|
-
initialized = true;
|
|
1775
|
-
},
|
|
1776
|
-
emit: (call) => {
|
|
1777
|
-
require_issue.profile.materialized.notification();
|
|
1778
|
-
const fault = owner.node.fault !== void 0;
|
|
1779
|
-
const resetEvent = Object.freeze({ kind: "reset" });
|
|
1780
|
-
Array.from(listeners).forEach((listener) => call(() => listener(change ?? resetEvent)));
|
|
1781
|
-
if (orderChanged || !change || fault || owner.node.statusChanged) Array.from(idsListeners).forEach((listener) => call(listener));
|
|
1782
|
-
Array.from(allListeners).forEach((listener) => call(listener));
|
|
1783
|
-
if (!change || fault || owner.node.statusChanged) for (const item of subscribedItems) Array.from(item.listeners ?? []).forEach((listener) => call(listener));
|
|
1784
|
-
else for (const key of changedKeys) Array.from(items.get(key)?.listeners ?? []).forEach((listener) => call(listener));
|
|
1785
|
-
},
|
|
1786
|
-
clear: () => {
|
|
1787
|
-
staged.clear();
|
|
1788
|
-
nextIds = ids;
|
|
1789
|
-
change = void 0;
|
|
1790
|
-
reset = false;
|
|
1791
|
-
changedKeys.clear();
|
|
1792
|
-
publishedTransitions = Object.freeze([]);
|
|
1793
|
-
batch = void 0;
|
|
1794
|
-
cause = void 0;
|
|
1795
|
-
orderChanged = false;
|
|
1796
|
-
},
|
|
1797
|
-
release: () => {
|
|
1798
|
-
values.clear();
|
|
1799
|
-
staged.clear();
|
|
1800
|
-
items.clear();
|
|
1801
|
-
subscribedItems.clear();
|
|
1802
|
-
idsListeners.clear();
|
|
1803
|
-
allListeners.clear();
|
|
1804
|
-
listeners.clear();
|
|
1805
|
-
ids = nextIds = Object.freeze([]);
|
|
1806
|
-
all = void 0;
|
|
1807
|
-
instance = void 0;
|
|
1808
|
-
}
|
|
1809
|
-
});
|
|
1810
|
-
const handle = {
|
|
1811
|
-
current: () => {
|
|
1812
|
-
owner.check();
|
|
1813
|
-
return current;
|
|
1814
|
-
},
|
|
1815
|
-
ids: {
|
|
1816
|
-
current: () => {
|
|
1817
|
-
owner.check();
|
|
1818
|
-
return ids;
|
|
1819
|
-
},
|
|
1820
|
-
revision: () => {
|
|
1821
|
-
owner.check();
|
|
1822
|
-
return idsRevision;
|
|
1823
|
-
},
|
|
1824
|
-
subscribe: (listener) => {
|
|
1825
|
-
owner.check();
|
|
1826
|
-
idsListeners.add(listener);
|
|
1827
|
-
return () => {
|
|
1828
|
-
idsListeners.delete(listener);
|
|
1829
|
-
};
|
|
1830
|
-
}
|
|
1831
|
-
},
|
|
1832
|
-
all: {
|
|
1833
|
-
current: () => {
|
|
1834
|
-
owner.check();
|
|
1835
|
-
if (!all) {
|
|
1836
|
-
require_issue.profile.collectionView.arrayCopied();
|
|
1837
|
-
all = Object.freeze(ids.map((key) => values.get(key)));
|
|
1838
|
-
}
|
|
1839
|
-
return all;
|
|
1840
|
-
},
|
|
1841
|
-
revision: () => {
|
|
1842
|
-
owner.check();
|
|
1843
|
-
return revision;
|
|
1844
|
-
},
|
|
1845
|
-
subscribe: (listener) => {
|
|
1846
|
-
owner.check();
|
|
1847
|
-
allListeners.add(listener);
|
|
1848
|
-
return () => {
|
|
1849
|
-
allListeners.delete(listener);
|
|
1850
|
-
};
|
|
1851
|
-
}
|
|
1852
|
-
},
|
|
1853
|
-
item: (key) => {
|
|
1854
|
-
owner.check();
|
|
1855
|
-
const old = items.get(key);
|
|
1856
|
-
if (old) return old.readable;
|
|
1857
|
-
const readable = Object.freeze({
|
|
1858
|
-
current: () => {
|
|
1859
|
-
owner.check();
|
|
1860
|
-
return values.get(key);
|
|
1861
|
-
},
|
|
1862
|
-
revision: () => {
|
|
1863
|
-
owner.check();
|
|
1864
|
-
return item.revision;
|
|
1865
|
-
},
|
|
1866
|
-
subscribe: (listener) => {
|
|
1867
|
-
owner.check();
|
|
1868
|
-
const set = item.listeners ??= /* @__PURE__ */ new Set();
|
|
1869
|
-
set.add(listener);
|
|
1870
|
-
subscribedItems.add(item);
|
|
1871
|
-
return () => {
|
|
1872
|
-
set.delete(listener);
|
|
1873
|
-
if (!set.size) subscribedItems.delete(item);
|
|
1874
|
-
};
|
|
1378
|
+
cleanup();
|
|
1379
|
+
} catch (error) {
|
|
1380
|
+
failures.push(error);
|
|
1875
1381
|
}
|
|
1876
1382
|
});
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1383
|
+
cleanups.clear();
|
|
1384
|
+
nodes.forEach((node) => {
|
|
1385
|
+
node.disposed = true;
|
|
1386
|
+
node.release();
|
|
1387
|
+
node.consumers.clear();
|
|
1388
|
+
});
|
|
1389
|
+
records.forEach((source) => {
|
|
1390
|
+
source.disposed = true;
|
|
1391
|
+
source.consumers.clear();
|
|
1392
|
+
source.clear();
|
|
1393
|
+
});
|
|
1394
|
+
nodes.clear();
|
|
1395
|
+
records.clear();
|
|
1396
|
+
pending.clear();
|
|
1397
|
+
heap.length = 0;
|
|
1398
|
+
completed.length = 0;
|
|
1399
|
+
queued.clear();
|
|
1400
|
+
emissions.clear();
|
|
1401
|
+
guards.clear();
|
|
1402
|
+
errors.length = 0;
|
|
1403
|
+
reportingFailures = [];
|
|
1404
|
+
if (failures.length) throw new AggregateError(failures, "Projection cleanup failed.");
|
|
1405
|
+
}
|
|
1898
1406
|
};
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1407
|
+
};
|
|
1408
|
+
const assertSynchronous = (value) => {
|
|
1409
|
+
if (value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function") throw new TypeError("Projection callbacks must be synchronous.");
|
|
1410
|
+
};
|
|
1411
|
+
const assertScope = (active) => {
|
|
1412
|
+
if (!active()) throw new Error("Projection reader or writer is no longer active.");
|
|
1904
1413
|
};
|
|
1905
1414
|
//#endregion
|
|
1906
1415
|
//#region core/src/projection/source.ts
|
|
1907
1416
|
const createSources = (scheduler) => {
|
|
1908
1417
|
const documents = /* @__PURE__ */ new Map();
|
|
1909
1418
|
const readables = /* @__PURE__ */ new Map();
|
|
1910
|
-
const collections = /* @__PURE__ */ new WeakSet();
|
|
1911
1419
|
const externalSources = /* @__PURE__ */ new WeakMap();
|
|
1420
|
+
const isCollectionSource = (value) => value.kind === "collection";
|
|
1912
1421
|
const document = (runtime) => {
|
|
1913
1422
|
scheduler.assertIdle();
|
|
1914
1423
|
const state = require_notification.accessOf(runtime);
|
|
@@ -1924,13 +1433,18 @@ const createSources = (scheduler) => {
|
|
|
1924
1433
|
targets.forEach((value) => {
|
|
1925
1434
|
if (!require_notification.belongs(value, state.schema)) throw new Error("Projection target belongs to another schema.");
|
|
1926
1435
|
});
|
|
1927
|
-
const old = bindings.find((entry) => Boolean(collection) ===
|
|
1436
|
+
const old = bindings.find((entry) => Boolean(collection) === isCollectionSource(entry.handle) && entry.targets.length === targets.length && entry.targets.every((value, i) => require_notification.same(value, targets[i])));
|
|
1928
1437
|
if (old) return old.handle;
|
|
1929
|
-
let commits = [];
|
|
1930
1438
|
let reset = false;
|
|
1931
|
-
let
|
|
1439
|
+
let orderChanged = false;
|
|
1932
1440
|
const keys = /* @__PURE__ */ new Set();
|
|
1933
|
-
let
|
|
1441
|
+
let baseline;
|
|
1442
|
+
let baselineIds = Object.freeze([]);
|
|
1443
|
+
let nextBaseline;
|
|
1444
|
+
let nextBaselineIds = Object.freeze([]);
|
|
1445
|
+
const collectionNode = collection ? require_issue.nodeAt(state.schema, collection.address, state.document) : void 0;
|
|
1446
|
+
const entryNode = collectionNode && (collectionNode.kind === "map" || collectionNode.kind === "table") ? collectionNode.value : void 0;
|
|
1447
|
+
const sameEntry = (left, right) => entryNode ? require_scope.equalValue(entryNode, left, right) : Object.is(left, right);
|
|
1934
1448
|
const record = {
|
|
1935
1449
|
consumers: /* @__PURE__ */ new Set(),
|
|
1936
1450
|
disposed: false,
|
|
@@ -1938,11 +1452,16 @@ const createSources = (scheduler) => {
|
|
|
1938
1452
|
revision: runtime.revision,
|
|
1939
1453
|
reset: () => reset,
|
|
1940
1454
|
clear: () => {
|
|
1941
|
-
|
|
1942
|
-
|
|
1455
|
+
if (collection && nextBaseline) {
|
|
1456
|
+
baseline = nextBaseline;
|
|
1457
|
+
baselineIds = nextBaselineIds;
|
|
1458
|
+
}
|
|
1459
|
+
reset = false;
|
|
1460
|
+
orderChanged = false;
|
|
1943
1461
|
batch = void 0;
|
|
1944
1462
|
keys.clear();
|
|
1945
|
-
|
|
1463
|
+
nextBaseline = void 0;
|
|
1464
|
+
nextBaselineIds = Object.freeze([]);
|
|
1946
1465
|
},
|
|
1947
1466
|
context: (active) => {
|
|
1948
1467
|
assertScope(active);
|
|
@@ -1951,22 +1470,109 @@ const createSources = (scheduler) => {
|
|
|
1951
1470
|
state,
|
|
1952
1471
|
active
|
|
1953
1472
|
};
|
|
1954
|
-
const
|
|
1955
|
-
|
|
1473
|
+
const rawRead = collection ? require_scope.collectionAccess(context, collection.address) : void 0;
|
|
1474
|
+
const snapshots = collection ? /* @__PURE__ */ new Map() : void 0;
|
|
1475
|
+
const read = collection ? Object.freeze({
|
|
1476
|
+
get: (key) => {
|
|
1477
|
+
assertScope(active);
|
|
1478
|
+
if (!snapshots.has(key)) snapshots.set(key, require_scope.snapshot(rawRead.get(key)));
|
|
1479
|
+
return snapshots.get(key);
|
|
1480
|
+
},
|
|
1481
|
+
has: (key) => {
|
|
1482
|
+
assertScope(active);
|
|
1483
|
+
return rawRead.has(key);
|
|
1484
|
+
},
|
|
1485
|
+
ids: () => {
|
|
1486
|
+
assertScope(active);
|
|
1487
|
+
return rawRead.ids();
|
|
1488
|
+
}
|
|
1489
|
+
}) : require_scope.createAccess(context);
|
|
1490
|
+
if (!collection) return Object.freeze({
|
|
1491
|
+
kind: "document",
|
|
1956
1492
|
read,
|
|
1957
1493
|
revision: runtime.revision(),
|
|
1958
|
-
commits: Object.freeze(commits.slice()),
|
|
1959
1494
|
reset: record.reset(),
|
|
1960
|
-
batch
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1495
|
+
cause: batch?.cause
|
|
1496
|
+
});
|
|
1497
|
+
if (!baseline) {
|
|
1498
|
+
const collectionRead = read;
|
|
1499
|
+
baseline = /* @__PURE__ */ new Map();
|
|
1500
|
+
baselineIds = Object.freeze(require_scope.collectionAccess(context, collection.address).ids().slice());
|
|
1501
|
+
for (const key of baselineIds) {
|
|
1502
|
+
const value = collectionRead.get(key);
|
|
1503
|
+
baseline.set(key, value);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
const changed = [...keys];
|
|
1507
|
+
const added = [];
|
|
1508
|
+
const updated = [];
|
|
1509
|
+
const removed = [];
|
|
1510
|
+
const next = new Map(baseline);
|
|
1511
|
+
const collectionRead = read;
|
|
1512
|
+
for (const key of changed) {
|
|
1513
|
+
const beforePresent = baseline.has(key);
|
|
1514
|
+
const afterPresent = collectionRead.has(key);
|
|
1515
|
+
const afterValue = afterPresent ? collectionRead.get(key) : void 0;
|
|
1516
|
+
if (!beforePresent && afterPresent) {
|
|
1517
|
+
added.push({
|
|
1518
|
+
key,
|
|
1519
|
+
after: afterValue
|
|
1520
|
+
});
|
|
1521
|
+
next.set(key, afterValue);
|
|
1522
|
+
} else if (beforePresent && !afterPresent) {
|
|
1523
|
+
removed.push({
|
|
1524
|
+
key,
|
|
1525
|
+
before: baseline.get(key)
|
|
1526
|
+
});
|
|
1527
|
+
next.delete(key);
|
|
1528
|
+
} else if (beforePresent && afterPresent) {
|
|
1529
|
+
const beforeValue = baseline.get(key);
|
|
1530
|
+
if (!sameEntry(beforeValue, afterValue)) updated.push({
|
|
1531
|
+
key,
|
|
1532
|
+
before: beforeValue,
|
|
1533
|
+
after: afterValue
|
|
1534
|
+
});
|
|
1535
|
+
next.set(key, afterValue);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
const ids = record.reset() || orderChanged ? Object.freeze([...collectionRead.ids()]) : baselineIds;
|
|
1539
|
+
if (record.reset()) {
|
|
1540
|
+
next.clear();
|
|
1541
|
+
for (const key of ids) {
|
|
1542
|
+
const value = collectionRead.get(key);
|
|
1543
|
+
next.set(key, value);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
let order;
|
|
1547
|
+
if (orderChanged) {
|
|
1548
|
+
const beforeKeys = new Set(baselineIds);
|
|
1549
|
+
const afterKeys = new Set(ids);
|
|
1550
|
+
const beforeCommon = baselineIds.filter((key) => afterKeys.has(key));
|
|
1551
|
+
const afterCommon = ids.filter((key) => beforeKeys.has(key));
|
|
1552
|
+
if (beforeCommon.some((key, index) => afterCommon[index] !== key)) order = {
|
|
1553
|
+
before: Object.freeze([...baselineIds]),
|
|
1554
|
+
after: ids
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
const change = record.reset() ? Object.freeze({ kind: "reset" }) : added.length || updated.length || removed.length || order ? Object.freeze({
|
|
1558
|
+
kind: "incremental",
|
|
1559
|
+
added: Object.freeze(added),
|
|
1560
|
+
updated: Object.freeze(updated),
|
|
1561
|
+
removed: Object.freeze(removed),
|
|
1562
|
+
...order ? { order: Object.freeze(order) } : {}
|
|
1563
|
+
}) : void 0;
|
|
1564
|
+
nextBaseline = next;
|
|
1565
|
+
nextBaselineIds = ids;
|
|
1566
|
+
return Object.freeze({
|
|
1567
|
+
kind: "collection",
|
|
1568
|
+
read,
|
|
1569
|
+
change,
|
|
1570
|
+
revision: runtime.revision(),
|
|
1571
|
+
cause: batch?.cause
|
|
1966
1572
|
});
|
|
1967
1573
|
}
|
|
1968
1574
|
};
|
|
1969
|
-
const handle = collection ? {} : {
|
|
1575
|
+
const handle = collection ? { kind: "collection" } : {
|
|
1970
1576
|
collection: (pick) => {
|
|
1971
1577
|
scheduler.assertIdle();
|
|
1972
1578
|
return make({ collection: compilePath(state.schema, "collection", pick) });
|
|
@@ -1978,10 +1584,8 @@ const createSources = (scheduler) => {
|
|
|
1978
1584
|
}
|
|
1979
1585
|
};
|
|
1980
1586
|
Object.freeze(handle);
|
|
1981
|
-
if (collection) collections.add(handle);
|
|
1982
1587
|
scheduler.register(handle, record);
|
|
1983
1588
|
const receive = (commit) => {
|
|
1984
|
-
commits.push(commit);
|
|
1985
1589
|
reset ||= commit.impact.kind === "reset";
|
|
1986
1590
|
if (collection) {
|
|
1987
1591
|
const change = collectionImpact(commit.impact, collection);
|
|
@@ -1990,9 +1594,8 @@ const createSources = (scheduler) => {
|
|
|
1990
1594
|
change.added.forEach((key) => keys.add(key));
|
|
1991
1595
|
change.removed.forEach((key) => keys.add(key));
|
|
1992
1596
|
change.updated.forEach((key) => keys.add(key));
|
|
1993
|
-
|
|
1597
|
+
orderChanged ||= Boolean(change.added.size || change.removed.size || change.orderChanged);
|
|
1994
1598
|
}
|
|
1995
|
-
candidates = void 0;
|
|
1996
1599
|
}
|
|
1997
1600
|
};
|
|
1998
1601
|
bindings.push({
|
|
@@ -2035,13 +1638,9 @@ const createSources = (scheduler) => {
|
|
|
2035
1638
|
};
|
|
2036
1639
|
const valueSource = (initial, equal) => {
|
|
2037
1640
|
let value = initial;
|
|
2038
|
-
let previous = initial;
|
|
2039
1641
|
let revision = 0;
|
|
2040
|
-
let detail;
|
|
2041
1642
|
let cause;
|
|
2042
|
-
let batch;
|
|
2043
1643
|
let reset = false;
|
|
2044
|
-
let pending = false;
|
|
2045
1644
|
const handle = Object.freeze({});
|
|
2046
1645
|
const record = {
|
|
2047
1646
|
consumers: /* @__PURE__ */ new Set(),
|
|
@@ -2050,24 +1649,17 @@ const createSources = (scheduler) => {
|
|
|
2050
1649
|
revision: () => revision,
|
|
2051
1650
|
reset: () => reset,
|
|
2052
1651
|
clear: () => {
|
|
2053
|
-
previous = value;
|
|
2054
|
-
pending = false;
|
|
2055
|
-
detail = void 0;
|
|
2056
1652
|
cause = void 0;
|
|
2057
|
-
batch = void 0;
|
|
2058
1653
|
reset = false;
|
|
2059
1654
|
},
|
|
2060
1655
|
context: (active) => {
|
|
2061
1656
|
assertScope(active);
|
|
2062
1657
|
return Object.freeze({
|
|
1658
|
+
kind: "value",
|
|
2063
1659
|
value,
|
|
2064
|
-
previous,
|
|
2065
1660
|
revision,
|
|
2066
|
-
changed: !equal(previous, value),
|
|
2067
1661
|
reset,
|
|
2068
|
-
|
|
2069
|
-
cause,
|
|
2070
|
-
batch
|
|
1662
|
+
cause
|
|
2071
1663
|
});
|
|
2072
1664
|
}
|
|
2073
1665
|
};
|
|
@@ -2075,18 +1667,14 @@ const createSources = (scheduler) => {
|
|
|
2075
1667
|
const accept = (next, metadata) => {
|
|
2076
1668
|
scheduler.assertIdle();
|
|
2077
1669
|
const recovering = record.fault !== void 0;
|
|
2078
|
-
const eventful = metadata !== void 0 && (metadata.reset === true || metadata.revision !== void 0 && metadata.revision !== revision || metadata.
|
|
1670
|
+
const eventful = metadata !== void 0 && (metadata.reset === true || metadata.revision !== void 0 && metadata.revision !== revision || metadata.cause !== void 0);
|
|
2079
1671
|
if (equal(value, next) && !recovering && !eventful) return;
|
|
2080
1672
|
record.fault = void 0;
|
|
2081
|
-
if (!pending) previous = value;
|
|
2082
1673
|
value = next;
|
|
2083
1674
|
revision = metadata?.revision ?? revision + 1;
|
|
2084
|
-
|
|
2085
|
-
cause = metadata?.cause ?? cause;
|
|
2086
|
-
batch = scheduler.batchContext() ?? metadata?.batch ?? batch;
|
|
1675
|
+
cause = metadata?.cause ?? scheduler.batchContext()?.cause ?? cause;
|
|
2087
1676
|
reset ||= metadata?.reset ?? false;
|
|
2088
1677
|
scheduler.capture(record);
|
|
2089
|
-
pending = true;
|
|
2090
1678
|
};
|
|
2091
1679
|
return {
|
|
2092
1680
|
source: handle,
|
|
@@ -2113,10 +1701,8 @@ const createSources = (scheduler) => {
|
|
|
2113
1701
|
try {
|
|
2114
1702
|
input.accept(event.value, {
|
|
2115
1703
|
revision: event.revision,
|
|
2116
|
-
reset: event.reset,
|
|
2117
|
-
|
|
2118
|
-
cause: event.cause,
|
|
2119
|
-
batch: event.batch
|
|
1704
|
+
reset: event.reset ?? false,
|
|
1705
|
+
cause: event.cause
|
|
2120
1706
|
});
|
|
2121
1707
|
} catch (cause) {
|
|
2122
1708
|
fail(cause);
|
|
@@ -2141,13 +1727,11 @@ const createSources = (scheduler) => {
|
|
|
2141
1727
|
let revision = port.revision();
|
|
2142
1728
|
let change;
|
|
2143
1729
|
let reset = false;
|
|
2144
|
-
let
|
|
1730
|
+
let orderChanged = false;
|
|
2145
1731
|
let cause;
|
|
2146
|
-
let batch;
|
|
2147
|
-
let publishedTransitions = Object.freeze([]);
|
|
2148
1732
|
let transitionKeys = /* @__PURE__ */ new Set();
|
|
2149
1733
|
let pending = false;
|
|
2150
|
-
const handle = Object.freeze({});
|
|
1734
|
+
const handle = Object.freeze({ kind: "collection" });
|
|
2151
1735
|
const record = {
|
|
2152
1736
|
consumers: /* @__PURE__ */ new Set(),
|
|
2153
1737
|
disposed: false,
|
|
@@ -2159,10 +1743,8 @@ const createSources = (scheduler) => {
|
|
|
2159
1743
|
pending = false;
|
|
2160
1744
|
change = void 0;
|
|
2161
1745
|
reset = false;
|
|
2162
|
-
|
|
1746
|
+
orderChanged = false;
|
|
2163
1747
|
cause = void 0;
|
|
2164
|
-
batch = void 0;
|
|
2165
|
-
publishedTransitions = Object.freeze([]);
|
|
2166
1748
|
transitionKeys.clear();
|
|
2167
1749
|
},
|
|
2168
1750
|
context: (active) => {
|
|
@@ -2181,101 +1763,65 @@ const createSources = (scheduler) => {
|
|
|
2181
1763
|
return read.ids();
|
|
2182
1764
|
}
|
|
2183
1765
|
});
|
|
2184
|
-
const previousRead = Object.freeze({
|
|
2185
|
-
get: (key) => {
|
|
2186
|
-
assertScope(active);
|
|
2187
|
-
return previous.get(key);
|
|
2188
|
-
},
|
|
2189
|
-
has: (key) => {
|
|
2190
|
-
assertScope(active);
|
|
2191
|
-
return previous.has(key);
|
|
2192
|
-
},
|
|
2193
|
-
ids: () => {
|
|
2194
|
-
assertScope(active);
|
|
2195
|
-
return previous.ids();
|
|
2196
|
-
}
|
|
2197
|
-
});
|
|
2198
1766
|
return Object.freeze({
|
|
2199
|
-
|
|
2200
|
-
|
|
1767
|
+
kind: "collection",
|
|
1768
|
+
read: currentRead,
|
|
2201
1769
|
change,
|
|
2202
1770
|
revision,
|
|
2203
|
-
|
|
2204
|
-
transitions: (keys) => {
|
|
2205
|
-
assertScope(active);
|
|
2206
|
-
const selected = keys ? new Set(keys) : void 0;
|
|
2207
|
-
return Object.freeze(publishedTransitions.filter((transition) => !selected || selected.has(transition.key)));
|
|
2208
|
-
},
|
|
2209
|
-
detail,
|
|
2210
|
-
cause,
|
|
2211
|
-
batch
|
|
1771
|
+
cause
|
|
2212
1772
|
});
|
|
2213
1773
|
}
|
|
2214
1774
|
};
|
|
2215
1775
|
scheduler.register(handle, record);
|
|
2216
|
-
collections.add(handle);
|
|
2217
1776
|
const fail = (error) => {
|
|
2218
1777
|
record.fault = new ProjectionError("source", "external collection source", [revision], error);
|
|
2219
1778
|
scheduler.capture(record);
|
|
2220
1779
|
};
|
|
2221
|
-
const
|
|
2222
|
-
if (
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
}
|
|
2227
|
-
};
|
|
2228
|
-
const recomputeTransitions = () => {
|
|
2229
|
-
const transitions = [];
|
|
1780
|
+
const deriveChange = () => {
|
|
1781
|
+
if (reset) return Object.freeze({ kind: "reset" });
|
|
1782
|
+
const added = [];
|
|
1783
|
+
const updated = [];
|
|
1784
|
+
const removed = [];
|
|
2230
1785
|
for (const key of transitionKeys) {
|
|
2231
1786
|
const beforePresent = previous.has(key);
|
|
2232
1787
|
const afterPresent = read.has(key);
|
|
2233
1788
|
const before = beforePresent ? previous.get(key) : void 0;
|
|
2234
1789
|
const after = afterPresent ? read.get(key) : void 0;
|
|
2235
1790
|
if (beforePresent === afterPresent && Object.is(before, after)) continue;
|
|
2236
|
-
if (!beforePresent)
|
|
1791
|
+
if (!beforePresent && afterPresent) added.push({
|
|
2237
1792
|
key,
|
|
2238
|
-
kind: "added",
|
|
2239
|
-
before: void 0,
|
|
2240
1793
|
after
|
|
2241
1794
|
});
|
|
2242
|
-
else if (!afterPresent)
|
|
1795
|
+
else if (beforePresent && !afterPresent) removed.push({
|
|
2243
1796
|
key,
|
|
2244
|
-
|
|
2245
|
-
before,
|
|
2246
|
-
after: void 0
|
|
1797
|
+
before
|
|
2247
1798
|
});
|
|
2248
|
-
else
|
|
1799
|
+
else if (beforePresent && afterPresent) updated.push({
|
|
2249
1800
|
key,
|
|
2250
|
-
kind: "updated",
|
|
2251
1801
|
before,
|
|
2252
1802
|
after
|
|
2253
1803
|
});
|
|
2254
1804
|
}
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
if (
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
const beforeCommon = beforeIds.filter((key) => read.has(key));
|
|
2270
|
-
const afterCommon = afterIds.filter((key) => previous.has(key));
|
|
2271
|
-
const orderChanged = beforeCommon.length !== afterCommon.length || beforeCommon.some((key, index) => afterCommon[index] !== key);
|
|
2272
|
-
if (!added.size && !removed.size && !updated.size && !orderChanged) return void 0;
|
|
1805
|
+
let order;
|
|
1806
|
+
if (orderChanged) {
|
|
1807
|
+
const beforeIds = previous.ids();
|
|
1808
|
+
const afterIds = read.ids();
|
|
1809
|
+
const beforeKeys = new Set(beforeIds);
|
|
1810
|
+
const afterKeys = new Set(afterIds);
|
|
1811
|
+
const beforeCommon = beforeIds.filter((key) => afterKeys.has(key));
|
|
1812
|
+
const afterCommon = afterIds.filter((key) => beforeKeys.has(key));
|
|
1813
|
+
if (beforeCommon.some((key, index) => afterCommon[index] !== key)) order = Object.freeze({
|
|
1814
|
+
before: Object.freeze([...beforeIds]),
|
|
1815
|
+
after: Object.freeze([...afterIds])
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
if (!added.length && !removed.length && !updated.length && !order) return void 0;
|
|
2273
1819
|
return Object.freeze({
|
|
2274
1820
|
kind: "incremental",
|
|
2275
|
-
added,
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
1821
|
+
added: Object.freeze(added),
|
|
1822
|
+
updated: Object.freeze(updated),
|
|
1823
|
+
removed: Object.freeze(removed),
|
|
1824
|
+
...order ? { order } : {}
|
|
2279
1825
|
});
|
|
2280
1826
|
};
|
|
2281
1827
|
let unsubscribe;
|
|
@@ -2286,12 +1832,18 @@ const createSources = (scheduler) => {
|
|
|
2286
1832
|
if (!pending) previous = event.previous;
|
|
2287
1833
|
read = port.current();
|
|
2288
1834
|
revision = event.revision;
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
1835
|
+
if (event.impact?.kind === "incremental") {
|
|
1836
|
+
event.impact.added.forEach((key) => transitionKeys.add(key));
|
|
1837
|
+
event.impact.removed.forEach((key) => transitionKeys.add(key));
|
|
1838
|
+
event.impact.updated.forEach((key) => transitionKeys.add(key));
|
|
1839
|
+
orderChanged ||= Boolean(event.impact.added.size || event.impact.removed.size || event.impact.orderChanged);
|
|
1840
|
+
} else if (!event.impact) {
|
|
1841
|
+
previous.ids().forEach((key) => transitionKeys.add(key));
|
|
1842
|
+
read.ids().forEach((key) => transitionKeys.add(key));
|
|
1843
|
+
orderChanged = true;
|
|
1844
|
+
}
|
|
1845
|
+
reset ||= event.impact?.kind === "reset";
|
|
1846
|
+
cause = event.cause ?? scheduler.batchContext()?.cause ?? cause;
|
|
2295
1847
|
change = deriveChange();
|
|
2296
1848
|
record.fault = void 0;
|
|
2297
1849
|
scheduler.capture(record);
|
|
@@ -2407,97 +1959,474 @@ const createSources = (scheduler) => {
|
|
|
2407
1959
|
},
|
|
2408
1960
|
assertCollection: (handle) => {
|
|
2409
1961
|
scheduler.source(handle);
|
|
2410
|
-
if (!
|
|
1962
|
+
if (!isCollectionSource(handle)) throw new Error("map requires a collection source.");
|
|
1963
|
+
}
|
|
1964
|
+
};
|
|
1965
|
+
};
|
|
1966
|
+
//#endregion
|
|
1967
|
+
//#region core/src/projection/node.ts
|
|
1968
|
+
const createNode = (scheduler, spec, behavior) => {
|
|
1969
|
+
scheduler.assertIdle();
|
|
1970
|
+
const entries = Object.entries(spec.sources).map(([key, handle]) => [key, scheduler.source(handle)]);
|
|
1971
|
+
const order = scheduler.order();
|
|
1972
|
+
const node = {
|
|
1973
|
+
...behavior,
|
|
1974
|
+
publish: () => {
|
|
1975
|
+
if (node.fault) behavior.clear();
|
|
1976
|
+
else behavior.publish();
|
|
1977
|
+
},
|
|
1978
|
+
order,
|
|
1979
|
+
name: spec.name ? `${spec.name} (${order})` : `projection-${order}`,
|
|
1980
|
+
sources: entries.map(([, source]) => source),
|
|
1981
|
+
consumers: /* @__PURE__ */ new Set(),
|
|
1982
|
+
disposed: false,
|
|
1983
|
+
fault: void 0,
|
|
1984
|
+
evaluate: (build) => {
|
|
1985
|
+
let active = true;
|
|
1986
|
+
try {
|
|
1987
|
+
const scopeActive = () => active;
|
|
1988
|
+
const inputs = Object.fromEntries(entries.map(([key, source]) => [key, source.context(scopeActive)]));
|
|
1989
|
+
return behavior.evaluate(inputs, build, scopeActive);
|
|
1990
|
+
} finally {
|
|
1991
|
+
active = false;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
scheduler.initialize(() => {
|
|
1996
|
+
const invalid = node.sources.find((source) => source.fault || source.disposed);
|
|
1997
|
+
if (invalid) throw invalid.fault ?? new ProjectionDisposedError();
|
|
1998
|
+
node.evaluate(true);
|
|
1999
|
+
node.publish();
|
|
2000
|
+
node.clear();
|
|
2001
|
+
});
|
|
2002
|
+
const check = () => {
|
|
2003
|
+
if (node.disposed || !scheduler.active) throw new ProjectionDisposedError();
|
|
2004
|
+
if (node.fault) throw node.fault;
|
|
2005
|
+
};
|
|
2006
|
+
return {
|
|
2007
|
+
node,
|
|
2008
|
+
check,
|
|
2009
|
+
install(handle) {
|
|
2010
|
+
scheduler.register(handle, node);
|
|
2011
|
+
scheduler.addNode(node);
|
|
2411
2012
|
}
|
|
2412
2013
|
};
|
|
2413
2014
|
};
|
|
2414
|
-
//#endregion
|
|
2415
|
-
//#region core/src/projection/value.ts
|
|
2416
|
-
const createValue = (scheduler, spec, options) => {
|
|
2015
|
+
//#endregion
|
|
2016
|
+
//#region core/src/projection/value.ts
|
|
2017
|
+
const createValue = (scheduler, spec, options) => {
|
|
2018
|
+
let instance;
|
|
2019
|
+
let value;
|
|
2020
|
+
let next;
|
|
2021
|
+
let initialized = false;
|
|
2022
|
+
let revision = 0;
|
|
2023
|
+
let changed = false;
|
|
2024
|
+
let reset = false;
|
|
2025
|
+
let cause;
|
|
2026
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2027
|
+
const owner = createNode(scheduler, spec, {
|
|
2028
|
+
evaluate: (sources, build) => {
|
|
2029
|
+
cause = Object.values(sources).find((value) => value && typeof value === "object" && "cause" in value && value.cause !== void 0)?.cause ?? scheduler.batchContext()?.cause;
|
|
2030
|
+
let candidate;
|
|
2031
|
+
if (build || !instance) {
|
|
2032
|
+
require_issue.profile.materialized.rebuilt();
|
|
2033
|
+
instance = void 0;
|
|
2034
|
+
const built = spec.build(sources);
|
|
2035
|
+
assertSynchronous(built);
|
|
2036
|
+
instance = built;
|
|
2037
|
+
candidate = built.value;
|
|
2038
|
+
} else {
|
|
2039
|
+
require_issue.profile.materialized.updated();
|
|
2040
|
+
const result = instance.update(sources);
|
|
2041
|
+
assertSynchronous(result);
|
|
2042
|
+
if (result.kind === "rebuild") {
|
|
2043
|
+
instance = void 0;
|
|
2044
|
+
const built = spec.build(sources);
|
|
2045
|
+
assertSynchronous(built);
|
|
2046
|
+
instance = built;
|
|
2047
|
+
candidate = built.value;
|
|
2048
|
+
build = true;
|
|
2049
|
+
} else candidate = result.kind === "changed" ? result.value : value;
|
|
2050
|
+
}
|
|
2051
|
+
changed = !initialized || !(options?.isEqual ?? Object.is)(value, candidate);
|
|
2052
|
+
next = changed ? candidate : value;
|
|
2053
|
+
reset = build;
|
|
2054
|
+
return changed;
|
|
2055
|
+
},
|
|
2056
|
+
context: (active) => {
|
|
2057
|
+
assertScope(active);
|
|
2058
|
+
return Object.freeze({
|
|
2059
|
+
kind: "value",
|
|
2060
|
+
value: next,
|
|
2061
|
+
revision: revision + (changed ? 1 : 0),
|
|
2062
|
+
reset,
|
|
2063
|
+
cause
|
|
2064
|
+
});
|
|
2065
|
+
},
|
|
2066
|
+
revision: () => revision,
|
|
2067
|
+
reset: () => reset,
|
|
2068
|
+
publish: () => {
|
|
2069
|
+
if (changed && initialized) revision++;
|
|
2070
|
+
value = next;
|
|
2071
|
+
initialized = true;
|
|
2072
|
+
},
|
|
2073
|
+
emit: (call) => {
|
|
2074
|
+
require_issue.profile.materialized.notification();
|
|
2075
|
+
Array.from(listeners).forEach((listener) => call(listener));
|
|
2076
|
+
},
|
|
2077
|
+
clear: () => {
|
|
2078
|
+
next = value;
|
|
2079
|
+
changed = false;
|
|
2080
|
+
reset = false;
|
|
2081
|
+
cause = void 0;
|
|
2082
|
+
},
|
|
2083
|
+
release: () => {
|
|
2084
|
+
listeners.clear();
|
|
2085
|
+
instance = void 0;
|
|
2086
|
+
value = next = void 0;
|
|
2087
|
+
}
|
|
2088
|
+
});
|
|
2089
|
+
const handle = Object.freeze({
|
|
2090
|
+
kind: "value",
|
|
2091
|
+
current: () => {
|
|
2092
|
+
owner.check();
|
|
2093
|
+
return value;
|
|
2094
|
+
},
|
|
2095
|
+
revision: () => {
|
|
2096
|
+
owner.check();
|
|
2097
|
+
return revision;
|
|
2098
|
+
},
|
|
2099
|
+
subscribe: (listener) => {
|
|
2100
|
+
owner.check();
|
|
2101
|
+
listeners.add(listener);
|
|
2102
|
+
return () => {
|
|
2103
|
+
listeners.delete(listener);
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
});
|
|
2107
|
+
owner.install(handle);
|
|
2108
|
+
return handle;
|
|
2109
|
+
};
|
|
2110
|
+
//#endregion
|
|
2111
|
+
//#region core/src/projection/collection.ts
|
|
2112
|
+
const height = (node) => node?.height ?? 0;
|
|
2113
|
+
const makeTreeNode = (key, value, left, right) => Object.freeze({
|
|
2114
|
+
key,
|
|
2115
|
+
value,
|
|
2116
|
+
left,
|
|
2117
|
+
right,
|
|
2118
|
+
height: Math.max(height(left), height(right)) + 1
|
|
2119
|
+
});
|
|
2120
|
+
const rotateLeft = (node) => {
|
|
2121
|
+
const right = node.right;
|
|
2122
|
+
return makeTreeNode(right.key, right.value, makeTreeNode(node.key, node.value, node.left, right.left), right.right);
|
|
2123
|
+
};
|
|
2124
|
+
const rotateRight = (node) => {
|
|
2125
|
+
const left = node.left;
|
|
2126
|
+
return makeTreeNode(left.key, left.value, left.left, makeTreeNode(node.key, node.value, left.right, node.right));
|
|
2127
|
+
};
|
|
2128
|
+
const rebalance = (node) => {
|
|
2129
|
+
const balance = height(node.left) - height(node.right);
|
|
2130
|
+
if (balance > 1) {
|
|
2131
|
+
if (height(node.left.left) < height(node.left.right)) return rotateRight(makeTreeNode(node.key, node.value, rotateLeft(node.left), node.right));
|
|
2132
|
+
return rotateRight(node);
|
|
2133
|
+
}
|
|
2134
|
+
if (balance < -1) {
|
|
2135
|
+
if (height(node.right.right) < height(node.right.left)) return rotateLeft(makeTreeNode(node.key, node.value, node.left, rotateRight(node.right)));
|
|
2136
|
+
return rotateLeft(node);
|
|
2137
|
+
}
|
|
2138
|
+
return node;
|
|
2139
|
+
};
|
|
2140
|
+
const treeSet = (node, key, value) => {
|
|
2141
|
+
if (!node) return makeTreeNode(key, value);
|
|
2142
|
+
if (key === node.key) return makeTreeNode(key, value, node.left, node.right);
|
|
2143
|
+
if (key < node.key) return rebalance(makeTreeNode(node.key, node.value, treeSet(node.left, key, value), node.right));
|
|
2144
|
+
return rebalance(makeTreeNode(node.key, node.value, node.left, treeSet(node.right, key, value)));
|
|
2145
|
+
};
|
|
2146
|
+
const treeMin = (node) => node.left ? treeMin(node.left) : node;
|
|
2147
|
+
const treeRemove = (node, key) => {
|
|
2148
|
+
if (!node) return void 0;
|
|
2149
|
+
if (key < node.key) return rebalance(makeTreeNode(node.key, node.value, treeRemove(node.left, key), node.right));
|
|
2150
|
+
if (key > node.key) return rebalance(makeTreeNode(node.key, node.value, node.left, treeRemove(node.right, key)));
|
|
2151
|
+
if (!node.left) return node.right;
|
|
2152
|
+
if (!node.right) return node.left;
|
|
2153
|
+
const next = treeMin(node.right);
|
|
2154
|
+
return rebalance(makeTreeNode(next.key, next.value, node.left, treeRemove(node.right, next.key)));
|
|
2155
|
+
};
|
|
2156
|
+
const treeGet = (node, key) => {
|
|
2157
|
+
let current = node;
|
|
2158
|
+
while (current) {
|
|
2159
|
+
if (key === current.key) return current.value;
|
|
2160
|
+
current = key < current.key ? current.left : current.right;
|
|
2161
|
+
}
|
|
2162
|
+
};
|
|
2163
|
+
const treeHas = (node, key) => {
|
|
2164
|
+
let current = node;
|
|
2165
|
+
while (current) {
|
|
2166
|
+
if (key === current.key) return true;
|
|
2167
|
+
current = key < current.key ? current.left : current.right;
|
|
2168
|
+
}
|
|
2169
|
+
return false;
|
|
2170
|
+
};
|
|
2171
|
+
const treeFromSorted = (entries, start = 0, end = entries.length) => {
|
|
2172
|
+
if (start >= end) return void 0;
|
|
2173
|
+
const middle = start + end >> 1;
|
|
2174
|
+
const [key, value] = entries[middle];
|
|
2175
|
+
return makeTreeNode(key, value, treeFromSorted(entries, start, middle), treeFromSorted(entries, middle + 1, end));
|
|
2176
|
+
};
|
|
2177
|
+
const createCollection = (scheduler, spec) => {
|
|
2178
|
+
const values = /* @__PURE__ */ new Map();
|
|
2179
|
+
let ids = Object.freeze([]);
|
|
2180
|
+
let staged = /* @__PURE__ */ new Map();
|
|
2181
|
+
let nextIds = ids;
|
|
2182
|
+
let change;
|
|
2417
2183
|
let instance;
|
|
2418
|
-
let value;
|
|
2419
|
-
let next;
|
|
2420
|
-
let previous;
|
|
2421
2184
|
let initialized = false;
|
|
2422
|
-
let revision = 0;
|
|
2423
|
-
let changed = false;
|
|
2424
2185
|
let reset = false;
|
|
2425
|
-
let
|
|
2186
|
+
let revision = 0;
|
|
2187
|
+
let publishedRoot;
|
|
2426
2188
|
let cause;
|
|
2427
2189
|
const listeners = /* @__PURE__ */ new Set();
|
|
2190
|
+
const changedKeys = /* @__PURE__ */ new Set();
|
|
2191
|
+
let orderChanged = false;
|
|
2192
|
+
const readPublished = (root, publishedIds) => Object.freeze({
|
|
2193
|
+
get: (key) => {
|
|
2194
|
+
owner.check();
|
|
2195
|
+
return treeGet(root, key);
|
|
2196
|
+
},
|
|
2197
|
+
has: (key) => {
|
|
2198
|
+
owner.check();
|
|
2199
|
+
return treeHas(root, key);
|
|
2200
|
+
},
|
|
2201
|
+
ids: () => {
|
|
2202
|
+
owner.check();
|
|
2203
|
+
return publishedIds;
|
|
2204
|
+
}
|
|
2205
|
+
});
|
|
2206
|
+
const hasNext = (key) => staged.has(key) ? staged.get(key).present : values.has(key);
|
|
2207
|
+
const getNext = (key) => {
|
|
2208
|
+
const entry = staged.get(key);
|
|
2209
|
+
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
2210
|
+
};
|
|
2428
2211
|
const owner = createNode(scheduler, spec, {
|
|
2429
|
-
evaluate: (sources, build) => {
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2212
|
+
evaluate: (sources, build, active) => {
|
|
2213
|
+
cause = Object.values(sources).find((value) => value && typeof value === "object" && "cause" in value && value.cause !== void 0)?.cause ?? scheduler.batchContext()?.cause;
|
|
2214
|
+
staged = /* @__PURE__ */ new Map();
|
|
2215
|
+
nextIds = ids;
|
|
2216
|
+
change = void 0;
|
|
2217
|
+
changedKeys.clear();
|
|
2218
|
+
let explicitOrder;
|
|
2219
|
+
let cleared = false;
|
|
2220
|
+
const nextHas = (key) => staged.has(key) ? staged.get(key).present : !cleared && values.has(key);
|
|
2221
|
+
const nextGet = (key) => {
|
|
2222
|
+
const entry = staged.get(key);
|
|
2223
|
+
return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : values.get(key);
|
|
2224
|
+
};
|
|
2225
|
+
const deriveIds = () => {
|
|
2226
|
+
const result = cleared ? [] : ids.filter(nextHas);
|
|
2227
|
+
for (const [key, entry] of staged) if (entry.present && (cleared || !values.has(key))) result.push(key);
|
|
2228
|
+
return result;
|
|
2229
|
+
};
|
|
2230
|
+
const read = (next) => ({
|
|
2231
|
+
get: (key) => {
|
|
2232
|
+
assertScope(active);
|
|
2233
|
+
return next ? nextGet(key) : values.get(key);
|
|
2234
|
+
},
|
|
2235
|
+
has: (key) => {
|
|
2236
|
+
assertScope(active);
|
|
2237
|
+
return next ? nextHas(key) : values.has(key);
|
|
2238
|
+
},
|
|
2239
|
+
ids: () => {
|
|
2240
|
+
assertScope(active);
|
|
2241
|
+
return next ? Object.freeze(explicitOrder ? explicitOrder.slice() : deriveIds()) : ids;
|
|
2242
|
+
}
|
|
2243
|
+
});
|
|
2244
|
+
const input = {
|
|
2245
|
+
sources,
|
|
2246
|
+
previous: read(false),
|
|
2247
|
+
next: read(true),
|
|
2248
|
+
output: {
|
|
2249
|
+
set: (key, value) => {
|
|
2250
|
+
assertScope(active);
|
|
2251
|
+
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
2252
|
+
staged.set(key, {
|
|
2253
|
+
present: true,
|
|
2254
|
+
value
|
|
2255
|
+
});
|
|
2256
|
+
},
|
|
2257
|
+
remove: (key) => {
|
|
2258
|
+
assertScope(active);
|
|
2259
|
+
staged.set(key, { present: false });
|
|
2260
|
+
},
|
|
2261
|
+
order: (order) => {
|
|
2262
|
+
assertScope(active);
|
|
2263
|
+
explicitOrder = order.slice();
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
};
|
|
2434
2267
|
if (build || !instance) {
|
|
2435
2268
|
require_issue.profile.materialized.rebuilt();
|
|
2436
2269
|
instance = void 0;
|
|
2437
|
-
|
|
2270
|
+
cleared = true;
|
|
2271
|
+
const built = spec.build(input);
|
|
2438
2272
|
assertSynchronous(built);
|
|
2439
2273
|
instance = built;
|
|
2440
|
-
candidate = built.value;
|
|
2441
2274
|
} else {
|
|
2442
2275
|
require_issue.profile.materialized.updated();
|
|
2443
|
-
const result = instance.update(
|
|
2276
|
+
const result = instance.update(input);
|
|
2444
2277
|
assertSynchronous(result);
|
|
2445
|
-
if (result
|
|
2278
|
+
if (result?.kind === "rebuild") {
|
|
2279
|
+
staged.clear();
|
|
2280
|
+
cleared = true;
|
|
2281
|
+
explicitOrder = void 0;
|
|
2446
2282
|
instance = void 0;
|
|
2447
|
-
const built = spec.build(
|
|
2283
|
+
const built = spec.build(input);
|
|
2448
2284
|
assertSynchronous(built);
|
|
2449
2285
|
instance = built;
|
|
2450
|
-
candidate = built.value;
|
|
2451
2286
|
build = true;
|
|
2452
|
-
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
if (cleared) {
|
|
2290
|
+
for (const key of values.keys()) if (!staged.has(key)) staged.set(key, { present: false });
|
|
2291
|
+
}
|
|
2292
|
+
const added = /* @__PURE__ */ new Set();
|
|
2293
|
+
const removed = /* @__PURE__ */ new Set();
|
|
2294
|
+
const updated = /* @__PURE__ */ new Set();
|
|
2295
|
+
require_issue.profile.projection("touchedKeys", staged.size);
|
|
2296
|
+
for (const [key, entry] of staged) {
|
|
2297
|
+
const existed = values.has(key);
|
|
2298
|
+
if (entry.present) if (!existed) added.add(key);
|
|
2299
|
+
else if (!(spec.isEqual ?? Object.is)(values.get(key), entry.value)) updated.add(key);
|
|
2300
|
+
else staged.delete(key);
|
|
2301
|
+
else if (existed) removed.add(key);
|
|
2302
|
+
else staged.delete(key);
|
|
2303
|
+
}
|
|
2304
|
+
if (explicitOrder || added.size || removed.size) {
|
|
2305
|
+
const order = explicitOrder ?? [...ids.filter((key) => !removed.has(key)), ...added];
|
|
2306
|
+
if (new Set(order).size !== order.length || order.length !== values.size + added.size - removed.size || order.some((key) => !hasNext(key))) throw new TypeError("Projection order must contain every key exactly once.");
|
|
2307
|
+
nextIds = ids.length === order.length && ids.every((key, i) => key === order[i]) ? ids : Object.freeze(order.slice());
|
|
2308
|
+
}
|
|
2309
|
+
orderChanged = nextIds !== ids;
|
|
2310
|
+
[
|
|
2311
|
+
...added,
|
|
2312
|
+
...removed,
|
|
2313
|
+
...updated
|
|
2314
|
+
].forEach((key) => changedKeys.add(key));
|
|
2315
|
+
require_issue.profile.projection("changedKeys", changedKeys.size);
|
|
2316
|
+
const addedEntries = [];
|
|
2317
|
+
const updatedEntries = [];
|
|
2318
|
+
const removedEntries = [];
|
|
2319
|
+
for (const key of changedKeys) {
|
|
2320
|
+
const beforePresent = values.has(key);
|
|
2321
|
+
const afterPresent = hasNext(key);
|
|
2322
|
+
if (beforePresent === afterPresent) {
|
|
2323
|
+
if (!beforePresent) continue;
|
|
2324
|
+
if ((spec.isEqual ?? Object.is)(values.get(key), getNext(key))) continue;
|
|
2325
|
+
}
|
|
2326
|
+
if (!beforePresent) addedEntries.push({
|
|
2327
|
+
key,
|
|
2328
|
+
after: getNext(key)
|
|
2329
|
+
});
|
|
2330
|
+
else if (!afterPresent) removedEntries.push({
|
|
2331
|
+
key,
|
|
2332
|
+
before: values.get(key)
|
|
2333
|
+
});
|
|
2334
|
+
else updatedEntries.push({
|
|
2335
|
+
key,
|
|
2336
|
+
before: values.get(key),
|
|
2337
|
+
after: getNext(key)
|
|
2338
|
+
});
|
|
2339
|
+
}
|
|
2340
|
+
if (build) change = Object.freeze({ kind: "reset" });
|
|
2341
|
+
else if (addedEntries.length || updatedEntries.length || removedEntries.length || orderChanged) {
|
|
2342
|
+
let commonOrderChanged = false;
|
|
2343
|
+
if (orderChanged) {
|
|
2344
|
+
const before = ids.filter((key) => !removed.has(key));
|
|
2345
|
+
const after = nextIds.filter((key) => !added.has(key));
|
|
2346
|
+
commonOrderChanged = before.some((key, i) => after[i] !== key);
|
|
2347
|
+
}
|
|
2348
|
+
change = Object.freeze({
|
|
2349
|
+
kind: "incremental",
|
|
2350
|
+
added: Object.freeze(addedEntries),
|
|
2351
|
+
updated: Object.freeze(updatedEntries),
|
|
2352
|
+
removed: Object.freeze(removedEntries),
|
|
2353
|
+
...commonOrderChanged ? { order: Object.freeze({
|
|
2354
|
+
before: Object.freeze([...ids]),
|
|
2355
|
+
after: Object.freeze([...nextIds])
|
|
2356
|
+
}) } : {}
|
|
2357
|
+
});
|
|
2453
2358
|
}
|
|
2454
|
-
previous = value;
|
|
2455
|
-
changed = !initialized || !(options?.isEqual ?? Object.is)(value, candidate);
|
|
2456
|
-
next = changed ? candidate : value;
|
|
2457
2359
|
reset = build;
|
|
2458
|
-
return
|
|
2360
|
+
return change !== void 0;
|
|
2459
2361
|
},
|
|
2460
2362
|
context: (active) => {
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2363
|
+
const input = {
|
|
2364
|
+
kind: "collection",
|
|
2365
|
+
read: {
|
|
2366
|
+
get: (key) => {
|
|
2367
|
+
assertScope(active);
|
|
2368
|
+
return getNext(key);
|
|
2369
|
+
},
|
|
2370
|
+
has: (key) => {
|
|
2371
|
+
assertScope(active);
|
|
2372
|
+
return hasNext(key);
|
|
2373
|
+
},
|
|
2374
|
+
ids: () => {
|
|
2375
|
+
assertScope(active);
|
|
2376
|
+
return nextIds;
|
|
2377
|
+
}
|
|
2378
|
+
},
|
|
2379
|
+
change,
|
|
2380
|
+
revision: revision + (change ? 1 : 0),
|
|
2381
|
+
cause
|
|
2382
|
+
};
|
|
2383
|
+
return Object.freeze(input);
|
|
2471
2384
|
},
|
|
2472
2385
|
revision: () => revision,
|
|
2473
2386
|
reset: () => reset,
|
|
2474
2387
|
publish: () => {
|
|
2475
|
-
if (
|
|
2476
|
-
|
|
2388
|
+
if (change) if (change.kind === "reset") {
|
|
2389
|
+
const entries = [];
|
|
2390
|
+
for (const [key, entry] of staged) if (entry.present) entries.push([key, entry.value]);
|
|
2391
|
+
entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
2392
|
+
publishedRoot = treeFromSorted(entries);
|
|
2393
|
+
} else for (const [key, entry] of staged) publishedRoot = entry.present ? treeSet(publishedRoot, key, entry.value) : treeRemove(publishedRoot, key);
|
|
2394
|
+
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
2395
|
+
else values.delete(key);
|
|
2396
|
+
if (change) {
|
|
2397
|
+
if (initialized) revision++;
|
|
2398
|
+
}
|
|
2399
|
+
ids = nextIds;
|
|
2477
2400
|
initialized = true;
|
|
2478
2401
|
},
|
|
2479
2402
|
emit: (call) => {
|
|
2480
2403
|
require_issue.profile.materialized.notification();
|
|
2481
|
-
|
|
2404
|
+
const resetEvent = Object.freeze({ kind: "reset" });
|
|
2405
|
+
Array.from(listeners).forEach((listener) => call(() => listener(change ?? resetEvent)));
|
|
2482
2406
|
},
|
|
2483
2407
|
clear: () => {
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2408
|
+
staged.clear();
|
|
2409
|
+
nextIds = ids;
|
|
2410
|
+
change = void 0;
|
|
2487
2411
|
reset = false;
|
|
2488
|
-
|
|
2412
|
+
changedKeys.clear();
|
|
2489
2413
|
cause = void 0;
|
|
2414
|
+
orderChanged = false;
|
|
2490
2415
|
},
|
|
2491
2416
|
release: () => {
|
|
2417
|
+
values.clear();
|
|
2418
|
+
staged.clear();
|
|
2419
|
+
publishedRoot = void 0;
|
|
2492
2420
|
listeners.clear();
|
|
2421
|
+
ids = nextIds = Object.freeze([]);
|
|
2493
2422
|
instance = void 0;
|
|
2494
|
-
value = next = previous = void 0;
|
|
2495
2423
|
}
|
|
2496
2424
|
});
|
|
2497
|
-
const handle =
|
|
2425
|
+
const handle = {
|
|
2426
|
+
kind: "collection",
|
|
2498
2427
|
current: () => {
|
|
2499
2428
|
owner.check();
|
|
2500
|
-
return
|
|
2429
|
+
return readPublished(publishedRoot, ids);
|
|
2501
2430
|
},
|
|
2502
2431
|
revision: () => {
|
|
2503
2432
|
owner.check();
|
|
@@ -2509,36 +2438,17 @@ const createValue = (scheduler, spec, options) => {
|
|
|
2509
2438
|
return () => {
|
|
2510
2439
|
listeners.delete(listener);
|
|
2511
2440
|
};
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
|
-
dispose: owner.dispose
|
|
2515
|
-
});
|
|
2441
|
+
}
|
|
2442
|
+
};
|
|
2516
2443
|
owner.install(handle);
|
|
2517
|
-
return handle;
|
|
2444
|
+
return Object.freeze(handle);
|
|
2518
2445
|
};
|
|
2519
2446
|
//#endregion
|
|
2520
|
-
//#region core/src/projection/
|
|
2521
|
-
const
|
|
2447
|
+
//#region core/src/projection/graph.ts
|
|
2448
|
+
const createProjectionGraph = (options) => {
|
|
2522
2449
|
const scheduler = createScheduler(options.onError);
|
|
2523
2450
|
const sources = createSources(scheduler);
|
|
2524
|
-
|
|
2525
|
-
if (typeof computeOrOptions !== "function") return createValue(scheduler, input, computeOrOptions);
|
|
2526
|
-
const compute = (input) => {
|
|
2527
|
-
const result = computeOrOptions(input);
|
|
2528
|
-
assertSynchronous(result);
|
|
2529
|
-
return result;
|
|
2530
|
-
};
|
|
2531
|
-
return createValue(scheduler, {
|
|
2532
|
-
sources: input,
|
|
2533
|
-
build: (input) => ({
|
|
2534
|
-
value: compute(input),
|
|
2535
|
-
update: (input) => ({
|
|
2536
|
-
kind: "changed",
|
|
2537
|
-
value: compute(input)
|
|
2538
|
-
})
|
|
2539
|
-
})
|
|
2540
|
-
}, options);
|
|
2541
|
-
}
|
|
2451
|
+
const value = (spec, options) => createValue(scheduler, spec, options);
|
|
2542
2452
|
const map = (source, mapper, options) => {
|
|
2543
2453
|
sources.assertCollection(source);
|
|
2544
2454
|
const calculate = (id, entry) => {
|
|
@@ -2549,58 +2459,47 @@ const createRuntimeExecutor = (options) => {
|
|
|
2549
2459
|
return createCollection(scheduler, {
|
|
2550
2460
|
sources: { source },
|
|
2551
2461
|
isEqual: options?.isEqual,
|
|
2552
|
-
build: ({ sources,
|
|
2553
|
-
const
|
|
2554
|
-
const read = "read" in input ? input.read : input;
|
|
2462
|
+
build: ({ sources, output }) => {
|
|
2463
|
+
const read = sources.source.read;
|
|
2555
2464
|
const ids = read.ids();
|
|
2556
2465
|
require_issue.profile.collectionView.idsScanned(ids.length);
|
|
2557
2466
|
for (const id of ids) {
|
|
2558
2467
|
require_issue.profile.collectionView.mapped();
|
|
2559
|
-
|
|
2468
|
+
output.set(id, calculate(id, read.get(id)));
|
|
2560
2469
|
}
|
|
2561
|
-
|
|
2562
|
-
return { update: ({ sources,
|
|
2470
|
+
output.order(ids);
|
|
2471
|
+
return { update: ({ sources, output }) => {
|
|
2563
2472
|
const input = sources.source;
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
} else {
|
|
2572
|
-
const change = input.change;
|
|
2573
|
-
if (!change) return;
|
|
2574
|
-
if (change.kind === "reset") return { kind: "rebuild" };
|
|
2575
|
-
keys = new Set([
|
|
2576
|
-
...change.added,
|
|
2577
|
-
...change.removed,
|
|
2578
|
-
...change.updated
|
|
2579
|
-
]);
|
|
2580
|
-
structural = Boolean(change.added.size || change.removed.size || change.orderChanged);
|
|
2473
|
+
const read = input.read;
|
|
2474
|
+
if (input.change?.kind === "reset") return { kind: "rebuild" };
|
|
2475
|
+
const change = input.change;
|
|
2476
|
+
if (!change) return;
|
|
2477
|
+
for (const entry of change.added) {
|
|
2478
|
+
require_issue.profile.collectionView.mapped();
|
|
2479
|
+
output.set(entry.key, calculate(entry.key, entry.after));
|
|
2581
2480
|
}
|
|
2582
|
-
for (const
|
|
2583
|
-
else {
|
|
2481
|
+
for (const entry of change.updated) {
|
|
2584
2482
|
require_issue.profile.collectionView.mapped();
|
|
2585
|
-
|
|
2483
|
+
output.set(entry.key, calculate(entry.key, entry.after));
|
|
2586
2484
|
}
|
|
2587
|
-
|
|
2485
|
+
for (const entry of change.removed) output.remove(entry.key);
|
|
2486
|
+
if (change.added.length || change.removed.length || change.order) {
|
|
2588
2487
|
const ids = read.ids();
|
|
2589
2488
|
require_issue.profile.collectionView.idsScanned(ids.length);
|
|
2590
|
-
|
|
2489
|
+
output.order(ids);
|
|
2591
2490
|
}
|
|
2592
2491
|
} };
|
|
2593
2492
|
}
|
|
2594
2493
|
});
|
|
2595
2494
|
};
|
|
2596
|
-
const
|
|
2495
|
+
const graph = {
|
|
2597
2496
|
document: sources.document,
|
|
2598
2497
|
fromSource: (source, options) => sources.fromSource(source, options),
|
|
2599
2498
|
fromCollectionSource: (source) => sources.fromCollectionSource(source),
|
|
2600
2499
|
input: sources.input,
|
|
2601
2500
|
fromReadable: sources.fromReadable,
|
|
2602
2501
|
value,
|
|
2603
|
-
collection: (
|
|
2502
|
+
collection: (spec) => createCollection(scheduler, spec),
|
|
2604
2503
|
map,
|
|
2605
2504
|
batch: scheduler.batch,
|
|
2606
2505
|
dispose: () => {
|
|
@@ -2611,10 +2510,10 @@ const createRuntimeExecutor = (options) => {
|
|
|
2611
2510
|
}
|
|
2612
2511
|
}
|
|
2613
2512
|
};
|
|
2614
|
-
return Object.freeze(
|
|
2513
|
+
return Object.freeze(graph);
|
|
2615
2514
|
};
|
|
2616
2515
|
//#endregion
|
|
2617
|
-
//#region core/src/projection/
|
|
2516
|
+
//#region core/src/projection/runtime.ts
|
|
2618
2517
|
let activeTracker;
|
|
2619
2518
|
const trackKey = (key) => {
|
|
2620
2519
|
activeTracker?.keys.add(key);
|
|
@@ -2667,6 +2566,50 @@ const mapView = (read) => {
|
|
|
2667
2566
|
};
|
|
2668
2567
|
return Object.freeze(view);
|
|
2669
2568
|
};
|
|
2569
|
+
const lazyMapView = (read) => {
|
|
2570
|
+
const ids = read.ids();
|
|
2571
|
+
const entries = function* () {
|
|
2572
|
+
for (const key of ids) yield [key, read.get(key)];
|
|
2573
|
+
};
|
|
2574
|
+
const values = function* () {
|
|
2575
|
+
for (const key of ids) yield read.get(key);
|
|
2576
|
+
};
|
|
2577
|
+
const view = {
|
|
2578
|
+
get: (key) => {
|
|
2579
|
+
trackKey(key);
|
|
2580
|
+
return read.get(key);
|
|
2581
|
+
},
|
|
2582
|
+
has: (key) => {
|
|
2583
|
+
trackKey(key);
|
|
2584
|
+
return read.has(key);
|
|
2585
|
+
},
|
|
2586
|
+
get size() {
|
|
2587
|
+
trackStructure();
|
|
2588
|
+
return ids.length;
|
|
2589
|
+
},
|
|
2590
|
+
keys: () => {
|
|
2591
|
+
trackStructure();
|
|
2592
|
+
return ids[Symbol.iterator]();
|
|
2593
|
+
},
|
|
2594
|
+
values: () => {
|
|
2595
|
+
trackAll();
|
|
2596
|
+
return values();
|
|
2597
|
+
},
|
|
2598
|
+
entries: () => {
|
|
2599
|
+
trackAll();
|
|
2600
|
+
return entries();
|
|
2601
|
+
},
|
|
2602
|
+
forEach: (callback, thisArg) => {
|
|
2603
|
+
trackAll();
|
|
2604
|
+
for (const key of ids) callback.call(thisArg, read.get(key), key, view);
|
|
2605
|
+
},
|
|
2606
|
+
[Symbol.iterator]: () => {
|
|
2607
|
+
trackAll();
|
|
2608
|
+
return entries();
|
|
2609
|
+
}
|
|
2610
|
+
};
|
|
2611
|
+
return Object.freeze(view);
|
|
2612
|
+
};
|
|
2670
2613
|
const isMapLike = (value) => value instanceof Map || value !== null && typeof value === "object" && typeof value.get === "function" && typeof value.keys === "function" && typeof value.entries === "function";
|
|
2671
2614
|
const mapRead = (value) => ({
|
|
2672
2615
|
get: (key) => value.get(key),
|
|
@@ -2674,22 +2617,35 @@ const mapRead = (value) => ({
|
|
|
2674
2617
|
ids: () => Object.freeze([...value.keys()])
|
|
2675
2618
|
});
|
|
2676
2619
|
const mapChange = (previous, current) => {
|
|
2677
|
-
const added =
|
|
2678
|
-
const removed =
|
|
2679
|
-
const updated =
|
|
2680
|
-
for (const key of previous.keys()) if (!current.has(key)) removed.
|
|
2681
|
-
|
|
2682
|
-
|
|
2620
|
+
const added = [];
|
|
2621
|
+
const removed = [];
|
|
2622
|
+
const updated = [];
|
|
2623
|
+
for (const key of previous.keys()) if (!current.has(key)) removed.push({
|
|
2624
|
+
key,
|
|
2625
|
+
before: previous.get(key)
|
|
2626
|
+
});
|
|
2627
|
+
else if (!Object.is(previous.get(key), current.get(key))) updated.push({
|
|
2628
|
+
key,
|
|
2629
|
+
before: previous.get(key),
|
|
2630
|
+
after: current.get(key)
|
|
2631
|
+
});
|
|
2632
|
+
for (const key of current.keys()) if (!previous.has(key)) added.push({
|
|
2633
|
+
key,
|
|
2634
|
+
after: current.get(key)
|
|
2635
|
+
});
|
|
2683
2636
|
const before = [...previous.keys()].filter((key) => current.has(key));
|
|
2684
2637
|
const after = [...current.keys()].filter((key) => previous.has(key));
|
|
2685
2638
|
const orderChanged = before.length !== after.length || before.some((key, index) => key !== after[index]);
|
|
2686
|
-
if (!added.
|
|
2639
|
+
if (!added.length && !removed.length && !updated.length && !orderChanged) return void 0;
|
|
2687
2640
|
return {
|
|
2688
2641
|
kind: "incremental",
|
|
2689
|
-
added,
|
|
2690
|
-
removed,
|
|
2691
|
-
updated,
|
|
2692
|
-
orderChanged
|
|
2642
|
+
added: Object.freeze(added),
|
|
2643
|
+
removed: Object.freeze(removed),
|
|
2644
|
+
updated: Object.freeze(updated),
|
|
2645
|
+
...orderChanged ? { order: Object.freeze({
|
|
2646
|
+
before: Object.freeze([...previous.keys()]),
|
|
2647
|
+
after: Object.freeze([...current.keys()])
|
|
2648
|
+
}) } : {}
|
|
2693
2649
|
};
|
|
2694
2650
|
};
|
|
2695
2651
|
const sameSelection = (left, right) => {
|
|
@@ -2699,45 +2655,71 @@ const sameSelection = (left, right) => {
|
|
|
2699
2655
|
};
|
|
2700
2656
|
const selectionAffects = (selection, change) => {
|
|
2701
2657
|
if (!change || change.kind === "reset") return true;
|
|
2702
|
-
if (selection.all && (change.added.
|
|
2703
|
-
if (selection.structure && (change.added.
|
|
2704
|
-
for (const key of selection.keys) if (change.added.
|
|
2658
|
+
if (selection.all && (change.added.length || change.removed.length || change.updated.length || change.order)) return true;
|
|
2659
|
+
if (selection.structure && (change.added.length || change.removed.length || change.order)) return true;
|
|
2660
|
+
for (const key of selection.keys) if (change.added.some((entry) => entry.key === key) || change.removed.some((entry) => entry.key === key) || change.updated.some((entry) => entry.key === key)) return true;
|
|
2705
2661
|
return false;
|
|
2706
2662
|
};
|
|
2707
2663
|
const currentValue = (event) => {
|
|
2708
2664
|
if (!event || typeof event !== "object") return event;
|
|
2709
|
-
if ("
|
|
2710
|
-
if ("
|
|
2711
|
-
if ("
|
|
2665
|
+
if ("kind" in event && event.kind === "value") return event.value;
|
|
2666
|
+
if ("kind" in event && event.kind === "document") return require_scope.snapshot(event.read);
|
|
2667
|
+
if ("kind" in event && event.kind === "collection") return mapView(event.read);
|
|
2712
2668
|
return event;
|
|
2713
2669
|
};
|
|
2714
2670
|
const sourceOf = (instance) => instance;
|
|
2715
2671
|
const createProjectionRuntime = (options) => {
|
|
2716
|
-
const runtime =
|
|
2717
|
-
const
|
|
2718
|
-
const inputs = /* @__PURE__ */ new WeakMap();
|
|
2719
|
-
const collections = /* @__PURE__ */ new WeakMap();
|
|
2672
|
+
const runtime = createProjectionGraph({ onError: options?.onError ?? (() => void 0) });
|
|
2673
|
+
const materialized = /* @__PURE__ */ new WeakMap();
|
|
2720
2674
|
const materialize = (projection) => {
|
|
2721
|
-
const old =
|
|
2722
|
-
if (old) return old;
|
|
2675
|
+
const old = materialized.get(projection);
|
|
2676
|
+
if (old) return old.node;
|
|
2723
2677
|
const definition = require_definition.definitionOf(projection);
|
|
2724
2678
|
let instance;
|
|
2679
|
+
let setter;
|
|
2725
2680
|
switch (definition.kind) {
|
|
2726
2681
|
case "input": {
|
|
2727
2682
|
const input = runtime.input(definition.initial, { isEqual: definition.isEqual });
|
|
2728
|
-
|
|
2729
|
-
instance = runtime.value({
|
|
2683
|
+
setter = input.set;
|
|
2684
|
+
instance = runtime.value({
|
|
2685
|
+
sources: { input: input.source },
|
|
2686
|
+
build: (values) => ({
|
|
2687
|
+
value: values.input.value,
|
|
2688
|
+
update: (next) => ({
|
|
2689
|
+
kind: "changed",
|
|
2690
|
+
value: next.input.value
|
|
2691
|
+
})
|
|
2692
|
+
})
|
|
2693
|
+
});
|
|
2730
2694
|
break;
|
|
2731
2695
|
}
|
|
2732
2696
|
case "readable": {
|
|
2733
2697
|
const source = runtime.fromReadable(definition.readable, { isEqual: definition.isEqual });
|
|
2734
|
-
instance = runtime.value({
|
|
2698
|
+
instance = runtime.value({
|
|
2699
|
+
sources: { source },
|
|
2700
|
+
build: (values) => ({
|
|
2701
|
+
value: values.source.value,
|
|
2702
|
+
update: (next) => ({
|
|
2703
|
+
kind: "changed",
|
|
2704
|
+
value: next.source.value
|
|
2705
|
+
})
|
|
2706
|
+
})
|
|
2707
|
+
});
|
|
2735
2708
|
break;
|
|
2736
2709
|
}
|
|
2737
2710
|
case "source-value":
|
|
2738
2711
|
{
|
|
2739
2712
|
const source = runtime.fromSource(definition.source);
|
|
2740
|
-
instance = runtime.value({
|
|
2713
|
+
instance = runtime.value({
|
|
2714
|
+
sources: { source },
|
|
2715
|
+
build: (values) => ({
|
|
2716
|
+
value: values.source.value,
|
|
2717
|
+
update: (next) => ({
|
|
2718
|
+
kind: "changed",
|
|
2719
|
+
value: next.source.value
|
|
2720
|
+
})
|
|
2721
|
+
})
|
|
2722
|
+
});
|
|
2741
2723
|
}
|
|
2742
2724
|
break;
|
|
2743
2725
|
case "source-collection": {
|
|
@@ -2748,26 +2730,51 @@ const createProjectionRuntime = (options) => {
|
|
|
2748
2730
|
case "document": {
|
|
2749
2731
|
const document = runtime.document(definition.document);
|
|
2750
2732
|
if (!definition.selector) {
|
|
2751
|
-
instance = runtime.value({
|
|
2733
|
+
instance = runtime.value({
|
|
2734
|
+
sources: { document },
|
|
2735
|
+
build: (values) => ({
|
|
2736
|
+
value: require_scope.snapshot(values.document.read),
|
|
2737
|
+
update: (next) => ({
|
|
2738
|
+
kind: "changed",
|
|
2739
|
+
value: require_scope.snapshot(next.document.read)
|
|
2740
|
+
})
|
|
2741
|
+
})
|
|
2742
|
+
});
|
|
2752
2743
|
break;
|
|
2753
2744
|
}
|
|
2754
2745
|
const state = require_notification.accessOf(definition.document);
|
|
2755
2746
|
try {
|
|
2756
2747
|
const collection = document.collection(definition.selector);
|
|
2757
|
-
instance = runtime.map(collection, (_id, entry) =>
|
|
2748
|
+
instance = runtime.map(collection, (_id, entry) => entry);
|
|
2758
2749
|
break;
|
|
2759
2750
|
} catch {
|
|
2760
2751
|
const selected = document.targets(definition.selector);
|
|
2761
2752
|
const address = compilePath(state.schema, "value", definition.selector).address;
|
|
2762
|
-
instance = runtime.value({
|
|
2763
|
-
|
|
2753
|
+
instance = runtime.value({
|
|
2754
|
+
sources: { selected },
|
|
2755
|
+
build: () => ({
|
|
2756
|
+
value: require_issue.read(require_scope.snapshot(state.document), address, state.schema),
|
|
2757
|
+
update: () => ({
|
|
2758
|
+
kind: "changed",
|
|
2759
|
+
value: require_issue.read(require_scope.snapshot(state.document), address, state.schema)
|
|
2760
|
+
})
|
|
2761
|
+
})
|
|
2764
2762
|
});
|
|
2765
2763
|
}
|
|
2766
2764
|
break;
|
|
2767
2765
|
}
|
|
2768
2766
|
case "derive": {
|
|
2769
2767
|
const sources = Object.fromEntries(definition.dependencies.map((dependency, index) => [`d${index}`, sourceOf(materialize(dependency))]));
|
|
2770
|
-
instance = runtime.value(
|
|
2768
|
+
instance = runtime.value({
|
|
2769
|
+
sources,
|
|
2770
|
+
build: (values) => ({
|
|
2771
|
+
value: definition.compute(...Object.values(values).map(currentValue)),
|
|
2772
|
+
update: (next) => ({
|
|
2773
|
+
kind: "changed",
|
|
2774
|
+
value: definition.compute(...Object.values(next).map(currentValue))
|
|
2775
|
+
})
|
|
2776
|
+
})
|
|
2777
|
+
}, { isEqual: definition.isEqual });
|
|
2771
2778
|
break;
|
|
2772
2779
|
}
|
|
2773
2780
|
case "incremental-value": {
|
|
@@ -2780,7 +2787,7 @@ const createProjectionRuntime = (options) => {
|
|
|
2780
2787
|
}
|
|
2781
2788
|
case "incremental-collection": {
|
|
2782
2789
|
const sources = Object.fromEntries(Object.entries(definition.dependencies).map(([key, dependency]) => [key, sourceOf(materialize(dependency))]));
|
|
2783
|
-
instance = runtime.collection(
|
|
2790
|
+
instance = runtime.collection({
|
|
2784
2791
|
sources,
|
|
2785
2792
|
build: definition.build,
|
|
2786
2793
|
isEqual: definition.isEqual,
|
|
@@ -2789,32 +2796,37 @@ const createProjectionRuntime = (options) => {
|
|
|
2789
2796
|
break;
|
|
2790
2797
|
}
|
|
2791
2798
|
}
|
|
2792
|
-
|
|
2799
|
+
materialized.set(projection, {
|
|
2800
|
+
node: instance,
|
|
2801
|
+
set: setter
|
|
2802
|
+
});
|
|
2793
2803
|
return instance;
|
|
2794
2804
|
};
|
|
2795
2805
|
const get = (projection) => {
|
|
2796
2806
|
const instance = materialize(projection);
|
|
2797
2807
|
const current = instance.current();
|
|
2798
|
-
if (
|
|
2799
|
-
const
|
|
2808
|
+
if (instance.kind === "collection") {
|
|
2809
|
+
const record = materialized.get(projection) ?? materialized.get(instance);
|
|
2810
|
+
const existing = record?.collection;
|
|
2800
2811
|
const revision = instance.revision();
|
|
2801
2812
|
if (existing?.revision === revision) return existing.value;
|
|
2802
|
-
const value =
|
|
2803
|
-
|
|
2813
|
+
const value = lazyMapView(current);
|
|
2814
|
+
if (record) record.collection = {
|
|
2804
2815
|
revision,
|
|
2805
2816
|
value
|
|
2806
|
-
}
|
|
2817
|
+
};
|
|
2807
2818
|
return value;
|
|
2808
2819
|
}
|
|
2809
2820
|
if (isMapLike(current)) {
|
|
2810
|
-
const
|
|
2821
|
+
const record = materialized.get(projection) ?? materialized.get(instance);
|
|
2822
|
+
const existing = record?.collection;
|
|
2811
2823
|
const revision = instance.revision();
|
|
2812
2824
|
if (existing?.revision === revision) return existing.value;
|
|
2813
2825
|
const value = mapView(mapRead(current));
|
|
2814
|
-
|
|
2826
|
+
if (record) record.collection = {
|
|
2815
2827
|
revision,
|
|
2816
2828
|
value
|
|
2817
|
-
}
|
|
2829
|
+
};
|
|
2818
2830
|
return value;
|
|
2819
2831
|
}
|
|
2820
2832
|
return current;
|
|
@@ -2932,8 +2944,8 @@ const createProjectionRuntime = (options) => {
|
|
|
2932
2944
|
readable,
|
|
2933
2945
|
set: ((input, value) => {
|
|
2934
2946
|
materialize(input);
|
|
2935
|
-
const target =
|
|
2936
|
-
if (!target) throw new TypeError("Projection is not an input.");
|
|
2947
|
+
const target = materialized.get(input);
|
|
2948
|
+
if (!target?.set) throw new TypeError("Projection is not an input.");
|
|
2937
2949
|
target.set(value);
|
|
2938
2950
|
}),
|
|
2939
2951
|
batch: runtime.batch,
|