doxum 0.1.19 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -11
- package/dist/{scope-d-L87NeC.js → access-BeSV5ve-.js} +114 -20
- package/dist/access-BeSV5ve-.js.map +1 -0
- package/dist/{scope-Ezs-Y3TY.cjs → access-Bienf28h.cjs} +149 -19
- package/dist/access-Bienf28h.cjs.map +1 -0
- package/dist/advanced-BRL5pN0y.cjs +1361 -0
- package/dist/advanced-BRL5pN0y.cjs.map +1 -0
- package/dist/advanced-COFJtWMT.js +1188 -0
- package/dist/advanced-COFJtWMT.js.map +1 -0
- package/dist/{advanced-C2fpjbK3.d.ts → advanced-DfKc7TZR.d.cts} +27 -22
- package/dist/{advanced-DlOYk1vi.d.cts → advanced-FgdTwu5b.d.ts} +27 -22
- package/dist/advanced.cjs +1 -1
- package/dist/advanced.d.cts +2 -2
- package/dist/advanced.d.ts +2 -2
- package/dist/advanced.js +1 -1
- package/dist/{contract-XmKnroJq.d.cts → contract-CdmDHRUq.d.cts} +8 -6
- package/dist/{contract-CATCCVu5.d.ts → contract-KG5XnQZt.d.ts} +8 -6
- package/dist/{driver-CSnhMjbP.js → driver-CLYJOGDQ.js} +2 -2
- package/dist/{driver-CSnhMjbP.js.map → driver-CLYJOGDQ.js.map} +1 -1
- package/dist/{driver-BCyfqxsl.cjs → driver-Djr3BVvo.cjs} +2 -2
- package/dist/{driver-BCyfqxsl.cjs.map → driver-Djr3BVvo.cjs.map} +1 -1
- package/dist/index.cjs +1150 -1993
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1105 -1948
- package/dist/index.js.map +1 -1
- package/dist/{integration-CxVhq-KD.js → integration-C4Isp8lJ.js} +3 -2
- package/dist/{integration-CxVhq-KD.js.map → integration-C4Isp8lJ.js.map} +1 -1
- package/dist/{integration-B7VDgAOJ.cjs → integration-Z6MsfFI7.cjs} +4 -3
- package/dist/{integration-B7VDgAOJ.cjs.map → integration-Z6MsfFI7.cjs.map} +1 -1
- package/dist/integration.cjs +3 -3
- package/dist/integration.d.cts +1 -1
- package/dist/integration.d.ts +1 -1
- package/dist/integration.js +3 -3
- package/dist/{issue-DhrNdQNg.cjs → issue-DjK1xp5K.cjs} +106 -22
- package/dist/issue-DjK1xp5K.cjs.map +1 -0
- package/dist/{issue-DVaGQGeP.js → issue-W0hT8u8D.js} +81 -15
- package/dist/issue-W0hT8u8D.js.map +1 -0
- package/dist/local-sync.cjs +2 -2
- package/dist/local-sync.d.cts +1 -1
- package/dist/local-sync.d.ts +1 -1
- package/dist/local-sync.js +2 -2
- package/dist/{notification-IQIJyH3Q.js → notification-Bkjau6B6.js} +3 -56
- package/dist/notification-Bkjau6B6.js.map +1 -0
- package/dist/{notification-CDSUfiip.cjs → notification-DJfouMEb.cjs} +2 -91
- package/dist/notification-DJfouMEb.cjs.map +1 -0
- package/dist/react.cjs +2 -2
- package/dist/react.js +2 -2
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +4 -1
- package/skills/doxum-runtime/references/guide.en.md +5 -2
- package/skills/doxum-runtime/references/guide.zh-CN.md +5 -2
- package/skills/doxum-runtime/references/projections.en.md +23 -9
- package/skills/doxum-runtime/references/projections.zh-CN.md +18 -6
- package/dist/advanced-BYZCjNWL.js +0 -680
- package/dist/advanced-BYZCjNWL.js.map +0 -1
- package/dist/advanced-CMMNk49G.cjs +0 -763
- package/dist/advanced-CMMNk49G.cjs.map +0 -1
- package/dist/issue-DVaGQGeP.js.map +0 -1
- package/dist/issue-DhrNdQNg.cjs.map +0 -1
- package/dist/notification-CDSUfiip.cjs.map +0 -1
- package/dist/notification-IQIJyH3Q.js.map +0 -1
- package/dist/scope-Ezs-Y3TY.cjs.map +0 -1
- package/dist/scope-d-L87NeC.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,96 +1,8 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { a as
|
|
3
|
-
import { _ as
|
|
4
|
-
import { n as
|
|
5
|
-
import {
|
|
6
|
-
//#region core/src/schema.ts
|
|
7
|
-
const node = (value) => Object.freeze(value);
|
|
8
|
-
const field = (validator) => node({
|
|
9
|
-
kind: "field",
|
|
10
|
-
...validator ? { validator } : {}
|
|
11
|
-
});
|
|
12
|
-
const optional = (value) => {
|
|
13
|
-
if (![
|
|
14
|
-
"field",
|
|
15
|
-
"variant",
|
|
16
|
-
"map",
|
|
17
|
-
"list",
|
|
18
|
-
"tree"
|
|
19
|
-
].includes(value.kind)) throw new TypeError("Only field, variant, map, list and tree nodes support optional presence.");
|
|
20
|
-
return node({
|
|
21
|
-
...value,
|
|
22
|
-
optional: true
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
const object = (shape) => node({
|
|
26
|
-
kind: "object",
|
|
27
|
-
shape: Object.freeze({ ...shape })
|
|
28
|
-
});
|
|
29
|
-
const variant = (tag, variants) => node({
|
|
30
|
-
kind: "variant",
|
|
31
|
-
tag,
|
|
32
|
-
variants: Object.freeze({ ...variants })
|
|
33
|
-
});
|
|
34
|
-
const table = (value, options) => node({
|
|
35
|
-
kind: "table",
|
|
36
|
-
value,
|
|
37
|
-
...options
|
|
38
|
-
});
|
|
39
|
-
const map = (value, options) => node({
|
|
40
|
-
kind: "map",
|
|
41
|
-
value,
|
|
42
|
-
...options
|
|
43
|
-
});
|
|
44
|
-
const list = (value, config) => node({
|
|
45
|
-
kind: "list",
|
|
46
|
-
keyOf: config.keyOf,
|
|
47
|
-
value
|
|
48
|
-
});
|
|
49
|
-
const tree = (value) => node({
|
|
50
|
-
kind: "tree",
|
|
51
|
-
value
|
|
52
|
-
});
|
|
53
|
-
const paths = /* @__PURE__ */ new WeakMap();
|
|
54
|
-
const pathProxy = (nodes, address, owner) => {
|
|
55
|
-
const proxy = new Proxy({}, { get: (_target, property) => {
|
|
56
|
-
if (typeof property !== "string") return void 0;
|
|
57
|
-
if (nodes.every((node) => node.kind === "table" || node.kind === "map") && property === "item") return (id) => {
|
|
58
|
-
if (typeof id !== "string") throw new TypeError("Collection keys must be strings.");
|
|
59
|
-
for (const node of nodes) if (node.kind === "table" || node.kind === "map") {
|
|
60
|
-
const invalid = checkKey(node.key, id, [...address, id]);
|
|
61
|
-
if (invalid) throw new TypeError(invalid.message);
|
|
62
|
-
}
|
|
63
|
-
return pathProxy(nodes.map((node) => node.value), [...address, id], owner);
|
|
64
|
-
};
|
|
65
|
-
const children = nodes.flatMap((node) => {
|
|
66
|
-
if (node.kind === "object") return Object.hasOwn(node.shape, property) ? [node.shape[property]] : [];
|
|
67
|
-
if (node.kind !== "variant") return [];
|
|
68
|
-
if (property === node.tag) return [field()];
|
|
69
|
-
return Object.values(node.variants).flatMap((branch) => Object.hasOwn(branch.shape, property) ? [branch.shape[property]] : []);
|
|
70
|
-
});
|
|
71
|
-
if (!children.length) throw new TypeError(`Invalid schema path segment: ${property}`);
|
|
72
|
-
return pathProxy(children, [...address, property], owner);
|
|
73
|
-
} });
|
|
74
|
-
paths.set(proxy, {
|
|
75
|
-
nodes,
|
|
76
|
-
address,
|
|
77
|
-
owner
|
|
78
|
-
});
|
|
79
|
-
return proxy;
|
|
80
|
-
};
|
|
81
|
-
const compilePath = (owner, kind, pick) => {
|
|
82
|
-
const scope = {};
|
|
83
|
-
const value = pick(pathProxy([owner], [], scope));
|
|
84
|
-
const selected = typeof value === "object" && value !== null ? paths.get(value) : void 0;
|
|
85
|
-
if (!selected || selected.owner !== scope) throw new TypeError("Selector must return a path from its callback.");
|
|
86
|
-
if (kind === "collection" && !selected.nodes.every((node) => node.kind === "table" || node.kind === "map")) throw new TypeError("Collection selectors require a table or map path.");
|
|
87
|
-
return Object.freeze({
|
|
88
|
-
kind,
|
|
89
|
-
schema: owner,
|
|
90
|
-
address: Object.freeze([...selected.address])
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
//#endregion
|
|
1
|
+
import { A as install, B as profile, E as equal, F as matches, L as remove, O as indexedKeys, R as toArray, _ as resolveTreeContainer, a as compiledShape, c as debugKey, d as overlaps, f as read$1, g as resolveLocated, h as resolveContainer, i as AddressIndex, k as insert, l as memberKey, n as fail, o as contains, p as resolveAddress, r as invalidValue, s as createAddressResolver, t as MutationRejected, u as nodeAt, v as resolveValue } from "./issue-W0hT8u8D.js";
|
|
2
|
+
import { a as DocumentReentrancyError, c as createAccess, d as ParseError, f as checkKey, g as parse, h as equalValue, i as DocumentDisposedError, l as replace, m as copyValue, n as bindRuntimeAccess, o as TransactionRejected, p as checkValue, r as readWith, s as collectionAccess, t as accessOf, u as snapshot } from "./access-BeSV5ve-.js";
|
|
3
|
+
import { C as list, D as table, E as optional, O as tree, S as field, T as object, _ as createScheduler, a as observe, b as collectionEntryNode, c as projectionRef, d as createCollectionOutput, f as mapRead, g as assertSynchronous, h as assertScope, i as isProjection, k as variant, l as collectionChangeBetween, m as stageCollectionRead, n as derive, o as ownProjection, p as snapshotCollectionView, r as input, s as producerOf, t as incremental, u as collectionView, v as ProjectionDisposedError, w as map, x as compilePath, y as ProjectionError } from "./advanced-COFJtWMT.js";
|
|
4
|
+
import { a as documentReadableOwner, d as affected, f as belongs, i as disposeNotification, l as subscribeRoot, n as bindDocumentReadable, o as notify, r as createNotification, s as shareNotification, t as attachProjection, u as subscribeTargets } from "./notification-Bkjau6B6.js";
|
|
5
|
+
import { n as bindRuntimeDriver, o as decodeChanges, r as disposeRuntimeDriver, s as sealChanges, t as assertRuntimeWritable } from "./driver-CLYJOGDQ.js";
|
|
94
6
|
//#region core/src/impact.ts
|
|
95
7
|
const queries = /* @__PURE__ */ new WeakMap();
|
|
96
8
|
const affectsTarget = (impact, value) => queries.get(impact).affects(value);
|
|
@@ -372,7 +284,7 @@ const installMember = (parent, key, present, value) => {
|
|
|
372
284
|
});
|
|
373
285
|
};
|
|
374
286
|
const orderOf = (node, value) => {
|
|
375
|
-
if (node.kind === "list") return value
|
|
287
|
+
if (node.kind === "list") return toArray(indexedKeys(value, node.keyOf));
|
|
376
288
|
if (node.kind === "table") return [...value.ids];
|
|
377
289
|
throw new Error("Order requires a table or list.");
|
|
378
290
|
};
|
|
@@ -514,7 +426,7 @@ const sealMembers = (fact, changes) => {
|
|
|
514
426
|
}
|
|
515
427
|
let order;
|
|
516
428
|
if (fact.order) {
|
|
517
|
-
const current = fact.node.kind === "list" ?
|
|
429
|
+
const current = fact.node.kind === "list" ? indexedKeys(fact.value, fact.node.keyOf) : fact.value.ids;
|
|
518
430
|
if (!equal(fact.order, current)) {
|
|
519
431
|
const after = orderOf(fact.node, fact.value);
|
|
520
432
|
profile.recorder("publishedOrderItems", after.length);
|
|
@@ -1103,1945 +1015,1219 @@ const asReadable = (runtime) => {
|
|
|
1103
1015
|
//#region core/src/projection/select.ts
|
|
1104
1016
|
const read = (runtime, selector) => readWith(runtime, selector);
|
|
1105
1017
|
//#endregion
|
|
1106
|
-
//#region core/src/projection/
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
let reset = false;
|
|
1130
|
-
let orderChanged = false;
|
|
1131
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1132
|
-
let baseline;
|
|
1133
|
-
let baselineIds = Object.freeze([]);
|
|
1134
|
-
let nextBaseline;
|
|
1135
|
-
let nextBaselineIds = Object.freeze([]);
|
|
1136
|
-
const collectionNode = collection ? nodeAt(state.schema, collection.address, state.document) : void 0;
|
|
1137
|
-
const entryNode = collectionNode && (collectionNode.kind === "map" || collectionNode.kind === "table") ? collectionNode.value : void 0;
|
|
1138
|
-
const sameEntry = (left, right) => entryNode ? equalValue(entryNode, left, right) : Object.is(left, right);
|
|
1139
|
-
const record = {
|
|
1140
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
1141
|
-
disposed: false,
|
|
1142
|
-
fault: void 0,
|
|
1143
|
-
revision: runtime.revision,
|
|
1144
|
-
reset: () => reset,
|
|
1145
|
-
clear: () => {
|
|
1146
|
-
if (collection && nextBaseline) {
|
|
1147
|
-
baseline = nextBaseline;
|
|
1148
|
-
baselineIds = nextBaselineIds;
|
|
1149
|
-
}
|
|
1150
|
-
reset = false;
|
|
1151
|
-
orderChanged = false;
|
|
1152
|
-
batch = void 0;
|
|
1153
|
-
keys.clear();
|
|
1154
|
-
nextBaseline = void 0;
|
|
1155
|
-
nextBaselineIds = Object.freeze([]);
|
|
1156
|
-
},
|
|
1157
|
-
context: (active) => {
|
|
1158
|
-
assertScope(active);
|
|
1159
|
-
if (state.disposed) throw new Error("Document has been disposed.");
|
|
1160
|
-
const context = {
|
|
1161
|
-
state,
|
|
1162
|
-
active
|
|
1163
|
-
};
|
|
1164
|
-
const rawRead = collection ? collectionAccess(context, collection.address) : void 0;
|
|
1165
|
-
const snapshots = collection ? /* @__PURE__ */ new Map() : void 0;
|
|
1166
|
-
const read = collection ? Object.freeze({
|
|
1167
|
-
get: (key) => {
|
|
1168
|
-
assertScope(active);
|
|
1169
|
-
if (!snapshots.has(key)) snapshots.set(key, snapshot(rawRead.get(key)));
|
|
1170
|
-
return snapshots.get(key);
|
|
1171
|
-
},
|
|
1172
|
-
has: (key) => {
|
|
1173
|
-
assertScope(active);
|
|
1174
|
-
return rawRead.has(key);
|
|
1175
|
-
},
|
|
1176
|
-
ids: () => {
|
|
1177
|
-
assertScope(active);
|
|
1178
|
-
return rawRead.ids();
|
|
1179
|
-
}
|
|
1180
|
-
}) : createAccess(context);
|
|
1181
|
-
if (!collection) return Object.freeze({
|
|
1182
|
-
kind: "document",
|
|
1183
|
-
read,
|
|
1184
|
-
revision: runtime.revision(),
|
|
1185
|
-
reset: record.reset(),
|
|
1186
|
-
cause: batch?.cause
|
|
1187
|
-
});
|
|
1188
|
-
if (!baseline) {
|
|
1189
|
-
const collectionRead = read;
|
|
1190
|
-
baseline = /* @__PURE__ */ new Map();
|
|
1191
|
-
baselineIds = Object.freeze(collectionAccess(context, collection.address).ids().slice());
|
|
1192
|
-
for (const key of baselineIds) {
|
|
1193
|
-
const value = collectionRead.get(key);
|
|
1194
|
-
baseline.set(key, value);
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
const changed = [...keys];
|
|
1198
|
-
const added = [];
|
|
1199
|
-
const updated = [];
|
|
1200
|
-
const removed = [];
|
|
1201
|
-
const next = new Map(baseline);
|
|
1202
|
-
const collectionRead = read;
|
|
1203
|
-
for (const key of changed) {
|
|
1204
|
-
const beforePresent = baseline.has(key);
|
|
1205
|
-
const afterPresent = collectionRead.has(key);
|
|
1206
|
-
const afterValue = afterPresent ? collectionRead.get(key) : void 0;
|
|
1207
|
-
if (!beforePresent && afterPresent) {
|
|
1208
|
-
added.push({
|
|
1209
|
-
key,
|
|
1210
|
-
after: afterValue
|
|
1211
|
-
});
|
|
1212
|
-
next.set(key, afterValue);
|
|
1213
|
-
} else if (beforePresent && !afterPresent) {
|
|
1214
|
-
removed.push({
|
|
1215
|
-
key,
|
|
1216
|
-
before: baseline.get(key)
|
|
1217
|
-
});
|
|
1218
|
-
next.delete(key);
|
|
1219
|
-
} else if (beforePresent && afterPresent) {
|
|
1220
|
-
const beforeValue = baseline.get(key);
|
|
1221
|
-
if (!sameEntry(beforeValue, afterValue)) updated.push({
|
|
1222
|
-
key,
|
|
1223
|
-
before: beforeValue,
|
|
1224
|
-
after: afterValue
|
|
1225
|
-
});
|
|
1226
|
-
next.set(key, afterValue);
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
const ids = record.reset() || orderChanged ? Object.freeze([...collectionRead.ids()]) : baselineIds;
|
|
1230
|
-
if (record.reset()) {
|
|
1231
|
-
next.clear();
|
|
1232
|
-
for (const key of ids) {
|
|
1233
|
-
const value = collectionRead.get(key);
|
|
1234
|
-
next.set(key, value);
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
let order;
|
|
1238
|
-
if (orderChanged) {
|
|
1239
|
-
const beforeKeys = new Set(baselineIds);
|
|
1240
|
-
const afterKeys = new Set(ids);
|
|
1241
|
-
const beforeCommon = baselineIds.filter((key) => afterKeys.has(key));
|
|
1242
|
-
const afterCommon = ids.filter((key) => beforeKeys.has(key));
|
|
1243
|
-
if (beforeCommon.some((key, index) => afterCommon[index] !== key)) order = {
|
|
1244
|
-
before: Object.freeze([...baselineIds]),
|
|
1245
|
-
after: ids
|
|
1246
|
-
};
|
|
1247
|
-
}
|
|
1248
|
-
const change = record.reset() ? Object.freeze({ kind: "reset" }) : added.length || updated.length || removed.length || order ? Object.freeze({
|
|
1249
|
-
kind: "incremental",
|
|
1250
|
-
added: Object.freeze(added),
|
|
1251
|
-
updated: Object.freeze(updated),
|
|
1252
|
-
removed: Object.freeze(removed),
|
|
1253
|
-
...order ? { order: Object.freeze(order) } : {}
|
|
1254
|
-
}) : void 0;
|
|
1255
|
-
nextBaseline = next;
|
|
1256
|
-
nextBaselineIds = ids;
|
|
1257
|
-
return Object.freeze({
|
|
1258
|
-
kind: "collection",
|
|
1259
|
-
read,
|
|
1260
|
-
change,
|
|
1261
|
-
revision: runtime.revision(),
|
|
1262
|
-
cause: batch?.cause
|
|
1263
|
-
});
|
|
1264
|
-
}
|
|
1265
|
-
};
|
|
1266
|
-
const handle = collection ? { kind: "collection" } : {
|
|
1267
|
-
collection: (pick) => {
|
|
1268
|
-
scheduler.assertIdle();
|
|
1269
|
-
return make({ collection: compilePath(state.schema, "collection", pick) });
|
|
1270
|
-
},
|
|
1271
|
-
targets: (...selected) => {
|
|
1272
|
-
scheduler.assertIdle();
|
|
1273
|
-
if (!selected.length) throw new TypeError("Expected at least one target.");
|
|
1274
|
-
return make({ targets: selected.map((pick) => compilePath(state.schema, "value", pick)) });
|
|
1275
|
-
}
|
|
1276
|
-
};
|
|
1277
|
-
Object.freeze(handle);
|
|
1278
|
-
scheduler.register(handle, record);
|
|
1279
|
-
const receive = (commit) => {
|
|
1280
|
-
reset ||= commit.impact.kind === "reset";
|
|
1281
|
-
if (collection) {
|
|
1282
|
-
const change = collectionImpact(commit.impact, collection);
|
|
1283
|
-
if (change.kind === "reset") reset = true;
|
|
1284
|
-
else {
|
|
1285
|
-
change.added.forEach((key) => keys.add(key));
|
|
1286
|
-
change.removed.forEach((key) => keys.add(key));
|
|
1287
|
-
change.updated.forEach((key) => keys.add(key));
|
|
1288
|
-
orderChanged ||= Boolean(change.added.size || change.removed.size || change.orderChanged);
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
};
|
|
1292
|
-
bindings.push({
|
|
1293
|
-
handle,
|
|
1294
|
-
targets,
|
|
1295
|
-
record,
|
|
1296
|
-
receive
|
|
1297
|
-
});
|
|
1298
|
-
return handle;
|
|
1299
|
-
};
|
|
1300
|
-
const handle = make({ targets: [] });
|
|
1301
|
-
const guard = (locked) => {
|
|
1302
|
-
state.projectionLocks = (state.projectionLocks ?? 0) + (locked ? 1 : -1);
|
|
1303
|
-
};
|
|
1304
|
-
scheduler.guards.add(guard);
|
|
1305
|
-
const unsubscribe = attachProjection(runtime, {
|
|
1306
|
-
capture: (commit) => {
|
|
1307
|
-
for (const binding of bindings) if (!binding.targets.length || binding.targets.some((value) => affectsTarget(commit.impact, value))) {
|
|
1308
|
-
binding.receive(commit);
|
|
1309
|
-
batch = scheduler.batchContext();
|
|
1310
|
-
scheduler.capture(binding.record);
|
|
1311
|
-
}
|
|
1312
|
-
},
|
|
1313
|
-
settle: scheduler.settle,
|
|
1314
|
-
flush: scheduler.flush,
|
|
1315
|
-
dispose: () => {
|
|
1316
|
-
bindings.forEach(({ record }) => {
|
|
1317
|
-
record.fault = new ProjectionError("source", "document disposed", [runtime.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
|
|
1318
|
-
scheduler.capture(record);
|
|
1319
|
-
});
|
|
1320
|
-
scheduler.run();
|
|
1321
|
-
}
|
|
1322
|
-
});
|
|
1323
|
-
scheduler.cleanups.add(() => {
|
|
1324
|
-
unsubscribe();
|
|
1325
|
-
scheduler.guards.delete(guard);
|
|
1326
|
-
});
|
|
1327
|
-
documents.set(state, handle);
|
|
1328
|
-
return handle;
|
|
1329
|
-
};
|
|
1330
|
-
const valueSource = (initial, equal) => {
|
|
1331
|
-
let value = initial;
|
|
1332
|
-
let revision = 0;
|
|
1333
|
-
let cause;
|
|
1334
|
-
let reset = false;
|
|
1335
|
-
const handle = Object.freeze({});
|
|
1336
|
-
const record = {
|
|
1337
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
1338
|
-
disposed: false,
|
|
1339
|
-
fault: void 0,
|
|
1340
|
-
revision: () => revision,
|
|
1341
|
-
reset: () => reset,
|
|
1342
|
-
clear: () => {
|
|
1343
|
-
cause = void 0;
|
|
1344
|
-
reset = false;
|
|
1018
|
+
//#region core/src/projection/value-output.ts
|
|
1019
|
+
const unset = Symbol("projection-value-unset");
|
|
1020
|
+
/** Owns staged and published state for one scalar Projection output. */
|
|
1021
|
+
const createValueOutput = () => {
|
|
1022
|
+
let value;
|
|
1023
|
+
let staged = unset;
|
|
1024
|
+
let next;
|
|
1025
|
+
let initialized = false;
|
|
1026
|
+
let revision = 0;
|
|
1027
|
+
let changed = false;
|
|
1028
|
+
let reset = false;
|
|
1029
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1030
|
+
const begin = (active, initialize) => {
|
|
1031
|
+
staged = unset;
|
|
1032
|
+
next = value;
|
|
1033
|
+
changed = false;
|
|
1034
|
+
reset = initialize;
|
|
1035
|
+
return {
|
|
1036
|
+
previous: initialized ? value : void 0,
|
|
1037
|
+
next: () => {
|
|
1038
|
+
assertScope(active);
|
|
1039
|
+
if (staged !== unset) return staged;
|
|
1040
|
+
return initialize ? void 0 : initialized ? value : void 0;
|
|
1345
1041
|
},
|
|
1346
|
-
|
|
1042
|
+
output: Object.freeze({ set: (candidate) => {
|
|
1347
1043
|
assertScope(active);
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
value,
|
|
1351
|
-
revision,
|
|
1352
|
-
reset,
|
|
1353
|
-
cause
|
|
1354
|
-
});
|
|
1355
|
-
}
|
|
1356
|
-
};
|
|
1357
|
-
scheduler.register(handle, record);
|
|
1358
|
-
const accept = (next, metadata) => {
|
|
1359
|
-
scheduler.assertIdle();
|
|
1360
|
-
const recovering = record.fault !== void 0;
|
|
1361
|
-
const eventful = metadata !== void 0 && (metadata.reset === true || metadata.revision !== void 0 && metadata.revision !== revision || metadata.cause !== void 0);
|
|
1362
|
-
if (equal(value, next) && !recovering && !eventful) return;
|
|
1363
|
-
record.fault = void 0;
|
|
1364
|
-
value = next;
|
|
1365
|
-
revision = metadata?.revision ?? revision + 1;
|
|
1366
|
-
cause = metadata?.cause ?? scheduler.batchContext()?.cause ?? cause;
|
|
1367
|
-
reset ||= metadata?.reset ?? false;
|
|
1368
|
-
scheduler.capture(record);
|
|
1369
|
-
};
|
|
1370
|
-
return {
|
|
1371
|
-
source: handle,
|
|
1372
|
-
accept,
|
|
1373
|
-
set(next) {
|
|
1374
|
-
accept(next);
|
|
1375
|
-
scheduler.run();
|
|
1376
|
-
}
|
|
1044
|
+
staged = candidate;
|
|
1045
|
+
} })
|
|
1377
1046
|
};
|
|
1378
1047
|
};
|
|
1379
|
-
const
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
before: before.value
|
|
1399
|
-
});
|
|
1400
|
-
else if (before.present && afterPresent && !Object.is(before.value, values.get(key))) updated.push({
|
|
1401
|
-
key,
|
|
1402
|
-
before: before.value,
|
|
1403
|
-
after: values.get(key)
|
|
1404
|
-
});
|
|
1405
|
-
}
|
|
1406
|
-
let order;
|
|
1407
|
-
if (beforeIds) {
|
|
1408
|
-
const afterIds = [...values.keys()];
|
|
1409
|
-
const beforeKeys = new Set(beforeIds);
|
|
1410
|
-
const afterKeys = new Set(afterIds);
|
|
1411
|
-
const beforeCommon = beforeIds.filter((key) => afterKeys.has(key));
|
|
1412
|
-
const afterCommon = afterIds.filter((key) => beforeKeys.has(key));
|
|
1413
|
-
if (beforeCommon.some((key, index) => key !== afterCommon[index])) order = Object.freeze({
|
|
1414
|
-
before: beforeIds,
|
|
1415
|
-
after: Object.freeze(afterIds)
|
|
1416
|
-
});
|
|
1417
|
-
}
|
|
1418
|
-
if (!added.length && !updated.length && !removed.length && !order) return void 0;
|
|
1048
|
+
const seal = (nextReset, isEqual) => {
|
|
1049
|
+
if (reset && staged === unset) throw new TypeError("Value output must be initialized on reset.");
|
|
1050
|
+
if (staged === unset) {
|
|
1051
|
+
reset = nextReset;
|
|
1052
|
+
changed = false;
|
|
1053
|
+
next = value;
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
const candidate = staged;
|
|
1057
|
+
changed = !initialized || !isEqual(value, candidate);
|
|
1058
|
+
next = changed ? candidate : value;
|
|
1059
|
+
reset = nextReset;
|
|
1060
|
+
return changed;
|
|
1061
|
+
};
|
|
1062
|
+
return {
|
|
1063
|
+
begin,
|
|
1064
|
+
seal,
|
|
1065
|
+
context: (active, cause) => {
|
|
1066
|
+
assertScope(active);
|
|
1419
1067
|
return Object.freeze({
|
|
1420
|
-
kind: "
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1068
|
+
kind: "value",
|
|
1069
|
+
value: next,
|
|
1070
|
+
revision,
|
|
1071
|
+
reset,
|
|
1072
|
+
cause
|
|
1425
1073
|
});
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
revision,
|
|
1460
|
-
cause
|
|
1461
|
-
});
|
|
1462
|
-
}
|
|
1463
|
-
};
|
|
1464
|
-
scheduler.register(handle, record);
|
|
1465
|
-
return {
|
|
1466
|
-
source: handle,
|
|
1467
|
-
update(run) {
|
|
1468
|
-
scheduler.assertIdle();
|
|
1469
|
-
const staged = /* @__PURE__ */ new Map();
|
|
1470
|
-
let active = true;
|
|
1471
|
-
const draft = Object.freeze({
|
|
1472
|
-
get: (key) => {
|
|
1473
|
-
assertScope(() => active);
|
|
1474
|
-
const entry = staged.get(key);
|
|
1475
|
-
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
1476
|
-
},
|
|
1477
|
-
has: (key) => {
|
|
1478
|
-
assertScope(() => active);
|
|
1479
|
-
const entry = staged.get(key);
|
|
1480
|
-
return entry ? entry.present : values.has(key);
|
|
1481
|
-
},
|
|
1482
|
-
set: (key, value) => {
|
|
1483
|
-
assertScope(() => active);
|
|
1484
|
-
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
1485
|
-
staged.set(key, {
|
|
1486
|
-
present: true,
|
|
1487
|
-
value
|
|
1488
|
-
});
|
|
1489
|
-
},
|
|
1490
|
-
remove: (key) => {
|
|
1491
|
-
assertScope(() => active);
|
|
1492
|
-
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
1493
|
-
staged.set(key, { present: false });
|
|
1494
|
-
}
|
|
1495
|
-
});
|
|
1496
|
-
try {
|
|
1497
|
-
assertSynchronous(run(draft));
|
|
1498
|
-
} finally {
|
|
1499
|
-
active = false;
|
|
1500
|
-
}
|
|
1501
|
-
let changed = false;
|
|
1502
|
-
for (const [key, entry] of staged) {
|
|
1503
|
-
const present = values.has(key);
|
|
1504
|
-
if (entry.present ? present && Object.is(values.get(key), entry.value) : !present) continue;
|
|
1505
|
-
if (!touched.has(key)) touched.set(key, {
|
|
1506
|
-
present,
|
|
1507
|
-
value: values.get(key)
|
|
1508
|
-
});
|
|
1509
|
-
if (!beforeIds && present !== entry.present) beforeIds = Object.freeze([...values.keys()]);
|
|
1510
|
-
if (entry.present) values.set(key, entry.value);
|
|
1511
|
-
else values.delete(key);
|
|
1512
|
-
changed = true;
|
|
1513
|
-
}
|
|
1514
|
-
if (!changed) return;
|
|
1515
|
-
revision++;
|
|
1516
|
-
cause = scheduler.batchContext()?.cause ?? cause;
|
|
1517
|
-
scheduler.capture(record);
|
|
1518
|
-
scheduler.run();
|
|
1519
|
-
}
|
|
1520
|
-
};
|
|
1074
|
+
},
|
|
1075
|
+
current: (check) => {
|
|
1076
|
+
check();
|
|
1077
|
+
return value;
|
|
1078
|
+
},
|
|
1079
|
+
revision: () => revision,
|
|
1080
|
+
reset: () => reset,
|
|
1081
|
+
publish: () => {
|
|
1082
|
+
if (changed && initialized) revision++;
|
|
1083
|
+
if (changed) value = next;
|
|
1084
|
+
initialized = true;
|
|
1085
|
+
},
|
|
1086
|
+
emit: (call) => {
|
|
1087
|
+
profile.materialized.notification();
|
|
1088
|
+
Array.from(listeners).forEach((listener) => call(listener));
|
|
1089
|
+
},
|
|
1090
|
+
clear: () => {
|
|
1091
|
+
staged = unset;
|
|
1092
|
+
next = value;
|
|
1093
|
+
changed = false;
|
|
1094
|
+
reset = false;
|
|
1095
|
+
},
|
|
1096
|
+
release: () => {
|
|
1097
|
+
listeners.clear();
|
|
1098
|
+
staged = unset;
|
|
1099
|
+
value = next = void 0;
|
|
1100
|
+
initialized = false;
|
|
1101
|
+
revision = 0;
|
|
1102
|
+
changed = false;
|
|
1103
|
+
reset = false;
|
|
1104
|
+
},
|
|
1105
|
+
subscribe: (listener) => listeners.add(listener),
|
|
1106
|
+
unsubscribe: (listener) => listeners.delete(listener)
|
|
1521
1107
|
};
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
unsubscribe = port.subscribe((event) => {
|
|
1535
|
-
try {
|
|
1536
|
-
input.accept(event.value, {
|
|
1537
|
-
revision: event.revision,
|
|
1538
|
-
reset: event.reset ?? false,
|
|
1539
|
-
cause: event.cause
|
|
1540
|
-
});
|
|
1541
|
-
} catch (cause) {
|
|
1542
|
-
fail(cause);
|
|
1543
|
-
}
|
|
1544
|
-
scheduler.run();
|
|
1545
|
-
});
|
|
1546
|
-
} catch (cause) {
|
|
1547
|
-
unsubscribe?.();
|
|
1548
|
-
scheduler.unregisterSource(input.source);
|
|
1549
|
-
throw cause;
|
|
1550
|
-
}
|
|
1551
|
-
scheduler.cleanups.add(() => unsubscribe?.());
|
|
1552
|
-
externalSources.set(port, input.source);
|
|
1553
|
-
return input.source;
|
|
1108
|
+
};
|
|
1109
|
+
//#endregion
|
|
1110
|
+
//#region core/src/projection/processor.ts
|
|
1111
|
+
/** Materializes every derive/incremental/group definition through one processor lifecycle. */
|
|
1112
|
+
const createProcessor = (scheduler, definition, dependencies) => {
|
|
1113
|
+
scheduler.assertIdle();
|
|
1114
|
+
let processor;
|
|
1115
|
+
let instance;
|
|
1116
|
+
let cause;
|
|
1117
|
+
const check = () => {
|
|
1118
|
+
if (!scheduler.active || processor.disposed) throw new ProjectionDisposedError();
|
|
1119
|
+
if (processor.fault) throw processor.fault;
|
|
1554
1120
|
};
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
let transitionKeys = /* @__PURE__ */ new Set();
|
|
1567
|
-
let pending = false;
|
|
1568
|
-
const handle = Object.freeze({ kind: "collection" });
|
|
1569
|
-
const record = {
|
|
1570
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
1571
|
-
disposed: false,
|
|
1572
|
-
fault: void 0,
|
|
1573
|
-
revision: () => revision,
|
|
1574
|
-
reset: () => reset,
|
|
1575
|
-
clear: () => {
|
|
1576
|
-
previous = read;
|
|
1577
|
-
pending = false;
|
|
1578
|
-
change = void 0;
|
|
1579
|
-
reset = false;
|
|
1580
|
-
orderChanged = false;
|
|
1581
|
-
cause = void 0;
|
|
1582
|
-
transitionKeys.clear();
|
|
1583
|
-
},
|
|
1584
|
-
context: (active) => {
|
|
1585
|
-
assertScope(active);
|
|
1586
|
-
const currentRead = Object.freeze({
|
|
1587
|
-
get: (key) => {
|
|
1588
|
-
assertScope(active);
|
|
1589
|
-
return read.get(key);
|
|
1121
|
+
const bound = definition.outputs.map((outputDefinition) => {
|
|
1122
|
+
if (outputDefinition.kind === "value") {
|
|
1123
|
+
const state = createValueOutput();
|
|
1124
|
+
return {
|
|
1125
|
+
kind: "value",
|
|
1126
|
+
definition: outputDefinition,
|
|
1127
|
+
state,
|
|
1128
|
+
output: {
|
|
1129
|
+
kind: "value",
|
|
1130
|
+
get owner() {
|
|
1131
|
+
return processor;
|
|
1590
1132
|
},
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1133
|
+
consumers: /* @__PURE__ */ new Set(),
|
|
1134
|
+
context: (active) => state.context(active, cause),
|
|
1135
|
+
current: () => state.current(check),
|
|
1136
|
+
revision: state.revision,
|
|
1137
|
+
reset: state.reset,
|
|
1138
|
+
subscribe: (listener) => {
|
|
1139
|
+
check();
|
|
1140
|
+
const wrapped = () => listener();
|
|
1141
|
+
state.subscribe(wrapped);
|
|
1142
|
+
return () => state.unsubscribe(wrapped);
|
|
1594
1143
|
},
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1144
|
+
emit: state.emit,
|
|
1145
|
+
clear: state.clear,
|
|
1146
|
+
release: state.release
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
const state = createCollectionOutput();
|
|
1151
|
+
return {
|
|
1152
|
+
kind: "collection",
|
|
1153
|
+
definition: outputDefinition,
|
|
1154
|
+
state,
|
|
1155
|
+
output: {
|
|
1156
|
+
kind: "collection",
|
|
1157
|
+
get owner() {
|
|
1158
|
+
return processor;
|
|
1159
|
+
},
|
|
1160
|
+
consumers: /* @__PURE__ */ new Set(),
|
|
1161
|
+
context: (active) => state.context(active, cause),
|
|
1162
|
+
current: () => state.current(check),
|
|
1163
|
+
revision: state.revision,
|
|
1164
|
+
reset: state.reset,
|
|
1165
|
+
subscribe: (listener) => {
|
|
1166
|
+
check();
|
|
1167
|
+
const wrapped = (change) => listener(change);
|
|
1168
|
+
state.subscribe(wrapped);
|
|
1169
|
+
return () => state.unsubscribe(wrapped);
|
|
1170
|
+
},
|
|
1171
|
+
emit: state.emit,
|
|
1172
|
+
clear: state.clear,
|
|
1173
|
+
release: state.release
|
|
1607
1174
|
}
|
|
1608
1175
|
};
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
};
|
|
1614
|
-
const deriveChange = () => {
|
|
1615
|
-
if (reset) return Object.freeze({ kind: "reset" });
|
|
1616
|
-
const added = [];
|
|
1617
|
-
const updated = [];
|
|
1618
|
-
const removed = [];
|
|
1619
|
-
for (const key of transitionKeys) {
|
|
1620
|
-
const beforePresent = previous.has(key);
|
|
1621
|
-
const afterPresent = read.has(key);
|
|
1622
|
-
const before = beforePresent ? previous.get(key) : void 0;
|
|
1623
|
-
const after = afterPresent ? read.get(key) : void 0;
|
|
1624
|
-
if (beforePresent === afterPresent && Object.is(before, after)) continue;
|
|
1625
|
-
if (!beforePresent && afterPresent) added.push({
|
|
1626
|
-
key,
|
|
1627
|
-
after
|
|
1628
|
-
});
|
|
1629
|
-
else if (beforePresent && !afterPresent) removed.push({
|
|
1630
|
-
key,
|
|
1631
|
-
before
|
|
1632
|
-
});
|
|
1633
|
-
else if (beforePresent && afterPresent) updated.push({
|
|
1634
|
-
key,
|
|
1635
|
-
before,
|
|
1636
|
-
after
|
|
1637
|
-
});
|
|
1638
|
-
}
|
|
1639
|
-
let order;
|
|
1640
|
-
if (orderChanged) {
|
|
1641
|
-
const beforeIds = previous.ids();
|
|
1642
|
-
const afterIds = read.ids();
|
|
1643
|
-
const beforeKeys = new Set(beforeIds);
|
|
1644
|
-
const afterKeys = new Set(afterIds);
|
|
1645
|
-
const beforeCommon = beforeIds.filter((key) => afterKeys.has(key));
|
|
1646
|
-
const afterCommon = afterIds.filter((key) => beforeKeys.has(key));
|
|
1647
|
-
if (beforeCommon.some((key, index) => afterCommon[index] !== key)) order = Object.freeze({
|
|
1648
|
-
before: Object.freeze([...beforeIds]),
|
|
1649
|
-
after: Object.freeze([...afterIds])
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
if (!added.length && !removed.length && !updated.length && !order) return void 0;
|
|
1176
|
+
});
|
|
1177
|
+
const begin = (active, initialize) => Object.freeze(bound.map((entry) => {
|
|
1178
|
+
if (entry.kind === "value") {
|
|
1179
|
+
const evaluation = entry.state.begin(active, initialize);
|
|
1653
1180
|
return Object.freeze({
|
|
1654
|
-
kind: "
|
|
1655
|
-
|
|
1656
|
-
updated: Object.freeze(updated),
|
|
1657
|
-
removed: Object.freeze(removed),
|
|
1658
|
-
...order ? { order } : {}
|
|
1659
|
-
});
|
|
1660
|
-
};
|
|
1661
|
-
let unsubscribe;
|
|
1662
|
-
try {
|
|
1663
|
-
unsubscribe = port.subscribe((event) => {
|
|
1664
|
-
try {
|
|
1665
|
-
scheduler.assertIdle();
|
|
1666
|
-
if (!pending) previous = event.previous;
|
|
1667
|
-
read = port.current();
|
|
1668
|
-
revision = event.revision;
|
|
1669
|
-
if (event.impact?.kind === "incremental") {
|
|
1670
|
-
event.impact.added.forEach((key) => transitionKeys.add(key));
|
|
1671
|
-
event.impact.removed.forEach((key) => transitionKeys.add(key));
|
|
1672
|
-
event.impact.updated.forEach((key) => transitionKeys.add(key));
|
|
1673
|
-
orderChanged ||= Boolean(event.impact.added.size || event.impact.removed.size || event.impact.orderChanged);
|
|
1674
|
-
} else if (!event.impact) {
|
|
1675
|
-
previous.ids().forEach((key) => transitionKeys.add(key));
|
|
1676
|
-
read.ids().forEach((key) => transitionKeys.add(key));
|
|
1677
|
-
orderChanged = true;
|
|
1678
|
-
}
|
|
1679
|
-
reset ||= event.impact?.kind === "reset";
|
|
1680
|
-
cause = event.cause ?? scheduler.batchContext()?.cause ?? cause;
|
|
1681
|
-
change = deriveChange();
|
|
1682
|
-
record.fault = void 0;
|
|
1683
|
-
scheduler.capture(record);
|
|
1684
|
-
pending = true;
|
|
1685
|
-
} catch (error) {
|
|
1686
|
-
fail(error);
|
|
1687
|
-
}
|
|
1688
|
-
scheduler.run();
|
|
1181
|
+
kind: "value",
|
|
1182
|
+
...evaluation
|
|
1689
1183
|
});
|
|
1690
|
-
} catch (error) {
|
|
1691
|
-
unsubscribe?.();
|
|
1692
|
-
scheduler.unregisterSource(handle);
|
|
1693
|
-
throw error;
|
|
1694
1184
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1185
|
+
const evaluation = entry.state.begin(active, initialize);
|
|
1186
|
+
return Object.freeze({
|
|
1187
|
+
kind: "collection",
|
|
1188
|
+
...evaluation
|
|
1189
|
+
});
|
|
1190
|
+
}));
|
|
1191
|
+
const seal = (reset) => {
|
|
1192
|
+
const changed = [];
|
|
1193
|
+
for (const entry of bound) if (entry.state.seal(reset, entry.definition.equality)) changed.push(entry.output);
|
|
1194
|
+
return Object.freeze(changed);
|
|
1698
1195
|
};
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1196
|
+
const run = (reset, runCause) => {
|
|
1197
|
+
cause = runCause;
|
|
1198
|
+
let active = true;
|
|
1199
|
+
try {
|
|
1200
|
+
const scopeActive = () => active;
|
|
1201
|
+
const sources = Object.freeze(dependencies.map((output) => output.context(scopeActive)));
|
|
1202
|
+
let outputs = begin(scopeActive, reset);
|
|
1203
|
+
if (!instance) instance = definition.create();
|
|
1204
|
+
profile.materialized[reset ? "rebuilt" : "updated"]();
|
|
1205
|
+
let result = instance.evaluate({
|
|
1206
|
+
reset,
|
|
1207
|
+
cause: runCause,
|
|
1208
|
+
sources,
|
|
1209
|
+
outputs
|
|
1712
1210
|
});
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
bindings.set(equal, {
|
|
1725
|
-
source: input.source,
|
|
1726
|
-
accept: (value) => input.accept(value)
|
|
1211
|
+
assertSynchronous(result);
|
|
1212
|
+
if (result?.kind === "rebuild") {
|
|
1213
|
+
if (reset) throw new TypeError("Initial or reset projection processor cannot rebuild.");
|
|
1214
|
+
instance.release?.();
|
|
1215
|
+
instance = definition.create();
|
|
1216
|
+
outputs = begin(scopeActive, true);
|
|
1217
|
+
result = instance.evaluate({
|
|
1218
|
+
reset: true,
|
|
1219
|
+
cause: runCause,
|
|
1220
|
+
sources,
|
|
1221
|
+
outputs
|
|
1727
1222
|
});
|
|
1728
|
-
|
|
1223
|
+
assertSynchronous(result);
|
|
1224
|
+
if (result?.kind === "rebuild") throw new TypeError("Reset projection processor cannot rebuild again.");
|
|
1225
|
+
return seal(true);
|
|
1729
1226
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
}]]);
|
|
1734
|
-
const members = bindings;
|
|
1735
|
-
const receive = (settle = true) => {
|
|
1736
|
-
if (!scheduler.active) return;
|
|
1737
|
-
let value;
|
|
1738
|
-
try {
|
|
1739
|
-
value = readable.current();
|
|
1740
|
-
} catch (cause) {
|
|
1741
|
-
for (const member of members.values()) {
|
|
1742
|
-
const record = scheduler.source(member.source);
|
|
1743
|
-
record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
|
|
1744
|
-
scheduler.capture(record);
|
|
1745
|
-
}
|
|
1746
|
-
if (settle) scheduler.run();
|
|
1747
|
-
return;
|
|
1748
|
-
}
|
|
1749
|
-
for (const member of members.values()) try {
|
|
1750
|
-
member.accept(value);
|
|
1751
|
-
} catch (cause) {
|
|
1752
|
-
const record = scheduler.source(member.source);
|
|
1753
|
-
record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
|
|
1754
|
-
scheduler.capture(record);
|
|
1755
|
-
}
|
|
1756
|
-
if (settle) scheduler.run();
|
|
1757
|
-
};
|
|
1758
|
-
let unsubscribe;
|
|
1759
|
-
let detach;
|
|
1760
|
-
try {
|
|
1761
|
-
const owner = documentReadableOwner(readable);
|
|
1762
|
-
if (owner) {
|
|
1763
|
-
document(owner);
|
|
1764
|
-
detach = attachProjection(owner, {
|
|
1765
|
-
capture: () => receive(false),
|
|
1766
|
-
settle: scheduler.settle,
|
|
1767
|
-
flush: scheduler.flush,
|
|
1768
|
-
dispose: () => {
|
|
1769
|
-
for (const member of members.values()) {
|
|
1770
|
-
const record = scheduler.source(member.source);
|
|
1771
|
-
record.fault = new ProjectionError("source", "document readable disposed", [record.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
|
|
1772
|
-
scheduler.capture(record);
|
|
1773
|
-
}
|
|
1774
|
-
scheduler.run();
|
|
1775
|
-
}
|
|
1776
|
-
});
|
|
1777
|
-
}
|
|
1778
|
-
unsubscribe = readable.subscribe(() => receive());
|
|
1779
|
-
input.accept(readable.current());
|
|
1780
|
-
} catch (error) {
|
|
1781
|
-
try {
|
|
1782
|
-
unsubscribe?.();
|
|
1783
|
-
detach?.();
|
|
1784
|
-
} finally {
|
|
1785
|
-
scheduler.unregisterSource(input.source);
|
|
1786
|
-
}
|
|
1787
|
-
throw error;
|
|
1788
|
-
}
|
|
1789
|
-
scheduler.cleanups.add(unsubscribe);
|
|
1790
|
-
if (detach) scheduler.cleanups.add(detach);
|
|
1791
|
-
readables.set(readable, bindings);
|
|
1792
|
-
scheduler.run();
|
|
1793
|
-
return input.source;
|
|
1794
|
-
},
|
|
1795
|
-
assertCollection: (handle) => {
|
|
1796
|
-
scheduler.source(handle);
|
|
1797
|
-
if (!isCollectionSource(handle)) throw new Error("map requires a collection source.");
|
|
1227
|
+
return seal(reset);
|
|
1228
|
+
} finally {
|
|
1229
|
+
active = false;
|
|
1798
1230
|
}
|
|
1799
1231
|
};
|
|
1800
|
-
};
|
|
1801
|
-
//#endregion
|
|
1802
|
-
//#region core/src/projection/node.ts
|
|
1803
|
-
const createNode = (scheduler, spec, behavior) => {
|
|
1804
|
-
scheduler.assertIdle();
|
|
1805
|
-
const entries = Object.entries(spec.sources).map(([key, handle]) => [key, scheduler.source(handle)]);
|
|
1806
1232
|
const order = scheduler.order();
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
...behavior,
|
|
1810
|
-
publish: () => {
|
|
1811
|
-
if (node.fault) behavior.clear();
|
|
1812
|
-
else behavior.publish();
|
|
1813
|
-
},
|
|
1233
|
+
processor = {
|
|
1234
|
+
kind: "processor",
|
|
1814
1235
|
order,
|
|
1815
|
-
name:
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
disposed: false,
|
|
1236
|
+
name: definition.name ? `${definition.name} (${order})` : `projection-${order}`,
|
|
1237
|
+
dependencies: Object.freeze([...dependencies]),
|
|
1238
|
+
outputs: Object.freeze(bound.map((entry) => entry.output)),
|
|
1819
1239
|
fault: void 0,
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
node.evaluate(true);
|
|
1836
|
-
node.publish();
|
|
1837
|
-
node.clear();
|
|
1838
|
-
});
|
|
1839
|
-
const check = () => {
|
|
1840
|
-
if (node.disposed || !scheduler.active) throw new ProjectionDisposedError();
|
|
1841
|
-
if (node.fault) throw node.fault;
|
|
1842
|
-
};
|
|
1843
|
-
return {
|
|
1844
|
-
node,
|
|
1845
|
-
check,
|
|
1846
|
-
install(handle) {
|
|
1847
|
-
scheduler.register(handle, node);
|
|
1848
|
-
scheduler.addNode(node);
|
|
1240
|
+
disposed: false,
|
|
1241
|
+
evaluate: run,
|
|
1242
|
+
publish: () => bound.forEach((entry) => entry.state.publish()),
|
|
1243
|
+
clear: () => {
|
|
1244
|
+
bound.forEach((entry) => entry.state.clear());
|
|
1245
|
+
cause = void 0;
|
|
1246
|
+
},
|
|
1247
|
+
release: () => {
|
|
1248
|
+
instance?.release?.();
|
|
1249
|
+
instance = void 0;
|
|
1250
|
+
bound.forEach((entry) => {
|
|
1251
|
+
entry.output.consumers.clear();
|
|
1252
|
+
entry.state.release();
|
|
1253
|
+
});
|
|
1254
|
+
cause = void 0;
|
|
1849
1255
|
}
|
|
1850
1256
|
};
|
|
1257
|
+
try {
|
|
1258
|
+
scheduler.initialize(() => {
|
|
1259
|
+
const invalid = dependencies.find((output) => output.owner.fault || output.owner.disposed);
|
|
1260
|
+
if (invalid) throw invalid.owner.fault ?? new ProjectionDisposedError();
|
|
1261
|
+
if (processor.evaluate(true, void 0).length) processor.publish();
|
|
1262
|
+
processor.clear();
|
|
1263
|
+
});
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
processor.release();
|
|
1266
|
+
throw error;
|
|
1267
|
+
}
|
|
1268
|
+
scheduler.addProcessor(processor);
|
|
1269
|
+
return processor;
|
|
1851
1270
|
};
|
|
1852
1271
|
//#endregion
|
|
1853
|
-
//#region core/src/projection/
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
instance = void 0;
|
|
1871
|
-
const built = spec.build(sources);
|
|
1872
|
-
assertSynchronous(built);
|
|
1873
|
-
instance = built;
|
|
1874
|
-
candidate = built.value;
|
|
1875
|
-
} else {
|
|
1876
|
-
profile.materialized.updated();
|
|
1877
|
-
const result = instance.update(sources);
|
|
1878
|
-
assertSynchronous(result);
|
|
1879
|
-
if (result.kind === "rebuild") {
|
|
1880
|
-
instance = void 0;
|
|
1881
|
-
const built = spec.build(sources);
|
|
1882
|
-
assertSynchronous(built);
|
|
1883
|
-
instance = built;
|
|
1884
|
-
candidate = built.value;
|
|
1885
|
-
build = true;
|
|
1886
|
-
} else candidate = result.kind === "changed" ? result.value : value;
|
|
1887
|
-
}
|
|
1888
|
-
changed = !initialized || !(options?.isEqual ?? Object.is)(value, candidate);
|
|
1889
|
-
next = changed ? candidate : value;
|
|
1890
|
-
reset = build;
|
|
1891
|
-
return changed;
|
|
1272
|
+
//#region core/src/projection/projection-readable.ts
|
|
1273
|
+
let activeTracker;
|
|
1274
|
+
const trackKey = (key) => {
|
|
1275
|
+
activeTracker?.keys.add(key);
|
|
1276
|
+
};
|
|
1277
|
+
const trackStructure = () => {
|
|
1278
|
+
if (activeTracker) activeTracker.structure = true;
|
|
1279
|
+
};
|
|
1280
|
+
const trackAll = () => {
|
|
1281
|
+
if (activeTracker) activeTracker.all = true;
|
|
1282
|
+
};
|
|
1283
|
+
const isMapLike = (value) => value instanceof Map || value !== null && typeof value === "object" && typeof value.get === "function" && typeof value.keys === "function" && typeof value.entries === "function";
|
|
1284
|
+
const trackedMap = (source) => {
|
|
1285
|
+
const view = {
|
|
1286
|
+
get: (key) => {
|
|
1287
|
+
trackKey(key);
|
|
1288
|
+
return source.get(key);
|
|
1892
1289
|
},
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
return
|
|
1896
|
-
kind: "value",
|
|
1897
|
-
value: next,
|
|
1898
|
-
revision: revision + (changed ? 1 : 0),
|
|
1899
|
-
reset,
|
|
1900
|
-
cause
|
|
1901
|
-
});
|
|
1290
|
+
has: (key) => {
|
|
1291
|
+
trackKey(key);
|
|
1292
|
+
return source.has(key);
|
|
1902
1293
|
},
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
if (changed && initialized) revision++;
|
|
1907
|
-
value = next;
|
|
1908
|
-
initialized = true;
|
|
1294
|
+
get size() {
|
|
1295
|
+
trackStructure();
|
|
1296
|
+
return source.size;
|
|
1909
1297
|
},
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1298
|
+
keys: () => {
|
|
1299
|
+
trackStructure();
|
|
1300
|
+
return source.keys();
|
|
1913
1301
|
},
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
reset = false;
|
|
1918
|
-
cause = void 0;
|
|
1302
|
+
values: () => {
|
|
1303
|
+
trackAll();
|
|
1304
|
+
return source.values();
|
|
1919
1305
|
},
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
value = next = void 0;
|
|
1924
|
-
}
|
|
1925
|
-
});
|
|
1926
|
-
const handle = Object.freeze({
|
|
1927
|
-
kind: "value",
|
|
1928
|
-
current: () => {
|
|
1929
|
-
owner.check();
|
|
1930
|
-
return value;
|
|
1306
|
+
entries: () => {
|
|
1307
|
+
trackAll();
|
|
1308
|
+
return source.entries();
|
|
1931
1309
|
},
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1310
|
+
forEach: (callback, thisArg) => {
|
|
1311
|
+
trackAll();
|
|
1312
|
+
source.forEach((value, key) => callback.call(thisArg, value, key, view));
|
|
1935
1313
|
},
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
return () => {
|
|
1940
|
-
listeners.delete(listener);
|
|
1941
|
-
};
|
|
1314
|
+
[Symbol.iterator]: () => {
|
|
1315
|
+
trackAll();
|
|
1316
|
+
return source[Symbol.iterator]();
|
|
1942
1317
|
}
|
|
1943
|
-
}
|
|
1944
|
-
|
|
1945
|
-
return handle;
|
|
1946
|
-
};
|
|
1947
|
-
//#endregion
|
|
1948
|
-
//#region core/src/projection/collection-state.ts
|
|
1949
|
-
const height = (node) => node?.height ?? 0;
|
|
1950
|
-
const makeTreeNode = (key, value, left, right) => Object.freeze({
|
|
1951
|
-
key,
|
|
1952
|
-
value,
|
|
1953
|
-
left,
|
|
1954
|
-
right,
|
|
1955
|
-
height: Math.max(height(left), height(right)) + 1
|
|
1956
|
-
});
|
|
1957
|
-
const rotateLeft = (node) => {
|
|
1958
|
-
const right = node.right;
|
|
1959
|
-
return makeTreeNode(right.key, right.value, makeTreeNode(node.key, node.value, node.left, right.left), right.right);
|
|
1960
|
-
};
|
|
1961
|
-
const rotateRight = (node) => {
|
|
1962
|
-
const left = node.left;
|
|
1963
|
-
return makeTreeNode(left.key, left.value, left.left, makeTreeNode(node.key, node.value, left.right, node.right));
|
|
1964
|
-
};
|
|
1965
|
-
const rebalance = (node) => {
|
|
1966
|
-
const balance = height(node.left) - height(node.right);
|
|
1967
|
-
if (balance > 1) {
|
|
1968
|
-
if (height(node.left.left) < height(node.left.right)) return rotateRight(makeTreeNode(node.key, node.value, rotateLeft(node.left), node.right));
|
|
1969
|
-
return rotateRight(node);
|
|
1970
|
-
}
|
|
1971
|
-
if (balance < -1) {
|
|
1972
|
-
if (height(node.right.right) < height(node.right.left)) return rotateLeft(makeTreeNode(node.key, node.value, node.left, rotateRight(node.right)));
|
|
1973
|
-
return rotateLeft(node);
|
|
1974
|
-
}
|
|
1975
|
-
return node;
|
|
1976
|
-
};
|
|
1977
|
-
const treeSet = (node, key, value) => {
|
|
1978
|
-
if (!node) return makeTreeNode(key, value);
|
|
1979
|
-
if (key === node.key) return makeTreeNode(key, value, node.left, node.right);
|
|
1980
|
-
if (key < node.key) return rebalance(makeTreeNode(node.key, node.value, treeSet(node.left, key, value), node.right));
|
|
1981
|
-
return rebalance(makeTreeNode(node.key, node.value, node.left, treeSet(node.right, key, value)));
|
|
1982
|
-
};
|
|
1983
|
-
const treeMin = (node) => node.left ? treeMin(node.left) : node;
|
|
1984
|
-
const treeRemove = (node, key) => {
|
|
1985
|
-
if (!node) return void 0;
|
|
1986
|
-
if (key < node.key) return rebalance(makeTreeNode(node.key, node.value, treeRemove(node.left, key), node.right));
|
|
1987
|
-
if (key > node.key) return rebalance(makeTreeNode(node.key, node.value, node.left, treeRemove(node.right, key)));
|
|
1988
|
-
if (!node.left) return node.right;
|
|
1989
|
-
if (!node.right) return node.left;
|
|
1990
|
-
const next = treeMin(node.right);
|
|
1991
|
-
return rebalance(makeTreeNode(next.key, next.value, node.left, treeRemove(node.right, next.key)));
|
|
1992
|
-
};
|
|
1993
|
-
const treeGet = (node, key) => {
|
|
1994
|
-
let current = node;
|
|
1995
|
-
while (current) {
|
|
1996
|
-
if (key === current.key) return current.value;
|
|
1997
|
-
current = key < current.key ? current.left : current.right;
|
|
1998
|
-
}
|
|
1318
|
+
};
|
|
1319
|
+
return Object.freeze(view);
|
|
1999
1320
|
};
|
|
2000
|
-
const
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
}
|
|
1321
|
+
const selectionAffects = (selection, change) => {
|
|
1322
|
+
if (!change || change.kind === "reset") return true;
|
|
1323
|
+
if (selection.all && (change.added.length || change.removed.length || change.updated.length || change.order)) return true;
|
|
1324
|
+
if (selection.structure && (change.added.length || change.removed.length || change.order)) return true;
|
|
1325
|
+
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;
|
|
2006
1326
|
return false;
|
|
2007
1327
|
};
|
|
2008
|
-
const
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
* The one keyed publication kernel used by ordinary collection nodes and
|
|
2016
|
-
* processor-group output leaves. It owns staged intent, exact transitions and
|
|
2017
|
-
* the persistent published snapshot; callers only provide the processor
|
|
2018
|
-
* callback and the equality policy.
|
|
2019
|
-
*/
|
|
2020
|
-
const createCollectionState = () => {
|
|
2021
|
-
const values = /* @__PURE__ */ new Map();
|
|
2022
|
-
let ids = Object.freeze([]);
|
|
2023
|
-
let staged = /* @__PURE__ */ new Map();
|
|
2024
|
-
let nextIds = ids;
|
|
2025
|
-
let change;
|
|
1328
|
+
const createDirectReadable = (source) => Object.freeze({
|
|
1329
|
+
current: source.current,
|
|
1330
|
+
revision: source.revision,
|
|
1331
|
+
subscribe: (listener) => source.subscribe(() => listener())
|
|
1332
|
+
});
|
|
1333
|
+
/** Consumer-side selector tracking; it never mutates processor dependencies. */
|
|
1334
|
+
const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
2026
1335
|
let initialized = false;
|
|
2027
|
-
let
|
|
2028
|
-
let
|
|
2029
|
-
let
|
|
2030
|
-
let
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
1336
|
+
let value;
|
|
1337
|
+
let selectedRevision = 0;
|
|
1338
|
+
let sourceRevision = -1;
|
|
1339
|
+
let selection = Object.freeze({
|
|
1340
|
+
keys: /* @__PURE__ */ new Set(),
|
|
1341
|
+
all: true,
|
|
1342
|
+
structure: false
|
|
1343
|
+
});
|
|
1344
|
+
let previousCollection;
|
|
1345
|
+
let unsubscribeSource;
|
|
2034
1346
|
const listeners = /* @__PURE__ */ new Set();
|
|
2035
|
-
const
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : values.get(key);
|
|
2039
|
-
};
|
|
2040
|
-
const deriveIds = () => {
|
|
2041
|
-
const result = cleared ? [] : ids.filter(hasNext);
|
|
2042
|
-
for (const [key, entry] of staged) if (entry.present && (cleared || !values.has(key))) result.push(key);
|
|
2043
|
-
return result;
|
|
1347
|
+
const updateCollectionSnapshot = (current) => {
|
|
1348
|
+
const candidate = current ?? source.current();
|
|
1349
|
+
previousCollection = isMapLike(candidate) ? candidate : void 0;
|
|
2044
1350
|
};
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
explicitOrder = void 0;
|
|
2051
|
-
orderChanged = false;
|
|
2052
|
-
cleared = nextReset;
|
|
2053
|
-
const read = (next) => ({
|
|
2054
|
-
get: (key) => {
|
|
2055
|
-
assertScope(active);
|
|
2056
|
-
return next ? getNext(key) : values.get(key);
|
|
2057
|
-
},
|
|
2058
|
-
has: (key) => {
|
|
2059
|
-
assertScope(active);
|
|
2060
|
-
return next ? hasNext(key) : values.has(key);
|
|
2061
|
-
},
|
|
2062
|
-
ids: () => {
|
|
2063
|
-
assertScope(active);
|
|
2064
|
-
return next ? Object.freeze(explicitOrder ? [...explicitOrder] : [...deriveIds()]) : ids;
|
|
2065
|
-
}
|
|
2066
|
-
});
|
|
2067
|
-
return {
|
|
2068
|
-
previous: read(false),
|
|
2069
|
-
next: read(true),
|
|
2070
|
-
output: {
|
|
2071
|
-
set: (key, value) => {
|
|
2072
|
-
assertScope(active);
|
|
2073
|
-
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
2074
|
-
staged.set(key, {
|
|
2075
|
-
present: true,
|
|
2076
|
-
value
|
|
2077
|
-
});
|
|
2078
|
-
},
|
|
2079
|
-
remove: (key) => {
|
|
2080
|
-
assertScope(active);
|
|
2081
|
-
staged.set(key, { present: false });
|
|
2082
|
-
},
|
|
2083
|
-
order: (order) => {
|
|
2084
|
-
assertScope(active);
|
|
2085
|
-
explicitOrder = Object.freeze([...order]);
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
1351
|
+
const evaluate = () => {
|
|
1352
|
+
const tracker = {
|
|
1353
|
+
keys: /* @__PURE__ */ new Set(),
|
|
1354
|
+
all: false,
|
|
1355
|
+
structure: false
|
|
2088
1356
|
};
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
1357
|
+
const previousTracker = activeTracker;
|
|
1358
|
+
activeTracker = tracker;
|
|
1359
|
+
let current;
|
|
1360
|
+
let next;
|
|
1361
|
+
try {
|
|
1362
|
+
current = source.current();
|
|
1363
|
+
next = selector(isMapLike(current) ? trackedMap(current) : current);
|
|
1364
|
+
} finally {
|
|
1365
|
+
activeTracker = previousTracker;
|
|
2093
1366
|
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
else if (!nextReset) staged.delete(key);
|
|
2104
|
-
} else if (existed) removed.add(key);
|
|
2105
|
-
else staged.delete(key);
|
|
1367
|
+
const nextSelection = Object.freeze({
|
|
1368
|
+
keys: new Set(tracker.keys),
|
|
1369
|
+
all: tracker.all || isMapLike(current) && tracker.keys.size === 0 && !tracker.structure,
|
|
1370
|
+
structure: tracker.structure
|
|
1371
|
+
});
|
|
1372
|
+
const changed = !initialized || !equality(value, next);
|
|
1373
|
+
if (changed) {
|
|
1374
|
+
if (initialized) selectedRevision++;
|
|
1375
|
+
value = next;
|
|
2106
1376
|
}
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
const
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
}
|
|
2130
|
-
else if (!afterPresent) removedEntries.push({
|
|
2131
|
-
key,
|
|
2132
|
-
before: values.get(key)
|
|
2133
|
-
});
|
|
2134
|
-
else updatedEntries.push({
|
|
2135
|
-
key,
|
|
2136
|
-
before: values.get(key),
|
|
2137
|
-
after: getNext(key)
|
|
2138
|
-
});
|
|
1377
|
+
selection = nextSelection;
|
|
1378
|
+
initialized = true;
|
|
1379
|
+
sourceRevision = source.revision();
|
|
1380
|
+
updateCollectionSnapshot(current);
|
|
1381
|
+
return changed;
|
|
1382
|
+
};
|
|
1383
|
+
const ensureCurrent = () => {
|
|
1384
|
+
const revision = source.revision();
|
|
1385
|
+
if (!initialized || !unsubscribeSource && sourceRevision !== revision) evaluate();
|
|
1386
|
+
return value;
|
|
1387
|
+
};
|
|
1388
|
+
const onProjectionChange = (incoming) => {
|
|
1389
|
+
let change = incoming;
|
|
1390
|
+
if (!change && previousCollection) {
|
|
1391
|
+
const current = source.current();
|
|
1392
|
+
if (isMapLike(current)) {
|
|
1393
|
+
change = collectionChangeBetween(previousCollection, current);
|
|
1394
|
+
previousCollection = current;
|
|
1395
|
+
if (!change) {
|
|
1396
|
+
sourceRevision = source.revision();
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
} else previousCollection = void 0;
|
|
2139
1400
|
}
|
|
2140
|
-
if (
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
const before = ids.filter((key) => !removed.has(key));
|
|
2145
|
-
const after = nextIds.filter((key) => !added.has(key));
|
|
2146
|
-
commonOrderChanged = before.some((key, index) => after[index] !== key);
|
|
2147
|
-
}
|
|
2148
|
-
change = Object.freeze({
|
|
2149
|
-
kind: "incremental",
|
|
2150
|
-
added: Object.freeze(addedEntries),
|
|
2151
|
-
updated: Object.freeze(updatedEntries),
|
|
2152
|
-
removed: Object.freeze(removedEntries),
|
|
2153
|
-
...commonOrderChanged ? { order: Object.freeze({
|
|
2154
|
-
before: Object.freeze([...ids]),
|
|
2155
|
-
after: Object.freeze([...nextIds])
|
|
2156
|
-
}) } : {}
|
|
2157
|
-
});
|
|
1401
|
+
if (!selectionAffects(selection, change)) {
|
|
1402
|
+
sourceRevision = source.revision();
|
|
1403
|
+
updateCollectionSnapshot();
|
|
1404
|
+
return;
|
|
2158
1405
|
}
|
|
2159
|
-
|
|
2160
|
-
return change !== void 0;
|
|
1406
|
+
if (evaluate()) Array.from(listeners).forEach((listener) => listener());
|
|
2161
1407
|
};
|
|
2162
|
-
const
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
assertScope(active);
|
|
2171
|
-
return hasNext(key);
|
|
2172
|
-
},
|
|
2173
|
-
ids: () => {
|
|
2174
|
-
assertScope(active);
|
|
2175
|
-
return nextIds;
|
|
2176
|
-
}
|
|
1408
|
+
const installSource = () => {
|
|
1409
|
+
if (!unsubscribeSource) unsubscribeSource = source.subscribe(onProjectionChange);
|
|
1410
|
+
};
|
|
1411
|
+
return Object.freeze({
|
|
1412
|
+
current: ensureCurrent,
|
|
1413
|
+
revision: () => {
|
|
1414
|
+
ensureCurrent();
|
|
1415
|
+
return selectedRevision;
|
|
2177
1416
|
},
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
1417
|
+
subscribe: (listener) => {
|
|
1418
|
+
ensureCurrent();
|
|
1419
|
+
listeners.add(listener);
|
|
1420
|
+
installSource();
|
|
1421
|
+
return () => {
|
|
1422
|
+
listeners.delete(listener);
|
|
1423
|
+
if (!listeners.size && unsubscribeSource) {
|
|
1424
|
+
unsubscribeSource();
|
|
1425
|
+
unsubscribeSource = void 0;
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
2181
1429
|
});
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
});
|
|
1430
|
+
};
|
|
1431
|
+
//#endregion
|
|
1432
|
+
//#region core/src/projection/source.ts
|
|
1433
|
+
const sourceError = (source, cause) => new ProjectionError("source", source.name, [source.outputs[0].revision()], cause);
|
|
1434
|
+
const createValueBoundary = (scheduler, name, initial, equality) => {
|
|
1435
|
+
const state = createValueOutput();
|
|
1436
|
+
let boundary;
|
|
1437
|
+
let latest = initial;
|
|
1438
|
+
let pendingReset = false;
|
|
1439
|
+
let pendingCause;
|
|
1440
|
+
let contextCause;
|
|
1441
|
+
let recovering = false;
|
|
1442
|
+
let cleanup;
|
|
1443
|
+
const check = () => {
|
|
1444
|
+
if (!scheduler.active || boundary.disposed) throw new ProjectionDisposedError();
|
|
1445
|
+
if (boundary.fault) throw boundary.fault;
|
|
2199
1446
|
};
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
current,
|
|
2205
|
-
revision: () => revision,
|
|
2206
|
-
reset: () => reset,
|
|
2207
|
-
publish: () => {
|
|
2208
|
-
if (change) if (change.kind === "reset") {
|
|
2209
|
-
const entries = [];
|
|
2210
|
-
for (const [key, entry] of staged) if (entry.present) entries.push([key, entry.value]);
|
|
2211
|
-
entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
2212
|
-
publishedRoot = treeFromSorted(entries);
|
|
2213
|
-
} else for (const [key, entry] of staged) publishedRoot = entry.present ? treeSet(publishedRoot, key, entry.value) : treeRemove(publishedRoot, key);
|
|
2214
|
-
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
2215
|
-
else values.delete(key);
|
|
2216
|
-
if (change && initialized) revision++;
|
|
2217
|
-
ids = nextIds;
|
|
2218
|
-
initialized = true;
|
|
1447
|
+
const output = {
|
|
1448
|
+
kind: "value",
|
|
1449
|
+
get owner() {
|
|
1450
|
+
return boundary;
|
|
2219
1451
|
},
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
1452
|
+
consumers: /* @__PURE__ */ new Set(),
|
|
1453
|
+
context: (active) => state.context(active, contextCause),
|
|
1454
|
+
current: () => state.current(check),
|
|
1455
|
+
revision: state.revision,
|
|
1456
|
+
reset: state.reset,
|
|
1457
|
+
subscribe: (listener) => {
|
|
1458
|
+
check();
|
|
1459
|
+
const wrapped = () => listener();
|
|
1460
|
+
state.subscribe(wrapped);
|
|
1461
|
+
return () => state.unsubscribe(wrapped);
|
|
1462
|
+
},
|
|
1463
|
+
emit: state.emit,
|
|
1464
|
+
clear: state.clear,
|
|
1465
|
+
release: state.release
|
|
1466
|
+
};
|
|
1467
|
+
boundary = {
|
|
1468
|
+
kind: "source",
|
|
1469
|
+
name,
|
|
1470
|
+
outputs: Object.freeze([output]),
|
|
1471
|
+
fault: void 0,
|
|
1472
|
+
disposed: false,
|
|
1473
|
+
pendingCause: () => pendingCause,
|
|
1474
|
+
recovering: () => recovering,
|
|
1475
|
+
prepare: (cause) => {
|
|
1476
|
+
contextCause = cause;
|
|
1477
|
+
let active = true;
|
|
1478
|
+
try {
|
|
1479
|
+
state.begin(() => active, pendingReset).output.set(latest);
|
|
1480
|
+
const changed = state.seal(pendingReset, equality);
|
|
1481
|
+
recovering = false;
|
|
1482
|
+
return {
|
|
1483
|
+
changed,
|
|
1484
|
+
force: pendingReset
|
|
1485
|
+
};
|
|
1486
|
+
} finally {
|
|
1487
|
+
active = false;
|
|
1488
|
+
}
|
|
2223
1489
|
},
|
|
1490
|
+
publish: state.publish,
|
|
2224
1491
|
clear: () => {
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
explicitOrder = void 0;
|
|
2231
|
-
orderChanged = false;
|
|
2232
|
-
cleared = false;
|
|
1492
|
+
state.clear();
|
|
1493
|
+
pendingReset = false;
|
|
1494
|
+
pendingCause = void 0;
|
|
1495
|
+
contextCause = void 0;
|
|
1496
|
+
recovering = false;
|
|
2233
1497
|
},
|
|
2234
1498
|
release: () => {
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
1499
|
+
cleanup?.();
|
|
1500
|
+
cleanup = void 0;
|
|
1501
|
+
state.release();
|
|
1502
|
+
output.consumers.clear();
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
scheduler.initialize(() => {
|
|
1506
|
+
let active = true;
|
|
1507
|
+
try {
|
|
1508
|
+
state.begin(() => active, true).output.set(initial);
|
|
1509
|
+
state.seal(true, equality);
|
|
1510
|
+
state.publish();
|
|
1511
|
+
state.clear();
|
|
1512
|
+
} finally {
|
|
1513
|
+
active = false;
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
scheduler.addSource(boundary);
|
|
1517
|
+
const mark = (metadata = {}) => {
|
|
1518
|
+
scheduler.assertIdle();
|
|
1519
|
+
pendingReset ||= metadata.reset ?? false;
|
|
1520
|
+
if (metadata.cause !== void 0) pendingCause ??= metadata.cause;
|
|
1521
|
+
if (boundary.fault) recovering = true;
|
|
1522
|
+
scheduler.capture(boundary);
|
|
1523
|
+
};
|
|
1524
|
+
const fail = (cause) => {
|
|
1525
|
+
boundary.fault = sourceError(boundary, cause);
|
|
1526
|
+
recovering = false;
|
|
1527
|
+
scheduler.capture(boundary);
|
|
1528
|
+
};
|
|
1529
|
+
return {
|
|
1530
|
+
producer: boundary,
|
|
1531
|
+
output,
|
|
1532
|
+
setLatest: (value) => {
|
|
1533
|
+
latest = value;
|
|
2246
1534
|
},
|
|
2247
|
-
|
|
2248
|
-
|
|
1535
|
+
mark,
|
|
1536
|
+
fail,
|
|
1537
|
+
detach: (next) => {
|
|
1538
|
+
cleanup = next;
|
|
2249
1539
|
}
|
|
2250
1540
|
};
|
|
2251
1541
|
};
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
let
|
|
2257
|
-
let
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
} else {
|
|
2272
|
-
profile.materialized.updated();
|
|
2273
|
-
const result = instance.update({
|
|
2274
|
-
sources,
|
|
2275
|
-
...input
|
|
2276
|
-
});
|
|
2277
|
-
assertSynchronous(result);
|
|
2278
|
-
if (result?.kind === "rebuild") {
|
|
2279
|
-
const rebuildInput = state.begin(active, true);
|
|
2280
|
-
instance = void 0;
|
|
2281
|
-
const built = spec.build({
|
|
2282
|
-
sources,
|
|
2283
|
-
...rebuildInput
|
|
2284
|
-
});
|
|
2285
|
-
assertSynchronous(built);
|
|
2286
|
-
instance = built;
|
|
2287
|
-
build = true;
|
|
2288
|
-
}
|
|
2289
|
-
}
|
|
2290
|
-
return state.seal(build, spec.isEqual ?? Object.is);
|
|
1542
|
+
const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
|
|
1543
|
+
const state = createCollectionOutput();
|
|
1544
|
+
let boundary;
|
|
1545
|
+
let pendingReset = false;
|
|
1546
|
+
let pendingCause;
|
|
1547
|
+
let contextCause;
|
|
1548
|
+
let recovering = false;
|
|
1549
|
+
let fullScan = false;
|
|
1550
|
+
let orderMayChange = false;
|
|
1551
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
1552
|
+
let cleanup;
|
|
1553
|
+
const check = () => {
|
|
1554
|
+
if (!scheduler.active || boundary.disposed) throw new ProjectionDisposedError();
|
|
1555
|
+
if (boundary.fault) throw boundary.fault;
|
|
1556
|
+
};
|
|
1557
|
+
const output = {
|
|
1558
|
+
kind: "collection",
|
|
1559
|
+
get owner() {
|
|
1560
|
+
return boundary;
|
|
2291
1561
|
},
|
|
2292
|
-
|
|
1562
|
+
consumers: /* @__PURE__ */ new Set(),
|
|
1563
|
+
context: (active) => state.context(active, contextCause),
|
|
1564
|
+
current: () => state.current(check),
|
|
2293
1565
|
revision: state.revision,
|
|
2294
1566
|
reset: state.reset,
|
|
2295
|
-
|
|
1567
|
+
subscribe: (listener) => {
|
|
1568
|
+
check();
|
|
1569
|
+
const wrapped = (change) => listener(change);
|
|
1570
|
+
state.subscribe(wrapped);
|
|
1571
|
+
return () => state.unsubscribe(wrapped);
|
|
1572
|
+
},
|
|
2296
1573
|
emit: state.emit,
|
|
2297
1574
|
clear: state.clear,
|
|
1575
|
+
release: state.release
|
|
1576
|
+
};
|
|
1577
|
+
boundary = {
|
|
1578
|
+
kind: "source",
|
|
1579
|
+
name,
|
|
1580
|
+
outputs: Object.freeze([output]),
|
|
1581
|
+
fault: void 0,
|
|
1582
|
+
disposed: false,
|
|
1583
|
+
pendingCause: () => pendingCause,
|
|
1584
|
+
recovering: () => recovering,
|
|
1585
|
+
prepare: (cause) => {
|
|
1586
|
+
contextCause = cause;
|
|
1587
|
+
let active = true;
|
|
1588
|
+
try {
|
|
1589
|
+
const changed = stageCollectionRead(state, readLatest(() => active), () => active, {
|
|
1590
|
+
reset: pendingReset,
|
|
1591
|
+
...fullScan ? {} : { candidates },
|
|
1592
|
+
orderMayChange,
|
|
1593
|
+
isEqual: equality
|
|
1594
|
+
});
|
|
1595
|
+
recovering = false;
|
|
1596
|
+
return {
|
|
1597
|
+
changed,
|
|
1598
|
+
force: pendingReset
|
|
1599
|
+
};
|
|
1600
|
+
} finally {
|
|
1601
|
+
active = false;
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
publish: state.publish,
|
|
1605
|
+
clear: () => {
|
|
1606
|
+
state.clear();
|
|
1607
|
+
pendingReset = false;
|
|
1608
|
+
pendingCause = void 0;
|
|
1609
|
+
contextCause = void 0;
|
|
1610
|
+
recovering = false;
|
|
1611
|
+
fullScan = false;
|
|
1612
|
+
orderMayChange = false;
|
|
1613
|
+
candidates.clear();
|
|
1614
|
+
},
|
|
2298
1615
|
release: () => {
|
|
1616
|
+
cleanup?.();
|
|
1617
|
+
cleanup = void 0;
|
|
2299
1618
|
state.release();
|
|
2300
|
-
|
|
1619
|
+
output.consumers.clear();
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
scheduler.initialize(() => {
|
|
1623
|
+
let active = true;
|
|
1624
|
+
try {
|
|
1625
|
+
stageCollectionRead(state, readLatest(() => active), () => active, {
|
|
1626
|
+
reset: true,
|
|
1627
|
+
isEqual: equality
|
|
1628
|
+
});
|
|
1629
|
+
state.publish();
|
|
1630
|
+
state.clear();
|
|
1631
|
+
} finally {
|
|
1632
|
+
active = false;
|
|
2301
1633
|
}
|
|
2302
1634
|
});
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
1635
|
+
scheduler.addSource(boundary);
|
|
1636
|
+
const mark = (metadata = {}) => {
|
|
1637
|
+
scheduler.assertIdle();
|
|
1638
|
+
pendingReset ||= metadata.reset ?? false;
|
|
1639
|
+
if (metadata.cause !== void 0) pendingCause ??= metadata.cause;
|
|
1640
|
+
fullScan ||= metadata.fullScan ?? false;
|
|
1641
|
+
orderMayChange ||= metadata.orderMayChange ?? false;
|
|
1642
|
+
if (metadata.candidates) for (const key of metadata.candidates) candidates.add(key);
|
|
1643
|
+
if (boundary.fault) recovering = true;
|
|
1644
|
+
scheduler.capture(boundary);
|
|
1645
|
+
};
|
|
1646
|
+
const fail = (cause) => {
|
|
1647
|
+
boundary.fault = sourceError(boundary, cause);
|
|
1648
|
+
recovering = false;
|
|
1649
|
+
scheduler.capture(boundary);
|
|
1650
|
+
};
|
|
1651
|
+
return {
|
|
1652
|
+
producer: boundary,
|
|
1653
|
+
output,
|
|
1654
|
+
mark,
|
|
1655
|
+
fail,
|
|
1656
|
+
detach: (next) => {
|
|
1657
|
+
cleanup = next;
|
|
2317
1658
|
}
|
|
2318
1659
|
};
|
|
2319
|
-
owner.install(handle);
|
|
2320
|
-
return Object.freeze(handle);
|
|
2321
1660
|
};
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
const
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
const
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
1661
|
+
const createSourceRegistry = (scheduler) => {
|
|
1662
|
+
const documents = /* @__PURE__ */ new Map();
|
|
1663
|
+
const readables = /* @__PURE__ */ new Map();
|
|
1664
|
+
const externalValues = /* @__PURE__ */ new WeakMap();
|
|
1665
|
+
const externalCollections = /* @__PURE__ */ new WeakMap();
|
|
1666
|
+
const documentConnection = (runtime) => {
|
|
1667
|
+
const state = accessOf(runtime);
|
|
1668
|
+
const cached = documents.get(state);
|
|
1669
|
+
if (cached) return cached;
|
|
1670
|
+
if (state.disposed) throw new Error("Document has been disposed.");
|
|
1671
|
+
const bindings = /* @__PURE__ */ new Set();
|
|
1672
|
+
const guard = (locked) => {
|
|
1673
|
+
state.projectionLocks = (state.projectionLocks ?? 0) + (locked ? 1 : -1);
|
|
1674
|
+
};
|
|
1675
|
+
scheduler.guards.add(guard);
|
|
1676
|
+
let closed = false;
|
|
1677
|
+
let detach = () => void 0;
|
|
1678
|
+
const connection = {
|
|
1679
|
+
add(binding) {
|
|
1680
|
+
if (closed) throw new Error("Document projection connection is closed.");
|
|
1681
|
+
bindings.add(binding);
|
|
1682
|
+
let active = true;
|
|
1683
|
+
return () => {
|
|
1684
|
+
if (!active) return;
|
|
1685
|
+
active = false;
|
|
1686
|
+
bindings.delete(binding);
|
|
1687
|
+
if (!bindings.size) connection.close();
|
|
1688
|
+
};
|
|
1689
|
+
},
|
|
1690
|
+
close() {
|
|
1691
|
+
if (closed) return;
|
|
1692
|
+
closed = true;
|
|
1693
|
+
detach();
|
|
1694
|
+
bindings.clear();
|
|
1695
|
+
scheduler.guards.delete(guard);
|
|
1696
|
+
documents.delete(state);
|
|
1697
|
+
}
|
|
2349
1698
|
};
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
1699
|
+
detach = attachProjection(runtime, {
|
|
1700
|
+
capture: (commit) => {
|
|
1701
|
+
for (const binding of bindings) if (binding.matches(commit)) binding.capture(commit);
|
|
1702
|
+
},
|
|
1703
|
+
settle: scheduler.settle,
|
|
1704
|
+
flush: scheduler.flush,
|
|
1705
|
+
dispose: () => {
|
|
1706
|
+
for (const binding of bindings) binding.dispose();
|
|
1707
|
+
scheduler.run();
|
|
2356
1708
|
}
|
|
2357
1709
|
});
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
const check = () => {
|
|
2361
|
-
if (node.disposed || !scheduler.active) throw new ProjectionDisposedError();
|
|
2362
|
-
if (node.fault) throw node.fault;
|
|
1710
|
+
documents.set(state, connection);
|
|
1711
|
+
return connection;
|
|
2363
1712
|
};
|
|
2364
|
-
const
|
|
2365
|
-
|
|
2366
|
-
const
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
1713
|
+
const valueInput = (definition) => {
|
|
1714
|
+
if (definition.source.kind !== "value-input") throw new Error("Invalid value input source.");
|
|
1715
|
+
const source = definition.source;
|
|
1716
|
+
const boundary = createValueBoundary(scheduler, "projection input", source.initial, source.equality);
|
|
1717
|
+
return {
|
|
1718
|
+
...boundary,
|
|
1719
|
+
write: {
|
|
1720
|
+
kind: "value",
|
|
1721
|
+
set(value) {
|
|
1722
|
+
scheduler.assertIdle();
|
|
1723
|
+
boundary.setLatest(value);
|
|
1724
|
+
boundary.mark();
|
|
1725
|
+
scheduler.run();
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
2374
1728
|
};
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
1729
|
+
};
|
|
1730
|
+
const collectionInput = (definition) => {
|
|
1731
|
+
if (definition.source.kind !== "collection-input") throw new Error("Invalid collection input source.");
|
|
1732
|
+
const values = new Map(definition.source.initial);
|
|
1733
|
+
const boundary = createCollectionBoundary(scheduler, "projection collection input", () => mapRead(values), definition.output.equality);
|
|
1734
|
+
return {
|
|
1735
|
+
...boundary,
|
|
1736
|
+
write: {
|
|
1737
|
+
kind: "collection",
|
|
1738
|
+
update(run) {
|
|
1739
|
+
scheduler.assertIdle();
|
|
1740
|
+
const staged = /* @__PURE__ */ new Map();
|
|
1741
|
+
const operations = [];
|
|
1742
|
+
let active = true;
|
|
1743
|
+
const draft = Object.freeze({
|
|
1744
|
+
get: (key) => {
|
|
1745
|
+
assertScope(() => active);
|
|
1746
|
+
const entry = staged.get(key);
|
|
1747
|
+
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
1748
|
+
},
|
|
1749
|
+
has: (key) => {
|
|
1750
|
+
assertScope(() => active);
|
|
1751
|
+
const entry = staged.get(key);
|
|
1752
|
+
return entry ? entry.present : values.has(key);
|
|
1753
|
+
},
|
|
1754
|
+
set: (key, value) => {
|
|
1755
|
+
assertScope(() => active);
|
|
1756
|
+
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
1757
|
+
staged.set(key, {
|
|
1758
|
+
present: true,
|
|
1759
|
+
value
|
|
1760
|
+
});
|
|
1761
|
+
operations.push({
|
|
1762
|
+
kind: "set",
|
|
1763
|
+
key,
|
|
1764
|
+
value
|
|
1765
|
+
});
|
|
1766
|
+
},
|
|
1767
|
+
remove: (key) => {
|
|
1768
|
+
assertScope(() => active);
|
|
1769
|
+
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
1770
|
+
staged.set(key, { present: false });
|
|
1771
|
+
operations.push({
|
|
1772
|
+
kind: "remove",
|
|
1773
|
+
key
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
});
|
|
1777
|
+
try {
|
|
1778
|
+
assertSynchronous(run(draft));
|
|
1779
|
+
} finally {
|
|
1780
|
+
active = false;
|
|
1781
|
+
}
|
|
1782
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
1783
|
+
let structural = false;
|
|
1784
|
+
let changed = false;
|
|
1785
|
+
for (const operation of operations) {
|
|
1786
|
+
const present = values.has(operation.key);
|
|
1787
|
+
if (operation.kind === "set") {
|
|
1788
|
+
if (present && Object.is(values.get(operation.key), operation.value)) continue;
|
|
1789
|
+
structural ||= !present;
|
|
1790
|
+
values.set(operation.key, operation.value);
|
|
1791
|
+
} else {
|
|
1792
|
+
if (!present) continue;
|
|
1793
|
+
structural = true;
|
|
1794
|
+
values.delete(operation.key);
|
|
1795
|
+
}
|
|
1796
|
+
candidates.add(operation.key);
|
|
1797
|
+
changed = true;
|
|
1798
|
+
}
|
|
1799
|
+
if (!changed) return;
|
|
1800
|
+
boundary.mark({
|
|
1801
|
+
candidates,
|
|
1802
|
+
orderMayChange: structural
|
|
1803
|
+
});
|
|
1804
|
+
scheduler.run();
|
|
1805
|
+
}
|
|
2393
1806
|
}
|
|
2394
|
-
}
|
|
2395
|
-
changedOutputs = Object.freeze(states.flatMap((state, index) => state.seal(build, spec.outputs[index].isEqual ?? Object.is) ? [outputs[index]] : []));
|
|
2396
|
-
return changedOutputs.length > 0;
|
|
1807
|
+
};
|
|
2397
1808
|
};
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
1809
|
+
const documentValue = (definition) => {
|
|
1810
|
+
if (definition.source.kind !== "document-value") throw new Error("Invalid document value source.");
|
|
1811
|
+
const { document, selector } = definition.source;
|
|
1812
|
+
const state = accessOf(document);
|
|
1813
|
+
const address = selector?.address ?? Object.freeze([]);
|
|
1814
|
+
const schemaNode = () => nodeAt(state.schema, address, state.document);
|
|
1815
|
+
const read = () => {
|
|
1816
|
+
if (state.disposed) throw new Error("Document has been disposed.");
|
|
1817
|
+
const node = schemaNode();
|
|
1818
|
+
if (!node) return void 0;
|
|
1819
|
+
return copyValue(node, selector ? read$1(state.document, address, state.schema) : state.document);
|
|
1820
|
+
};
|
|
1821
|
+
const boundary = createValueBoundary(scheduler, "document source", read(), (left, right) => {
|
|
1822
|
+
const node = schemaNode();
|
|
1823
|
+
return node ? equalValue(node, left, right) : Object.is(left, right);
|
|
1824
|
+
});
|
|
1825
|
+
const remove = documentConnection(document).add({
|
|
1826
|
+
matches: (commit) => !selector || affectsTarget(commit.impact, selector),
|
|
1827
|
+
capture: (commit) => {
|
|
1828
|
+
try {
|
|
1829
|
+
boundary.setLatest(read());
|
|
1830
|
+
boundary.mark({ reset: commit.impact.kind === "reset" });
|
|
1831
|
+
} catch (cause) {
|
|
1832
|
+
boundary.fail(cause);
|
|
1833
|
+
}
|
|
1834
|
+
},
|
|
1835
|
+
dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
|
|
1836
|
+
});
|
|
1837
|
+
boundary.detach(remove);
|
|
1838
|
+
return boundary;
|
|
1839
|
+
};
|
|
1840
|
+
const documentCollection = (definition) => {
|
|
1841
|
+
if (definition.source.kind !== "document-collection") throw new Error("Invalid document collection source.");
|
|
1842
|
+
const { document, selector } = definition.source;
|
|
1843
|
+
const state = accessOf(document);
|
|
1844
|
+
const read = (active) => {
|
|
1845
|
+
if (state.disposed) throw new Error("Document has been disposed.");
|
|
1846
|
+
const raw = collectionAccess({
|
|
1847
|
+
state,
|
|
1848
|
+
active
|
|
1849
|
+
}, selector.address);
|
|
1850
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
1851
|
+
return Object.freeze({
|
|
1852
|
+
get: (key) => {
|
|
1853
|
+
assertScope(active);
|
|
1854
|
+
if (!snapshots.has(key)) snapshots.set(key, snapshot(raw.get(key)));
|
|
1855
|
+
return snapshots.get(key);
|
|
1856
|
+
},
|
|
1857
|
+
has: (key) => {
|
|
1858
|
+
assertScope(active);
|
|
1859
|
+
return raw.has(key);
|
|
1860
|
+
},
|
|
1861
|
+
ids: () => {
|
|
1862
|
+
assertScope(active);
|
|
1863
|
+
return raw.ids();
|
|
1864
|
+
}
|
|
1865
|
+
});
|
|
1866
|
+
};
|
|
1867
|
+
const collectionNode = () => nodeAt(state.schema, selector.address, state.document);
|
|
1868
|
+
const boundary = createCollectionBoundary(scheduler, "document collection source", read, (left, right) => {
|
|
1869
|
+
const node = collectionNode();
|
|
1870
|
+
const entry = node && collectionEntryNode(node);
|
|
1871
|
+
return entry ? equalValue(entry, left, right) : Object.is(left, right);
|
|
1872
|
+
});
|
|
1873
|
+
const remove = documentConnection(document).add({
|
|
1874
|
+
matches: (commit) => affectsTarget(commit.impact, selector),
|
|
1875
|
+
capture: (commit) => {
|
|
1876
|
+
const impact = collectionImpact(commit.impact, selector);
|
|
1877
|
+
if (impact.kind === "reset") {
|
|
1878
|
+
boundary.mark({ reset: true });
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
const candidates = new Set([
|
|
1882
|
+
...impact.added,
|
|
1883
|
+
...impact.updated,
|
|
1884
|
+
...impact.removed
|
|
1885
|
+
]);
|
|
1886
|
+
boundary.mark({
|
|
1887
|
+
candidates,
|
|
1888
|
+
orderMayChange: Boolean(impact.added.size || impact.removed.size || impact.orderChanged)
|
|
1889
|
+
});
|
|
1890
|
+
},
|
|
1891
|
+
dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
|
|
1892
|
+
});
|
|
1893
|
+
boundary.detach(remove);
|
|
1894
|
+
return boundary;
|
|
1895
|
+
};
|
|
1896
|
+
const readable = (definition) => {
|
|
1897
|
+
if (definition.source.kind !== "readable") throw new Error("Invalid Readable source.");
|
|
1898
|
+
const { readable, equality } = definition.source;
|
|
1899
|
+
let initial = readable.current();
|
|
1900
|
+
const boundary = createValueBoundary(scheduler, "external readable", initial, equality);
|
|
1901
|
+
const member = {
|
|
1902
|
+
boundary,
|
|
1903
|
+
receive(value, metadata) {
|
|
1904
|
+
initial = value;
|
|
1905
|
+
boundary.setLatest(value);
|
|
1906
|
+
boundary.mark(metadata);
|
|
2420
1907
|
}
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
1908
|
+
};
|
|
1909
|
+
let connection = readables.get(readable);
|
|
1910
|
+
if (!connection) {
|
|
1911
|
+
const members = /* @__PURE__ */ new Set();
|
|
1912
|
+
let closed = false;
|
|
1913
|
+
let unsubscribe;
|
|
1914
|
+
let detachDocument;
|
|
1915
|
+
const receive = (settle) => {
|
|
1916
|
+
if (closed || !scheduler.active) return;
|
|
1917
|
+
try {
|
|
1918
|
+
const value = readable.current();
|
|
1919
|
+
members.forEach((next) => next.receive(value));
|
|
1920
|
+
} catch (cause) {
|
|
1921
|
+
members.forEach((next) => next.boundary.fail(cause));
|
|
1922
|
+
}
|
|
1923
|
+
if (settle) scheduler.run();
|
|
1924
|
+
};
|
|
1925
|
+
const created = {
|
|
1926
|
+
members,
|
|
1927
|
+
close() {
|
|
1928
|
+
if (closed) return;
|
|
1929
|
+
closed = true;
|
|
1930
|
+
unsubscribe?.();
|
|
1931
|
+
detachDocument?.();
|
|
1932
|
+
readables.delete(readable);
|
|
1933
|
+
}
|
|
1934
|
+
};
|
|
1935
|
+
const owner = documentReadableOwner(readable);
|
|
1936
|
+
if (owner) detachDocument = documentConnection(owner).add({
|
|
1937
|
+
matches: () => true,
|
|
1938
|
+
capture: () => receive(false),
|
|
1939
|
+
dispose: () => {
|
|
1940
|
+
members.forEach((next) => next.boundary.fail(/* @__PURE__ */ new Error("Document has been disposed.")));
|
|
1941
|
+
}
|
|
2438
1942
|
});
|
|
2439
|
-
|
|
1943
|
+
unsubscribe = readable.subscribe(() => receive(true));
|
|
1944
|
+
connection = created;
|
|
1945
|
+
readables.set(readable, connection);
|
|
2440
1946
|
}
|
|
1947
|
+
connection.members.add(member);
|
|
1948
|
+
boundary.detach(() => {
|
|
1949
|
+
connection.members.delete(member);
|
|
1950
|
+
if (!connection.members.size) connection.close();
|
|
1951
|
+
});
|
|
1952
|
+
return boundary;
|
|
2441
1953
|
};
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
node.clear();
|
|
2452
|
-
});
|
|
2453
|
-
const handles = outputs.map((output, index) => {
|
|
2454
|
-
const handle = {
|
|
2455
|
-
kind: "collection",
|
|
2456
|
-
current: () => {
|
|
2457
|
-
check();
|
|
2458
|
-
return states[index].current(check);
|
|
2459
|
-
},
|
|
2460
|
-
revision: () => {
|
|
2461
|
-
check();
|
|
2462
|
-
return states[index].revision();
|
|
2463
|
-
},
|
|
2464
|
-
subscribe: (listener) => {
|
|
2465
|
-
check();
|
|
2466
|
-
states[index].subscribe(listener);
|
|
2467
|
-
return () => states[index].unsubscribe(listener);
|
|
1954
|
+
const externalValue = (definition) => {
|
|
1955
|
+
if (definition.source.kind !== "external-value") throw new Error("Invalid external value source.");
|
|
1956
|
+
const { source, equality } = definition.source;
|
|
1957
|
+
const boundary = createValueBoundary(scheduler, "external source", source.current(), equality);
|
|
1958
|
+
const member = {
|
|
1959
|
+
boundary,
|
|
1960
|
+
receive(value, metadata) {
|
|
1961
|
+
boundary.setLatest(value);
|
|
1962
|
+
boundary.mark(metadata);
|
|
2468
1963
|
}
|
|
2469
1964
|
};
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
const map = (source, mapper, options) => {
|
|
2483
|
-
sources.assertCollection(source);
|
|
2484
|
-
const calculate = (id, entry) => {
|
|
2485
|
-
const value = mapper(id, entry);
|
|
2486
|
-
assertSynchronous(value);
|
|
2487
|
-
return value;
|
|
2488
|
-
};
|
|
2489
|
-
return createCollection(scheduler, {
|
|
2490
|
-
sources: { source },
|
|
2491
|
-
isEqual: options?.isEqual,
|
|
2492
|
-
build: ({ sources, output }) => {
|
|
2493
|
-
const read = sources.source.read;
|
|
2494
|
-
const ids = read.ids();
|
|
2495
|
-
profile.collectionView.idsScanned(ids.length);
|
|
2496
|
-
for (const id of ids) {
|
|
2497
|
-
profile.collectionView.mapped();
|
|
2498
|
-
output.set(id, calculate(id, read.get(id)));
|
|
1965
|
+
let connection = externalValues.get(source);
|
|
1966
|
+
if (!connection) {
|
|
1967
|
+
const members = /* @__PURE__ */ new Set();
|
|
1968
|
+
let closed = false;
|
|
1969
|
+
let unsubscribe;
|
|
1970
|
+
const created = {
|
|
1971
|
+
members,
|
|
1972
|
+
close() {
|
|
1973
|
+
if (closed) return;
|
|
1974
|
+
closed = true;
|
|
1975
|
+
unsubscribe?.();
|
|
1976
|
+
externalValues.delete(source);
|
|
2499
1977
|
}
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
}
|
|
2511
|
-
for (const entry of change.updated) {
|
|
2512
|
-
profile.collectionView.mapped();
|
|
2513
|
-
output.set(entry.key, calculate(entry.key, entry.after));
|
|
2514
|
-
}
|
|
2515
|
-
for (const entry of change.removed) output.remove(entry.key);
|
|
2516
|
-
if (change.added.length || change.removed.length || change.order) {
|
|
2517
|
-
const ids = read.ids();
|
|
2518
|
-
profile.collectionView.idsScanned(ids.length);
|
|
2519
|
-
output.order(ids);
|
|
1978
|
+
};
|
|
1979
|
+
try {
|
|
1980
|
+
unsubscribe = source.subscribe((event) => {
|
|
1981
|
+
try {
|
|
1982
|
+
members.forEach((next) => next.receive(event.value, {
|
|
1983
|
+
reset: event.reset,
|
|
1984
|
+
cause: event.cause
|
|
1985
|
+
}));
|
|
1986
|
+
} catch (cause) {
|
|
1987
|
+
members.forEach((next) => next.boundary.fail(cause));
|
|
2520
1988
|
}
|
|
2521
|
-
|
|
1989
|
+
scheduler.run();
|
|
1990
|
+
});
|
|
1991
|
+
} catch (cause) {
|
|
1992
|
+
created.close();
|
|
1993
|
+
throw cause;
|
|
2522
1994
|
}
|
|
1995
|
+
connection = created;
|
|
1996
|
+
externalValues.set(source, connection);
|
|
1997
|
+
}
|
|
1998
|
+
connection.members.add(member);
|
|
1999
|
+
boundary.detach(() => {
|
|
2000
|
+
connection.members.delete(member);
|
|
2001
|
+
if (!connection.members.size) connection.close();
|
|
2523
2002
|
});
|
|
2003
|
+
return boundary;
|
|
2524
2004
|
};
|
|
2525
|
-
const
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2005
|
+
const externalCollection = (definition) => {
|
|
2006
|
+
if (definition.source.kind !== "external-collection") throw new Error("Invalid external collection source.");
|
|
2007
|
+
const source = definition.source.source;
|
|
2008
|
+
const boundary = createCollectionBoundary(scheduler, "external collection source", () => {
|
|
2009
|
+
const current = source.current();
|
|
2010
|
+
return Object.freeze({
|
|
2011
|
+
get: (key) => current.get(key),
|
|
2012
|
+
has: (key) => current.has(key),
|
|
2013
|
+
ids: () => current.ids()
|
|
2014
|
+
});
|
|
2015
|
+
}, definition.output.equality);
|
|
2016
|
+
let connection = externalCollections.get(source);
|
|
2017
|
+
if (!connection) {
|
|
2018
|
+
const members = /* @__PURE__ */ new Set();
|
|
2019
|
+
let closed = false;
|
|
2020
|
+
let unsubscribe;
|
|
2021
|
+
const created = {
|
|
2022
|
+
members,
|
|
2023
|
+
close() {
|
|
2024
|
+
if (closed) return;
|
|
2025
|
+
closed = true;
|
|
2026
|
+
unsubscribe?.();
|
|
2027
|
+
externalCollections.delete(source);
|
|
2028
|
+
}
|
|
2029
|
+
};
|
|
2541
2030
|
try {
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2031
|
+
unsubscribe = source.subscribe((event) => {
|
|
2032
|
+
try {
|
|
2033
|
+
const impact = event.impact;
|
|
2034
|
+
if (impact?.kind === "reset") members.forEach((next) => next.mark({
|
|
2035
|
+
reset: true,
|
|
2036
|
+
cause: event.cause
|
|
2037
|
+
}));
|
|
2038
|
+
else if (impact?.kind === "incremental") {
|
|
2039
|
+
const candidates = new Set([
|
|
2040
|
+
...impact.added,
|
|
2041
|
+
...impact.updated,
|
|
2042
|
+
...impact.removed
|
|
2043
|
+
]);
|
|
2044
|
+
members.forEach((next) => next.mark({
|
|
2045
|
+
candidates,
|
|
2046
|
+
orderMayChange: Boolean(impact.added.size || impact.removed.size || impact.orderChanged),
|
|
2047
|
+
cause: event.cause
|
|
2048
|
+
}));
|
|
2049
|
+
} else members.forEach((next) => next.mark({
|
|
2050
|
+
fullScan: true,
|
|
2051
|
+
orderMayChange: true,
|
|
2052
|
+
cause: event.cause
|
|
2053
|
+
}));
|
|
2054
|
+
} catch (cause) {
|
|
2055
|
+
members.forEach((next) => next.fail(cause));
|
|
2056
|
+
}
|
|
2057
|
+
scheduler.run();
|
|
2058
|
+
});
|
|
2059
|
+
} catch (cause) {
|
|
2060
|
+
created.close();
|
|
2061
|
+
throw cause;
|
|
2545
2062
|
}
|
|
2063
|
+
connection = created;
|
|
2064
|
+
externalCollections.set(source, connection);
|
|
2546
2065
|
}
|
|
2066
|
+
connection.members.add(boundary);
|
|
2067
|
+
boundary.detach(() => {
|
|
2068
|
+
connection.members.delete(boundary);
|
|
2069
|
+
if (!connection.members.size) connection.close();
|
|
2070
|
+
});
|
|
2071
|
+
return boundary;
|
|
2547
2072
|
};
|
|
2548
|
-
return
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
};
|
|
2560
|
-
const trackAll = () => {
|
|
2561
|
-
if (!activeTracker) return;
|
|
2562
|
-
activeTracker.all = true;
|
|
2563
|
-
};
|
|
2564
|
-
const mapView = (read) => {
|
|
2565
|
-
const keys = read.ids();
|
|
2566
|
-
const source = /* @__PURE__ */ new Map();
|
|
2567
|
-
for (const key of keys) source.set(key, read.get(key));
|
|
2568
|
-
const view = {
|
|
2569
|
-
get: (key) => {
|
|
2570
|
-
trackKey(key);
|
|
2571
|
-
return source.get(key);
|
|
2572
|
-
},
|
|
2573
|
-
has: (key) => {
|
|
2574
|
-
trackKey(key);
|
|
2575
|
-
return source.has(key);
|
|
2576
|
-
},
|
|
2577
|
-
get size() {
|
|
2578
|
-
trackStructure();
|
|
2579
|
-
return source.size;
|
|
2580
|
-
},
|
|
2581
|
-
keys: () => {
|
|
2582
|
-
trackStructure();
|
|
2583
|
-
return source.keys();
|
|
2584
|
-
},
|
|
2585
|
-
values: () => {
|
|
2586
|
-
trackAll();
|
|
2587
|
-
return source.values();
|
|
2588
|
-
},
|
|
2589
|
-
entries: () => {
|
|
2590
|
-
trackAll();
|
|
2591
|
-
return source.entries();
|
|
2592
|
-
},
|
|
2593
|
-
forEach: (callback, thisArg) => {
|
|
2594
|
-
trackAll();
|
|
2595
|
-
source.forEach((value, key) => callback.call(thisArg, value, key, view));
|
|
2596
|
-
},
|
|
2597
|
-
[Symbol.iterator]: () => {
|
|
2598
|
-
trackAll();
|
|
2599
|
-
return source[Symbol.iterator]();
|
|
2600
|
-
}
|
|
2601
|
-
};
|
|
2602
|
-
return Object.freeze(view);
|
|
2603
|
-
};
|
|
2604
|
-
const lazyMapView = (read) => {
|
|
2605
|
-
const ids = read.ids();
|
|
2606
|
-
const entries = function* () {
|
|
2607
|
-
for (const key of ids) yield [key, read.get(key)];
|
|
2608
|
-
};
|
|
2609
|
-
const values = function* () {
|
|
2610
|
-
for (const key of ids) yield read.get(key);
|
|
2611
|
-
};
|
|
2612
|
-
const view = {
|
|
2613
|
-
get: (key) => {
|
|
2614
|
-
trackKey(key);
|
|
2615
|
-
return read.get(key);
|
|
2616
|
-
},
|
|
2617
|
-
has: (key) => {
|
|
2618
|
-
trackKey(key);
|
|
2619
|
-
return read.has(key);
|
|
2620
|
-
},
|
|
2621
|
-
get size() {
|
|
2622
|
-
trackStructure();
|
|
2623
|
-
return ids.length;
|
|
2624
|
-
},
|
|
2625
|
-
keys: () => {
|
|
2626
|
-
trackStructure();
|
|
2627
|
-
return ids[Symbol.iterator]();
|
|
2628
|
-
},
|
|
2629
|
-
values: () => {
|
|
2630
|
-
trackAll();
|
|
2631
|
-
return values();
|
|
2632
|
-
},
|
|
2633
|
-
entries: () => {
|
|
2634
|
-
trackAll();
|
|
2635
|
-
return entries();
|
|
2636
|
-
},
|
|
2637
|
-
forEach: (callback, thisArg) => {
|
|
2638
|
-
trackAll();
|
|
2639
|
-
for (const key of ids) callback.call(thisArg, read.get(key), key, view);
|
|
2073
|
+
return {
|
|
2074
|
+
materialize(definition) {
|
|
2075
|
+
switch (definition.source.kind) {
|
|
2076
|
+
case "value-input": return valueInput(definition);
|
|
2077
|
+
case "collection-input": return collectionInput(definition);
|
|
2078
|
+
case "document-value": return documentValue(definition);
|
|
2079
|
+
case "document-collection": return documentCollection(definition);
|
|
2080
|
+
case "readable": return readable(definition);
|
|
2081
|
+
case "external-value": return externalValue(definition);
|
|
2082
|
+
case "external-collection": return externalCollection(definition);
|
|
2083
|
+
}
|
|
2640
2084
|
},
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2085
|
+
dispose() {
|
|
2086
|
+
documents.forEach((connection) => connection.close());
|
|
2087
|
+
readables.forEach((connection) => connection.close());
|
|
2088
|
+
documents.clear();
|
|
2089
|
+
readables.clear();
|
|
2644
2090
|
}
|
|
2645
2091
|
};
|
|
2646
|
-
return Object.freeze(view);
|
|
2647
|
-
};
|
|
2648
|
-
const isMapLike = (value) => value instanceof Map || value !== null && typeof value === "object" && typeof value.get === "function" && typeof value.keys === "function" && typeof value.entries === "function";
|
|
2649
|
-
const mapRead = (value) => ({
|
|
2650
|
-
get: (key) => value.get(key),
|
|
2651
|
-
has: (key) => value.has(key),
|
|
2652
|
-
ids: () => Object.freeze([...value.keys()])
|
|
2653
|
-
});
|
|
2654
|
-
const mapChange = (previous, current) => {
|
|
2655
|
-
const added = [];
|
|
2656
|
-
const removed = [];
|
|
2657
|
-
const updated = [];
|
|
2658
|
-
for (const key of previous.keys()) if (!current.has(key)) removed.push({
|
|
2659
|
-
key,
|
|
2660
|
-
before: previous.get(key)
|
|
2661
|
-
});
|
|
2662
|
-
else if (!Object.is(previous.get(key), current.get(key))) updated.push({
|
|
2663
|
-
key,
|
|
2664
|
-
before: previous.get(key),
|
|
2665
|
-
after: current.get(key)
|
|
2666
|
-
});
|
|
2667
|
-
for (const key of current.keys()) if (!previous.has(key)) added.push({
|
|
2668
|
-
key,
|
|
2669
|
-
after: current.get(key)
|
|
2670
|
-
});
|
|
2671
|
-
const before = [...previous.keys()].filter((key) => current.has(key));
|
|
2672
|
-
const after = [...current.keys()].filter((key) => previous.has(key));
|
|
2673
|
-
const orderChanged = before.length !== after.length || before.some((key, index) => key !== after[index]);
|
|
2674
|
-
if (!added.length && !removed.length && !updated.length && !orderChanged) return void 0;
|
|
2675
|
-
return {
|
|
2676
|
-
kind: "incremental",
|
|
2677
|
-
added: Object.freeze(added),
|
|
2678
|
-
removed: Object.freeze(removed),
|
|
2679
|
-
updated: Object.freeze(updated),
|
|
2680
|
-
...orderChanged ? { order: Object.freeze({
|
|
2681
|
-
before: Object.freeze([...previous.keys()]),
|
|
2682
|
-
after: Object.freeze([...current.keys()])
|
|
2683
|
-
}) } : {}
|
|
2684
|
-
};
|
|
2685
|
-
};
|
|
2686
|
-
const sameSelection = (left, right) => {
|
|
2687
|
-
if (left.all !== right.all || left.structure !== right.structure || left.keys.size !== right.keys.size) return false;
|
|
2688
|
-
for (const key of left.keys) if (!right.keys.has(key)) return false;
|
|
2689
|
-
return true;
|
|
2690
|
-
};
|
|
2691
|
-
const selectionAffects = (selection, change) => {
|
|
2692
|
-
if (!change || change.kind === "reset") return true;
|
|
2693
|
-
if (selection.all && (change.added.length || change.removed.length || change.updated.length || change.order)) return true;
|
|
2694
|
-
if (selection.structure && (change.added.length || change.removed.length || change.order)) return true;
|
|
2695
|
-
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;
|
|
2696
|
-
return false;
|
|
2697
2092
|
};
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
if ("kind" in event && event.kind === "value") return event.value;
|
|
2701
|
-
if ("kind" in event && event.kind === "document") return snapshot(event.read);
|
|
2702
|
-
if ("kind" in event && event.kind === "collection") return mapView(event.read);
|
|
2703
|
-
return event;
|
|
2704
|
-
};
|
|
2705
|
-
const sourceOf = (instance) => instance;
|
|
2093
|
+
//#endregion
|
|
2094
|
+
//#region core/src/projection/runtime.ts
|
|
2706
2095
|
const createProjectionRuntime = (options) => {
|
|
2707
|
-
const
|
|
2096
|
+
const scheduler = createScheduler(options?.onError ?? (() => void 0));
|
|
2097
|
+
const sources = createSourceRegistry(scheduler);
|
|
2708
2098
|
const materialized = /* @__PURE__ */ new WeakMap();
|
|
2709
|
-
const
|
|
2710
|
-
let disposed = false;
|
|
2099
|
+
const publicViews = /* @__PURE__ */ new WeakMap();
|
|
2711
2100
|
const scopes = /* @__PURE__ */ new Set();
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
const
|
|
2715
|
-
if (
|
|
2716
|
-
if (
|
|
2717
|
-
|
|
2718
|
-
const nodes = runtime.group({
|
|
2719
|
-
sources,
|
|
2720
|
-
outputs: definition.outputs,
|
|
2721
|
-
build: definition.build
|
|
2722
|
-
});
|
|
2723
|
-
const group = Object.freeze({
|
|
2724
|
-
definition,
|
|
2725
|
-
nodes
|
|
2726
|
-
});
|
|
2727
|
-
groups.set(definition, group);
|
|
2728
|
-
for (let index = 0; index < definition.projections.length; index++) materialized.set(definition.projections[index], {
|
|
2729
|
-
kind: "projection",
|
|
2730
|
-
node: nodes[index]
|
|
2731
|
-
});
|
|
2732
|
-
if (requester) requester.materialized.push(definition);
|
|
2733
|
-
return group;
|
|
2101
|
+
const assertAccess = (definition, requester) => {
|
|
2102
|
+
scheduler.assertActive();
|
|
2103
|
+
const owner = definition.owner;
|
|
2104
|
+
if (owner === void 0) return;
|
|
2105
|
+
if (owner !== requester) throw new TypeError("Projection belongs to another scope.");
|
|
2106
|
+
if (!requester?.active) throw new ProjectionDisposedError();
|
|
2734
2107
|
};
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
value: values.input.value,
|
|
2759
|
-
update: (next) => ({
|
|
2760
|
-
kind: "changed",
|
|
2761
|
-
value: next.input.value
|
|
2762
|
-
})
|
|
2763
|
-
})
|
|
2764
|
-
});
|
|
2765
|
-
break;
|
|
2766
|
-
}
|
|
2767
|
-
case "collection-input": {
|
|
2768
|
-
const source = runtime.collectionInput(definition.initial);
|
|
2769
|
-
write = {
|
|
2770
|
-
kind: "collection-input",
|
|
2771
|
-
source: source.source,
|
|
2772
|
-
update: source.update
|
|
2773
|
-
};
|
|
2774
|
-
instance = runtime.map(source.source, (_key, value) => value);
|
|
2775
|
-
break;
|
|
2776
|
-
}
|
|
2777
|
-
case "readable": {
|
|
2778
|
-
const source = runtime.fromReadable(definition.readable, { isEqual: definition.isEqual });
|
|
2779
|
-
instance = runtime.value({
|
|
2780
|
-
sources: { source },
|
|
2781
|
-
build: (values) => ({
|
|
2782
|
-
value: values.source.value,
|
|
2783
|
-
update: (next) => ({
|
|
2784
|
-
kind: "changed",
|
|
2785
|
-
value: next.source.value
|
|
2786
|
-
})
|
|
2787
|
-
})
|
|
2788
|
-
});
|
|
2789
|
-
break;
|
|
2790
|
-
}
|
|
2791
|
-
case "source-value":
|
|
2792
|
-
{
|
|
2793
|
-
const source = runtime.fromSource(definition.source);
|
|
2794
|
-
instance = runtime.value({
|
|
2795
|
-
sources: { source },
|
|
2796
|
-
build: (values) => ({
|
|
2797
|
-
value: values.source.value,
|
|
2798
|
-
update: (next) => ({
|
|
2799
|
-
kind: "changed",
|
|
2800
|
-
value: next.source.value
|
|
2801
|
-
})
|
|
2802
|
-
})
|
|
2803
|
-
});
|
|
2804
|
-
}
|
|
2805
|
-
break;
|
|
2806
|
-
case "source-collection": {
|
|
2807
|
-
const source = runtime.fromCollectionSource(definition.source);
|
|
2808
|
-
instance = runtime.map(source, (_key, value) => value);
|
|
2809
|
-
break;
|
|
2810
|
-
}
|
|
2811
|
-
case "document": {
|
|
2812
|
-
const document = runtime.document(definition.document);
|
|
2813
|
-
if (!definition.selector) {
|
|
2814
|
-
instance = runtime.value({
|
|
2815
|
-
sources: { document },
|
|
2816
|
-
build: (values) => ({
|
|
2817
|
-
value: snapshot(values.document.read),
|
|
2818
|
-
update: (next) => ({
|
|
2819
|
-
kind: "changed",
|
|
2820
|
-
value: snapshot(next.document.read)
|
|
2821
|
-
})
|
|
2822
|
-
})
|
|
2823
|
-
});
|
|
2824
|
-
break;
|
|
2825
|
-
}
|
|
2826
|
-
const state = accessOf(definition.document);
|
|
2827
|
-
try {
|
|
2828
|
-
const collection = document.collection(definition.selector);
|
|
2829
|
-
instance = runtime.map(collection, (_id, entry) => entry);
|
|
2830
|
-
break;
|
|
2831
|
-
} catch {
|
|
2832
|
-
const selected = document.targets(definition.selector);
|
|
2833
|
-
const address = compilePath(state.schema, "value", definition.selector).address;
|
|
2834
|
-
instance = runtime.value({
|
|
2835
|
-
sources: { selected },
|
|
2836
|
-
build: () => ({
|
|
2837
|
-
value: read$1(snapshot(state.document), address, state.schema),
|
|
2838
|
-
update: () => ({
|
|
2839
|
-
kind: "changed",
|
|
2840
|
-
value: read$1(snapshot(state.document), address, state.schema)
|
|
2841
|
-
})
|
|
2842
|
-
})
|
|
2843
|
-
});
|
|
2844
|
-
}
|
|
2845
|
-
break;
|
|
2846
|
-
}
|
|
2847
|
-
case "derive": {
|
|
2848
|
-
const sources = Object.fromEntries(definition.dependencies.map((dependency, index) => [`d${index}`, sourceOf(materialize(dependency, owner))]));
|
|
2849
|
-
instance = runtime.value({
|
|
2850
|
-
sources,
|
|
2851
|
-
build: (values) => ({
|
|
2852
|
-
value: definition.compute(...Object.values(values).map(currentValue)),
|
|
2853
|
-
update: (next) => ({
|
|
2854
|
-
kind: "changed",
|
|
2855
|
-
value: definition.compute(...Object.values(next).map(currentValue))
|
|
2856
|
-
})
|
|
2857
|
-
})
|
|
2858
|
-
}, { isEqual: definition.isEqual });
|
|
2859
|
-
break;
|
|
2860
|
-
}
|
|
2861
|
-
case "incremental-value": {
|
|
2862
|
-
const sources = Object.fromEntries(Object.entries(definition.dependencies).map(([key, dependency]) => [key, sourceOf(materialize(dependency, owner))]));
|
|
2863
|
-
instance = runtime.value({
|
|
2864
|
-
sources,
|
|
2865
|
-
build: definition.build
|
|
2866
|
-
}, { isEqual: definition.isEqual });
|
|
2867
|
-
break;
|
|
2868
|
-
}
|
|
2869
|
-
case "incremental-collection": {
|
|
2870
|
-
const sources = Object.fromEntries(Object.entries(definition.dependencies).map(([key, dependency]) => [key, sourceOf(materialize(dependency, owner))]));
|
|
2871
|
-
instance = runtime.collection({
|
|
2872
|
-
sources,
|
|
2873
|
-
build: definition.build,
|
|
2874
|
-
isEqual: definition.isEqual,
|
|
2875
|
-
name: definition.name
|
|
2876
|
-
});
|
|
2877
|
-
break;
|
|
2878
|
-
}
|
|
2108
|
+
const rememberScoped = (definition, requester) => {
|
|
2109
|
+
if (requester && definition.owner === requester) requester.materialized.push(definition);
|
|
2110
|
+
};
|
|
2111
|
+
const materializeProducer = (definition, requester) => {
|
|
2112
|
+
assertAccess(definition, requester);
|
|
2113
|
+
const existing = materialized.get(definition);
|
|
2114
|
+
if (existing) return existing;
|
|
2115
|
+
let result;
|
|
2116
|
+
if (definition.kind === "source") {
|
|
2117
|
+
const source = sources.materialize(definition);
|
|
2118
|
+
result = Object.freeze({
|
|
2119
|
+
record: source.producer,
|
|
2120
|
+
outputs: Object.freeze([source.output]),
|
|
2121
|
+
...source.write ? { write: source.write } : {}
|
|
2122
|
+
});
|
|
2123
|
+
} else {
|
|
2124
|
+
if (definition.owner === void 0 && definition.dependencies.some((dependency) => producerOf(dependency).owner !== void 0)) throw new TypeError("A root projection cannot depend on a scoped projection.");
|
|
2125
|
+
const dependencies = definition.dependencies.map((dependency) => resolveOutput(dependency, requester));
|
|
2126
|
+
const processor = createProcessor(scheduler, definition, Object.freeze(dependencies));
|
|
2127
|
+
result = Object.freeze({
|
|
2128
|
+
record: processor,
|
|
2129
|
+
outputs: processor.outputs
|
|
2130
|
+
});
|
|
2879
2131
|
}
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2132
|
+
materialized.set(definition, result);
|
|
2133
|
+
rememberScoped(definition, requester);
|
|
2134
|
+
return result;
|
|
2135
|
+
};
|
|
2136
|
+
const resolveOutput = (projection, requester) => {
|
|
2137
|
+
const ref = projectionRef(projection);
|
|
2138
|
+
const output = materializeProducer(ref.producer, requester).outputs[ref.output];
|
|
2139
|
+
if (!output) throw new TypeError("Projection output does not exist.");
|
|
2140
|
+
return output;
|
|
2141
|
+
};
|
|
2142
|
+
const assertOutput = (output) => {
|
|
2143
|
+
scheduler.assertActive();
|
|
2144
|
+
if (output.owner.disposed) throw new ProjectionDisposedError();
|
|
2145
|
+
if (output.owner.fault) throw output.owner.fault;
|
|
2890
2146
|
};
|
|
2891
|
-
const
|
|
2892
|
-
|
|
2893
|
-
const current =
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
if (record) record.collection = {
|
|
2147
|
+
const publicCurrent = (output) => {
|
|
2148
|
+
assertOutput(output);
|
|
2149
|
+
const current = output.current();
|
|
2150
|
+
const revision = output.revision();
|
|
2151
|
+
const cached = publicViews.get(output);
|
|
2152
|
+
if (cached?.revision === revision) return cached.value;
|
|
2153
|
+
if (output.kind === "collection") {
|
|
2154
|
+
const value = collectionView(current);
|
|
2155
|
+
publicViews.set(output, {
|
|
2901
2156
|
revision,
|
|
2902
2157
|
value
|
|
2903
|
-
};
|
|
2158
|
+
});
|
|
2904
2159
|
return value;
|
|
2905
2160
|
}
|
|
2906
2161
|
if (isMapLike(current)) {
|
|
2907
|
-
const
|
|
2908
|
-
|
|
2909
|
-
const revision = instance.revision();
|
|
2910
|
-
if (existing?.revision === revision) return existing.value;
|
|
2911
|
-
const value = mapView(mapRead(current));
|
|
2912
|
-
if (record) record.collection = {
|
|
2162
|
+
const value = snapshotCollectionView(mapRead(current));
|
|
2163
|
+
publicViews.set(output, {
|
|
2913
2164
|
revision,
|
|
2914
2165
|
value
|
|
2915
|
-
};
|
|
2166
|
+
});
|
|
2916
2167
|
return value;
|
|
2917
2168
|
}
|
|
2169
|
+
publicViews.delete(output);
|
|
2918
2170
|
return current;
|
|
2919
2171
|
};
|
|
2920
|
-
const
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
revision: () => materialize(projection, requester).revision(),
|
|
2924
|
-
subscribe: (listener) => materialize(projection, requester).subscribe(listener)
|
|
2925
|
-
});
|
|
2926
|
-
let initialized = false;
|
|
2927
|
-
let value;
|
|
2928
|
-
let selectedRevision = 0;
|
|
2929
|
-
let sourceRevision = -1;
|
|
2930
|
-
let selection = Object.freeze({
|
|
2931
|
-
keys: /* @__PURE__ */ new Set(),
|
|
2932
|
-
all: true,
|
|
2933
|
-
structure: false
|
|
2934
|
-
});
|
|
2935
|
-
let previousCollection;
|
|
2936
|
-
let unsubscribeSource;
|
|
2937
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
2938
|
-
const updateCollectionSnapshot = () => {
|
|
2939
|
-
const current = get(projection, requester);
|
|
2940
|
-
previousCollection = isMapLike(current) ? current : void 0;
|
|
2941
|
-
};
|
|
2942
|
-
const evaluate = () => {
|
|
2943
|
-
const tracker = {
|
|
2944
|
-
keys: /* @__PURE__ */ new Set(),
|
|
2945
|
-
all: false,
|
|
2946
|
-
structure: false
|
|
2947
|
-
};
|
|
2948
|
-
const previousTracker = activeTracker;
|
|
2949
|
-
activeTracker = tracker;
|
|
2950
|
-
let source;
|
|
2951
|
-
let next;
|
|
2952
|
-
try {
|
|
2953
|
-
source = get(projection, requester);
|
|
2954
|
-
next = selector(source);
|
|
2955
|
-
} finally {
|
|
2956
|
-
activeTracker = previousTracker;
|
|
2957
|
-
}
|
|
2958
|
-
const nextSelection = Object.freeze({
|
|
2959
|
-
keys: new Set(tracker.keys),
|
|
2960
|
-
all: tracker.all || isMapLike(source) && tracker.keys.size === 0,
|
|
2961
|
-
structure: tracker.structure
|
|
2962
|
-
});
|
|
2963
|
-
const changed = !initialized || !equality(value, next);
|
|
2964
|
-
if (changed) {
|
|
2965
|
-
if (initialized) selectedRevision++;
|
|
2966
|
-
value = next;
|
|
2967
|
-
}
|
|
2968
|
-
selection = nextSelection;
|
|
2969
|
-
initialized = true;
|
|
2970
|
-
sourceRevision = materialize(projection, requester).revision();
|
|
2971
|
-
updateCollectionSnapshot();
|
|
2972
|
-
return changed;
|
|
2973
|
-
};
|
|
2974
|
-
const ensureCurrent = () => {
|
|
2975
|
-
const revision = materialize(projection, requester).revision();
|
|
2976
|
-
if (!initialized || !unsubscribeSource && sourceRevision !== revision) evaluate();
|
|
2977
|
-
return value;
|
|
2978
|
-
};
|
|
2979
|
-
const onProjectionChange = (incoming) => {
|
|
2980
|
-
let change = incoming;
|
|
2981
|
-
if (!change && previousCollection) {
|
|
2982
|
-
const current = get(projection, requester);
|
|
2983
|
-
if (isMapLike(current)) {
|
|
2984
|
-
change = mapChange(previousCollection, current);
|
|
2985
|
-
previousCollection = current;
|
|
2986
|
-
if (!change) {
|
|
2987
|
-
sourceRevision = materialize(projection, requester).revision();
|
|
2988
|
-
return;
|
|
2989
|
-
}
|
|
2990
|
-
} else previousCollection = void 0;
|
|
2991
|
-
}
|
|
2992
|
-
if (!selectionAffects(selection, change)) {
|
|
2993
|
-
sourceRevision = materialize(projection, requester).revision();
|
|
2994
|
-
updateCollectionSnapshot();
|
|
2995
|
-
return;
|
|
2996
|
-
}
|
|
2997
|
-
const previousSelection = selection;
|
|
2998
|
-
const changed = evaluate();
|
|
2999
|
-
if (unsubscribeSource && !sameSelection(previousSelection, selection)) {
|
|
3000
|
-
unsubscribeSource();
|
|
3001
|
-
unsubscribeSource = materialize(projection, requester).subscribe(onProjectionChange);
|
|
3002
|
-
}
|
|
3003
|
-
if (changed) Array.from(listeners).forEach((listener) => listener());
|
|
3004
|
-
};
|
|
3005
|
-
const installSource = () => {
|
|
3006
|
-
if (!unsubscribeSource) unsubscribeSource = materialize(projection, requester).subscribe(onProjectionChange);
|
|
3007
|
-
};
|
|
2172
|
+
const get = (projection, requester) => publicCurrent(resolveOutput(projection, requester));
|
|
2173
|
+
const readableSource = (projection, requester) => {
|
|
2174
|
+
const output = resolveOutput(projection, requester);
|
|
3008
2175
|
return Object.freeze({
|
|
3009
|
-
|
|
2176
|
+
kind: output.kind,
|
|
2177
|
+
current: () => get(projection, requester),
|
|
3010
2178
|
revision: () => {
|
|
3011
|
-
|
|
3012
|
-
return
|
|
2179
|
+
assertOutput(output);
|
|
2180
|
+
return output.revision();
|
|
3013
2181
|
},
|
|
3014
|
-
subscribe:
|
|
3015
|
-
ensureCurrent();
|
|
3016
|
-
listeners.add(listener);
|
|
3017
|
-
installSource();
|
|
3018
|
-
return () => {
|
|
3019
|
-
listeners.delete(listener);
|
|
3020
|
-
if (!listeners.size && unsubscribeSource) {
|
|
3021
|
-
unsubscribeSource();
|
|
3022
|
-
unsubscribeSource = void 0;
|
|
3023
|
-
}
|
|
3024
|
-
};
|
|
3025
|
-
}
|
|
2182
|
+
subscribe: output.subscribe
|
|
3026
2183
|
});
|
|
3027
2184
|
};
|
|
2185
|
+
const makeReadable = (projection, selector, equality, requester) => {
|
|
2186
|
+
const source = readableSource(projection, requester);
|
|
2187
|
+
return selector ? createSelectorReadable(source, selector, equality) : createDirectReadable(source);
|
|
2188
|
+
};
|
|
3028
2189
|
function readable(projection, selector, equality) {
|
|
3029
2190
|
return makeReadable(projection, selector, equality);
|
|
3030
2191
|
}
|
|
2192
|
+
const inputWrite = (target, requester) => {
|
|
2193
|
+
const ref = projectionRef(target);
|
|
2194
|
+
if (ref.output !== 0 || ref.producer.kind !== "source") throw new TypeError("Projection is not an input.");
|
|
2195
|
+
if (ref.producer.source.kind !== "value-input" && ref.producer.source.kind !== "collection-input") throw new TypeError("Projection is not an input.");
|
|
2196
|
+
const write = materializeProducer(ref.producer, requester).write;
|
|
2197
|
+
if (!write) throw new TypeError("Projection input is not writable.");
|
|
2198
|
+
return write;
|
|
2199
|
+
};
|
|
3031
2200
|
const setInput = (target, value, requester) => {
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
record.set(value);
|
|
2201
|
+
const write = inputWrite(target, requester);
|
|
2202
|
+
if (write.kind !== "value") throw new TypeError("Projection is not a value input.");
|
|
2203
|
+
write.set(value);
|
|
3036
2204
|
};
|
|
3037
2205
|
const updateInput = (target, run, requester) => {
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
2206
|
+
const write = inputWrite(target, requester);
|
|
2207
|
+
if (write.kind !== "collection") throw new TypeError("Projection is not a collection input.");
|
|
2208
|
+
write.update(run);
|
|
2209
|
+
};
|
|
2210
|
+
function batch(optionsOrCallback, maybeCallback) {
|
|
2211
|
+
return typeof optionsOrCallback === "function" ? scheduler.batch(optionsOrCallback) : scheduler.batch(optionsOrCallback, maybeCallback);
|
|
2212
|
+
}
|
|
2213
|
+
const disposeScope = (state) => {
|
|
2214
|
+
if (!state.active) return;
|
|
2215
|
+
scheduler.assertIdle();
|
|
2216
|
+
state.active = false;
|
|
2217
|
+
state.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
2218
|
+
state.subscriptions.clear();
|
|
2219
|
+
for (let index = state.materialized.length - 1; index >= 0; index--) {
|
|
2220
|
+
const definition = state.materialized[index];
|
|
2221
|
+
const current = materialized.get(definition);
|
|
2222
|
+
if (!current) continue;
|
|
2223
|
+
scheduler.releaseProducer(current.record);
|
|
2224
|
+
materialized.delete(definition);
|
|
2225
|
+
}
|
|
2226
|
+
state.materialized.length = 0;
|
|
2227
|
+
scopes.delete(state);
|
|
3042
2228
|
};
|
|
3043
2229
|
const createScope = () => {
|
|
3044
|
-
|
|
2230
|
+
scheduler.assertIdle();
|
|
3045
2231
|
const state = {
|
|
3046
2232
|
active: true,
|
|
3047
2233
|
materialized: [],
|
|
@@ -3050,33 +2236,26 @@ const createProjectionRuntime = (options) => {
|
|
|
3050
2236
|
scopes.add(state);
|
|
3051
2237
|
const assertActive = () => {
|
|
3052
2238
|
if (!state.active) throw new ProjectionDisposedError();
|
|
2239
|
+
scheduler.assertActive();
|
|
3053
2240
|
};
|
|
3054
2241
|
const own = (projection) => {
|
|
3055
2242
|
assertActive();
|
|
3056
|
-
|
|
3057
|
-
const dependencies = definition.kind === "derive" ? definition.dependencies : definition.kind === "incremental-value" || definition.kind === "incremental-collection" ? Object.values(definition.dependencies) : definition.kind === "incremental-group-output" ? Object.values(definition.group.dependencies) : [];
|
|
3058
|
-
for (const dependency of dependencies) {
|
|
3059
|
-
const dependencyOwner = ownerOf(dependency);
|
|
3060
|
-
if (dependencyOwner && dependencyOwner !== state) throw new TypeError("A scope cannot depend on another scope.");
|
|
3061
|
-
}
|
|
3062
|
-
ownDefinition(projection, state);
|
|
3063
|
-
return projection;
|
|
2243
|
+
return ownProjection(projection, state);
|
|
3064
2244
|
};
|
|
3065
|
-
const scopedInput = Object.assign((initial, equality) => own(input(initial, equality)), { collection: (initial) => own(input.collection(initial)) });
|
|
3066
|
-
const scopedDerive = (dependencies, compute, equality) => own(derive(dependencies, compute, equality));
|
|
3067
2245
|
const ownOutputTree = (tree) => {
|
|
3068
2246
|
if (isProjection(tree)) return own(tree);
|
|
3069
2247
|
if (!tree || typeof tree !== "object") throw new TypeError("Incremental group output tree must be an object.");
|
|
3070
2248
|
for (const value of Object.values(tree)) ownOutputTree(value);
|
|
3071
2249
|
return tree;
|
|
3072
2250
|
};
|
|
2251
|
+
const scopedInput = Object.assign((initial, equality) => own(input(initial, equality)), { collection: (initial) => own(input.collection(initial)) });
|
|
2252
|
+
const scopedDerive = (dependencies, compute, equality) => own(derive(dependencies, compute, equality));
|
|
3073
2253
|
const scopedIncremental = Object.assign((dependencies, processor) => own(incremental(dependencies, processor)), {
|
|
3074
2254
|
collection: (dependencies, processor) => own(incremental.collection(dependencies, processor)),
|
|
3075
2255
|
group: (dependencies, defineOutputs, processor) => ownOutputTree(incremental.group(dependencies, defineOutputs, processor))
|
|
3076
2256
|
});
|
|
3077
2257
|
function scopeReadable(projection, selector, equality) {
|
|
3078
2258
|
assertActive();
|
|
3079
|
-
materialize(projection, state);
|
|
3080
2259
|
const source = makeReadable(projection, selector, equality, state);
|
|
3081
2260
|
return Object.freeze({
|
|
3082
2261
|
current: () => {
|
|
@@ -3104,7 +2283,7 @@ const createProjectionRuntime = (options) => {
|
|
|
3104
2283
|
}
|
|
3105
2284
|
function scopeBatch(optionsOrCallback, maybeCallback) {
|
|
3106
2285
|
assertActive();
|
|
3107
|
-
return typeof optionsOrCallback === "function" ?
|
|
2286
|
+
return typeof optionsOrCallback === "function" ? batch(optionsOrCallback) : batch(optionsOrCallback, maybeCallback);
|
|
3108
2287
|
}
|
|
3109
2288
|
return Object.freeze({
|
|
3110
2289
|
input: scopedInput,
|
|
@@ -3124,41 +2303,19 @@ const createProjectionRuntime = (options) => {
|
|
|
3124
2303
|
updateInput(target, run, state);
|
|
3125
2304
|
},
|
|
3126
2305
|
batch: scopeBatch,
|
|
3127
|
-
dispose: () =>
|
|
3128
|
-
if (!state.active) return;
|
|
3129
|
-
runtime.assertIdle();
|
|
3130
|
-
state.active = false;
|
|
3131
|
-
state.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
3132
|
-
for (const definition of state.materialized.reverse()) {
|
|
3133
|
-
const group = groups.get(definition);
|
|
3134
|
-
if (group) {
|
|
3135
|
-
runtime.releaseNode(group.nodes[0]);
|
|
3136
|
-
groups.delete(definition);
|
|
3137
|
-
group.definition.projections.forEach((projection) => materialized.delete(projection));
|
|
3138
|
-
continue;
|
|
3139
|
-
}
|
|
3140
|
-
const record = materialized.get(definition);
|
|
3141
|
-
if (!record) continue;
|
|
3142
|
-
runtime.releaseNode(record.node);
|
|
3143
|
-
if (record.kind !== "projection") runtime.releaseInput(record.source);
|
|
3144
|
-
materialized.delete(definition);
|
|
3145
|
-
}
|
|
3146
|
-
state.materialized.length = 0;
|
|
3147
|
-
scopes.delete(state);
|
|
3148
|
-
}
|
|
2306
|
+
dispose: () => disposeScope(state)
|
|
3149
2307
|
});
|
|
3150
2308
|
};
|
|
3151
|
-
|
|
2309
|
+
return Object.freeze({
|
|
3152
2310
|
get,
|
|
3153
2311
|
readable,
|
|
3154
2312
|
set: setInput,
|
|
3155
2313
|
update: updateInput,
|
|
3156
|
-
batch
|
|
2314
|
+
batch,
|
|
3157
2315
|
scope: createScope,
|
|
3158
2316
|
dispose: () => {
|
|
3159
|
-
if (
|
|
3160
|
-
|
|
3161
|
-
disposed = true;
|
|
2317
|
+
if (!scheduler.active) return;
|
|
2318
|
+
scheduler.assertIdle();
|
|
3162
2319
|
for (const scope of scopes) {
|
|
3163
2320
|
scope.active = false;
|
|
3164
2321
|
scope.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
@@ -3166,10 +2323,10 @@ const createProjectionRuntime = (options) => {
|
|
|
3166
2323
|
scope.materialized.length = 0;
|
|
3167
2324
|
}
|
|
3168
2325
|
scopes.clear();
|
|
3169
|
-
|
|
2326
|
+
sources.dispose();
|
|
2327
|
+
scheduler.dispose();
|
|
3170
2328
|
}
|
|
3171
|
-
};
|
|
3172
|
-
return Object.freeze(store);
|
|
2329
|
+
});
|
|
3173
2330
|
};
|
|
3174
2331
|
//#endregion
|
|
3175
2332
|
export { DocumentDisposedError, DocumentReentrancyError, ParseError, ProjectionDisposedError, ProjectionError, TransactionRejected, asReadable, createDocument, createProjectionRuntime, derive, field, input, list, map, object, observe, optional, parse, read, replace, snapshot, table, tree, variant };
|