doxum 0.1.13 → 0.1.15
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 +10 -2
- package/dist/advanced.cjs +3 -2
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +1 -1
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.js +2 -1
- package/dist/advanced.js.map +1 -1
- package/dist/{contract-BT53Gg94.d.ts → contract-CATCCVu5.d.ts} +1 -1
- package/dist/{definition-BlMeHwdz.d.ts → definition-B6J0SSNa.d.ts} +38 -37
- package/dist/{definition-kxnWUvX9.d.cts → definition-DN6yW2vi.d.cts} +37 -36
- package/dist/definition-Z6TagMup.cjs +107 -0
- package/dist/definition-Z6TagMup.cjs.map +1 -0
- package/dist/definition-c6XQXzvK.js +72 -0
- package/dist/definition-c6XQXzvK.js.map +1 -0
- package/dist/index.cjs +2054 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -3
- package/dist/index.d.ts +17 -4
- package/dist/index.js +2001 -2
- package/dist/index.js.map +1 -1
- package/dist/integration-B7VDgAOJ.cjs +30 -0
- package/dist/integration-B7VDgAOJ.cjs.map +1 -0
- package/dist/{integration-Bco8ZB16.js → integration-CxVhq-KD.js} +3 -7
- package/dist/integration-CxVhq-KD.js.map +1 -0
- package/dist/integration.cjs +4 -9
- package/dist/integration.d.cts +2 -7
- package/dist/integration.d.ts +2 -7
- package/dist/integration.js +3 -3
- package/dist/local-sync.d.ts +1 -1
- package/dist/notification-CDSUfiip.cjs +398 -0
- package/dist/notification-CDSUfiip.cjs.map +1 -0
- package/dist/notification-IQIJyH3Q.js +285 -0
- package/dist/notification-IQIJyH3Q.js.map +1 -0
- package/dist/react.cjs +6 -52
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +4 -50
- package/dist/react.js.map +1 -1
- package/dist/{definition-177L4Amk.cjs → scope-Ezs-Y3TY.cjs} +1 -118
- package/dist/scope-Ezs-Y3TY.cjs.map +1 -0
- package/dist/{definition-DXrKMVs7.js → scope-d-L87NeC.js} +2 -65
- package/dist/scope-d-L87NeC.js.map +1 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/references/guide.en.md +2 -1
- package/skills/doxum-runtime/references/guide.zh-CN.md +2 -1
- package/skills/doxum-runtime/references/projections.en.md +15 -6
- package/skills/doxum-runtime/references/projections.zh-CN.md +13 -5
- package/dist/definition-177L4Amk.cjs.map +0 -1
- package/dist/definition-DXrKMVs7.js.map +0 -1
- package/dist/integration-Bco8ZB16.js.map +0 -1
- package/dist/integration-CEUn96tx.cjs +0 -52
- package/dist/integration-CEUn96tx.cjs.map +0 -1
- package/dist/store-CDeqZEE3.d.cts +0 -28
- package/dist/store-CtgvCVFg.js +0 -2222
- package/dist/store-CtgvCVFg.js.map +0 -1
- package/dist/store-DcDpDgjk.cjs +0 -2401
- package/dist/store-DcDpDgjk.cjs.map +0 -1
- package/dist/store-P5dJ7TVe.d.ts +0 -28
package/dist/store-CtgvCVFg.js
DELETED
|
@@ -1,2222 +0,0 @@
|
|
|
1
|
-
import { L as profile, f as read, i as AddressIndex, o as contains } from "./issue-DVaGQGeP.js";
|
|
2
|
-
import { d as createAccess, h as checkKey, o as definitionOf, p as snapshot, u as collectionAccess } from "./definition-DXrKMVs7.js";
|
|
3
|
-
//#region core/src/schema.ts
|
|
4
|
-
const node = (value) => Object.freeze(value);
|
|
5
|
-
const field = (validator) => node({
|
|
6
|
-
kind: "field",
|
|
7
|
-
...validator ? { validator } : {}
|
|
8
|
-
});
|
|
9
|
-
const optional = (value) => {
|
|
10
|
-
if (![
|
|
11
|
-
"field",
|
|
12
|
-
"variant",
|
|
13
|
-
"map",
|
|
14
|
-
"list",
|
|
15
|
-
"tree"
|
|
16
|
-
].includes(value.kind)) throw new TypeError("Only field, variant, map, list and tree nodes support optional presence.");
|
|
17
|
-
return node({
|
|
18
|
-
...value,
|
|
19
|
-
optional: true
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
const object = (shape) => node({
|
|
23
|
-
kind: "object",
|
|
24
|
-
shape: Object.freeze({ ...shape })
|
|
25
|
-
});
|
|
26
|
-
const variant = (tag, variants) => node({
|
|
27
|
-
kind: "variant",
|
|
28
|
-
tag,
|
|
29
|
-
variants: Object.freeze({ ...variants })
|
|
30
|
-
});
|
|
31
|
-
const table = (value, options) => node({
|
|
32
|
-
kind: "table",
|
|
33
|
-
value,
|
|
34
|
-
...options
|
|
35
|
-
});
|
|
36
|
-
const map = (value, options) => node({
|
|
37
|
-
kind: "map",
|
|
38
|
-
value,
|
|
39
|
-
...options
|
|
40
|
-
});
|
|
41
|
-
const list = (value, config) => node({
|
|
42
|
-
kind: "list",
|
|
43
|
-
keyOf: config.keyOf,
|
|
44
|
-
value
|
|
45
|
-
});
|
|
46
|
-
const tree = (value) => node({
|
|
47
|
-
kind: "tree",
|
|
48
|
-
value
|
|
49
|
-
});
|
|
50
|
-
const paths = /* @__PURE__ */ new WeakMap();
|
|
51
|
-
const pathProxy = (nodes, address, owner) => {
|
|
52
|
-
const proxy = new Proxy({}, { get: (_target, property) => {
|
|
53
|
-
if (typeof property !== "string") return void 0;
|
|
54
|
-
if (nodes.every((node) => node.kind === "table" || node.kind === "map") && property === "item") return (id) => {
|
|
55
|
-
if (typeof id !== "string") throw new TypeError("Collection keys must be strings.");
|
|
56
|
-
for (const node of nodes) if (node.kind === "table" || node.kind === "map") {
|
|
57
|
-
const invalid = checkKey(node.key, id, [...address, id]);
|
|
58
|
-
if (invalid) throw new TypeError(invalid.message);
|
|
59
|
-
}
|
|
60
|
-
return pathProxy(nodes.map((node) => node.value), [...address, id], owner);
|
|
61
|
-
};
|
|
62
|
-
const children = nodes.flatMap((node) => {
|
|
63
|
-
if (node.kind === "object") return Object.hasOwn(node.shape, property) ? [node.shape[property]] : [];
|
|
64
|
-
if (node.kind !== "variant") return [];
|
|
65
|
-
if (property === node.tag) return [field()];
|
|
66
|
-
return Object.values(node.variants).flatMap((branch) => Object.hasOwn(branch.shape, property) ? [branch.shape[property]] : []);
|
|
67
|
-
});
|
|
68
|
-
if (!children.length) throw new TypeError(`Invalid schema path segment: ${property}`);
|
|
69
|
-
return pathProxy(children, [...address, property], owner);
|
|
70
|
-
} });
|
|
71
|
-
paths.set(proxy, {
|
|
72
|
-
nodes,
|
|
73
|
-
address,
|
|
74
|
-
owner
|
|
75
|
-
});
|
|
76
|
-
return proxy;
|
|
77
|
-
};
|
|
78
|
-
const compilePath = (owner, kind, pick) => {
|
|
79
|
-
const scope = {};
|
|
80
|
-
const value = pick(pathProxy([owner], [], scope));
|
|
81
|
-
const selected = typeof value === "object" && value !== null ? paths.get(value) : void 0;
|
|
82
|
-
if (!selected || selected.owner !== scope) throw new TypeError("Selector must return a path from its callback.");
|
|
83
|
-
if (kind === "collection" && !selected.nodes.every((node) => node.kind === "table" || node.kind === "map")) throw new TypeError("Collection selectors require a table or map path.");
|
|
84
|
-
return Object.freeze({
|
|
85
|
-
kind,
|
|
86
|
-
schema: owner,
|
|
87
|
-
address: Object.freeze([...selected.address])
|
|
88
|
-
});
|
|
89
|
-
};
|
|
90
|
-
//#endregion
|
|
91
|
-
//#region core/src/impact-target.ts
|
|
92
|
-
const address = (target) => "at" in target ? target.at : target.address;
|
|
93
|
-
const id = (target) => target.kind === "collection" && "id" in target ? target.id : void 0;
|
|
94
|
-
const belongs = (target, schema) => !("schema" in target) || target.schema === schema;
|
|
95
|
-
const same = (left, right) => {
|
|
96
|
-
if (left.kind !== right.kind) return false;
|
|
97
|
-
if ("schema" in left && "schema" in right && left.schema !== right.schema) return false;
|
|
98
|
-
const leftAddress = address(left);
|
|
99
|
-
const rightAddress = address(right);
|
|
100
|
-
if (leftAddress.length !== rightAddress.length) return false;
|
|
101
|
-
for (let index = 0; index < leftAddress.length; index += 1) if (leftAddress[index] !== rightAddress[index]) return false;
|
|
102
|
-
return left.kind !== "collection" || id(left) === id(right);
|
|
103
|
-
};
|
|
104
|
-
const indexedAddress = (target) => {
|
|
105
|
-
const key = id(target);
|
|
106
|
-
return key === void 0 ? address(target) : [...address(target), key];
|
|
107
|
-
};
|
|
108
|
-
const affected = (target, values, orders) => {
|
|
109
|
-
const at = indexedAddress(target);
|
|
110
|
-
return id(target) !== void 0 ? values.hasAncestor(at) : values.overlaps(at) || orders.hasDescendant(at);
|
|
111
|
-
};
|
|
112
|
-
/** Targets are classified once; commit matching never builds a reverse impact index. */
|
|
113
|
-
var SubscriptionIndex = class {
|
|
114
|
-
ordinary = new AddressIndex();
|
|
115
|
-
membership = new AddressIndex();
|
|
116
|
-
constructor(schema) {
|
|
117
|
-
this.schema = schema;
|
|
118
|
-
}
|
|
119
|
-
add(target, value) {
|
|
120
|
-
if (belongs(target, this.schema)) (id(target) === void 0 ? this.ordinary : this.membership).add(indexedAddress(target), value);
|
|
121
|
-
}
|
|
122
|
-
delete(target, value) {
|
|
123
|
-
if (belongs(target, this.schema)) (id(target) === void 0 ? this.ordinary : this.membership).delete(indexedAddress(target), value);
|
|
124
|
-
}
|
|
125
|
-
collect(changes, visit) {
|
|
126
|
-
const values = this.ordinary.query(visit);
|
|
127
|
-
const members = this.membership.query(visit, "descendants");
|
|
128
|
-
const orders = this.ordinary.query(visit, "ancestors");
|
|
129
|
-
for (const change of changes.changes) if (change.kind === "reset") {
|
|
130
|
-
values([]);
|
|
131
|
-
members([]);
|
|
132
|
-
} else if (change.kind === "members") {
|
|
133
|
-
values(change.at, change.members);
|
|
134
|
-
members(change.at, change.members);
|
|
135
|
-
if (change.order) orders(change.at);
|
|
136
|
-
} else {
|
|
137
|
-
values(change.at);
|
|
138
|
-
members(change.at);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
clear() {
|
|
142
|
-
this.ordinary.clear();
|
|
143
|
-
this.membership.clear();
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
//#endregion
|
|
147
|
-
//#region core/src/impact.ts
|
|
148
|
-
const queries = /* @__PURE__ */ new WeakMap();
|
|
149
|
-
const affectsTarget = (impact, value) => queries.get(impact).affects(value);
|
|
150
|
-
const collectionImpact = (impact, selector) => queries.get(impact).collection(selector);
|
|
151
|
-
const createImpact = (schema, changes) => {
|
|
152
|
-
const reset = changes.changes.some((change) => change.kind === "reset");
|
|
153
|
-
let values;
|
|
154
|
-
let orders;
|
|
155
|
-
const index = () => {
|
|
156
|
-
if (!values) {
|
|
157
|
-
profile.impact.index();
|
|
158
|
-
values = new AddressIndex();
|
|
159
|
-
orders = new AddressIndex();
|
|
160
|
-
for (const change of changes.changes) if (change.kind === "members") {
|
|
161
|
-
for (const member of change.members) values.add(change.at, true, member.key);
|
|
162
|
-
if (change.order) orders.add(change.at, true);
|
|
163
|
-
} else if (change.kind === "tree") values.add(change.at, true);
|
|
164
|
-
}
|
|
165
|
-
return values;
|
|
166
|
-
};
|
|
167
|
-
const cache = /* @__PURE__ */ new Map();
|
|
168
|
-
const implementation = {
|
|
169
|
-
affects(value) {
|
|
170
|
-
profile.impact.affects();
|
|
171
|
-
if (!belongs(value, schema)) return false;
|
|
172
|
-
if (reset) return true;
|
|
173
|
-
return affected(value, index(), orders);
|
|
174
|
-
},
|
|
175
|
-
collection(selector) {
|
|
176
|
-
if (selector.schema !== schema) throw new TypeError("Collection belongs to another root model.");
|
|
177
|
-
const key = JSON.stringify(selector.address), cached = cache.get(key);
|
|
178
|
-
if (cached) return cached;
|
|
179
|
-
if (reset) {
|
|
180
|
-
const result = { kind: "reset" };
|
|
181
|
-
cache.set(key, result);
|
|
182
|
-
return result;
|
|
183
|
-
}
|
|
184
|
-
const at = selector.address;
|
|
185
|
-
const added = /* @__PURE__ */ new Set(), removed = /* @__PURE__ */ new Set(), updated = /* @__PURE__ */ new Set();
|
|
186
|
-
let orderChanged = false;
|
|
187
|
-
for (const change of changes.changes) {
|
|
188
|
-
if (change.kind === "reset") continue;
|
|
189
|
-
if (change.kind === "members") {
|
|
190
|
-
if (change.at.length < at.length && contains(change.at, at)) {
|
|
191
|
-
if (change.members.some((member) => member.key === at[change.at.length])) {
|
|
192
|
-
const result = { kind: "reset" };
|
|
193
|
-
cache.set(key, result);
|
|
194
|
-
return result;
|
|
195
|
-
}
|
|
196
|
-
} else if (contains(at, change.at)) if (change.at.length === at.length) {
|
|
197
|
-
if (change.order) {
|
|
198
|
-
const positions = new Map(change.order.after.map((id, i) => [id, i]));
|
|
199
|
-
let previous = -1;
|
|
200
|
-
for (const id of change.order.before) {
|
|
201
|
-
const position = positions.get(id);
|
|
202
|
-
if (position === void 0) continue;
|
|
203
|
-
if (position < previous) orderChanged = true;
|
|
204
|
-
previous = position;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
for (const member of change.members) if (member.kind === "added") added.add(member.key);
|
|
208
|
-
else if (member.kind === "removed") removed.add(member.key);
|
|
209
|
-
else updated.add(member.key);
|
|
210
|
-
} else updated.add(change.at[at.length]);
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (change.kind === "tree" && contains(change.at, at)) {
|
|
214
|
-
const result = { kind: "reset" };
|
|
215
|
-
cache.set(key, result);
|
|
216
|
-
return result;
|
|
217
|
-
}
|
|
218
|
-
if (!contains(at, change.at)) continue;
|
|
219
|
-
if (change.at.length === at.length) continue;
|
|
220
|
-
const id = change.at[at.length];
|
|
221
|
-
updated.add(id);
|
|
222
|
-
}
|
|
223
|
-
added.forEach((id) => updated.delete(id));
|
|
224
|
-
removed.forEach((id) => updated.delete(id));
|
|
225
|
-
const result = {
|
|
226
|
-
kind: "incremental",
|
|
227
|
-
added,
|
|
228
|
-
removed,
|
|
229
|
-
updated,
|
|
230
|
-
orderChanged
|
|
231
|
-
};
|
|
232
|
-
cache.set(key, result);
|
|
233
|
-
return result;
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
const impact = Object.freeze({
|
|
237
|
-
kind: reset ? "reset" : "incremental",
|
|
238
|
-
affects: (pick) => implementation.affects(compilePath(schema, "value", pick)),
|
|
239
|
-
collection: (pick) => implementation.collection(compilePath(schema, "collection", pick))
|
|
240
|
-
});
|
|
241
|
-
queries.set(impact, implementation);
|
|
242
|
-
return impact;
|
|
243
|
-
};
|
|
244
|
-
//#endregion
|
|
245
|
-
//#region core/src/runtime/contract.ts
|
|
246
|
-
var DocumentReentrancyError = class extends Error {
|
|
247
|
-
constructor() {
|
|
248
|
-
super("Document writes cannot be re-entered during an update or notification.");
|
|
249
|
-
this.name = "DocumentReentrancyError";
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
var DocumentDisposedError = class extends Error {
|
|
253
|
-
constructor() {
|
|
254
|
-
super("Doxum runtime has been disposed.");
|
|
255
|
-
this.name = "DocumentDisposedError";
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
var TransactionRejected = class extends Error {
|
|
259
|
-
issues;
|
|
260
|
-
constructor(input) {
|
|
261
|
-
super("Document update rejected.");
|
|
262
|
-
this.name = "TransactionRejected";
|
|
263
|
-
const issues = Array.isArray(input) ? input : [input];
|
|
264
|
-
this.issues = Object.freeze(issues.map((issue) => Object.freeze({
|
|
265
|
-
code: issue.code,
|
|
266
|
-
message: issue.message,
|
|
267
|
-
source: "application",
|
|
268
|
-
...issue.address === void 0 ? {} : { address: Object.freeze([...issue.address]) }
|
|
269
|
-
})));
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
//#endregion
|
|
273
|
-
//#region core/src/runtime/access.ts
|
|
274
|
-
const states = /* @__PURE__ */ new WeakMap();
|
|
275
|
-
const bindRuntimeAccess = (runtime, state) => {
|
|
276
|
-
states.set(runtime, state);
|
|
277
|
-
};
|
|
278
|
-
const accessOf = (runtime) => {
|
|
279
|
-
const state = states.get(runtime);
|
|
280
|
-
if (!state) throw new Error("Unknown Doxum runtime.");
|
|
281
|
-
return state;
|
|
282
|
-
};
|
|
283
|
-
const readWith = (runtime, run, dependencies) => {
|
|
284
|
-
const state = accessOf(runtime);
|
|
285
|
-
if (state.disposed) throw new DocumentDisposedError();
|
|
286
|
-
state.projectionLocks = (state.projectionLocks ?? 0) + 1;
|
|
287
|
-
try {
|
|
288
|
-
return run(createAccess({
|
|
289
|
-
state,
|
|
290
|
-
dependencies
|
|
291
|
-
}));
|
|
292
|
-
} finally {
|
|
293
|
-
state.projectionLocks--;
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
//#endregion
|
|
297
|
-
//#region core/src/runtime/notification.ts
|
|
298
|
-
const notifications = /* @__PURE__ */ new WeakMap();
|
|
299
|
-
const readableOwners = /* @__PURE__ */ new WeakMap();
|
|
300
|
-
const bindDocumentReadable = (readable, runtime) => {
|
|
301
|
-
readableOwners.set(readable, runtime);
|
|
302
|
-
};
|
|
303
|
-
const documentReadableOwner = (readable) => readableOwners.get(readable);
|
|
304
|
-
const createNotification = (runtime) => {
|
|
305
|
-
const notification = {
|
|
306
|
-
root: /* @__PURE__ */ new Set(),
|
|
307
|
-
filtered: /* @__PURE__ */ new Set(),
|
|
308
|
-
index: new SubscriptionIndex(runtime.schema),
|
|
309
|
-
cleanups: /* @__PURE__ */ new Set(),
|
|
310
|
-
processors: [],
|
|
311
|
-
candidates: /* @__PURE__ */ new Set(),
|
|
312
|
-
rootSnapshot: [],
|
|
313
|
-
notifying: false
|
|
314
|
-
};
|
|
315
|
-
notifications.set(runtime, notification);
|
|
316
|
-
return notification;
|
|
317
|
-
};
|
|
318
|
-
const notificationOf = (runtime) => {
|
|
319
|
-
const value = notifications.get(runtime);
|
|
320
|
-
if (!value) throw new Error("Unknown Doxum runtime.");
|
|
321
|
-
return value;
|
|
322
|
-
};
|
|
323
|
-
const shareNotification = (source, target) => {
|
|
324
|
-
notifications.set(target, notificationOf(source));
|
|
325
|
-
};
|
|
326
|
-
const attachProjection = (runtime, processor) => {
|
|
327
|
-
const notification = notificationOf(runtime);
|
|
328
|
-
const entry = {
|
|
329
|
-
processor,
|
|
330
|
-
active: true
|
|
331
|
-
};
|
|
332
|
-
notification.processors.push(entry);
|
|
333
|
-
return () => {
|
|
334
|
-
if (!entry.active) return;
|
|
335
|
-
entry.active = false;
|
|
336
|
-
if (!notification.notifying) {
|
|
337
|
-
const index = notification.processors.indexOf(entry);
|
|
338
|
-
if (index >= 0) notification.processors.splice(index, 1);
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
};
|
|
342
|
-
const subscribeRoot = (notification, listener) => {
|
|
343
|
-
const entry = {
|
|
344
|
-
listener,
|
|
345
|
-
active: true
|
|
346
|
-
};
|
|
347
|
-
notification.root.add(entry);
|
|
348
|
-
return () => {
|
|
349
|
-
entry.active = false;
|
|
350
|
-
if (!notification.notifying) notification.root.delete(entry);
|
|
351
|
-
else notification.cleanups.add(() => {
|
|
352
|
-
notification.root.delete(entry);
|
|
353
|
-
});
|
|
354
|
-
};
|
|
355
|
-
};
|
|
356
|
-
const subscribeTargets = (notification, targets, listener) => {
|
|
357
|
-
const entry = {
|
|
358
|
-
targets: Object.freeze(targets.slice()),
|
|
359
|
-
listener,
|
|
360
|
-
active: true
|
|
361
|
-
};
|
|
362
|
-
notification.filtered.add(entry);
|
|
363
|
-
entry.targets.forEach((value) => notification.index.add(value, entry));
|
|
364
|
-
const remove = () => {
|
|
365
|
-
notification.filtered.delete(entry);
|
|
366
|
-
entry.targets.forEach((value) => notification.index.delete(value, entry));
|
|
367
|
-
};
|
|
368
|
-
return () => {
|
|
369
|
-
if (!entry.active) return;
|
|
370
|
-
entry.active = false;
|
|
371
|
-
if (notification.notifying) notification.cleanups.add(remove);
|
|
372
|
-
else remove();
|
|
373
|
-
};
|
|
374
|
-
};
|
|
375
|
-
const notify = (notification, commit, afterSettle) => {
|
|
376
|
-
notification.notifying = true;
|
|
377
|
-
const errors = [];
|
|
378
|
-
const call = (listener) => {
|
|
379
|
-
try {
|
|
380
|
-
listener(commit);
|
|
381
|
-
} catch (error) {
|
|
382
|
-
errors.push(Object.freeze({
|
|
383
|
-
phase: "listener",
|
|
384
|
-
error
|
|
385
|
-
}));
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
try {
|
|
389
|
-
const processorCount = notification.processors.length;
|
|
390
|
-
for (let index = 0; index < processorCount; index += 1) {
|
|
391
|
-
const entry = notification.processors[index];
|
|
392
|
-
if (!entry?.active) continue;
|
|
393
|
-
try {
|
|
394
|
-
entry.processor.capture(commit);
|
|
395
|
-
} catch (error) {
|
|
396
|
-
errors.push(Object.freeze({
|
|
397
|
-
phase: "processor",
|
|
398
|
-
error
|
|
399
|
-
}));
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
for (let index = 0; index < processorCount; index += 1) {
|
|
403
|
-
const entry = notification.processors[index];
|
|
404
|
-
if (!entry?.active) continue;
|
|
405
|
-
try {
|
|
406
|
-
entry.processor.settle();
|
|
407
|
-
} catch (error) {
|
|
408
|
-
errors.push(Object.freeze({
|
|
409
|
-
phase: "processor",
|
|
410
|
-
error
|
|
411
|
-
}));
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
for (let index = 0; index < processorCount; index += 1) {
|
|
415
|
-
const entry = notification.processors[index];
|
|
416
|
-
if (!entry?.active) continue;
|
|
417
|
-
try {
|
|
418
|
-
errors.push(...entry.processor.flush());
|
|
419
|
-
} catch (error) {
|
|
420
|
-
errors.push(Object.freeze({
|
|
421
|
-
phase: "flush",
|
|
422
|
-
error
|
|
423
|
-
}));
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
if (afterSettle) errors.push(...afterSettle());
|
|
427
|
-
const candidates = notification.candidates;
|
|
428
|
-
candidates.clear();
|
|
429
|
-
if (notification.filtered.size) notification.index.collect(commit.changes, (entry) => candidates.add(entry));
|
|
430
|
-
candidates.forEach((entry) => {
|
|
431
|
-
if (entry.active) call(entry.listener);
|
|
432
|
-
});
|
|
433
|
-
const rootSnapshot = notification.rootSnapshot;
|
|
434
|
-
rootSnapshot.length = 0;
|
|
435
|
-
notification.root.forEach((entry) => rootSnapshot.push(entry));
|
|
436
|
-
for (const entry of rootSnapshot) if (entry.active) call(entry.listener);
|
|
437
|
-
} finally {
|
|
438
|
-
notification.notifying = false;
|
|
439
|
-
let writeIndex = 0;
|
|
440
|
-
for (const entry of notification.processors) if (entry.active) notification.processors[writeIndex++] = entry;
|
|
441
|
-
notification.processors.length = writeIndex;
|
|
442
|
-
notification.cleanups.forEach((cleanup) => cleanup());
|
|
443
|
-
notification.cleanups.clear();
|
|
444
|
-
}
|
|
445
|
-
return Object.freeze(errors);
|
|
446
|
-
};
|
|
447
|
-
const subscribeDependencies = (runtime, targets, listener) => subscribeTargets(notificationOf(runtime), targets, listener);
|
|
448
|
-
const disposeNotification = (notification) => {
|
|
449
|
-
const attachments = notification.processors.slice();
|
|
450
|
-
notification.root.clear();
|
|
451
|
-
notification.filtered.clear();
|
|
452
|
-
notification.index.clear();
|
|
453
|
-
notification.cleanups.clear();
|
|
454
|
-
notification.processors.length = 0;
|
|
455
|
-
notification.candidates.clear();
|
|
456
|
-
notification.rootSnapshot.length = 0;
|
|
457
|
-
const errors = [];
|
|
458
|
-
attachments.forEach((entry) => {
|
|
459
|
-
if (!entry.active) return;
|
|
460
|
-
try {
|
|
461
|
-
entry.processor.dispose();
|
|
462
|
-
} catch (error) {
|
|
463
|
-
errors.push(error);
|
|
464
|
-
}
|
|
465
|
-
});
|
|
466
|
-
if (errors.length) throw new AggregateError(errors, "Projection disposal notification failed.");
|
|
467
|
-
};
|
|
468
|
-
//#endregion
|
|
469
|
-
//#region core/src/projection/contract.ts
|
|
470
|
-
var ProjectionError = class extends Error {
|
|
471
|
-
constructor(phase, identity, revisions, cause) {
|
|
472
|
-
super(`Projection ${phase} failed: ${identity}`, { cause });
|
|
473
|
-
this.phase = phase;
|
|
474
|
-
this.identity = identity;
|
|
475
|
-
this.revisions = revisions;
|
|
476
|
-
this.name = "ProjectionError";
|
|
477
|
-
}
|
|
478
|
-
};
|
|
479
|
-
var ProjectionDisposedError = class extends Error {
|
|
480
|
-
constructor() {
|
|
481
|
-
super("Projection has been disposed.");
|
|
482
|
-
this.name = "ProjectionDisposedError";
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
//#endregion
|
|
486
|
-
//#region core/src/projection/scheduler.ts
|
|
487
|
-
const projectionHandles = /* @__PURE__ */ new WeakSet();
|
|
488
|
-
const createScheduler = (onError) => {
|
|
489
|
-
let disposed = false;
|
|
490
|
-
let depth = 0;
|
|
491
|
-
let batchSequence = 0;
|
|
492
|
-
let activeBatch;
|
|
493
|
-
let phase = "idle";
|
|
494
|
-
let sequence = 0;
|
|
495
|
-
const records = /* @__PURE__ */ new Map();
|
|
496
|
-
const nodes = /* @__PURE__ */ new Set();
|
|
497
|
-
const pending = /* @__PURE__ */ new Set();
|
|
498
|
-
const heap = [];
|
|
499
|
-
const queued = /* @__PURE__ */ new Set();
|
|
500
|
-
const completed = [];
|
|
501
|
-
const emissions = /* @__PURE__ */ new Set();
|
|
502
|
-
const errors = [];
|
|
503
|
-
let reportingFailures = [];
|
|
504
|
-
const guards = /* @__PURE__ */ new Set();
|
|
505
|
-
const cleanups = /* @__PURE__ */ new Set();
|
|
506
|
-
const assertActive = () => {
|
|
507
|
-
if (disposed) throw new ProjectionDisposedError();
|
|
508
|
-
};
|
|
509
|
-
const assertIdle = () => {
|
|
510
|
-
assertActive();
|
|
511
|
-
if (phase !== "idle") throw new Error("Projection cannot be re-entered during processing or notification.");
|
|
512
|
-
};
|
|
513
|
-
const lock = (value) => guards.forEach((guard) => guard(value));
|
|
514
|
-
const enqueue = (node) => {
|
|
515
|
-
if (node.disposed || queued.has(node)) return;
|
|
516
|
-
queued.add(node);
|
|
517
|
-
profile.projection("scheduledNodes");
|
|
518
|
-
let i = heap.length;
|
|
519
|
-
heap.push(node);
|
|
520
|
-
while (i > 0) {
|
|
521
|
-
const p = i - 1 >> 1;
|
|
522
|
-
if (heap[p].order < node.order) break;
|
|
523
|
-
heap[i] = heap[p];
|
|
524
|
-
i = p;
|
|
525
|
-
}
|
|
526
|
-
heap[i] = node;
|
|
527
|
-
};
|
|
528
|
-
const pop = () => {
|
|
529
|
-
const first = heap[0];
|
|
530
|
-
const last = heap.pop();
|
|
531
|
-
if (heap.length) {
|
|
532
|
-
let i = 0;
|
|
533
|
-
while (i * 2 + 1 < heap.length) {
|
|
534
|
-
let c = i * 2 + 1;
|
|
535
|
-
if (c + 1 < heap.length && heap[c + 1].order < heap[c].order) c++;
|
|
536
|
-
if (last.order < heap[c].order) break;
|
|
537
|
-
heap[i] = heap[c];
|
|
538
|
-
i = c;
|
|
539
|
-
}
|
|
540
|
-
heap[i] = last;
|
|
541
|
-
}
|
|
542
|
-
return first;
|
|
543
|
-
};
|
|
544
|
-
const errorFor = (node, cause, kind = "processor") => new ProjectionError(kind, node.name, node.sources.map((source) => source.revision()), cause);
|
|
545
|
-
const capture = (source) => {
|
|
546
|
-
if (disposed) return;
|
|
547
|
-
if (phase !== "idle") {
|
|
548
|
-
source.fault = new ProjectionError("source", "reentrant source", [source.revision()], /* @__PURE__ */ new Error("Source changed while projection was running."));
|
|
549
|
-
errors.push(source.fault);
|
|
550
|
-
throw source.fault;
|
|
551
|
-
}
|
|
552
|
-
pending.add(source);
|
|
553
|
-
profile.projection("sourceEvents");
|
|
554
|
-
if (source.fault) errors.push(source.fault);
|
|
555
|
-
};
|
|
556
|
-
const settle = () => {
|
|
557
|
-
if (disposed || depth || phase !== "idle") return;
|
|
558
|
-
phase = "compute";
|
|
559
|
-
lock(true);
|
|
560
|
-
try {
|
|
561
|
-
pending.forEach((source) => source.consumers.forEach(enqueue));
|
|
562
|
-
while (heap.length) {
|
|
563
|
-
const node = pop();
|
|
564
|
-
if (node.disposed) continue;
|
|
565
|
-
const wasFaulted = node.fault !== void 0;
|
|
566
|
-
profile.projection("processedNodes");
|
|
567
|
-
let changed = false;
|
|
568
|
-
const blocked = node.sources.find((source) => source.fault || source.disposed);
|
|
569
|
-
if (blocked) node.fault = errorFor(node, blocked.fault ?? new ProjectionDisposedError(), "blocked");
|
|
570
|
-
else {
|
|
571
|
-
const build = node.forced || wasFaulted || node.sources.some((source) => source.reset());
|
|
572
|
-
try {
|
|
573
|
-
changed = node.evaluate(build);
|
|
574
|
-
const failure = node.sources.find((source) => source.fault)?.fault;
|
|
575
|
-
if (failure) throw failure;
|
|
576
|
-
node.fault = void 0;
|
|
577
|
-
} catch (cause) {
|
|
578
|
-
const error = errorFor(node, cause);
|
|
579
|
-
errors.push(error);
|
|
580
|
-
node.fault = error;
|
|
581
|
-
if (!build) try {
|
|
582
|
-
changed = node.evaluate(true);
|
|
583
|
-
const failure = node.sources.find((source) => source.fault)?.fault;
|
|
584
|
-
if (failure) throw failure;
|
|
585
|
-
node.fault = void 0;
|
|
586
|
-
} catch (cause) {
|
|
587
|
-
node.fault = errorFor(node, cause);
|
|
588
|
-
errors.push(node.fault);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
node.forced = false;
|
|
593
|
-
node.statusChanged = wasFaulted !== (node.fault !== void 0);
|
|
594
|
-
completed.push(node);
|
|
595
|
-
if (changed || wasFaulted || node.fault) {
|
|
596
|
-
profile.projection("publishedNodes");
|
|
597
|
-
emissions.add(node);
|
|
598
|
-
node.consumers.forEach(enqueue);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
completed.forEach((node) => node.publish());
|
|
602
|
-
} finally {
|
|
603
|
-
lock(false);
|
|
604
|
-
phase = "idle";
|
|
605
|
-
}
|
|
606
|
-
};
|
|
607
|
-
const flush = () => {
|
|
608
|
-
if (disposed || depth) return [];
|
|
609
|
-
if (pending.size || completed.length) profile.projection("flushes");
|
|
610
|
-
phase = "notify";
|
|
611
|
-
lock(true);
|
|
612
|
-
const reportFailures = [];
|
|
613
|
-
try {
|
|
614
|
-
emissions.forEach((node) => node.emit((listener) => {
|
|
615
|
-
try {
|
|
616
|
-
listener();
|
|
617
|
-
} catch (cause) {
|
|
618
|
-
errors.push(errorFor(node, cause, "listener"));
|
|
619
|
-
}
|
|
620
|
-
}));
|
|
621
|
-
for (const error of errors.slice()) try {
|
|
622
|
-
onError(error);
|
|
623
|
-
} catch (cause) {
|
|
624
|
-
reportFailures.push(cause);
|
|
625
|
-
}
|
|
626
|
-
return Object.freeze([...errors.map((error) => Object.freeze({
|
|
627
|
-
phase: error.phase === "listener" ? "listener" : "processor",
|
|
628
|
-
error
|
|
629
|
-
})), ...reportFailures.map((error) => Object.freeze({
|
|
630
|
-
phase: "listener",
|
|
631
|
-
error
|
|
632
|
-
}))]);
|
|
633
|
-
} finally {
|
|
634
|
-
pending.forEach((source) => source.clear());
|
|
635
|
-
completed.forEach((node) => node.clear());
|
|
636
|
-
pending.clear();
|
|
637
|
-
queued.clear();
|
|
638
|
-
completed.length = 0;
|
|
639
|
-
emissions.clear();
|
|
640
|
-
errors.length = 0;
|
|
641
|
-
reportingFailures = reportFailures;
|
|
642
|
-
lock(false);
|
|
643
|
-
phase = "idle";
|
|
644
|
-
}
|
|
645
|
-
};
|
|
646
|
-
const run = () => {
|
|
647
|
-
settle();
|
|
648
|
-
const result = flush();
|
|
649
|
-
const failures = reportingFailures;
|
|
650
|
-
reportingFailures = [];
|
|
651
|
-
if (failures.length) throw new AggregateError(failures, "Projection error reporter failed.");
|
|
652
|
-
return result;
|
|
653
|
-
};
|
|
654
|
-
const batch = (optionsOrCallback, maybeCallback) => {
|
|
655
|
-
assertIdle();
|
|
656
|
-
const options = typeof optionsOrCallback === "function" ? void 0 : optionsOrCallback;
|
|
657
|
-
const callback = typeof optionsOrCallback === "function" ? optionsOrCallback : maybeCallback;
|
|
658
|
-
if (!callback) throw new TypeError("Projection batch requires a callback.");
|
|
659
|
-
if (depth === 0) activeBatch = Object.freeze({
|
|
660
|
-
id: ++batchSequence,
|
|
661
|
-
...options?.cause === void 0 ? {} : { cause: options.cause }
|
|
662
|
-
});
|
|
663
|
-
depth++;
|
|
664
|
-
let failed = false;
|
|
665
|
-
try {
|
|
666
|
-
const value = callback();
|
|
667
|
-
assertSynchronous(value);
|
|
668
|
-
return value;
|
|
669
|
-
} catch (error) {
|
|
670
|
-
failed = true;
|
|
671
|
-
throw error;
|
|
672
|
-
} finally {
|
|
673
|
-
depth--;
|
|
674
|
-
if (!depth) try {
|
|
675
|
-
if (failed) try {
|
|
676
|
-
run();
|
|
677
|
-
} catch {}
|
|
678
|
-
else run();
|
|
679
|
-
} finally {
|
|
680
|
-
activeBatch = void 0;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
return {
|
|
685
|
-
assertActive,
|
|
686
|
-
assertIdle,
|
|
687
|
-
capture,
|
|
688
|
-
settle,
|
|
689
|
-
flush,
|
|
690
|
-
run,
|
|
691
|
-
records,
|
|
692
|
-
guards,
|
|
693
|
-
cleanups,
|
|
694
|
-
get active() {
|
|
695
|
-
return !disposed;
|
|
696
|
-
},
|
|
697
|
-
register(handle, record) {
|
|
698
|
-
assertIdle();
|
|
699
|
-
records.set(handle, record);
|
|
700
|
-
projectionHandles.add(handle);
|
|
701
|
-
},
|
|
702
|
-
unregisterSource(handle) {
|
|
703
|
-
const record = records.get(handle);
|
|
704
|
-
if (record) {
|
|
705
|
-
record.disposed = true;
|
|
706
|
-
pending.delete(record);
|
|
707
|
-
record.clear();
|
|
708
|
-
records.delete(handle);
|
|
709
|
-
}
|
|
710
|
-
},
|
|
711
|
-
source(handle) {
|
|
712
|
-
assertActive();
|
|
713
|
-
const source = records.get(handle);
|
|
714
|
-
if (!source || source.disposed) throw new Error("Unknown, disposed, or foreign projection source.");
|
|
715
|
-
return source;
|
|
716
|
-
},
|
|
717
|
-
addNode(node) {
|
|
718
|
-
nodes.add(node);
|
|
719
|
-
node.sources.forEach((source) => source.consumers.add(node));
|
|
720
|
-
},
|
|
721
|
-
order: () => sequence++,
|
|
722
|
-
initialize(run) {
|
|
723
|
-
assertIdle();
|
|
724
|
-
phase = "compute";
|
|
725
|
-
lock(true);
|
|
726
|
-
try {
|
|
727
|
-
return run();
|
|
728
|
-
} finally {
|
|
729
|
-
lock(false);
|
|
730
|
-
phase = "idle";
|
|
731
|
-
}
|
|
732
|
-
},
|
|
733
|
-
rebuild(node) {
|
|
734
|
-
assertIdle();
|
|
735
|
-
if (node.disposed) throw new ProjectionDisposedError();
|
|
736
|
-
node.forced = true;
|
|
737
|
-
enqueue(node);
|
|
738
|
-
run();
|
|
739
|
-
},
|
|
740
|
-
disposeNode(node) {
|
|
741
|
-
if (node.disposed) return;
|
|
742
|
-
assertIdle();
|
|
743
|
-
if (node.consumers.size) throw new Error("Cannot dispose a projection node with downstream consumers.");
|
|
744
|
-
node.disposed = true;
|
|
745
|
-
node.sources.forEach((source) => source.consumers.delete(node));
|
|
746
|
-
nodes.delete(node);
|
|
747
|
-
node.release();
|
|
748
|
-
for (const [handle, record] of records) if (record === node) records.delete(handle);
|
|
749
|
-
},
|
|
750
|
-
batch,
|
|
751
|
-
batchContext: () => activeBatch,
|
|
752
|
-
dispose() {
|
|
753
|
-
if (disposed) return;
|
|
754
|
-
assertIdle();
|
|
755
|
-
disposed = true;
|
|
756
|
-
const failures = [];
|
|
757
|
-
cleanups.forEach((cleanup) => {
|
|
758
|
-
try {
|
|
759
|
-
cleanup();
|
|
760
|
-
} catch (error) {
|
|
761
|
-
failures.push(error);
|
|
762
|
-
}
|
|
763
|
-
});
|
|
764
|
-
cleanups.clear();
|
|
765
|
-
nodes.forEach((node) => {
|
|
766
|
-
node.disposed = true;
|
|
767
|
-
node.release();
|
|
768
|
-
node.consumers.clear();
|
|
769
|
-
});
|
|
770
|
-
records.forEach((source) => {
|
|
771
|
-
source.disposed = true;
|
|
772
|
-
source.consumers.clear();
|
|
773
|
-
source.clear();
|
|
774
|
-
});
|
|
775
|
-
nodes.clear();
|
|
776
|
-
records.clear();
|
|
777
|
-
pending.clear();
|
|
778
|
-
heap.length = 0;
|
|
779
|
-
completed.length = 0;
|
|
780
|
-
queued.clear();
|
|
781
|
-
emissions.clear();
|
|
782
|
-
guards.clear();
|
|
783
|
-
errors.length = 0;
|
|
784
|
-
reportingFailures = [];
|
|
785
|
-
if (failures.length) throw new AggregateError(failures, "Projection cleanup failed.");
|
|
786
|
-
},
|
|
787
|
-
debug: () => ({
|
|
788
|
-
nodes: nodes.size,
|
|
789
|
-
sources: records.size - nodes.size,
|
|
790
|
-
subscriptions: cleanups.size,
|
|
791
|
-
pending: pending.size + heap.length
|
|
792
|
-
})
|
|
793
|
-
};
|
|
794
|
-
};
|
|
795
|
-
const assertSynchronous = (value) => {
|
|
796
|
-
if (value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function") throw new TypeError("Projection callbacks must be synchronous.");
|
|
797
|
-
};
|
|
798
|
-
const assertScope = (active) => {
|
|
799
|
-
if (!active()) throw new Error("Projection reader or writer is no longer active.");
|
|
800
|
-
};
|
|
801
|
-
//#endregion
|
|
802
|
-
//#region core/src/projection/node.ts
|
|
803
|
-
const createNode = (scheduler, spec, behavior) => {
|
|
804
|
-
scheduler.assertIdle();
|
|
805
|
-
const entries = Object.entries(spec.sources).map(([key, handle]) => [key, scheduler.source(handle)]);
|
|
806
|
-
const order = scheduler.order();
|
|
807
|
-
const node = {
|
|
808
|
-
...behavior,
|
|
809
|
-
publish: () => {
|
|
810
|
-
if (node.fault) behavior.clear();
|
|
811
|
-
else behavior.publish();
|
|
812
|
-
},
|
|
813
|
-
order,
|
|
814
|
-
name: spec.name ? `${spec.name} (${order})` : `projection-${order}`,
|
|
815
|
-
sources: entries.map(([, source]) => source),
|
|
816
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
817
|
-
disposed: false,
|
|
818
|
-
fault: void 0,
|
|
819
|
-
forced: false,
|
|
820
|
-
statusChanged: false,
|
|
821
|
-
evaluate: (build) => {
|
|
822
|
-
let active = true;
|
|
823
|
-
try {
|
|
824
|
-
const scopeActive = () => active;
|
|
825
|
-
const inputs = Object.fromEntries(entries.map(([key, source]) => [key, source.context(scopeActive)]));
|
|
826
|
-
return behavior.evaluate(inputs, build, scopeActive);
|
|
827
|
-
} finally {
|
|
828
|
-
active = false;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
};
|
|
832
|
-
scheduler.initialize(() => {
|
|
833
|
-
const invalid = node.sources.find((source) => source.fault || source.disposed);
|
|
834
|
-
if (invalid) throw invalid.fault ?? new ProjectionDisposedError();
|
|
835
|
-
node.evaluate(true);
|
|
836
|
-
node.publish();
|
|
837
|
-
node.clear();
|
|
838
|
-
});
|
|
839
|
-
const check = () => {
|
|
840
|
-
if (node.disposed || !scheduler.active) throw new ProjectionDisposedError();
|
|
841
|
-
if (node.fault) throw node.fault;
|
|
842
|
-
};
|
|
843
|
-
return {
|
|
844
|
-
node,
|
|
845
|
-
check,
|
|
846
|
-
install(handle) {
|
|
847
|
-
scheduler.register(handle, node);
|
|
848
|
-
scheduler.addNode(node);
|
|
849
|
-
},
|
|
850
|
-
rebuild: () => scheduler.rebuild(node),
|
|
851
|
-
dispose: () => scheduler.disposeNode(node)
|
|
852
|
-
};
|
|
853
|
-
};
|
|
854
|
-
//#endregion
|
|
855
|
-
//#region core/src/projection/collection.ts
|
|
856
|
-
const collectionHandles = /* @__PURE__ */ new WeakSet();
|
|
857
|
-
const readonlySet = (values) => {
|
|
858
|
-
const set = new Set(values);
|
|
859
|
-
const view = Object.freeze({
|
|
860
|
-
get size() {
|
|
861
|
-
return set.size;
|
|
862
|
-
},
|
|
863
|
-
has: (value) => set.has(value),
|
|
864
|
-
entries: () => set.entries(),
|
|
865
|
-
keys: () => set.keys(),
|
|
866
|
-
values: () => set.values(),
|
|
867
|
-
[Symbol.iterator]: () => set.values(),
|
|
868
|
-
forEach: (callback, thisArg) => set.forEach((value) => callback.call(thisArg, value, value, view))
|
|
869
|
-
});
|
|
870
|
-
return view;
|
|
871
|
-
};
|
|
872
|
-
const createCollection = (scheduler, spec) => {
|
|
873
|
-
const values = /* @__PURE__ */ new Map();
|
|
874
|
-
let ids = Object.freeze([]);
|
|
875
|
-
let staged = /* @__PURE__ */ new Map();
|
|
876
|
-
let nextIds = ids;
|
|
877
|
-
let change;
|
|
878
|
-
let instance;
|
|
879
|
-
let initialized = false;
|
|
880
|
-
let reset = false;
|
|
881
|
-
let revision = 0;
|
|
882
|
-
let idsRevision = 0;
|
|
883
|
-
let batch;
|
|
884
|
-
let cause;
|
|
885
|
-
let publishedTransitions = Object.freeze([]);
|
|
886
|
-
let all;
|
|
887
|
-
const items = /* @__PURE__ */ new Map();
|
|
888
|
-
const subscribedItems = /* @__PURE__ */ new Set();
|
|
889
|
-
const idsListeners = /* @__PURE__ */ new Set();
|
|
890
|
-
const allListeners = /* @__PURE__ */ new Set();
|
|
891
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
892
|
-
const changedKeys = /* @__PURE__ */ new Set();
|
|
893
|
-
let orderChanged = false;
|
|
894
|
-
const current = Object.freeze({
|
|
895
|
-
get: (key) => {
|
|
896
|
-
owner.check();
|
|
897
|
-
return values.get(key);
|
|
898
|
-
},
|
|
899
|
-
has: (key) => {
|
|
900
|
-
owner.check();
|
|
901
|
-
return values.has(key);
|
|
902
|
-
},
|
|
903
|
-
ids: () => {
|
|
904
|
-
owner.check();
|
|
905
|
-
return ids;
|
|
906
|
-
}
|
|
907
|
-
});
|
|
908
|
-
const hasNext = (key) => staged.has(key) ? staged.get(key).present : values.has(key);
|
|
909
|
-
const getNext = (key) => {
|
|
910
|
-
const entry = staged.get(key);
|
|
911
|
-
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
912
|
-
};
|
|
913
|
-
const owner = createNode(scheduler, spec, {
|
|
914
|
-
evaluate: (sources, build, active) => {
|
|
915
|
-
const metadata = Object.values(sources).find((value) => value && typeof value === "object" && ("batch" in value && value.batch !== void 0 || "cause" in value && value.cause !== void 0));
|
|
916
|
-
batch = scheduler.batchContext() ?? metadata?.batch;
|
|
917
|
-
cause = metadata?.cause ?? batch?.cause;
|
|
918
|
-
staged = /* @__PURE__ */ new Map();
|
|
919
|
-
nextIds = ids;
|
|
920
|
-
change = void 0;
|
|
921
|
-
changedKeys.clear();
|
|
922
|
-
let explicitOrder;
|
|
923
|
-
let cleared = false;
|
|
924
|
-
const nextHas = (key) => staged.has(key) ? staged.get(key).present : !cleared && values.has(key);
|
|
925
|
-
const nextGet = (key) => {
|
|
926
|
-
const entry = staged.get(key);
|
|
927
|
-
return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : values.get(key);
|
|
928
|
-
};
|
|
929
|
-
const deriveIds = () => {
|
|
930
|
-
const result = cleared ? [] : ids.filter(nextHas);
|
|
931
|
-
for (const [key, entry] of staged) if (entry.present && (cleared || !values.has(key))) result.push(key);
|
|
932
|
-
return result;
|
|
933
|
-
};
|
|
934
|
-
const read = (next) => ({
|
|
935
|
-
get: (key) => {
|
|
936
|
-
assertScope(active);
|
|
937
|
-
return next ? nextGet(key) : values.get(key);
|
|
938
|
-
},
|
|
939
|
-
has: (key) => {
|
|
940
|
-
assertScope(active);
|
|
941
|
-
return next ? nextHas(key) : values.has(key);
|
|
942
|
-
},
|
|
943
|
-
ids: () => {
|
|
944
|
-
assertScope(active);
|
|
945
|
-
return next ? Object.freeze(explicitOrder ? explicitOrder.slice() : deriveIds()) : ids;
|
|
946
|
-
}
|
|
947
|
-
});
|
|
948
|
-
const writer = {
|
|
949
|
-
set: (key, value) => {
|
|
950
|
-
assertScope(active);
|
|
951
|
-
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
952
|
-
staged.set(key, {
|
|
953
|
-
present: true,
|
|
954
|
-
value
|
|
955
|
-
});
|
|
956
|
-
},
|
|
957
|
-
remove: (key) => {
|
|
958
|
-
assertScope(active);
|
|
959
|
-
staged.set(key, { present: false });
|
|
960
|
-
},
|
|
961
|
-
order: (order) => {
|
|
962
|
-
assertScope(active);
|
|
963
|
-
explicitOrder = order.slice();
|
|
964
|
-
},
|
|
965
|
-
replace: (entries) => {
|
|
966
|
-
assertScope(active);
|
|
967
|
-
staged.clear();
|
|
968
|
-
cleared = true;
|
|
969
|
-
const order = [];
|
|
970
|
-
for (const [key, value] of entries) {
|
|
971
|
-
if (staged.has(key)) throw new TypeError("Duplicate projection key.");
|
|
972
|
-
writer.set(key, value);
|
|
973
|
-
order.push(key);
|
|
974
|
-
}
|
|
975
|
-
explicitOrder = order;
|
|
976
|
-
}
|
|
977
|
-
};
|
|
978
|
-
const input = {
|
|
979
|
-
sources,
|
|
980
|
-
previous: read(false),
|
|
981
|
-
next: read(true),
|
|
982
|
-
writer
|
|
983
|
-
};
|
|
984
|
-
if (build || !instance) {
|
|
985
|
-
profile.materialized.rebuilt();
|
|
986
|
-
instance = void 0;
|
|
987
|
-
cleared = true;
|
|
988
|
-
const built = spec.build(input);
|
|
989
|
-
assertSynchronous(built);
|
|
990
|
-
instance = built;
|
|
991
|
-
} else {
|
|
992
|
-
profile.materialized.updated();
|
|
993
|
-
const result = instance.update(input);
|
|
994
|
-
assertSynchronous(result);
|
|
995
|
-
if (result?.kind === "rebuild") {
|
|
996
|
-
staged.clear();
|
|
997
|
-
cleared = true;
|
|
998
|
-
explicitOrder = void 0;
|
|
999
|
-
instance = void 0;
|
|
1000
|
-
const built = spec.build(input);
|
|
1001
|
-
assertSynchronous(built);
|
|
1002
|
-
instance = built;
|
|
1003
|
-
build = true;
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
if (cleared) {
|
|
1007
|
-
for (const key of values.keys()) if (!staged.has(key)) staged.set(key, { present: false });
|
|
1008
|
-
}
|
|
1009
|
-
const added = /* @__PURE__ */ new Set();
|
|
1010
|
-
const removed = /* @__PURE__ */ new Set();
|
|
1011
|
-
const updated = /* @__PURE__ */ new Set();
|
|
1012
|
-
profile.projection("touchedKeys", staged.size);
|
|
1013
|
-
for (const [key, entry] of staged) {
|
|
1014
|
-
const existed = values.has(key);
|
|
1015
|
-
if (entry.present) if (!existed) added.add(key);
|
|
1016
|
-
else if (!(spec.isEqual ?? Object.is)(values.get(key), entry.value)) updated.add(key);
|
|
1017
|
-
else staged.delete(key);
|
|
1018
|
-
else if (existed) removed.add(key);
|
|
1019
|
-
else staged.delete(key);
|
|
1020
|
-
}
|
|
1021
|
-
if (explicitOrder || added.size || removed.size) {
|
|
1022
|
-
const order = explicitOrder ?? [...ids.filter((key) => !removed.has(key)), ...added];
|
|
1023
|
-
if (new Set(order).size !== order.length || order.length !== values.size + added.size - removed.size || order.some((key) => !hasNext(key))) throw new TypeError("Projection order must contain every key exactly once.");
|
|
1024
|
-
nextIds = ids.length === order.length && ids.every((key, i) => key === order[i]) ? ids : Object.freeze(order.slice());
|
|
1025
|
-
}
|
|
1026
|
-
orderChanged = nextIds !== ids;
|
|
1027
|
-
if (added.size || removed.size || updated.size || orderChanged) {
|
|
1028
|
-
let commonOrderChanged = false;
|
|
1029
|
-
if (orderChanged) {
|
|
1030
|
-
const before = ids.filter((key) => !removed.has(key));
|
|
1031
|
-
const after = nextIds.filter((key) => !added.has(key));
|
|
1032
|
-
commonOrderChanged = before.some((key, i) => after[i] !== key);
|
|
1033
|
-
}
|
|
1034
|
-
change = Object.freeze({
|
|
1035
|
-
kind: "incremental",
|
|
1036
|
-
added: readonlySet(added),
|
|
1037
|
-
removed: readonlySet(removed),
|
|
1038
|
-
updated: readonlySet(updated),
|
|
1039
|
-
orderChanged: commonOrderChanged
|
|
1040
|
-
});
|
|
1041
|
-
[
|
|
1042
|
-
...added,
|
|
1043
|
-
...removed,
|
|
1044
|
-
...updated
|
|
1045
|
-
].forEach((key) => changedKeys.add(key));
|
|
1046
|
-
profile.projection("changedKeys", changedKeys.size);
|
|
1047
|
-
}
|
|
1048
|
-
publishedTransitions = Object.freeze([...changedKeys].flatMap((key) => {
|
|
1049
|
-
const beforePresent = values.has(key);
|
|
1050
|
-
const afterPresent = hasNext(key);
|
|
1051
|
-
if (beforePresent === afterPresent) {
|
|
1052
|
-
if (!beforePresent) return [];
|
|
1053
|
-
if ((spec.isEqual ?? Object.is)(values.get(key), getNext(key))) return [];
|
|
1054
|
-
}
|
|
1055
|
-
return [{
|
|
1056
|
-
key,
|
|
1057
|
-
kind: !beforePresent ? "added" : !afterPresent ? "removed" : "updated",
|
|
1058
|
-
before: beforePresent ? values.get(key) : void 0,
|
|
1059
|
-
after: afterPresent ? getNext(key) : void 0
|
|
1060
|
-
}];
|
|
1061
|
-
}));
|
|
1062
|
-
reset = build;
|
|
1063
|
-
return change !== void 0;
|
|
1064
|
-
},
|
|
1065
|
-
context: (active) => {
|
|
1066
|
-
const transitions = (keys) => {
|
|
1067
|
-
assertScope(active);
|
|
1068
|
-
const selected = keys ? new Set(keys) : void 0;
|
|
1069
|
-
return Object.freeze(publishedTransitions.filter((transition) => !selected || selected.has(transition.key)));
|
|
1070
|
-
};
|
|
1071
|
-
const input = {
|
|
1072
|
-
get: (key) => {
|
|
1073
|
-
assertScope(active);
|
|
1074
|
-
return getNext(key);
|
|
1075
|
-
},
|
|
1076
|
-
has: (key) => {
|
|
1077
|
-
assertScope(active);
|
|
1078
|
-
return hasNext(key);
|
|
1079
|
-
},
|
|
1080
|
-
ids: () => {
|
|
1081
|
-
assertScope(active);
|
|
1082
|
-
return nextIds;
|
|
1083
|
-
},
|
|
1084
|
-
previous: Object.freeze({
|
|
1085
|
-
get: (key) => {
|
|
1086
|
-
assertScope(active);
|
|
1087
|
-
return values.get(key);
|
|
1088
|
-
},
|
|
1089
|
-
has: (key) => {
|
|
1090
|
-
assertScope(active);
|
|
1091
|
-
return values.has(key);
|
|
1092
|
-
},
|
|
1093
|
-
ids: () => {
|
|
1094
|
-
assertScope(active);
|
|
1095
|
-
return ids;
|
|
1096
|
-
}
|
|
1097
|
-
}),
|
|
1098
|
-
change,
|
|
1099
|
-
revision: revision + (change ? 1 : 0),
|
|
1100
|
-
reset,
|
|
1101
|
-
transitions,
|
|
1102
|
-
batch,
|
|
1103
|
-
cause
|
|
1104
|
-
};
|
|
1105
|
-
return Object.freeze(input);
|
|
1106
|
-
},
|
|
1107
|
-
revision: () => revision,
|
|
1108
|
-
reset: () => reset,
|
|
1109
|
-
publish: () => {
|
|
1110
|
-
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
1111
|
-
else values.delete(key);
|
|
1112
|
-
if (change) {
|
|
1113
|
-
if (initialized) revision++;
|
|
1114
|
-
if (orderChanged && initialized) idsRevision++;
|
|
1115
|
-
all = void 0;
|
|
1116
|
-
changedKeys.forEach((key) => {
|
|
1117
|
-
const item = items.get(key);
|
|
1118
|
-
if (item && initialized) item.revision++;
|
|
1119
|
-
});
|
|
1120
|
-
}
|
|
1121
|
-
ids = nextIds;
|
|
1122
|
-
initialized = true;
|
|
1123
|
-
},
|
|
1124
|
-
emit: (call) => {
|
|
1125
|
-
profile.materialized.notification();
|
|
1126
|
-
const fault = owner.node.fault !== void 0;
|
|
1127
|
-
const resetEvent = Object.freeze({ kind: "reset" });
|
|
1128
|
-
Array.from(listeners).forEach((listener) => call(() => listener(change ?? resetEvent)));
|
|
1129
|
-
if (orderChanged || !change || fault || owner.node.statusChanged) Array.from(idsListeners).forEach((listener) => call(listener));
|
|
1130
|
-
Array.from(allListeners).forEach((listener) => call(listener));
|
|
1131
|
-
if (!change || fault || owner.node.statusChanged) for (const item of subscribedItems) Array.from(item.listeners ?? []).forEach((listener) => call(listener));
|
|
1132
|
-
else for (const key of changedKeys) Array.from(items.get(key)?.listeners ?? []).forEach((listener) => call(listener));
|
|
1133
|
-
},
|
|
1134
|
-
clear: () => {
|
|
1135
|
-
staged.clear();
|
|
1136
|
-
nextIds = ids;
|
|
1137
|
-
change = void 0;
|
|
1138
|
-
reset = false;
|
|
1139
|
-
changedKeys.clear();
|
|
1140
|
-
publishedTransitions = Object.freeze([]);
|
|
1141
|
-
batch = void 0;
|
|
1142
|
-
cause = void 0;
|
|
1143
|
-
orderChanged = false;
|
|
1144
|
-
},
|
|
1145
|
-
release: () => {
|
|
1146
|
-
values.clear();
|
|
1147
|
-
staged.clear();
|
|
1148
|
-
items.clear();
|
|
1149
|
-
subscribedItems.clear();
|
|
1150
|
-
idsListeners.clear();
|
|
1151
|
-
allListeners.clear();
|
|
1152
|
-
listeners.clear();
|
|
1153
|
-
ids = nextIds = Object.freeze([]);
|
|
1154
|
-
all = void 0;
|
|
1155
|
-
instance = void 0;
|
|
1156
|
-
}
|
|
1157
|
-
});
|
|
1158
|
-
const handle = {
|
|
1159
|
-
current: () => {
|
|
1160
|
-
owner.check();
|
|
1161
|
-
return current;
|
|
1162
|
-
},
|
|
1163
|
-
ids: {
|
|
1164
|
-
current: () => {
|
|
1165
|
-
owner.check();
|
|
1166
|
-
return ids;
|
|
1167
|
-
},
|
|
1168
|
-
revision: () => {
|
|
1169
|
-
owner.check();
|
|
1170
|
-
return idsRevision;
|
|
1171
|
-
},
|
|
1172
|
-
subscribe: (listener) => {
|
|
1173
|
-
owner.check();
|
|
1174
|
-
idsListeners.add(listener);
|
|
1175
|
-
return () => {
|
|
1176
|
-
idsListeners.delete(listener);
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
},
|
|
1180
|
-
all: {
|
|
1181
|
-
current: () => {
|
|
1182
|
-
owner.check();
|
|
1183
|
-
if (!all) {
|
|
1184
|
-
profile.collectionView.arrayCopied();
|
|
1185
|
-
all = Object.freeze(ids.map((key) => values.get(key)));
|
|
1186
|
-
}
|
|
1187
|
-
return all;
|
|
1188
|
-
},
|
|
1189
|
-
revision: () => {
|
|
1190
|
-
owner.check();
|
|
1191
|
-
return revision;
|
|
1192
|
-
},
|
|
1193
|
-
subscribe: (listener) => {
|
|
1194
|
-
owner.check();
|
|
1195
|
-
allListeners.add(listener);
|
|
1196
|
-
return () => {
|
|
1197
|
-
allListeners.delete(listener);
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
},
|
|
1201
|
-
item: (key) => {
|
|
1202
|
-
owner.check();
|
|
1203
|
-
const old = items.get(key);
|
|
1204
|
-
if (old) return old.readable;
|
|
1205
|
-
const readable = Object.freeze({
|
|
1206
|
-
current: () => {
|
|
1207
|
-
owner.check();
|
|
1208
|
-
return values.get(key);
|
|
1209
|
-
},
|
|
1210
|
-
revision: () => {
|
|
1211
|
-
owner.check();
|
|
1212
|
-
return item.revision;
|
|
1213
|
-
},
|
|
1214
|
-
subscribe: (listener) => {
|
|
1215
|
-
owner.check();
|
|
1216
|
-
const set = item.listeners ??= /* @__PURE__ */ new Set();
|
|
1217
|
-
set.add(listener);
|
|
1218
|
-
subscribedItems.add(item);
|
|
1219
|
-
return () => {
|
|
1220
|
-
set.delete(listener);
|
|
1221
|
-
if (!set.size) subscribedItems.delete(item);
|
|
1222
|
-
};
|
|
1223
|
-
}
|
|
1224
|
-
});
|
|
1225
|
-
const item = {
|
|
1226
|
-
readable,
|
|
1227
|
-
revision: 0
|
|
1228
|
-
};
|
|
1229
|
-
projectionHandles.add(readable);
|
|
1230
|
-
items.set(key, item);
|
|
1231
|
-
return readable;
|
|
1232
|
-
},
|
|
1233
|
-
revision: () => {
|
|
1234
|
-
owner.check();
|
|
1235
|
-
return revision;
|
|
1236
|
-
},
|
|
1237
|
-
subscribe: (listener) => {
|
|
1238
|
-
owner.check();
|
|
1239
|
-
listeners.add(listener);
|
|
1240
|
-
return () => {
|
|
1241
|
-
listeners.delete(listener);
|
|
1242
|
-
};
|
|
1243
|
-
},
|
|
1244
|
-
rebuild: owner.rebuild,
|
|
1245
|
-
dispose: owner.dispose
|
|
1246
|
-
};
|
|
1247
|
-
projectionHandles.add(handle.ids);
|
|
1248
|
-
projectionHandles.add(handle.all);
|
|
1249
|
-
collectionHandles.add(handle);
|
|
1250
|
-
owner.install(handle);
|
|
1251
|
-
return Object.freeze(handle);
|
|
1252
|
-
};
|
|
1253
|
-
//#endregion
|
|
1254
|
-
//#region core/src/projection/source.ts
|
|
1255
|
-
const createSources = (scheduler) => {
|
|
1256
|
-
const documents = /* @__PURE__ */ new Map();
|
|
1257
|
-
const readables = /* @__PURE__ */ new Map();
|
|
1258
|
-
const collections = /* @__PURE__ */ new WeakSet();
|
|
1259
|
-
const externalSources = /* @__PURE__ */ new WeakMap();
|
|
1260
|
-
const document = (runtime) => {
|
|
1261
|
-
scheduler.assertIdle();
|
|
1262
|
-
const state = accessOf(runtime);
|
|
1263
|
-
if (state.disposed) throw new Error("Document has been disposed.");
|
|
1264
|
-
const existing = documents.get(state);
|
|
1265
|
-
if (existing) return existing;
|
|
1266
|
-
const bindings = [];
|
|
1267
|
-
let batch;
|
|
1268
|
-
const make = (input) => {
|
|
1269
|
-
const collection = "collection" in input ? input.collection : void 0;
|
|
1270
|
-
const targets = "targets" in input ? input.targets : [input.collection];
|
|
1271
|
-
if (state.disposed) throw new Error("Document has been disposed.");
|
|
1272
|
-
targets.forEach((value) => {
|
|
1273
|
-
if (!belongs(value, state.schema)) throw new Error("Projection target belongs to another schema.");
|
|
1274
|
-
});
|
|
1275
|
-
const old = bindings.find((entry) => Boolean(collection) === collections.has(entry.handle) && entry.targets.length === targets.length && entry.targets.every((value, i) => same(value, targets[i])));
|
|
1276
|
-
if (old) return old.handle;
|
|
1277
|
-
let commits = [];
|
|
1278
|
-
let reset = false;
|
|
1279
|
-
let orderDirty = false;
|
|
1280
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1281
|
-
let candidates;
|
|
1282
|
-
const record = {
|
|
1283
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
1284
|
-
disposed: false,
|
|
1285
|
-
fault: void 0,
|
|
1286
|
-
revision: runtime.revision,
|
|
1287
|
-
reset: () => reset,
|
|
1288
|
-
clear: () => {
|
|
1289
|
-
commits = [];
|
|
1290
|
-
reset = orderDirty = false;
|
|
1291
|
-
batch = void 0;
|
|
1292
|
-
keys.clear();
|
|
1293
|
-
candidates = void 0;
|
|
1294
|
-
},
|
|
1295
|
-
context: (active) => {
|
|
1296
|
-
assertScope(active);
|
|
1297
|
-
if (state.disposed) throw new Error("Document has been disposed.");
|
|
1298
|
-
const context = {
|
|
1299
|
-
state,
|
|
1300
|
-
active
|
|
1301
|
-
};
|
|
1302
|
-
const read = collection ? collectionAccess(context, collection.address) : createAccess(context);
|
|
1303
|
-
return Object.freeze({
|
|
1304
|
-
read,
|
|
1305
|
-
revision: runtime.revision(),
|
|
1306
|
-
commits: Object.freeze(commits.slice()),
|
|
1307
|
-
reset: record.reset(),
|
|
1308
|
-
batch,
|
|
1309
|
-
cause: batch?.cause,
|
|
1310
|
-
...collection ? { candidates: candidates ??= Object.freeze({
|
|
1311
|
-
keys: Object.freeze([...keys]),
|
|
1312
|
-
orderDirty
|
|
1313
|
-
}) } : {}
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
};
|
|
1317
|
-
const handle = collection ? {} : {
|
|
1318
|
-
collection: (pick) => {
|
|
1319
|
-
scheduler.assertIdle();
|
|
1320
|
-
return make({ collection: compilePath(state.schema, "collection", pick) });
|
|
1321
|
-
},
|
|
1322
|
-
targets: (...selected) => {
|
|
1323
|
-
scheduler.assertIdle();
|
|
1324
|
-
if (!selected.length) throw new TypeError("Expected at least one target.");
|
|
1325
|
-
return make({ targets: selected.map((pick) => compilePath(state.schema, "value", pick)) });
|
|
1326
|
-
}
|
|
1327
|
-
};
|
|
1328
|
-
Object.freeze(handle);
|
|
1329
|
-
if (collection) collections.add(handle);
|
|
1330
|
-
scheduler.register(handle, record);
|
|
1331
|
-
const receive = (commit) => {
|
|
1332
|
-
commits.push(commit);
|
|
1333
|
-
reset ||= commit.impact.kind === "reset";
|
|
1334
|
-
if (collection) {
|
|
1335
|
-
const change = collectionImpact(commit.impact, collection);
|
|
1336
|
-
if (change.kind === "reset") reset = true;
|
|
1337
|
-
else {
|
|
1338
|
-
change.added.forEach((key) => keys.add(key));
|
|
1339
|
-
change.removed.forEach((key) => keys.add(key));
|
|
1340
|
-
change.updated.forEach((key) => keys.add(key));
|
|
1341
|
-
orderDirty ||= Boolean(change.added.size || change.removed.size || change.orderChanged);
|
|
1342
|
-
}
|
|
1343
|
-
candidates = void 0;
|
|
1344
|
-
}
|
|
1345
|
-
};
|
|
1346
|
-
bindings.push({
|
|
1347
|
-
handle,
|
|
1348
|
-
targets,
|
|
1349
|
-
record,
|
|
1350
|
-
receive
|
|
1351
|
-
});
|
|
1352
|
-
return handle;
|
|
1353
|
-
};
|
|
1354
|
-
const handle = make({ targets: [] });
|
|
1355
|
-
const guard = (locked) => {
|
|
1356
|
-
state.projectionLocks = (state.projectionLocks ?? 0) + (locked ? 1 : -1);
|
|
1357
|
-
};
|
|
1358
|
-
scheduler.guards.add(guard);
|
|
1359
|
-
const unsubscribe = attachProjection(runtime, {
|
|
1360
|
-
capture: (commit) => {
|
|
1361
|
-
for (const binding of bindings) if (!binding.targets.length || binding.targets.some((value) => affectsTarget(commit.impact, value))) {
|
|
1362
|
-
binding.receive(commit);
|
|
1363
|
-
batch = scheduler.batchContext();
|
|
1364
|
-
scheduler.capture(binding.record);
|
|
1365
|
-
}
|
|
1366
|
-
},
|
|
1367
|
-
settle: scheduler.settle,
|
|
1368
|
-
flush: scheduler.flush,
|
|
1369
|
-
dispose: () => {
|
|
1370
|
-
bindings.forEach(({ record }) => {
|
|
1371
|
-
record.fault = new ProjectionError("source", "document disposed", [runtime.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
|
|
1372
|
-
scheduler.capture(record);
|
|
1373
|
-
});
|
|
1374
|
-
scheduler.run();
|
|
1375
|
-
}
|
|
1376
|
-
});
|
|
1377
|
-
scheduler.cleanups.add(() => {
|
|
1378
|
-
unsubscribe();
|
|
1379
|
-
scheduler.guards.delete(guard);
|
|
1380
|
-
});
|
|
1381
|
-
documents.set(state, handle);
|
|
1382
|
-
return handle;
|
|
1383
|
-
};
|
|
1384
|
-
const valueSource = (initial, equal) => {
|
|
1385
|
-
let value = initial;
|
|
1386
|
-
let previous = initial;
|
|
1387
|
-
let revision = 0;
|
|
1388
|
-
let detail;
|
|
1389
|
-
let cause;
|
|
1390
|
-
let batch;
|
|
1391
|
-
let reset = false;
|
|
1392
|
-
let pending = false;
|
|
1393
|
-
const handle = Object.freeze({});
|
|
1394
|
-
const record = {
|
|
1395
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
1396
|
-
disposed: false,
|
|
1397
|
-
fault: void 0,
|
|
1398
|
-
revision: () => revision,
|
|
1399
|
-
reset: () => reset,
|
|
1400
|
-
clear: () => {
|
|
1401
|
-
previous = value;
|
|
1402
|
-
pending = false;
|
|
1403
|
-
detail = void 0;
|
|
1404
|
-
cause = void 0;
|
|
1405
|
-
batch = void 0;
|
|
1406
|
-
reset = false;
|
|
1407
|
-
},
|
|
1408
|
-
context: (active) => {
|
|
1409
|
-
assertScope(active);
|
|
1410
|
-
return Object.freeze({
|
|
1411
|
-
value,
|
|
1412
|
-
previous,
|
|
1413
|
-
revision,
|
|
1414
|
-
changed: !equal(previous, value),
|
|
1415
|
-
reset,
|
|
1416
|
-
detail,
|
|
1417
|
-
cause,
|
|
1418
|
-
batch
|
|
1419
|
-
});
|
|
1420
|
-
}
|
|
1421
|
-
};
|
|
1422
|
-
scheduler.register(handle, record);
|
|
1423
|
-
const accept = (next, metadata) => {
|
|
1424
|
-
scheduler.assertIdle();
|
|
1425
|
-
const recovering = record.fault !== void 0;
|
|
1426
|
-
const eventful = metadata !== void 0 && (metadata.reset === true || metadata.revision !== void 0 && metadata.revision !== revision || metadata.detail !== void 0 || metadata.cause !== void 0 || metadata.batch !== void 0);
|
|
1427
|
-
if (equal(value, next) && !recovering && !eventful) return;
|
|
1428
|
-
record.fault = void 0;
|
|
1429
|
-
if (!pending) previous = value;
|
|
1430
|
-
value = next;
|
|
1431
|
-
revision = metadata?.revision ?? revision + 1;
|
|
1432
|
-
detail = metadata?.detail;
|
|
1433
|
-
cause = metadata?.cause ?? cause;
|
|
1434
|
-
batch = scheduler.batchContext() ?? metadata?.batch ?? batch;
|
|
1435
|
-
reset ||= metadata?.reset ?? false;
|
|
1436
|
-
scheduler.capture(record);
|
|
1437
|
-
pending = true;
|
|
1438
|
-
};
|
|
1439
|
-
return {
|
|
1440
|
-
source: handle,
|
|
1441
|
-
accept,
|
|
1442
|
-
set(next) {
|
|
1443
|
-
accept(next);
|
|
1444
|
-
scheduler.run();
|
|
1445
|
-
}
|
|
1446
|
-
};
|
|
1447
|
-
};
|
|
1448
|
-
const externalValueSource = (port, equal) => {
|
|
1449
|
-
scheduler.assertIdle();
|
|
1450
|
-
const old = externalSources.get(port);
|
|
1451
|
-
if (old) return old;
|
|
1452
|
-
const input = valueSource(port.current(), equal);
|
|
1453
|
-
const fail = (cause) => {
|
|
1454
|
-
const record = scheduler.source(input.source);
|
|
1455
|
-
record.fault = new ProjectionError("source", "external source", [record.revision()], cause);
|
|
1456
|
-
scheduler.capture(record);
|
|
1457
|
-
};
|
|
1458
|
-
let unsubscribe;
|
|
1459
|
-
try {
|
|
1460
|
-
unsubscribe = port.subscribe((event) => {
|
|
1461
|
-
try {
|
|
1462
|
-
input.accept(event.value, {
|
|
1463
|
-
revision: event.revision,
|
|
1464
|
-
reset: event.reset,
|
|
1465
|
-
detail: event.detail,
|
|
1466
|
-
cause: event.cause,
|
|
1467
|
-
batch: event.batch
|
|
1468
|
-
});
|
|
1469
|
-
} catch (cause) {
|
|
1470
|
-
fail(cause);
|
|
1471
|
-
}
|
|
1472
|
-
scheduler.run();
|
|
1473
|
-
});
|
|
1474
|
-
} catch (cause) {
|
|
1475
|
-
unsubscribe?.();
|
|
1476
|
-
scheduler.unregisterSource(input.source);
|
|
1477
|
-
throw cause;
|
|
1478
|
-
}
|
|
1479
|
-
scheduler.cleanups.add(() => unsubscribe?.());
|
|
1480
|
-
externalSources.set(port, input.source);
|
|
1481
|
-
return input.source;
|
|
1482
|
-
};
|
|
1483
|
-
const externalCollectionSource = (port) => {
|
|
1484
|
-
scheduler.assertIdle();
|
|
1485
|
-
const old = externalSources.get(port);
|
|
1486
|
-
if (old) return old;
|
|
1487
|
-
let read = port.current();
|
|
1488
|
-
let previous = read;
|
|
1489
|
-
let revision = port.revision();
|
|
1490
|
-
let change;
|
|
1491
|
-
let reset = false;
|
|
1492
|
-
let detail;
|
|
1493
|
-
let cause;
|
|
1494
|
-
let batch;
|
|
1495
|
-
let publishedTransitions = Object.freeze([]);
|
|
1496
|
-
let transitionKeys = /* @__PURE__ */ new Set();
|
|
1497
|
-
let pending = false;
|
|
1498
|
-
const handle = Object.freeze({});
|
|
1499
|
-
const record = {
|
|
1500
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
1501
|
-
disposed: false,
|
|
1502
|
-
fault: void 0,
|
|
1503
|
-
revision: () => revision,
|
|
1504
|
-
reset: () => reset,
|
|
1505
|
-
clear: () => {
|
|
1506
|
-
previous = read;
|
|
1507
|
-
pending = false;
|
|
1508
|
-
change = void 0;
|
|
1509
|
-
reset = false;
|
|
1510
|
-
detail = void 0;
|
|
1511
|
-
cause = void 0;
|
|
1512
|
-
batch = void 0;
|
|
1513
|
-
publishedTransitions = Object.freeze([]);
|
|
1514
|
-
transitionKeys.clear();
|
|
1515
|
-
},
|
|
1516
|
-
context: (active) => {
|
|
1517
|
-
assertScope(active);
|
|
1518
|
-
const currentRead = Object.freeze({
|
|
1519
|
-
get: (key) => {
|
|
1520
|
-
assertScope(active);
|
|
1521
|
-
return read.get(key);
|
|
1522
|
-
},
|
|
1523
|
-
has: (key) => {
|
|
1524
|
-
assertScope(active);
|
|
1525
|
-
return read.has(key);
|
|
1526
|
-
},
|
|
1527
|
-
ids: () => {
|
|
1528
|
-
assertScope(active);
|
|
1529
|
-
return read.ids();
|
|
1530
|
-
}
|
|
1531
|
-
});
|
|
1532
|
-
const previousRead = Object.freeze({
|
|
1533
|
-
get: (key) => {
|
|
1534
|
-
assertScope(active);
|
|
1535
|
-
return previous.get(key);
|
|
1536
|
-
},
|
|
1537
|
-
has: (key) => {
|
|
1538
|
-
assertScope(active);
|
|
1539
|
-
return previous.has(key);
|
|
1540
|
-
},
|
|
1541
|
-
ids: () => {
|
|
1542
|
-
assertScope(active);
|
|
1543
|
-
return previous.ids();
|
|
1544
|
-
}
|
|
1545
|
-
});
|
|
1546
|
-
return Object.freeze({
|
|
1547
|
-
...currentRead,
|
|
1548
|
-
previous: previousRead,
|
|
1549
|
-
change,
|
|
1550
|
-
revision,
|
|
1551
|
-
reset,
|
|
1552
|
-
transitions: (keys) => {
|
|
1553
|
-
assertScope(active);
|
|
1554
|
-
const selected = keys ? new Set(keys) : void 0;
|
|
1555
|
-
return Object.freeze(publishedTransitions.filter((transition) => !selected || selected.has(transition.key)));
|
|
1556
|
-
},
|
|
1557
|
-
detail,
|
|
1558
|
-
cause,
|
|
1559
|
-
batch
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
};
|
|
1563
|
-
scheduler.register(handle, record);
|
|
1564
|
-
collections.add(handle);
|
|
1565
|
-
const fail = (error) => {
|
|
1566
|
-
record.fault = new ProjectionError("source", "external collection source", [revision], error);
|
|
1567
|
-
scheduler.capture(record);
|
|
1568
|
-
};
|
|
1569
|
-
const addTransitionKeys = (event) => {
|
|
1570
|
-
event.transitions().forEach((transition) => transitionKeys.add(transition.key));
|
|
1571
|
-
const impact = event.change;
|
|
1572
|
-
if (impact?.kind === "incremental") {
|
|
1573
|
-
impact.added.forEach((key) => transitionKeys.add(key));
|
|
1574
|
-
impact.removed.forEach((key) => transitionKeys.add(key));
|
|
1575
|
-
impact.updated.forEach((key) => transitionKeys.add(key));
|
|
1576
|
-
}
|
|
1577
|
-
};
|
|
1578
|
-
const recomputeTransitions = () => {
|
|
1579
|
-
const transitions = [];
|
|
1580
|
-
for (const key of transitionKeys) {
|
|
1581
|
-
const beforePresent = previous.has(key);
|
|
1582
|
-
const afterPresent = read.has(key);
|
|
1583
|
-
const before = beforePresent ? previous.get(key) : void 0;
|
|
1584
|
-
const after = afterPresent ? read.get(key) : void 0;
|
|
1585
|
-
if (beforePresent === afterPresent && Object.is(before, after)) continue;
|
|
1586
|
-
transitions.push({
|
|
1587
|
-
key,
|
|
1588
|
-
kind: !beforePresent ? "added" : !afterPresent ? "removed" : "updated",
|
|
1589
|
-
before,
|
|
1590
|
-
after
|
|
1591
|
-
});
|
|
1592
|
-
}
|
|
1593
|
-
publishedTransitions = Object.freeze(transitions);
|
|
1594
|
-
};
|
|
1595
|
-
const deriveChange = () => {
|
|
1596
|
-
if (reset) return Object.freeze({ kind: "reset" });
|
|
1597
|
-
const added = /* @__PURE__ */ new Set();
|
|
1598
|
-
const removed = /* @__PURE__ */ new Set();
|
|
1599
|
-
const updated = /* @__PURE__ */ new Set();
|
|
1600
|
-
publishedTransitions.forEach((transition) => {
|
|
1601
|
-
if (transition.kind === "added") added.add(transition.key);
|
|
1602
|
-
else if (transition.kind === "removed") removed.add(transition.key);
|
|
1603
|
-
else updated.add(transition.key);
|
|
1604
|
-
});
|
|
1605
|
-
const beforeIds = previous.ids();
|
|
1606
|
-
const afterIds = read.ids();
|
|
1607
|
-
const beforeCommon = beforeIds.filter((key) => read.has(key));
|
|
1608
|
-
const afterCommon = afterIds.filter((key) => previous.has(key));
|
|
1609
|
-
const orderChanged = beforeCommon.length !== afterCommon.length || beforeCommon.some((key, index) => afterCommon[index] !== key);
|
|
1610
|
-
if (!added.size && !removed.size && !updated.size && !orderChanged) return void 0;
|
|
1611
|
-
return Object.freeze({
|
|
1612
|
-
kind: "incremental",
|
|
1613
|
-
added,
|
|
1614
|
-
removed,
|
|
1615
|
-
updated,
|
|
1616
|
-
orderChanged
|
|
1617
|
-
});
|
|
1618
|
-
};
|
|
1619
|
-
let unsubscribe;
|
|
1620
|
-
try {
|
|
1621
|
-
unsubscribe = port.subscribe((event) => {
|
|
1622
|
-
try {
|
|
1623
|
-
scheduler.assertIdle();
|
|
1624
|
-
if (!pending) previous = event.previous;
|
|
1625
|
-
read = port.current();
|
|
1626
|
-
revision = event.revision;
|
|
1627
|
-
addTransitionKeys(event);
|
|
1628
|
-
reset ||= event.reset || event.change?.kind === "reset";
|
|
1629
|
-
detail = event.detail;
|
|
1630
|
-
cause = event.cause ?? cause;
|
|
1631
|
-
batch = scheduler.batchContext() ?? event.batch;
|
|
1632
|
-
recomputeTransitions();
|
|
1633
|
-
change = deriveChange();
|
|
1634
|
-
record.fault = void 0;
|
|
1635
|
-
scheduler.capture(record);
|
|
1636
|
-
pending = true;
|
|
1637
|
-
} catch (error) {
|
|
1638
|
-
fail(error);
|
|
1639
|
-
}
|
|
1640
|
-
scheduler.run();
|
|
1641
|
-
});
|
|
1642
|
-
} catch (error) {
|
|
1643
|
-
unsubscribe?.();
|
|
1644
|
-
scheduler.unregisterSource(handle);
|
|
1645
|
-
throw error;
|
|
1646
|
-
}
|
|
1647
|
-
scheduler.cleanups.add(() => unsubscribe?.());
|
|
1648
|
-
externalSources.set(port, handle);
|
|
1649
|
-
return handle;
|
|
1650
|
-
};
|
|
1651
|
-
return {
|
|
1652
|
-
dispose: () => {
|
|
1653
|
-
documents.clear();
|
|
1654
|
-
readables.clear();
|
|
1655
|
-
},
|
|
1656
|
-
document,
|
|
1657
|
-
fromSource: (source, options) => externalValueSource(source, options?.isEqual ?? Object.is),
|
|
1658
|
-
fromCollectionSource: (source) => externalCollectionSource(source),
|
|
1659
|
-
input: (initial, options) => {
|
|
1660
|
-
const { source, set } = valueSource(initial, options?.isEqual ?? Object.is);
|
|
1661
|
-
return Object.freeze({
|
|
1662
|
-
source,
|
|
1663
|
-
set
|
|
1664
|
-
});
|
|
1665
|
-
},
|
|
1666
|
-
fromReadable: (readable, options) => {
|
|
1667
|
-
scheduler.assertIdle();
|
|
1668
|
-
if (projectionHandles.has(readable)) throw new Error("Projection nodes must be declared directly as sources.");
|
|
1669
|
-
const equal = options?.isEqual ?? Object.is;
|
|
1670
|
-
let bindings = readables.get(readable);
|
|
1671
|
-
const old = bindings?.get(equal);
|
|
1672
|
-
if (old) return old.source;
|
|
1673
|
-
const input = valueSource(readable.current(), equal);
|
|
1674
|
-
if (bindings) {
|
|
1675
|
-
bindings.set(equal, {
|
|
1676
|
-
source: input.source,
|
|
1677
|
-
accept: (value) => input.accept(value)
|
|
1678
|
-
});
|
|
1679
|
-
return input.source;
|
|
1680
|
-
}
|
|
1681
|
-
bindings = new Map([[equal, {
|
|
1682
|
-
source: input.source,
|
|
1683
|
-
accept: (value) => input.accept(value)
|
|
1684
|
-
}]]);
|
|
1685
|
-
const members = bindings;
|
|
1686
|
-
const receive = (settle = true) => {
|
|
1687
|
-
if (!scheduler.active) return;
|
|
1688
|
-
let value;
|
|
1689
|
-
try {
|
|
1690
|
-
value = readable.current();
|
|
1691
|
-
} catch (cause) {
|
|
1692
|
-
for (const member of members.values()) {
|
|
1693
|
-
const record = scheduler.source(member.source);
|
|
1694
|
-
record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
|
|
1695
|
-
scheduler.capture(record);
|
|
1696
|
-
}
|
|
1697
|
-
if (settle) scheduler.run();
|
|
1698
|
-
return;
|
|
1699
|
-
}
|
|
1700
|
-
for (const member of members.values()) try {
|
|
1701
|
-
member.accept(value);
|
|
1702
|
-
} catch (cause) {
|
|
1703
|
-
const record = scheduler.source(member.source);
|
|
1704
|
-
record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
|
|
1705
|
-
scheduler.capture(record);
|
|
1706
|
-
}
|
|
1707
|
-
if (settle) scheduler.run();
|
|
1708
|
-
};
|
|
1709
|
-
let unsubscribe;
|
|
1710
|
-
let detach;
|
|
1711
|
-
try {
|
|
1712
|
-
const owner = documentReadableOwner(readable);
|
|
1713
|
-
if (owner) {
|
|
1714
|
-
document(owner);
|
|
1715
|
-
detach = attachProjection(owner, {
|
|
1716
|
-
capture: () => receive(false),
|
|
1717
|
-
settle: scheduler.settle,
|
|
1718
|
-
flush: scheduler.flush,
|
|
1719
|
-
dispose: () => {
|
|
1720
|
-
for (const member of members.values()) {
|
|
1721
|
-
const record = scheduler.source(member.source);
|
|
1722
|
-
record.fault = new ProjectionError("source", "document readable disposed", [record.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
|
|
1723
|
-
scheduler.capture(record);
|
|
1724
|
-
}
|
|
1725
|
-
scheduler.run();
|
|
1726
|
-
}
|
|
1727
|
-
});
|
|
1728
|
-
}
|
|
1729
|
-
unsubscribe = readable.subscribe(() => receive());
|
|
1730
|
-
input.accept(readable.current());
|
|
1731
|
-
} catch (error) {
|
|
1732
|
-
try {
|
|
1733
|
-
unsubscribe?.();
|
|
1734
|
-
detach?.();
|
|
1735
|
-
} finally {
|
|
1736
|
-
scheduler.unregisterSource(input.source);
|
|
1737
|
-
}
|
|
1738
|
-
throw error;
|
|
1739
|
-
}
|
|
1740
|
-
scheduler.cleanups.add(unsubscribe);
|
|
1741
|
-
if (detach) scheduler.cleanups.add(detach);
|
|
1742
|
-
readables.set(readable, bindings);
|
|
1743
|
-
scheduler.run();
|
|
1744
|
-
return input.source;
|
|
1745
|
-
},
|
|
1746
|
-
assertCollection: (handle) => {
|
|
1747
|
-
scheduler.source(handle);
|
|
1748
|
-
if (!collections.has(handle) && !collectionHandles.has(handle)) throw new Error("map requires a collection source.");
|
|
1749
|
-
}
|
|
1750
|
-
};
|
|
1751
|
-
};
|
|
1752
|
-
//#endregion
|
|
1753
|
-
//#region core/src/projection/value.ts
|
|
1754
|
-
const createValue = (scheduler, spec, options) => {
|
|
1755
|
-
let instance;
|
|
1756
|
-
let value;
|
|
1757
|
-
let next;
|
|
1758
|
-
let previous;
|
|
1759
|
-
let initialized = false;
|
|
1760
|
-
let revision = 0;
|
|
1761
|
-
let changed = false;
|
|
1762
|
-
let reset = false;
|
|
1763
|
-
let batch;
|
|
1764
|
-
let cause;
|
|
1765
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
1766
|
-
const owner = createNode(scheduler, spec, {
|
|
1767
|
-
evaluate: (sources, build) => {
|
|
1768
|
-
const metadata = Object.values(sources).find((value) => value && typeof value === "object" && ("batch" in value && value.batch !== void 0 || "cause" in value && value.cause !== void 0));
|
|
1769
|
-
batch = scheduler.batchContext() ?? metadata?.batch;
|
|
1770
|
-
cause = metadata?.cause ?? batch?.cause;
|
|
1771
|
-
let candidate;
|
|
1772
|
-
if (build || !instance) {
|
|
1773
|
-
profile.materialized.rebuilt();
|
|
1774
|
-
instance = void 0;
|
|
1775
|
-
const built = spec.build(sources);
|
|
1776
|
-
assertSynchronous(built);
|
|
1777
|
-
instance = built;
|
|
1778
|
-
candidate = built.value;
|
|
1779
|
-
} else {
|
|
1780
|
-
profile.materialized.updated();
|
|
1781
|
-
const result = instance.update(sources);
|
|
1782
|
-
assertSynchronous(result);
|
|
1783
|
-
if (result.kind === "rebuild") {
|
|
1784
|
-
instance = void 0;
|
|
1785
|
-
const built = spec.build(sources);
|
|
1786
|
-
assertSynchronous(built);
|
|
1787
|
-
instance = built;
|
|
1788
|
-
candidate = built.value;
|
|
1789
|
-
build = true;
|
|
1790
|
-
} else candidate = result.kind === "changed" ? result.value : value;
|
|
1791
|
-
}
|
|
1792
|
-
previous = value;
|
|
1793
|
-
changed = !initialized || !(options?.isEqual ?? Object.is)(value, candidate);
|
|
1794
|
-
next = changed ? candidate : value;
|
|
1795
|
-
reset = build;
|
|
1796
|
-
return changed;
|
|
1797
|
-
},
|
|
1798
|
-
context: (active) => {
|
|
1799
|
-
assertScope(active);
|
|
1800
|
-
return Object.freeze({
|
|
1801
|
-
value: next,
|
|
1802
|
-
previous,
|
|
1803
|
-
changed,
|
|
1804
|
-
revision: revision + (changed ? 1 : 0),
|
|
1805
|
-
reset,
|
|
1806
|
-
cause,
|
|
1807
|
-
batch
|
|
1808
|
-
});
|
|
1809
|
-
},
|
|
1810
|
-
revision: () => revision,
|
|
1811
|
-
reset: () => reset,
|
|
1812
|
-
publish: () => {
|
|
1813
|
-
if (changed && initialized) revision++;
|
|
1814
|
-
value = next;
|
|
1815
|
-
initialized = true;
|
|
1816
|
-
},
|
|
1817
|
-
emit: (call) => {
|
|
1818
|
-
profile.materialized.notification();
|
|
1819
|
-
Array.from(listeners).forEach((listener) => call(listener));
|
|
1820
|
-
},
|
|
1821
|
-
clear: () => {
|
|
1822
|
-
next = value;
|
|
1823
|
-
previous = value;
|
|
1824
|
-
changed = false;
|
|
1825
|
-
reset = false;
|
|
1826
|
-
batch = void 0;
|
|
1827
|
-
cause = void 0;
|
|
1828
|
-
},
|
|
1829
|
-
release: () => {
|
|
1830
|
-
listeners.clear();
|
|
1831
|
-
instance = void 0;
|
|
1832
|
-
value = next = previous = void 0;
|
|
1833
|
-
}
|
|
1834
|
-
});
|
|
1835
|
-
const handle = Object.freeze({
|
|
1836
|
-
current: () => {
|
|
1837
|
-
owner.check();
|
|
1838
|
-
return value;
|
|
1839
|
-
},
|
|
1840
|
-
revision: () => {
|
|
1841
|
-
owner.check();
|
|
1842
|
-
return revision;
|
|
1843
|
-
},
|
|
1844
|
-
subscribe: (listener) => {
|
|
1845
|
-
owner.check();
|
|
1846
|
-
listeners.add(listener);
|
|
1847
|
-
return () => {
|
|
1848
|
-
listeners.delete(listener);
|
|
1849
|
-
};
|
|
1850
|
-
},
|
|
1851
|
-
rebuild: owner.rebuild,
|
|
1852
|
-
dispose: owner.dispose
|
|
1853
|
-
});
|
|
1854
|
-
owner.install(handle);
|
|
1855
|
-
return handle;
|
|
1856
|
-
};
|
|
1857
|
-
//#endregion
|
|
1858
|
-
//#region core/src/projection/runtime.ts
|
|
1859
|
-
const createRuntimeExecutor = (options) => {
|
|
1860
|
-
const scheduler = createScheduler(options.onError);
|
|
1861
|
-
const sources = createSources(scheduler);
|
|
1862
|
-
function value(input, computeOrOptions, options) {
|
|
1863
|
-
if (typeof computeOrOptions !== "function") return createValue(scheduler, input, computeOrOptions);
|
|
1864
|
-
const compute = (input) => {
|
|
1865
|
-
const result = computeOrOptions(input);
|
|
1866
|
-
assertSynchronous(result);
|
|
1867
|
-
return result;
|
|
1868
|
-
};
|
|
1869
|
-
return createValue(scheduler, {
|
|
1870
|
-
sources: input,
|
|
1871
|
-
build: (input) => ({
|
|
1872
|
-
value: compute(input),
|
|
1873
|
-
update: (input) => ({
|
|
1874
|
-
kind: "changed",
|
|
1875
|
-
value: compute(input)
|
|
1876
|
-
})
|
|
1877
|
-
})
|
|
1878
|
-
}, options);
|
|
1879
|
-
}
|
|
1880
|
-
const map = (source, mapper, options) => {
|
|
1881
|
-
sources.assertCollection(source);
|
|
1882
|
-
const calculate = (id, entry) => {
|
|
1883
|
-
const value = mapper(id, entry);
|
|
1884
|
-
assertSynchronous(value);
|
|
1885
|
-
return value;
|
|
1886
|
-
};
|
|
1887
|
-
return createCollection(scheduler, {
|
|
1888
|
-
sources: { source },
|
|
1889
|
-
isEqual: options?.isEqual,
|
|
1890
|
-
build: ({ sources, writer }) => {
|
|
1891
|
-
const input = sources.source;
|
|
1892
|
-
const read = "read" in input ? input.read : input;
|
|
1893
|
-
const ids = read.ids();
|
|
1894
|
-
profile.collectionView.idsScanned(ids.length);
|
|
1895
|
-
for (const id of ids) {
|
|
1896
|
-
profile.collectionView.mapped();
|
|
1897
|
-
writer.set(id, calculate(id, read.get(id)));
|
|
1898
|
-
}
|
|
1899
|
-
writer.order(ids);
|
|
1900
|
-
return { update: ({ sources, writer }) => {
|
|
1901
|
-
const input = sources.source;
|
|
1902
|
-
if (input.reset) return { kind: "rebuild" };
|
|
1903
|
-
const read = "read" in input ? input.read : input;
|
|
1904
|
-
let keys;
|
|
1905
|
-
let structural;
|
|
1906
|
-
if ("candidates" in input) {
|
|
1907
|
-
keys = input.candidates.keys;
|
|
1908
|
-
structural = input.candidates.orderDirty;
|
|
1909
|
-
} else {
|
|
1910
|
-
const change = input.change;
|
|
1911
|
-
if (!change) return;
|
|
1912
|
-
if (change.kind === "reset") return { kind: "rebuild" };
|
|
1913
|
-
keys = new Set([
|
|
1914
|
-
...change.added,
|
|
1915
|
-
...change.removed,
|
|
1916
|
-
...change.updated
|
|
1917
|
-
]);
|
|
1918
|
-
structural = Boolean(change.added.size || change.removed.size || change.orderChanged);
|
|
1919
|
-
}
|
|
1920
|
-
for (const key of keys) if (!read.has(key)) writer.remove(key);
|
|
1921
|
-
else {
|
|
1922
|
-
profile.collectionView.mapped();
|
|
1923
|
-
writer.set(key, calculate(key, read.get(key)));
|
|
1924
|
-
}
|
|
1925
|
-
if (structural) {
|
|
1926
|
-
const ids = read.ids();
|
|
1927
|
-
profile.collectionView.idsScanned(ids.length);
|
|
1928
|
-
writer.order(ids);
|
|
1929
|
-
}
|
|
1930
|
-
} };
|
|
1931
|
-
}
|
|
1932
|
-
});
|
|
1933
|
-
};
|
|
1934
|
-
const runtime = {
|
|
1935
|
-
document: sources.document,
|
|
1936
|
-
fromSource: (source, options) => sources.fromSource(source, options),
|
|
1937
|
-
fromCollectionSource: (source) => sources.fromCollectionSource(source),
|
|
1938
|
-
input: sources.input,
|
|
1939
|
-
fromReadable: sources.fromReadable,
|
|
1940
|
-
value,
|
|
1941
|
-
collection: () => (spec) => createCollection(scheduler, spec),
|
|
1942
|
-
map,
|
|
1943
|
-
batch: scheduler.batch,
|
|
1944
|
-
dispose: () => {
|
|
1945
|
-
try {
|
|
1946
|
-
scheduler.dispose();
|
|
1947
|
-
} finally {
|
|
1948
|
-
if (!scheduler.active) sources.dispose();
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
};
|
|
1952
|
-
return Object.freeze(runtime);
|
|
1953
|
-
};
|
|
1954
|
-
//#endregion
|
|
1955
|
-
//#region core/src/projection/store.ts
|
|
1956
|
-
const runtimeStates = /* @__PURE__ */ new WeakMap();
|
|
1957
|
-
let activeTracker;
|
|
1958
|
-
const trackKey = (key) => {
|
|
1959
|
-
activeTracker?.keys.add(key);
|
|
1960
|
-
};
|
|
1961
|
-
const trackAll = (structure = false) => {
|
|
1962
|
-
if (!activeTracker) return;
|
|
1963
|
-
activeTracker.all = true;
|
|
1964
|
-
activeTracker.structure ||= structure;
|
|
1965
|
-
};
|
|
1966
|
-
const mapView = (read) => {
|
|
1967
|
-
const keys = read.ids();
|
|
1968
|
-
const source = /* @__PURE__ */ new Map();
|
|
1969
|
-
for (const key of keys) source.set(key, read.get(key));
|
|
1970
|
-
const view = {
|
|
1971
|
-
get: (key) => {
|
|
1972
|
-
trackKey(key);
|
|
1973
|
-
return source.get(key);
|
|
1974
|
-
},
|
|
1975
|
-
has: (key) => {
|
|
1976
|
-
trackKey(key);
|
|
1977
|
-
return source.has(key);
|
|
1978
|
-
},
|
|
1979
|
-
get size() {
|
|
1980
|
-
trackAll(true);
|
|
1981
|
-
return source.size;
|
|
1982
|
-
},
|
|
1983
|
-
keys: () => {
|
|
1984
|
-
trackAll(true);
|
|
1985
|
-
return source.keys();
|
|
1986
|
-
},
|
|
1987
|
-
values: () => {
|
|
1988
|
-
trackAll(true);
|
|
1989
|
-
return source.values();
|
|
1990
|
-
},
|
|
1991
|
-
entries: () => {
|
|
1992
|
-
trackAll(true);
|
|
1993
|
-
return source.entries();
|
|
1994
|
-
},
|
|
1995
|
-
forEach: (callback, thisArg) => {
|
|
1996
|
-
trackAll(true);
|
|
1997
|
-
source.forEach((value, key) => callback.call(thisArg, value, key, view));
|
|
1998
|
-
},
|
|
1999
|
-
[Symbol.iterator]: () => {
|
|
2000
|
-
trackAll(true);
|
|
2001
|
-
return source[Symbol.iterator]();
|
|
2002
|
-
}
|
|
2003
|
-
};
|
|
2004
|
-
return Object.freeze(view);
|
|
2005
|
-
};
|
|
2006
|
-
const isMapLike = (value) => value instanceof Map || value !== null && typeof value === "object" && typeof value.get === "function" && typeof value.keys === "function" && typeof value.entries === "function";
|
|
2007
|
-
const mapRead = (value) => ({
|
|
2008
|
-
get: (key) => value.get(key),
|
|
2009
|
-
has: (key) => value.has(key),
|
|
2010
|
-
ids: () => Object.freeze([...value.keys()])
|
|
2011
|
-
});
|
|
2012
|
-
const mapChange = (previous, current) => {
|
|
2013
|
-
const added = /* @__PURE__ */ new Set();
|
|
2014
|
-
const removed = /* @__PURE__ */ new Set();
|
|
2015
|
-
const updated = /* @__PURE__ */ new Set();
|
|
2016
|
-
for (const key of previous.keys()) if (!current.has(key)) removed.add(key);
|
|
2017
|
-
else if (!Object.is(previous.get(key), current.get(key))) updated.add(key);
|
|
2018
|
-
for (const key of current.keys()) if (!previous.has(key)) added.add(key);
|
|
2019
|
-
const before = [...previous.keys()].filter((key) => current.has(key));
|
|
2020
|
-
const after = [...current.keys()].filter((key) => previous.has(key));
|
|
2021
|
-
const orderChanged = before.length !== after.length || before.some((key, index) => key !== after[index]);
|
|
2022
|
-
if (!added.size && !removed.size && !updated.size && !orderChanged) return void 0;
|
|
2023
|
-
return {
|
|
2024
|
-
kind: "incremental",
|
|
2025
|
-
added,
|
|
2026
|
-
removed,
|
|
2027
|
-
updated,
|
|
2028
|
-
orderChanged
|
|
2029
|
-
};
|
|
2030
|
-
};
|
|
2031
|
-
const currentValue = (event) => {
|
|
2032
|
-
if (!event || typeof event !== "object") return event;
|
|
2033
|
-
if ("value" in event) return event.value;
|
|
2034
|
-
if ("read" in event) return snapshot(event.read);
|
|
2035
|
-
if ("ids" in event && typeof event.ids === "function") return mapView(event);
|
|
2036
|
-
return event;
|
|
2037
|
-
};
|
|
2038
|
-
const sourceOf = (instance) => instance;
|
|
2039
|
-
const createProjectionRuntime = (options) => {
|
|
2040
|
-
const runtime = createRuntimeExecutor({ onError: options?.onError ?? (() => void 0) });
|
|
2041
|
-
const instances = /* @__PURE__ */ new WeakMap();
|
|
2042
|
-
const inputs = /* @__PURE__ */ new WeakMap();
|
|
2043
|
-
const collections = /* @__PURE__ */ new WeakMap();
|
|
2044
|
-
const materialize = (projection) => {
|
|
2045
|
-
const old = instances.get(projection);
|
|
2046
|
-
if (old) return old;
|
|
2047
|
-
const definition = definitionOf(projection);
|
|
2048
|
-
let instance;
|
|
2049
|
-
switch (definition.kind) {
|
|
2050
|
-
case "input": {
|
|
2051
|
-
const input = runtime.input(definition.initial, { isEqual: definition.isEqual });
|
|
2052
|
-
inputs.set(projection, input);
|
|
2053
|
-
instance = runtime.value({ input: input.source }, ({ input }) => input.value);
|
|
2054
|
-
break;
|
|
2055
|
-
}
|
|
2056
|
-
case "readable": {
|
|
2057
|
-
const source = runtime.fromReadable(definition.readable, { isEqual: definition.isEqual });
|
|
2058
|
-
instance = runtime.value({ source }, ({ source }) => source.value);
|
|
2059
|
-
break;
|
|
2060
|
-
}
|
|
2061
|
-
case "source-value":
|
|
2062
|
-
{
|
|
2063
|
-
const source = runtime.fromSource(definition.source);
|
|
2064
|
-
instance = runtime.value({ source }, ({ source }) => source.value);
|
|
2065
|
-
}
|
|
2066
|
-
break;
|
|
2067
|
-
case "source-collection": {
|
|
2068
|
-
const source = runtime.fromCollectionSource(definition.source);
|
|
2069
|
-
instance = runtime.map(source, (_key, value) => value);
|
|
2070
|
-
break;
|
|
2071
|
-
}
|
|
2072
|
-
case "document": {
|
|
2073
|
-
const document = runtime.document(definition.document);
|
|
2074
|
-
if (!definition.selector) {
|
|
2075
|
-
instance = runtime.value({ document }, ({ document }) => snapshot(document.read));
|
|
2076
|
-
break;
|
|
2077
|
-
}
|
|
2078
|
-
const state = accessOf(definition.document);
|
|
2079
|
-
try {
|
|
2080
|
-
const collection = document.collection(definition.selector);
|
|
2081
|
-
instance = runtime.map(collection, (_id, entry) => snapshot(entry));
|
|
2082
|
-
break;
|
|
2083
|
-
} catch {
|
|
2084
|
-
const selected = document.targets(definition.selector);
|
|
2085
|
-
const address = compilePath(state.schema, "value", definition.selector).address;
|
|
2086
|
-
instance = runtime.value({ selected }, () => {
|
|
2087
|
-
return read(snapshot(state.document), address, state.schema);
|
|
2088
|
-
});
|
|
2089
|
-
}
|
|
2090
|
-
break;
|
|
2091
|
-
}
|
|
2092
|
-
case "derive": {
|
|
2093
|
-
const sources = Object.fromEntries(definition.dependencies.map((dependency, index) => [`d${index}`, sourceOf(materialize(dependency))]));
|
|
2094
|
-
instance = runtime.value(sources, (values) => definition.compute(...Object.values(values).map(currentValue)), { isEqual: definition.isEqual });
|
|
2095
|
-
break;
|
|
2096
|
-
}
|
|
2097
|
-
case "incremental-value": {
|
|
2098
|
-
const sources = Object.fromEntries(Object.entries(definition.dependencies).map(([key, dependency]) => [key, sourceOf(materialize(dependency))]));
|
|
2099
|
-
instance = runtime.value({
|
|
2100
|
-
sources,
|
|
2101
|
-
build: definition.build
|
|
2102
|
-
}, { isEqual: definition.isEqual });
|
|
2103
|
-
break;
|
|
2104
|
-
}
|
|
2105
|
-
case "incremental-collection": {
|
|
2106
|
-
const sources = Object.fromEntries(Object.entries(definition.dependencies).map(([key, dependency]) => [key, sourceOf(materialize(dependency))]));
|
|
2107
|
-
instance = runtime.collection()({
|
|
2108
|
-
sources,
|
|
2109
|
-
build: definition.build,
|
|
2110
|
-
isEqual: definition.isEqual,
|
|
2111
|
-
name: definition.name
|
|
2112
|
-
});
|
|
2113
|
-
break;
|
|
2114
|
-
}
|
|
2115
|
-
}
|
|
2116
|
-
instances.set(projection, instance);
|
|
2117
|
-
return instance;
|
|
2118
|
-
};
|
|
2119
|
-
const get = (projection) => {
|
|
2120
|
-
const instance = materialize(projection);
|
|
2121
|
-
const current = instance.current();
|
|
2122
|
-
if (current && typeof current === "object" && "ids" in current && typeof current.ids === "function") {
|
|
2123
|
-
const existing = collections.get(instance);
|
|
2124
|
-
const revision = instance.revision();
|
|
2125
|
-
if (existing?.revision === revision) return existing.value;
|
|
2126
|
-
const value = mapView(current);
|
|
2127
|
-
collections.set(instance, {
|
|
2128
|
-
revision,
|
|
2129
|
-
value
|
|
2130
|
-
});
|
|
2131
|
-
return value;
|
|
2132
|
-
}
|
|
2133
|
-
if (isMapLike(current)) {
|
|
2134
|
-
const existing = collections.get(instance);
|
|
2135
|
-
const revision = instance.revision();
|
|
2136
|
-
if (existing?.revision === revision) return existing.value;
|
|
2137
|
-
const value = mapView(mapRead(current));
|
|
2138
|
-
collections.set(instance, {
|
|
2139
|
-
revision,
|
|
2140
|
-
value
|
|
2141
|
-
});
|
|
2142
|
-
return value;
|
|
2143
|
-
}
|
|
2144
|
-
return current;
|
|
2145
|
-
};
|
|
2146
|
-
const store = {
|
|
2147
|
-
get,
|
|
2148
|
-
subscribe: ((projection, listener) => {
|
|
2149
|
-
return materialize(projection).subscribe(listener);
|
|
2150
|
-
}),
|
|
2151
|
-
set: ((input, value) => {
|
|
2152
|
-
materialize(input);
|
|
2153
|
-
const target = inputs.get(input);
|
|
2154
|
-
if (!target) throw new TypeError("Projection is not an input.");
|
|
2155
|
-
target.set(value);
|
|
2156
|
-
}),
|
|
2157
|
-
batch: runtime.batch,
|
|
2158
|
-
dispose: runtime.dispose
|
|
2159
|
-
};
|
|
2160
|
-
runtimeStates.set(store, {
|
|
2161
|
-
materialize,
|
|
2162
|
-
get
|
|
2163
|
-
});
|
|
2164
|
-
return Object.freeze(store);
|
|
2165
|
-
};
|
|
2166
|
-
const trackProjection = (owner, projection, selector) => {
|
|
2167
|
-
const state = runtimeStates.get(owner);
|
|
2168
|
-
if (!state) throw new TypeError("Unknown projection runtime.");
|
|
2169
|
-
const tracker = {
|
|
2170
|
-
keys: /* @__PURE__ */ new Set(),
|
|
2171
|
-
all: false,
|
|
2172
|
-
structure: false
|
|
2173
|
-
};
|
|
2174
|
-
const previous = activeTracker;
|
|
2175
|
-
activeTracker = tracker;
|
|
2176
|
-
try {
|
|
2177
|
-
return {
|
|
2178
|
-
value: selector(state.get(projection)),
|
|
2179
|
-
selection: Object.freeze({
|
|
2180
|
-
keys: new Set(tracker.keys),
|
|
2181
|
-
all: tracker.all,
|
|
2182
|
-
structure: tracker.structure
|
|
2183
|
-
})
|
|
2184
|
-
};
|
|
2185
|
-
} finally {
|
|
2186
|
-
activeTracker = previous;
|
|
2187
|
-
}
|
|
2188
|
-
};
|
|
2189
|
-
const subscribeProjection = (owner, projection, selection, listener) => {
|
|
2190
|
-
const state = runtimeStates.get(owner);
|
|
2191
|
-
if (!state) throw new TypeError("Unknown projection runtime.");
|
|
2192
|
-
const instance = state.materialize(projection);
|
|
2193
|
-
let previousCollection;
|
|
2194
|
-
const initialValue = state.get(projection);
|
|
2195
|
-
if (isMapLike(initialValue)) previousCollection = initialValue;
|
|
2196
|
-
return instance.subscribe(((change) => {
|
|
2197
|
-
if (!change && previousCollection) {
|
|
2198
|
-
const current = state.get(projection);
|
|
2199
|
-
if (isMapLike(current)) {
|
|
2200
|
-
change = mapChange(previousCollection, current);
|
|
2201
|
-
previousCollection = current;
|
|
2202
|
-
if (!change) return;
|
|
2203
|
-
} else previousCollection = void 0;
|
|
2204
|
-
}
|
|
2205
|
-
if (!change || change.kind === "reset") {
|
|
2206
|
-
listener();
|
|
2207
|
-
return;
|
|
2208
|
-
}
|
|
2209
|
-
if (selection.all && (selection.structure || change.added.size || change.removed.size || change.updated.size)) {
|
|
2210
|
-
listener();
|
|
2211
|
-
return;
|
|
2212
|
-
}
|
|
2213
|
-
for (const key of selection.keys) if (change.added.has(key) || change.removed.has(key) || change.updated.has(key)) {
|
|
2214
|
-
listener();
|
|
2215
|
-
return;
|
|
2216
|
-
}
|
|
2217
|
-
}));
|
|
2218
|
-
};
|
|
2219
|
-
//#endregion
|
|
2220
|
-
export { variant as A, field as C, optional as D, object as E, table as O, compilePath as S, map as T, DocumentDisposedError as _, ProjectionError as a, createImpact as b, disposeNotification as c, subscribeDependencies as d, subscribeRoot as f, readWith as g, bindRuntimeAccess as h, ProjectionDisposedError as i, tree as k, notify as l, accessOf as m, subscribeProjection as n, bindDocumentReadable as o, subscribeTargets as p, trackProjection as r, createNotification as s, createProjectionRuntime as t, shareNotification as u, DocumentReentrancyError as v, list as w, same as x, TransactionRejected as y };
|
|
2221
|
-
|
|
2222
|
-
//# sourceMappingURL=store-CtgvCVFg.js.map
|