doxum 0.1.11 → 0.1.13
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 +43 -33
- package/dist/advanced.cjs +116 -0
- package/dist/advanced.cjs.map +1 -0
- package/dist/advanced.d.cts +42 -0
- package/dist/advanced.d.ts +42 -0
- package/dist/advanced.js +115 -0
- package/dist/advanced.js.map +1 -0
- package/dist/{contract-CeAnEPBA.d.ts → contract-BT53Gg94.d.ts} +5 -4
- package/dist/{contract-DtGVSXSK.d.cts → contract-XmKnroJq.d.cts} +5 -4
- package/dist/definition-177L4Amk.cjs +898 -0
- package/dist/definition-177L4Amk.cjs.map +1 -0
- package/dist/definition-BlMeHwdz.d.ts +98 -0
- package/dist/definition-DXrKMVs7.js +785 -0
- package/dist/definition-DXrKMVs7.js.map +1 -0
- package/dist/definition-kxnWUvX9.d.cts +98 -0
- package/dist/{driver-xOIkwrB8.cjs → driver-BCyfqxsl.cjs} +1 -1
- package/dist/{driver-xOIkwrB8.cjs.map → driver-BCyfqxsl.cjs.map} +1 -1
- package/dist/{driver-BlR81Dqg.js → driver-CSnhMjbP.js} +1 -1
- package/dist/{driver-BlR81Dqg.js.map → driver-CSnhMjbP.js.map} +1 -1
- package/dist/index.cjs +31 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/{integration-VTZ3ICsO.js → integration-Bco8ZB16.js} +7 -3
- package/dist/integration-Bco8ZB16.js.map +1 -0
- package/dist/integration-CEUn96tx.cjs +52 -0
- package/dist/integration-CEUn96tx.cjs.map +1 -0
- package/dist/integration.cjs +7 -3
- package/dist/integration.d.cts +7 -3
- package/dist/integration.d.ts +7 -3
- package/dist/integration.js +3 -3
- package/dist/local-sync.cjs +1 -1
- package/dist/local-sync.d.cts +1 -1
- package/dist/local-sync.d.ts +1 -1
- package/dist/local-sync.js +1 -1
- package/dist/react.cjs +66 -12
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +8 -6
- package/dist/react.d.ts +8 -6
- package/dist/react.js +66 -12
- package/dist/react.js.map +1 -1
- package/dist/store-CDeqZEE3.d.cts +28 -0
- package/dist/{store-DHFXfWSz.js → store-CtgvCVFg.js} +585 -917
- package/dist/store-CtgvCVFg.js.map +1 -0
- package/dist/{store-DU2u-qFH.cjs → store-DcDpDgjk.cjs} +595 -987
- package/dist/store-DcDpDgjk.cjs.map +1 -0
- package/dist/store-P5dJ7TVe.d.ts +28 -0
- package/package.json +6 -1
- package/skills/doxum-runtime/SKILL.md +5 -5
- package/skills/doxum-runtime/references/guide.en.md +12 -12
- package/skills/doxum-runtime/references/guide.zh-CN.md +9 -11
- package/skills/doxum-runtime/references/patterns.en.md +12 -17
- package/skills/doxum-runtime/references/patterns.zh-CN.md +12 -16
- package/skills/doxum-runtime/references/projections.en.md +56 -190
- package/skills/doxum-runtime/references/projections.zh-CN.md +49 -171
- package/dist/integration-BKgGuodm.cjs +0 -30
- package/dist/integration-BKgGuodm.cjs.map +0 -1
- package/dist/integration-VTZ3ICsO.js.map +0 -1
- package/dist/store-Bs0C0rNd.d.ts +0 -262
- package/dist/store-CJuwJl_K.d.cts +0 -262
- package/dist/store-DHFXfWSz.js.map +0 -1
- package/dist/store-DU2u-qFH.cjs.map +0 -1
|
@@ -1,189 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
var ParseError = class extends TypeError {
|
|
4
|
-
issues;
|
|
5
|
-
constructor(issue) {
|
|
6
|
-
super(`${issue.message} at ${JSON.stringify(issue.address)}`);
|
|
7
|
-
this.name = "ParseError";
|
|
8
|
-
this.issues = Object.freeze([Object.freeze({
|
|
9
|
-
...issue,
|
|
10
|
-
address: Object.freeze([...issue.address])
|
|
11
|
-
})]);
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
const issue = (address, message, code = "invalid-value") => ({
|
|
15
|
-
address,
|
|
16
|
-
message,
|
|
17
|
-
code
|
|
18
|
-
});
|
|
19
|
-
const checkScalar = (validator, value, address, strict) => {
|
|
20
|
-
if (!validator) return strict ? issue(address, "A validator is required to parse an opaque value.", "missing-validator") : void 0;
|
|
21
|
-
try {
|
|
22
|
-
const result = typeof validator === "function" ? validator(value) : validator["~standard"].validate(value);
|
|
23
|
-
if (result && (typeof result === "object" || typeof result === "function") && "then" in result && typeof result.then === "function") return issue(address, "Validators must be synchronous.");
|
|
24
|
-
if (typeof validator !== "function") {
|
|
25
|
-
if (!isRecord(result)) return issue(address, "Malformed validator result.");
|
|
26
|
-
if (Array.isArray(result.issues)) {
|
|
27
|
-
const first = result.issues[0];
|
|
28
|
-
return issue([...address, ...(first?.path ?? []).map((part) => String(typeof part === "object" ? part.key : part))], first?.message ?? "Value validation failed.");
|
|
29
|
-
}
|
|
30
|
-
if (!Object.hasOwn(result, "value")) return issue(address, "Malformed validator result.");
|
|
31
|
-
}
|
|
32
|
-
return;
|
|
33
|
-
} catch (error) {
|
|
34
|
-
return issue(address, error instanceof Error ? error.message : "Value validation failed.");
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const checkKey = (validator, value, address) => {
|
|
38
|
-
if (typeof value !== "string") return issue(address, "Collection keys must be strings.", "invalid-key");
|
|
39
|
-
const failure = checkScalar(validator, value, address, false);
|
|
40
|
-
return failure ? {
|
|
41
|
-
...failure,
|
|
42
|
-
code: "invalid-key"
|
|
43
|
-
} : void 0;
|
|
44
|
-
};
|
|
45
|
-
const checkValue = (node, value, address = [], strict = false) => {
|
|
46
|
-
if (node.optional && value === void 0) return void 0;
|
|
47
|
-
if (node.kind === "field") return checkScalar(node.validator, value, address, strict);
|
|
48
|
-
if (node.kind === "object" || node.kind === "variant") {
|
|
49
|
-
if (!isPlainObject(value)) return issue(address, "Expected an object.");
|
|
50
|
-
if (node.kind === "variant") {
|
|
51
|
-
const tag = value[node.tag];
|
|
52
|
-
if (!Object.hasOwn(value, node.tag) || typeof tag !== "string" || !Object.hasOwn(node.variants, tag)) return issue([...address, node.tag], "Unknown variant tag.");
|
|
53
|
-
}
|
|
54
|
-
const { members } = compiledShape(node, value);
|
|
55
|
-
for (const key of Reflect.ownKeys(value)) {
|
|
56
|
-
if (node.kind === "variant" && key === node.tag) continue;
|
|
57
|
-
if (typeof key !== "string" || !members.has(key)) return issue([...address, String(key)], "Property is not declared in the object schema.");
|
|
58
|
-
}
|
|
59
|
-
for (const [key, member] of members) {
|
|
60
|
-
const child = member.node;
|
|
61
|
-
if (!Object.prototype.hasOwnProperty.call(value, key) && !child.optional) return issue([...address, key], "Required property is missing.");
|
|
62
|
-
const failure = checkValue(child, value[key], [...address, key], strict);
|
|
63
|
-
if (failure) return failure;
|
|
64
|
-
}
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
if (node.kind === "table" || node.kind === "map") {
|
|
68
|
-
if (!isPlainObject(value)) return issue(address, "Expected a collection object.");
|
|
69
|
-
const entries = node.kind === "table" ? value.byId : value;
|
|
70
|
-
if (!isPlainObject(entries)) return issue(address, "Expected a byId object.");
|
|
71
|
-
if (node.kind === "table") {
|
|
72
|
-
if (!Array.isArray(value.ids) || value.ids.length !== Object.keys(entries).length) return issue(address, "Table ids and entries must agree.");
|
|
73
|
-
const seen = /* @__PURE__ */ new Set();
|
|
74
|
-
for (const id of value.ids) {
|
|
75
|
-
const failure = checkKey(node.key, id, address);
|
|
76
|
-
if (failure) return failure;
|
|
77
|
-
if (seen.has(id) || !Object.prototype.hasOwnProperty.call(entries, id)) return issue(address, "Table ids must be unique and reference existing entries.");
|
|
78
|
-
seen.add(id);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
for (const id of Object.keys(entries)) {
|
|
82
|
-
const failure = checkKey(node.key, id, [...address, id]) ?? checkValue(node.value, entries[id], [...address, id], strict);
|
|
83
|
-
if (failure) return failure;
|
|
84
|
-
}
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (node.kind === "list") {
|
|
88
|
-
if (!Array.isArray(value)) return issue(address, "Expected a list.");
|
|
89
|
-
const seen = /* @__PURE__ */ new Set();
|
|
90
|
-
const order = keys(value, node.keyOf);
|
|
91
|
-
for (let i = 0; i < value.length; i++) {
|
|
92
|
-
const failure = checkValue(node.value, value[i], [...address, String(i)], strict);
|
|
93
|
-
if (failure) return failure;
|
|
94
|
-
let key;
|
|
95
|
-
try {
|
|
96
|
-
key = order.at(i);
|
|
97
|
-
} catch {
|
|
98
|
-
return issue(address, "List keyOf failed.");
|
|
99
|
-
}
|
|
100
|
-
if (typeof key !== "string" || seen.has(key)) return issue(address, "List keys must be unique strings.", "invalid-key");
|
|
101
|
-
seen.add(key);
|
|
102
|
-
}
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
if (!validate(value)) return issue(address, "Invalid tree structure.", "invalid-tree");
|
|
106
|
-
if (isRecord(value) && isRecord(value.nodes)) for (const id of Object.keys(value.nodes)) {
|
|
107
|
-
const entry = value.nodes[id];
|
|
108
|
-
if (isRecord(entry) && Object.prototype.hasOwnProperty.call(entry, "value")) {
|
|
109
|
-
const failure = checkValue(node.value, entry.value, [...address, id], strict);
|
|
110
|
-
if (failure) return failure;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
/** Copy editable structure; atomic payloads retain their immutable ownership contract. */
|
|
115
|
-
const copyValue = (node, value) => {
|
|
116
|
-
if (value === void 0 || node.kind === "field") return value;
|
|
117
|
-
profile.copy.structure();
|
|
118
|
-
if ((node.kind === "object" || node.kind === "variant") && isRecord(value)) {
|
|
119
|
-
const result = Object.create(Object.getPrototypeOf(value));
|
|
120
|
-
const { members } = compiledShape(node, value);
|
|
121
|
-
for (const key of Object.getOwnPropertyNames(value)) Object.defineProperty(result, key, {
|
|
122
|
-
value: node.kind === "variant" && key === node.tag ? value[key] : copyValue(members.get(key).node, value[key]),
|
|
123
|
-
writable: true,
|
|
124
|
-
enumerable: Object.getOwnPropertyDescriptor(value, key)?.enumerable,
|
|
125
|
-
configurable: true
|
|
126
|
-
});
|
|
127
|
-
return result;
|
|
128
|
-
}
|
|
129
|
-
if ((node.kind === "table" || node.kind === "map") && isRecord(value)) {
|
|
130
|
-
const entries = node.kind === "table" ? value.byId : value;
|
|
131
|
-
const result = Object.fromEntries(Object.keys(entries).map((id) => [id, copyValue(node.value, entries[id])]));
|
|
132
|
-
return node.kind === "table" ? {
|
|
133
|
-
ids: [...value.ids],
|
|
134
|
-
byId: result
|
|
135
|
-
} : result;
|
|
136
|
-
}
|
|
137
|
-
if (node.kind === "list") return [...value];
|
|
138
|
-
if (node.kind === "tree" && isRecord(value)) {
|
|
139
|
-
const entries = value.nodes;
|
|
140
|
-
return {
|
|
141
|
-
...value,
|
|
142
|
-
nodes: Object.fromEntries(Object.entries(entries).map(([id, n]) => [id, {
|
|
143
|
-
...n,
|
|
144
|
-
children: [...n.children]
|
|
145
|
-
}]))
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
return value;
|
|
149
|
-
};
|
|
150
|
-
const equalValue = (node, left, right) => {
|
|
151
|
-
if (Object.is(left, right)) return true;
|
|
152
|
-
if (node.kind === "field" || left === void 0 || right === void 0) return false;
|
|
153
|
-
if ((node.kind === "object" || node.kind === "variant") && isRecord(left) && isRecord(right)) {
|
|
154
|
-
if (node.kind === "variant" && left[node.tag] !== right[node.tag]) return false;
|
|
155
|
-
for (const [key, member] of compiledShape(node, left).members) {
|
|
156
|
-
if (Object.hasOwn(left, key) !== Object.hasOwn(right, key)) return false;
|
|
157
|
-
if (!equalValue(member.node, left[key], right[key])) return false;
|
|
158
|
-
}
|
|
159
|
-
return true;
|
|
160
|
-
}
|
|
161
|
-
if (node.kind === "list" && Array.isArray(left) && Array.isArray(right)) return left.length === right.length && left.every((v, i) => equalValue(node.value, v, right[i]));
|
|
162
|
-
if (node.kind === "table" && isRecord(left) && isRecord(right)) return equal(left.ids, right.ids) && equalValue({
|
|
163
|
-
kind: "map",
|
|
164
|
-
value: node.value
|
|
165
|
-
}, left.byId, right.byId);
|
|
166
|
-
if (node.kind === "tree" && isRecord(left) && isRecord(right)) {
|
|
167
|
-
if (left.rootId !== right.rootId) return false;
|
|
168
|
-
const a = left.nodes, b = right.nodes;
|
|
169
|
-
return Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((id) => Object.hasOwn(b, id) && a[id].parentId === b[id].parentId && equal(a[id].children, b[id].children) && Object.hasOwn(a[id], "value") === Object.hasOwn(b[id], "value") && equalValue(node.value, a[id].value, b[id].value));
|
|
170
|
-
}
|
|
171
|
-
if (node.kind === "map" && isRecord(left) && isRecord(right)) {
|
|
172
|
-
const keys = Object.keys(left);
|
|
173
|
-
return keys.length === Object.keys(right).length && keys.every((key) => Object.hasOwn(right, key) && equalValue(node.value, left[key], right[key]));
|
|
174
|
-
}
|
|
175
|
-
return false;
|
|
176
|
-
};
|
|
177
|
-
const parse = (node, input) => {
|
|
178
|
-
const valueNode = node;
|
|
179
|
-
const failure = checkValue(valueNode, input, [], true);
|
|
180
|
-
if (failure) throw new ParseError(failure);
|
|
181
|
-
return copyValue(valueNode, input);
|
|
182
|
-
};
|
|
183
|
-
//#endregion
|
|
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";
|
|
184
3
|
//#region core/src/schema.ts
|
|
185
|
-
const node
|
|
186
|
-
const field = (validator) => node
|
|
4
|
+
const node = (value) => Object.freeze(value);
|
|
5
|
+
const field = (validator) => node({
|
|
187
6
|
kind: "field",
|
|
188
7
|
...validator ? { validator } : {}
|
|
189
8
|
});
|
|
@@ -195,36 +14,36 @@ const optional = (value) => {
|
|
|
195
14
|
"list",
|
|
196
15
|
"tree"
|
|
197
16
|
].includes(value.kind)) throw new TypeError("Only field, variant, map, list and tree nodes support optional presence.");
|
|
198
|
-
return node
|
|
17
|
+
return node({
|
|
199
18
|
...value,
|
|
200
19
|
optional: true
|
|
201
20
|
});
|
|
202
21
|
};
|
|
203
|
-
const object = (shape) => node
|
|
22
|
+
const object = (shape) => node({
|
|
204
23
|
kind: "object",
|
|
205
24
|
shape: Object.freeze({ ...shape })
|
|
206
25
|
});
|
|
207
|
-
const variant = (tag, variants) => node
|
|
26
|
+
const variant = (tag, variants) => node({
|
|
208
27
|
kind: "variant",
|
|
209
28
|
tag,
|
|
210
29
|
variants: Object.freeze({ ...variants })
|
|
211
30
|
});
|
|
212
|
-
const table = (value, options) => node
|
|
31
|
+
const table = (value, options) => node({
|
|
213
32
|
kind: "table",
|
|
214
33
|
value,
|
|
215
34
|
...options
|
|
216
35
|
});
|
|
217
|
-
const map = (value, options) => node
|
|
36
|
+
const map = (value, options) => node({
|
|
218
37
|
kind: "map",
|
|
219
38
|
value,
|
|
220
39
|
...options
|
|
221
40
|
});
|
|
222
|
-
const list = (value, config) => node
|
|
41
|
+
const list = (value, config) => node({
|
|
223
42
|
kind: "list",
|
|
224
43
|
keyOf: config.keyOf,
|
|
225
44
|
value
|
|
226
45
|
});
|
|
227
|
-
const tree = (value) => node
|
|
46
|
+
const tree = (value) => node({
|
|
228
47
|
kind: "tree",
|
|
229
48
|
value
|
|
230
49
|
});
|
|
@@ -269,542 +88,6 @@ const compilePath = (owner, kind, pick) => {
|
|
|
269
88
|
});
|
|
270
89
|
};
|
|
271
90
|
//#endregion
|
|
272
|
-
//#region core/src/mutation/operations/map.ts
|
|
273
|
-
function put(session, container, id, value) {
|
|
274
|
-
if (container.node.kind !== "map") return fail(container.at, "invalid-collection", "Expected a map.");
|
|
275
|
-
session.writeMember(container, id, value, "set");
|
|
276
|
-
}
|
|
277
|
-
function remove$3(session, container, id) {
|
|
278
|
-
if (container.node.kind !== "map") return fail(container.at, "invalid-collection", "Expected a map.");
|
|
279
|
-
session.writeMember(container, id, void 0, "remove");
|
|
280
|
-
}
|
|
281
|
-
//#endregion
|
|
282
|
-
//#region core/src/mutation/operations/table.ts
|
|
283
|
-
function create(session, container, entries, position) {
|
|
284
|
-
const { at, node, value } = container;
|
|
285
|
-
if (node.kind !== "table") return fail(at, "invalid-collection", "Expected a table.");
|
|
286
|
-
const table = value;
|
|
287
|
-
if (!valid(table.ids, position)) return fail(at, "invalid-anchor", "Unknown table anchor.");
|
|
288
|
-
const ids = /* @__PURE__ */ new Set();
|
|
289
|
-
for (const entry of entries) {
|
|
290
|
-
if (Object.hasOwn(table.byId, entry.id) || ids.has(entry.id)) return fail(at, "duplicate-entity", "Table key already exists.");
|
|
291
|
-
ids.add(entry.id);
|
|
292
|
-
}
|
|
293
|
-
if (!entries.length) return;
|
|
294
|
-
const index$1 = index(table.ids, position);
|
|
295
|
-
for (const entry of entries) session.writeMember(container, entry.id, entry.value, "set");
|
|
296
|
-
const length = table.ids.length;
|
|
297
|
-
table.ids.length += entries.length;
|
|
298
|
-
table.ids.copyWithin(index$1 + entries.length, index$1, length);
|
|
299
|
-
for (let i = 0; i < entries.length; i++) table.ids[index$1 + i] = entries[i].id;
|
|
300
|
-
}
|
|
301
|
-
function remove$2(session, container, ids) {
|
|
302
|
-
const { at, node, value } = container;
|
|
303
|
-
if (node.kind !== "table") return fail(at, "invalid-collection", "Expected a table.");
|
|
304
|
-
const table = value;
|
|
305
|
-
for (const id of ids) if (!Object.hasOwn(table.byId, id)) return fail(at, "missing-entity", "Table key does not exist.");
|
|
306
|
-
if (!ids.length) return;
|
|
307
|
-
const removed = new Set(ids);
|
|
308
|
-
for (const id of removed) session.writeMember(container, id, void 0, "remove");
|
|
309
|
-
table.ids = table.ids.filter((id) => !removed.has(id));
|
|
310
|
-
}
|
|
311
|
-
function replace$3(session, container, id, value) {
|
|
312
|
-
const { at, node, value: tableValue } = container;
|
|
313
|
-
if (node.kind !== "table") return fail(at, "invalid-collection", "Expected a table.");
|
|
314
|
-
const table = tableValue;
|
|
315
|
-
if (!Object.hasOwn(table.byId, id)) return fail(at.concat(id), "missing-entity", "Table key does not exist.");
|
|
316
|
-
session.writeMember(container, id, value, "set");
|
|
317
|
-
}
|
|
318
|
-
//#endregion
|
|
319
|
-
//#region core/src/mutation/operations/list.ts
|
|
320
|
-
function insert$1(session, container, value, position) {
|
|
321
|
-
const { at, node, value: current } = container;
|
|
322
|
-
if (node.kind !== "list") return fail(at, "invalid-list-key", "Expected a list.");
|
|
323
|
-
session.validate(node.value, value, at);
|
|
324
|
-
const id = node.keyOf(value);
|
|
325
|
-
if (typeof id !== "string") return fail(at, "invalid-list-key", "List key must be a string.");
|
|
326
|
-
const items = current, keys$2 = keys(items, node.keyOf);
|
|
327
|
-
if (keys$2.index(id) >= 0) return fail(at, "duplicate-list-item", "List key already exists.");
|
|
328
|
-
if (!valid(keys$2, position)) return fail(at, "invalid-anchor", "Unknown list anchor.");
|
|
329
|
-
session.recorder.order(container);
|
|
330
|
-
session.recorder.member(container, id, session.definition(container, id), -1);
|
|
331
|
-
insert$2(items, index(keys$2, position), value);
|
|
332
|
-
session.invalidate();
|
|
333
|
-
}
|
|
334
|
-
function replace$2(session, container, id, value) {
|
|
335
|
-
const { at, node, value: items } = container;
|
|
336
|
-
if (node.kind !== "list") return fail(at, "invalid-list-key", "Expected a list.");
|
|
337
|
-
const index = indexedKeys(items, node.keyOf).index(id);
|
|
338
|
-
if (index < 0) return fail(at, "missing-list-item", "List key does not exist.");
|
|
339
|
-
session.writeMember(container, id, value, "set", index);
|
|
340
|
-
}
|
|
341
|
-
function remove$1(session, container, id) {
|
|
342
|
-
const { at, node, value } = container;
|
|
343
|
-
if (node.kind !== "list") return fail(at, "missing-list-item", "List key does not exist.");
|
|
344
|
-
const index = keys(value, node.keyOf).index(id);
|
|
345
|
-
if (index < 0) return fail(at, "missing-list-item", "List key does not exist.");
|
|
346
|
-
session.writeMember(container, id, void 0, "remove", index);
|
|
347
|
-
}
|
|
348
|
-
//#endregion
|
|
349
|
-
//#region core/src/mutation/operations/tree.ts
|
|
350
|
-
const node = (tree, id) => Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : void 0;
|
|
351
|
-
const positionIndex = (index, length, at) => {
|
|
352
|
-
if (index !== void 0 && (!Number.isInteger(index) || index < 0)) return fail(at, "invalid-tree-index", "Tree index must be nonnegative.");
|
|
353
|
-
return Math.min(index ?? length, length);
|
|
354
|
-
};
|
|
355
|
-
const insert = (session, container, id, value, position) => {
|
|
356
|
-
const { at, node: schema, value: tree } = container;
|
|
357
|
-
if (typeof id !== "string") return fail(at, "invalid-key", "Tree keys must be strings.");
|
|
358
|
-
if (contains(tree, id)) return fail(at, "duplicate-tree-node", "Tree node already exists.");
|
|
359
|
-
const parentId = position?.parentId;
|
|
360
|
-
const parent = parentId === void 0 ? void 0 : node(tree, parentId);
|
|
361
|
-
if (tree.rootId !== void 0 && !parent || tree.rootId === void 0 && parentId !== void 0) return fail(at, "missing-tree-parent", "A nonempty tree requires an existing parent.");
|
|
362
|
-
const index = positionIndex(position?.index, parent?.children.length ?? 0, at);
|
|
363
|
-
session.validate(schema.value, value, at.concat(id));
|
|
364
|
-
session.recorder.tree(container, id);
|
|
365
|
-
if (parentId !== void 0) session.recorder.tree(container, parentId);
|
|
366
|
-
Object.defineProperty(tree.nodes, id, {
|
|
367
|
-
value: {
|
|
368
|
-
...parentId === void 0 ? {} : { parentId },
|
|
369
|
-
children: [],
|
|
370
|
-
value
|
|
371
|
-
},
|
|
372
|
-
writable: true,
|
|
373
|
-
configurable: true,
|
|
374
|
-
enumerable: true
|
|
375
|
-
});
|
|
376
|
-
if (parent) parent.children.splice(index, 0, id);
|
|
377
|
-
else tree.rootId = id;
|
|
378
|
-
session.invalidate();
|
|
379
|
-
};
|
|
380
|
-
const replace$1 = (session, container, id, value) => {
|
|
381
|
-
const { at, node: schema, value: tree } = container;
|
|
382
|
-
const entry = node(tree, id);
|
|
383
|
-
if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
|
|
384
|
-
if (Object.is(entry.value, value) && Object.hasOwn(entry, "value")) return;
|
|
385
|
-
session.validate(schema.value, value, at.concat(id));
|
|
386
|
-
session.recorder.tree(container, id);
|
|
387
|
-
entry.value = value;
|
|
388
|
-
};
|
|
389
|
-
const remove = (session, container, id) => {
|
|
390
|
-
const { at, value: tree } = container;
|
|
391
|
-
const entry = node(tree, id);
|
|
392
|
-
if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
|
|
393
|
-
const ids = [], stack = [id];
|
|
394
|
-
while (stack.length) {
|
|
395
|
-
const current = stack.pop();
|
|
396
|
-
ids.push(current);
|
|
397
|
-
for (const child of tree.nodes[current].children) stack.push(child);
|
|
398
|
-
}
|
|
399
|
-
for (const key of ids) session.recorder.tree(container, key);
|
|
400
|
-
if (entry.parentId !== void 0) session.recorder.tree(container, entry.parentId);
|
|
401
|
-
if (entry.parentId === void 0) delete tree.rootId;
|
|
402
|
-
else {
|
|
403
|
-
const children = tree.nodes[entry.parentId].children;
|
|
404
|
-
children.splice(children.indexOf(id), 1);
|
|
405
|
-
}
|
|
406
|
-
for (const key of ids) delete tree.nodes[key];
|
|
407
|
-
session.invalidate();
|
|
408
|
-
};
|
|
409
|
-
const move$1 = (session, container, id, position) => {
|
|
410
|
-
const { at, value: tree } = container;
|
|
411
|
-
const entry = node(tree, id), parentId = position?.parentId;
|
|
412
|
-
if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
|
|
413
|
-
if (entry.parentId === void 0 || parentId === void 0 || !contains(tree, parentId)) return fail(at, "missing-tree-parent", "Moving requires a non-root node and an existing parent.");
|
|
414
|
-
for (let ancestor = parentId; ancestor !== void 0; ancestor = tree.nodes[ancestor].parentId) if (ancestor === id) return fail(at, "tree-cycle", "Cannot move a node under its descendant.");
|
|
415
|
-
const old = tree.nodes[entry.parentId], next = tree.nodes[parentId];
|
|
416
|
-
const index = positionIndex(position?.index, next.children.length - (old === next ? 1 : 0), at);
|
|
417
|
-
const previous = old.children.indexOf(id);
|
|
418
|
-
if (old === next && previous === index) return;
|
|
419
|
-
session.recorder.tree(container, id);
|
|
420
|
-
session.recorder.tree(container, entry.parentId);
|
|
421
|
-
session.recorder.tree(container, parentId);
|
|
422
|
-
old.children.splice(previous, 1);
|
|
423
|
-
next.children.splice(index, 0, id);
|
|
424
|
-
entry.parentId = parentId;
|
|
425
|
-
session.invalidate();
|
|
426
|
-
};
|
|
427
|
-
//#endregion
|
|
428
|
-
//#region core/src/mutation/operations/order.ts
|
|
429
|
-
function move(session, container, id, position) {
|
|
430
|
-
const { at, node, value } = container;
|
|
431
|
-
if (node.kind !== "table" && node.kind !== "list") return fail(at, "invalid-collection", "Expected an ordered container.");
|
|
432
|
-
const items = node.kind === "table" ? value.ids : value;
|
|
433
|
-
const keys$1 = node.kind === "table" ? items : keys(items, node.keyOf);
|
|
434
|
-
const index = node.kind === "table" ? items.indexOf(id) : keys$1.index(id);
|
|
435
|
-
if (index < 0) return fail(at, "missing-entity", "Ordered key does not exist.");
|
|
436
|
-
if (!valid(keys$1, position)) return fail(at, "invalid-anchor", "Unknown order anchor.");
|
|
437
|
-
const next = afterRemove(keys$1, index, position);
|
|
438
|
-
if (next === index) return;
|
|
439
|
-
session.recorder.order(container);
|
|
440
|
-
move$2(items, index, next);
|
|
441
|
-
session.invalidate();
|
|
442
|
-
}
|
|
443
|
-
//#endregion
|
|
444
|
-
//#region core/src/access/scope.ts
|
|
445
|
-
const locations = /* @__PURE__ */ new WeakMap();
|
|
446
|
-
const locationOf = (value) => locations.get(value);
|
|
447
|
-
const addressOf = (target) => {
|
|
448
|
-
if (target.at) return target.at;
|
|
449
|
-
profile.access("addresses");
|
|
450
|
-
return target.at = addressOf(target.parent).concat(target.key);
|
|
451
|
-
};
|
|
452
|
-
const assertActive = (context) => {
|
|
453
|
-
if (context.active && !context.active()) throw new Error("Document access scope has expired.");
|
|
454
|
-
};
|
|
455
|
-
const collect = (context, at, kind = "value", id) => {
|
|
456
|
-
context.dependencies?.record(kind === "value" ? {
|
|
457
|
-
kind,
|
|
458
|
-
at
|
|
459
|
-
} : {
|
|
460
|
-
kind,
|
|
461
|
-
at,
|
|
462
|
-
...id === void 0 ? {} : { id }
|
|
463
|
-
});
|
|
464
|
-
};
|
|
465
|
-
const snapshot = (value) => {
|
|
466
|
-
const location = value && typeof value === "object" ? locationOf(value) : void 0;
|
|
467
|
-
if (!location) return value;
|
|
468
|
-
const { context } = location;
|
|
469
|
-
const at = addressOf(location.target);
|
|
470
|
-
profile.access("snapshots");
|
|
471
|
-
collect(context, at);
|
|
472
|
-
const resolved = resolve(context, location.target);
|
|
473
|
-
if (!resolved.node) throw new Error("The selected schema address no longer exists.");
|
|
474
|
-
return copyValue(resolved.node, resolved.value);
|
|
475
|
-
};
|
|
476
|
-
const replace = (container, key, value) => {
|
|
477
|
-
if (!locationOf(container)) throw new TypeError("replace requires scoped document access.");
|
|
478
|
-
if (typeof key !== "string") throw new TypeError("Document keys must be strings.");
|
|
479
|
-
Reflect.set(container, key, value);
|
|
480
|
-
};
|
|
481
|
-
const resolve = (context, target) => {
|
|
482
|
-
assertActive(context);
|
|
483
|
-
const generation = context.session?.generation ?? 0;
|
|
484
|
-
if (target.generation !== generation) {
|
|
485
|
-
profile.access("resolutions");
|
|
486
|
-
if (!target.parent && !target.at.length) {
|
|
487
|
-
target.node = context.state.schema;
|
|
488
|
-
target.value = context.state.document;
|
|
489
|
-
} else {
|
|
490
|
-
const parent = target.parent && resolve(context, target.parent);
|
|
491
|
-
const location = parent ? resolveChild(parent.node, parent.value, target.key) : resolveLocated(context.state.schema, context.state.document, target.at);
|
|
492
|
-
target.node = location?.node ?? nodeAt(context.state.schema, addressOf(target), context.state.document);
|
|
493
|
-
target.value = location && Object.hasOwn(location.parent, location.key) ? location.parent[location.key] : void 0;
|
|
494
|
-
}
|
|
495
|
-
target.generation = generation;
|
|
496
|
-
}
|
|
497
|
-
return target;
|
|
498
|
-
};
|
|
499
|
-
const createAccess = (context, initial = []) => {
|
|
500
|
-
profile.access("scopes");
|
|
501
|
-
const mutable = () => {
|
|
502
|
-
assertActive(context);
|
|
503
|
-
if (!context.session) throw new TypeError("Cannot modify read-only document access.");
|
|
504
|
-
return context.session;
|
|
505
|
-
};
|
|
506
|
-
const writableContainer = (target, session) => {
|
|
507
|
-
if (target.container?.generation === session.generation && "layout" in target.container) return target.container;
|
|
508
|
-
return target.container = session.bind(addressOf(target), target.node, target.value);
|
|
509
|
-
};
|
|
510
|
-
const childAt = (target, key) => {
|
|
511
|
-
profile.access("addresses");
|
|
512
|
-
return addressOf(target).concat(key);
|
|
513
|
-
};
|
|
514
|
-
const access = (target) => {
|
|
515
|
-
if (target.proxy) return target.proxy;
|
|
516
|
-
const proxy = new Proxy(target, handler);
|
|
517
|
-
profile.access("proxies");
|
|
518
|
-
locations.set(proxy, {
|
|
519
|
-
context,
|
|
520
|
-
target
|
|
521
|
-
});
|
|
522
|
-
target.proxy = proxy;
|
|
523
|
-
return proxy;
|
|
524
|
-
};
|
|
525
|
-
const createTarget = (at, parent, key) => ({
|
|
526
|
-
at,
|
|
527
|
-
parent,
|
|
528
|
-
key,
|
|
529
|
-
node: void 0,
|
|
530
|
-
value: void 0,
|
|
531
|
-
generation: -1,
|
|
532
|
-
children: void 0,
|
|
533
|
-
proxy: void 0
|
|
534
|
-
});
|
|
535
|
-
const childAccess = (target, member, node, value) => {
|
|
536
|
-
const key = typeof member === "string" ? member : member.key;
|
|
537
|
-
if (node.kind === "field" || value === void 0) {
|
|
538
|
-
if (context.dependencies) collect(context, childAt(target, key));
|
|
539
|
-
return value;
|
|
540
|
-
}
|
|
541
|
-
let children = target.children;
|
|
542
|
-
let child;
|
|
543
|
-
if (typeof member !== "string") {
|
|
544
|
-
if (children?.kind !== "fixed" || children.schema !== target.node) children = {
|
|
545
|
-
kind: "fixed",
|
|
546
|
-
schema: target.node,
|
|
547
|
-
slots: new Array(compiledShape(target.node, target.value).members.size)
|
|
548
|
-
};
|
|
549
|
-
child = children.slots[member.slot] ??= createTarget(void 0, target, key);
|
|
550
|
-
} else {
|
|
551
|
-
if (children?.kind !== "dynamic") children = {
|
|
552
|
-
kind: "dynamic",
|
|
553
|
-
keys: /* @__PURE__ */ new Map()
|
|
554
|
-
};
|
|
555
|
-
const retained = children.keys.get(key);
|
|
556
|
-
child = retained ?? createTarget(void 0, target, key);
|
|
557
|
-
if (!retained) children.keys.set(key, child);
|
|
558
|
-
}
|
|
559
|
-
target.children = children;
|
|
560
|
-
if (child.generation === target.generation) return child.proxy;
|
|
561
|
-
child.node = node;
|
|
562
|
-
child.value = value;
|
|
563
|
-
child.generation = target.generation;
|
|
564
|
-
return access(child);
|
|
565
|
-
};
|
|
566
|
-
const collectionValue = (target, node) => {
|
|
567
|
-
const current = resolve(context, target);
|
|
568
|
-
if (current.node !== node || current.value === void 0) throw new TypeError("The collection method belongs to a replaced schema branch.");
|
|
569
|
-
return current.value;
|
|
570
|
-
};
|
|
571
|
-
const writableCollection = (target, node) => {
|
|
572
|
-
const session = mutable();
|
|
573
|
-
collectionValue(target, node);
|
|
574
|
-
return writableContainer(target, session);
|
|
575
|
-
};
|
|
576
|
-
const writableTree = (target, node) => {
|
|
577
|
-
const session = mutable();
|
|
578
|
-
collectionValue(target, node);
|
|
579
|
-
if (target.container?.generation === session.generation && !("layout" in target.container)) return target.container;
|
|
580
|
-
return target.container = session.bindTree(addressOf(target), target.node, target.value);
|
|
581
|
-
};
|
|
582
|
-
const replaceCollection = (target, node, value) => {
|
|
583
|
-
const session = mutable();
|
|
584
|
-
collectionValue(target, node);
|
|
585
|
-
session.replace(addressOf(target), value);
|
|
586
|
-
};
|
|
587
|
-
const mapMethod = (target, property, node) => {
|
|
588
|
-
if (property === "ids") return () => {
|
|
589
|
-
const current = collectionValue(target, node);
|
|
590
|
-
if (context.dependencies) collect(context, addressOf(target), "collection");
|
|
591
|
-
return Object.keys(current);
|
|
592
|
-
};
|
|
593
|
-
if (property === "has") return (id) => {
|
|
594
|
-
const current = collectionValue(target, node);
|
|
595
|
-
if (context.dependencies) collect(context, addressOf(target), "collection", id);
|
|
596
|
-
return Object.hasOwn(current, id);
|
|
597
|
-
};
|
|
598
|
-
if (property === "get") return (id) => {
|
|
599
|
-
const current = collectionValue(target, node);
|
|
600
|
-
if (context.dependencies) collect(context, addressOf(target), "collection", id);
|
|
601
|
-
if (!Object.hasOwn(current, id)) return void 0;
|
|
602
|
-
if (node.value.kind === "field") {
|
|
603
|
-
if (context.dependencies) collect(context, childAt(target, id));
|
|
604
|
-
return current[id];
|
|
605
|
-
}
|
|
606
|
-
return childAccess(target, id, node.value, current[id]);
|
|
607
|
-
};
|
|
608
|
-
if (property === "put") return (id, value) => {
|
|
609
|
-
const writable = writableCollection(target, node);
|
|
610
|
-
put(context.session, writable, id, value);
|
|
611
|
-
};
|
|
612
|
-
if (property === "remove") return (id) => {
|
|
613
|
-
const writable = writableCollection(target, node);
|
|
614
|
-
remove$3(context.session, writable, id);
|
|
615
|
-
};
|
|
616
|
-
if (property === "replace") return (value) => replaceCollection(target, node, value);
|
|
617
|
-
};
|
|
618
|
-
const orderedMethod = (target, property, node) => {
|
|
619
|
-
const at = addressOf(target);
|
|
620
|
-
if (property === "ids") return () => {
|
|
621
|
-
const current = collectionValue(target, node);
|
|
622
|
-
collect(context, at, "collection");
|
|
623
|
-
return node.kind === "table" ? [...current.ids] : current.map(node.keyOf);
|
|
624
|
-
};
|
|
625
|
-
if (property === "has") return (id) => {
|
|
626
|
-
const current = collectionValue(target, node);
|
|
627
|
-
collect(context, at, "collection", id);
|
|
628
|
-
return node.kind === "table" ? Object.hasOwn(current.byId, id) : indexedKeys(current, node.keyOf).index(id) >= 0;
|
|
629
|
-
};
|
|
630
|
-
if (property === "get") return (id) => {
|
|
631
|
-
const current = collectionValue(target, node);
|
|
632
|
-
collect(context, at, "collection", id);
|
|
633
|
-
const location = resolveChild(node, current, id);
|
|
634
|
-
if (!location || !Object.hasOwn(location.parent, location.key)) return void 0;
|
|
635
|
-
return childAccess(target, id, location.node, location.parent[location.key]);
|
|
636
|
-
};
|
|
637
|
-
if (property === "move") return (id, position) => {
|
|
638
|
-
const writable = writableCollection(target, node);
|
|
639
|
-
move(context.session, writable, id, position);
|
|
640
|
-
};
|
|
641
|
-
};
|
|
642
|
-
const tableMethod = (target, property, node) => {
|
|
643
|
-
if (property === "create") return (input, valueOrPosition, position) => {
|
|
644
|
-
const writable = writableCollection(target, node);
|
|
645
|
-
create(context.session, writable, Array.isArray(input) ? input : [{
|
|
646
|
-
id: input,
|
|
647
|
-
value: valueOrPosition
|
|
648
|
-
}], Array.isArray(input) ? valueOrPosition : position);
|
|
649
|
-
};
|
|
650
|
-
if (property === "remove") return (ids) => {
|
|
651
|
-
const writable = writableCollection(target, node);
|
|
652
|
-
remove$2(context.session, writable, typeof ids === "string" ? [ids] : ids);
|
|
653
|
-
};
|
|
654
|
-
if (property === "replace") return (...args) => {
|
|
655
|
-
const [valueOrId, value] = args;
|
|
656
|
-
if (args.length === 1) return replaceCollection(target, node, valueOrId);
|
|
657
|
-
const writable = writableCollection(target, node);
|
|
658
|
-
replace$3(context.session, writable, valueOrId, value);
|
|
659
|
-
};
|
|
660
|
-
return orderedMethod(target, property, node);
|
|
661
|
-
};
|
|
662
|
-
const listMethod = (target, property, node) => {
|
|
663
|
-
if (property === "insert") return (value, position) => {
|
|
664
|
-
const writable = writableCollection(target, node);
|
|
665
|
-
insert$1(context.session, writable, value, position);
|
|
666
|
-
};
|
|
667
|
-
if (property === "remove") return (id) => {
|
|
668
|
-
const writable = writableCollection(target, node);
|
|
669
|
-
remove$1(context.session, writable, id);
|
|
670
|
-
};
|
|
671
|
-
if (property === "replace") return (...args) => {
|
|
672
|
-
const [valueOrId, value] = args;
|
|
673
|
-
if (args.length === 1) return replaceCollection(target, node, valueOrId);
|
|
674
|
-
const writable = writableCollection(target, node);
|
|
675
|
-
replace$2(context.session, writable, valueOrId, value);
|
|
676
|
-
};
|
|
677
|
-
return orderedMethod(target, property, node);
|
|
678
|
-
};
|
|
679
|
-
const treeMethod = (target, property, node) => {
|
|
680
|
-
const at = addressOf(target);
|
|
681
|
-
if (property === "insert") return (id, value, position) => {
|
|
682
|
-
const writable = writableTree(target, node);
|
|
683
|
-
insert(context.session, writable, id, value, position);
|
|
684
|
-
};
|
|
685
|
-
if (property === "remove") return (id) => {
|
|
686
|
-
const writable = writableTree(target, node);
|
|
687
|
-
remove(context.session, writable, id);
|
|
688
|
-
};
|
|
689
|
-
if (property === "move") return (id, position) => {
|
|
690
|
-
const writable = writableTree(target, node);
|
|
691
|
-
move$1(context.session, writable, id, position);
|
|
692
|
-
};
|
|
693
|
-
if (property === "replace") return (...args) => {
|
|
694
|
-
const [valueOrId, value] = args;
|
|
695
|
-
if (args.length === 1) return replaceCollection(target, node, valueOrId);
|
|
696
|
-
const writable = writableTree(target, node);
|
|
697
|
-
replace$1(context.session, writable, valueOrId, value);
|
|
698
|
-
};
|
|
699
|
-
const current = () => {
|
|
700
|
-
collect(context, at);
|
|
701
|
-
return collectionValue(target, node);
|
|
702
|
-
};
|
|
703
|
-
if (property === "rootId") return () => current().rootId;
|
|
704
|
-
if (property === "get") return (id) => {
|
|
705
|
-
const value = current();
|
|
706
|
-
return contains(value, id) ? value.nodes[id].value : void 0;
|
|
707
|
-
};
|
|
708
|
-
if (property === "has") return (id) => contains(current(), id);
|
|
709
|
-
if (property === "parent") return (id) => parent(current(), id);
|
|
710
|
-
if (property === "children") return (id) => {
|
|
711
|
-
const children$1 = children(current(), id);
|
|
712
|
-
return children$1 ? [...children$1] : void 0;
|
|
713
|
-
};
|
|
714
|
-
};
|
|
715
|
-
const handler = {
|
|
716
|
-
get: (target, property) => {
|
|
717
|
-
const { node, value } = resolve(context, target);
|
|
718
|
-
if (!node || value === void 0) return void 0;
|
|
719
|
-
if (typeof property !== "string") return void 0;
|
|
720
|
-
if (node.kind === "map") return mapMethod(target, property, node);
|
|
721
|
-
if (node.kind === "table") return tableMethod(target, property, node);
|
|
722
|
-
if (node.kind === "list") return listMethod(target, property, node);
|
|
723
|
-
if (node.kind === "tree") return treeMethod(target, property, node);
|
|
724
|
-
if (node.kind === "variant" && property === node.tag) {
|
|
725
|
-
collect(context, childAt(target, property));
|
|
726
|
-
return value[property];
|
|
727
|
-
}
|
|
728
|
-
const member = compiledShape(node, value)?.members.get(property);
|
|
729
|
-
if (!member) return void 0;
|
|
730
|
-
if (member.field) {
|
|
731
|
-
if (context.dependencies) collect(context, childAt(target, property));
|
|
732
|
-
return value[property];
|
|
733
|
-
}
|
|
734
|
-
return childAccess(target, node.kind === "object" ? member : property, member.node, value[property]);
|
|
735
|
-
},
|
|
736
|
-
set: (target, property, value) => {
|
|
737
|
-
const session = mutable(), { node } = resolve(context, target);
|
|
738
|
-
if (typeof property !== "string" || !node || node.kind !== "object" && node.kind !== "variant") throw new TypeError("Invalid structural assignment.");
|
|
739
|
-
if (node.kind === "variant" && node.tag === property) throw new TypeError("Variant discriminants are read-only.");
|
|
740
|
-
session.assignMember(writableContainer(target, session), property, value);
|
|
741
|
-
return true;
|
|
742
|
-
},
|
|
743
|
-
deleteProperty: (target, property) => {
|
|
744
|
-
const session = mutable(), { node } = resolve(context, target);
|
|
745
|
-
if (typeof property !== "string" || node?.kind === "map" || node?.kind === "variant" && node.tag === property) throw new TypeError("Invalid structural deletion.");
|
|
746
|
-
session.removeMember(writableContainer(target, session), property);
|
|
747
|
-
return true;
|
|
748
|
-
},
|
|
749
|
-
has: (target, property) => {
|
|
750
|
-
const { node, value } = resolve(context, target);
|
|
751
|
-
if (typeof property !== "string") return false;
|
|
752
|
-
if (node?.kind === "map") return false;
|
|
753
|
-
collect(context, addressOf(target));
|
|
754
|
-
return value !== void 0 && Object.hasOwn(value, property);
|
|
755
|
-
},
|
|
756
|
-
ownKeys: (target) => {
|
|
757
|
-
const { node, value } = resolve(context, target);
|
|
758
|
-
if (node?.kind === "map") return [];
|
|
759
|
-
if (context.dependencies) collect(context, addressOf(target));
|
|
760
|
-
return value && typeof value === "object" ? Object.keys(value) : [];
|
|
761
|
-
},
|
|
762
|
-
getOwnPropertyDescriptor: (target, property) => {
|
|
763
|
-
const { node, value } = resolve(context, target);
|
|
764
|
-
if (node?.kind === "map") return void 0;
|
|
765
|
-
return value && Object.hasOwn(value, property) ? {
|
|
766
|
-
enumerable: true,
|
|
767
|
-
configurable: true
|
|
768
|
-
} : void 0;
|
|
769
|
-
},
|
|
770
|
-
defineProperty: () => {
|
|
771
|
-
assertActive(context);
|
|
772
|
-
throw new TypeError("Use schema assignments.");
|
|
773
|
-
},
|
|
774
|
-
setPrototypeOf: () => {
|
|
775
|
-
assertActive(context);
|
|
776
|
-
throw new TypeError("Document prototypes are immutable.");
|
|
777
|
-
},
|
|
778
|
-
preventExtensions: () => {
|
|
779
|
-
assertActive(context);
|
|
780
|
-
throw new TypeError("Document access cannot be frozen.");
|
|
781
|
-
}
|
|
782
|
-
};
|
|
783
|
-
const root = resolve(context, createTarget(initial, void 0, ""));
|
|
784
|
-
if (!root.node || root.node.kind === "field" || root.value === void 0) {
|
|
785
|
-
if (context.dependencies) collect(context, initial);
|
|
786
|
-
return root.value;
|
|
787
|
-
}
|
|
788
|
-
return access(root);
|
|
789
|
-
};
|
|
790
|
-
const collectionAccess = (context, at) => {
|
|
791
|
-
const value = createAccess(context, at);
|
|
792
|
-
if (value === void 0) return {
|
|
793
|
-
get: () => {
|
|
794
|
-
assertActive(context);
|
|
795
|
-
},
|
|
796
|
-
has: () => {
|
|
797
|
-
assertActive(context);
|
|
798
|
-
return false;
|
|
799
|
-
},
|
|
800
|
-
ids: () => {
|
|
801
|
-
assertActive(context);
|
|
802
|
-
return [];
|
|
803
|
-
}
|
|
804
|
-
};
|
|
805
|
-
return value;
|
|
806
|
-
};
|
|
807
|
-
//#endregion
|
|
808
91
|
//#region core/src/impact-target.ts
|
|
809
92
|
const address = (target) => "at" in target ? target.at : target.address;
|
|
810
93
|
const id = (target) => target.kind === "collection" && "id" in target ? target.id : void 0;
|
|
@@ -904,13 +187,13 @@ const createImpact = (schema, changes) => {
|
|
|
904
187
|
for (const change of changes.changes) {
|
|
905
188
|
if (change.kind === "reset") continue;
|
|
906
189
|
if (change.kind === "members") {
|
|
907
|
-
if (change.at.length < at.length && contains
|
|
190
|
+
if (change.at.length < at.length && contains(change.at, at)) {
|
|
908
191
|
if (change.members.some((member) => member.key === at[change.at.length])) {
|
|
909
192
|
const result = { kind: "reset" };
|
|
910
193
|
cache.set(key, result);
|
|
911
194
|
return result;
|
|
912
195
|
}
|
|
913
|
-
} else if (contains
|
|
196
|
+
} else if (contains(at, change.at)) if (change.at.length === at.length) {
|
|
914
197
|
if (change.order) {
|
|
915
198
|
const positions = new Map(change.order.after.map((id, i) => [id, i]));
|
|
916
199
|
let previous = -1;
|
|
@@ -927,12 +210,12 @@ const createImpact = (schema, changes) => {
|
|
|
927
210
|
} else updated.add(change.at[at.length]);
|
|
928
211
|
continue;
|
|
929
212
|
}
|
|
930
|
-
if (change.kind === "tree" && contains
|
|
213
|
+
if (change.kind === "tree" && contains(change.at, at)) {
|
|
931
214
|
const result = { kind: "reset" };
|
|
932
215
|
cache.set(key, result);
|
|
933
216
|
return result;
|
|
934
217
|
}
|
|
935
|
-
if (!contains
|
|
218
|
+
if (!contains(at, change.at)) continue;
|
|
936
219
|
if (change.at.length === at.length) continue;
|
|
937
220
|
const id = change.at[at.length];
|
|
938
221
|
updated.add(id);
|
|
@@ -1183,84 +466,6 @@ const disposeNotification = (notification) => {
|
|
|
1183
466
|
if (errors.length) throw new AggregateError(errors, "Projection disposal notification failed.");
|
|
1184
467
|
};
|
|
1185
468
|
//#endregion
|
|
1186
|
-
//#region core/src/projection/definition.ts
|
|
1187
|
-
const definitions = /* @__PURE__ */ new WeakMap();
|
|
1188
|
-
const freezeSources = (sources) => Object.freeze({ ...sources });
|
|
1189
|
-
const define = (definition) => {
|
|
1190
|
-
const handle = Object.freeze({});
|
|
1191
|
-
definitions.set(handle, definition);
|
|
1192
|
-
return handle;
|
|
1193
|
-
};
|
|
1194
|
-
const definitionOf = (projection) => {
|
|
1195
|
-
const definition = definitions.get(projection);
|
|
1196
|
-
if (!definition) throw new TypeError("Unknown projection definition.");
|
|
1197
|
-
return definition;
|
|
1198
|
-
};
|
|
1199
|
-
const input = (initial, options) => define({
|
|
1200
|
-
kind: "input",
|
|
1201
|
-
initial,
|
|
1202
|
-
isEqual: options?.isEqual
|
|
1203
|
-
});
|
|
1204
|
-
function project(first, second, third, fourth) {
|
|
1205
|
-
if (first && typeof first === "object" && "kind" in first && (first.kind === "value" || first.kind === "collection")) {
|
|
1206
|
-
const spec = first;
|
|
1207
|
-
return define({
|
|
1208
|
-
kind: spec.kind === "value" ? "advanced-value" : "advanced-collection",
|
|
1209
|
-
sources: freezeSources(spec.sources),
|
|
1210
|
-
build: spec.build,
|
|
1211
|
-
isEqual: spec.isEqual,
|
|
1212
|
-
name: spec.name
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1215
|
-
if (first && typeof first === "object" && "revision" in first && "subscribe" in first) {
|
|
1216
|
-
if ("address" in first) {
|
|
1217
|
-
const document = first;
|
|
1218
|
-
if (!second) return define({
|
|
1219
|
-
kind: "document",
|
|
1220
|
-
document
|
|
1221
|
-
});
|
|
1222
|
-
if (Array.isArray(second)) return define({
|
|
1223
|
-
kind: "document",
|
|
1224
|
-
document,
|
|
1225
|
-
targets: Object.freeze([...second])
|
|
1226
|
-
});
|
|
1227
|
-
if (typeof second !== "function") throw new TypeError("Invalid document projection.");
|
|
1228
|
-
const source = define({
|
|
1229
|
-
kind: "document-collection",
|
|
1230
|
-
document,
|
|
1231
|
-
pick: second
|
|
1232
|
-
});
|
|
1233
|
-
if (!third || typeof third !== "function") return source;
|
|
1234
|
-
return define({
|
|
1235
|
-
kind: "map",
|
|
1236
|
-
source,
|
|
1237
|
-
mapper: third,
|
|
1238
|
-
isEqual: fourth?.isEqual
|
|
1239
|
-
});
|
|
1240
|
-
}
|
|
1241
|
-
return define({
|
|
1242
|
-
kind: "readable",
|
|
1243
|
-
readable: first
|
|
1244
|
-
});
|
|
1245
|
-
}
|
|
1246
|
-
if (second) {
|
|
1247
|
-
if (typeof second !== "function") throw new TypeError("Invalid project declaration.");
|
|
1248
|
-
if (definitions.has(first)) return define({
|
|
1249
|
-
kind: "map",
|
|
1250
|
-
source: first,
|
|
1251
|
-
mapper: second,
|
|
1252
|
-
isEqual: third?.isEqual
|
|
1253
|
-
});
|
|
1254
|
-
return define({
|
|
1255
|
-
kind: "value",
|
|
1256
|
-
sources: freezeSources(first),
|
|
1257
|
-
compute: second,
|
|
1258
|
-
isEqual: third?.isEqual
|
|
1259
|
-
});
|
|
1260
|
-
}
|
|
1261
|
-
throw new TypeError("Invalid project declaration.");
|
|
1262
|
-
}
|
|
1263
|
-
//#endregion
|
|
1264
469
|
//#region core/src/projection/contract.ts
|
|
1265
470
|
var ProjectionError = class extends Error {
|
|
1266
471
|
constructor(phase, identity, revisions, cause) {
|
|
@@ -1283,6 +488,8 @@ const projectionHandles = /* @__PURE__ */ new WeakSet();
|
|
|
1283
488
|
const createScheduler = (onError) => {
|
|
1284
489
|
let disposed = false;
|
|
1285
490
|
let depth = 0;
|
|
491
|
+
let batchSequence = 0;
|
|
492
|
+
let activeBatch;
|
|
1286
493
|
let phase = "idle";
|
|
1287
494
|
let sequence = 0;
|
|
1288
495
|
const records = /* @__PURE__ */ new Map();
|
|
@@ -1444,6 +651,36 @@ const createScheduler = (onError) => {
|
|
|
1444
651
|
if (failures.length) throw new AggregateError(failures, "Projection error reporter failed.");
|
|
1445
652
|
return result;
|
|
1446
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
|
+
};
|
|
1447
684
|
return {
|
|
1448
685
|
assertActive,
|
|
1449
686
|
assertIdle,
|
|
@@ -1510,25 +747,8 @@ const createScheduler = (onError) => {
|
|
|
1510
747
|
node.release();
|
|
1511
748
|
for (const [handle, record] of records) if (record === node) records.delete(handle);
|
|
1512
749
|
},
|
|
1513
|
-
batch
|
|
1514
|
-
|
|
1515
|
-
depth++;
|
|
1516
|
-
let failed = false;
|
|
1517
|
-
try {
|
|
1518
|
-
const value = callback();
|
|
1519
|
-
assertSynchronous(value);
|
|
1520
|
-
return value;
|
|
1521
|
-
} catch (error) {
|
|
1522
|
-
failed = true;
|
|
1523
|
-
throw error;
|
|
1524
|
-
} finally {
|
|
1525
|
-
depth--;
|
|
1526
|
-
if (!depth) if (failed) try {
|
|
1527
|
-
run();
|
|
1528
|
-
} catch {}
|
|
1529
|
-
else run();
|
|
1530
|
-
}
|
|
1531
|
-
},
|
|
750
|
+
batch,
|
|
751
|
+
batchContext: () => activeBatch,
|
|
1532
752
|
dispose() {
|
|
1533
753
|
if (disposed) return;
|
|
1534
754
|
assertIdle();
|
|
@@ -1660,6 +880,9 @@ const createCollection = (scheduler, spec) => {
|
|
|
1660
880
|
let reset = false;
|
|
1661
881
|
let revision = 0;
|
|
1662
882
|
let idsRevision = 0;
|
|
883
|
+
let batch;
|
|
884
|
+
let cause;
|
|
885
|
+
let publishedTransitions = Object.freeze([]);
|
|
1663
886
|
let all;
|
|
1664
887
|
const items = /* @__PURE__ */ new Map();
|
|
1665
888
|
const subscribedItems = /* @__PURE__ */ new Set();
|
|
@@ -1689,6 +912,9 @@ const createCollection = (scheduler, spec) => {
|
|
|
1689
912
|
};
|
|
1690
913
|
const owner = createNode(scheduler, spec, {
|
|
1691
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;
|
|
1692
918
|
staged = /* @__PURE__ */ new Map();
|
|
1693
919
|
nextIds = ids;
|
|
1694
920
|
change = void 0;
|
|
@@ -1819,10 +1045,29 @@ const createCollection = (scheduler, spec) => {
|
|
|
1819
1045
|
].forEach((key) => changedKeys.add(key));
|
|
1820
1046
|
profile.projection("changedKeys", changedKeys.size);
|
|
1821
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
|
+
}));
|
|
1822
1062
|
reset = build;
|
|
1823
1063
|
return change !== void 0;
|
|
1824
1064
|
},
|
|
1825
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
|
+
};
|
|
1826
1071
|
const input = {
|
|
1827
1072
|
get: (key) => {
|
|
1828
1073
|
assertScope(active);
|
|
@@ -1836,9 +1081,26 @@ const createCollection = (scheduler, spec) => {
|
|
|
1836
1081
|
assertScope(active);
|
|
1837
1082
|
return nextIds;
|
|
1838
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
|
+
}),
|
|
1839
1098
|
change,
|
|
1840
1099
|
revision: revision + (change ? 1 : 0),
|
|
1841
|
-
reset
|
|
1100
|
+
reset,
|
|
1101
|
+
transitions,
|
|
1102
|
+
batch,
|
|
1103
|
+
cause
|
|
1842
1104
|
};
|
|
1843
1105
|
return Object.freeze(input);
|
|
1844
1106
|
},
|
|
@@ -1875,6 +1137,9 @@ const createCollection = (scheduler, spec) => {
|
|
|
1875
1137
|
change = void 0;
|
|
1876
1138
|
reset = false;
|
|
1877
1139
|
changedKeys.clear();
|
|
1140
|
+
publishedTransitions = Object.freeze([]);
|
|
1141
|
+
batch = void 0;
|
|
1142
|
+
cause = void 0;
|
|
1878
1143
|
orderChanged = false;
|
|
1879
1144
|
},
|
|
1880
1145
|
release: () => {
|
|
@@ -1991,6 +1256,7 @@ const createSources = (scheduler) => {
|
|
|
1991
1256
|
const documents = /* @__PURE__ */ new Map();
|
|
1992
1257
|
const readables = /* @__PURE__ */ new Map();
|
|
1993
1258
|
const collections = /* @__PURE__ */ new WeakSet();
|
|
1259
|
+
const externalSources = /* @__PURE__ */ new WeakMap();
|
|
1994
1260
|
const document = (runtime) => {
|
|
1995
1261
|
scheduler.assertIdle();
|
|
1996
1262
|
const state = accessOf(runtime);
|
|
@@ -1998,6 +1264,7 @@ const createSources = (scheduler) => {
|
|
|
1998
1264
|
const existing = documents.get(state);
|
|
1999
1265
|
if (existing) return existing;
|
|
2000
1266
|
const bindings = [];
|
|
1267
|
+
let batch;
|
|
2001
1268
|
const make = (input) => {
|
|
2002
1269
|
const collection = "collection" in input ? input.collection : void 0;
|
|
2003
1270
|
const targets = "targets" in input ? input.targets : [input.collection];
|
|
@@ -2021,6 +1288,7 @@ const createSources = (scheduler) => {
|
|
|
2021
1288
|
clear: () => {
|
|
2022
1289
|
commits = [];
|
|
2023
1290
|
reset = orderDirty = false;
|
|
1291
|
+
batch = void 0;
|
|
2024
1292
|
keys.clear();
|
|
2025
1293
|
candidates = void 0;
|
|
2026
1294
|
},
|
|
@@ -2037,6 +1305,8 @@ const createSources = (scheduler) => {
|
|
|
2037
1305
|
revision: runtime.revision(),
|
|
2038
1306
|
commits: Object.freeze(commits.slice()),
|
|
2039
1307
|
reset: record.reset(),
|
|
1308
|
+
batch,
|
|
1309
|
+
cause: batch?.cause,
|
|
2040
1310
|
...collection ? { candidates: candidates ??= Object.freeze({
|
|
2041
1311
|
keys: Object.freeze([...keys]),
|
|
2042
1312
|
orderDirty
|
|
@@ -2090,6 +1360,7 @@ const createSources = (scheduler) => {
|
|
|
2090
1360
|
capture: (commit) => {
|
|
2091
1361
|
for (const binding of bindings) if (!binding.targets.length || binding.targets.some((value) => affectsTarget(commit.impact, value))) {
|
|
2092
1362
|
binding.receive(commit);
|
|
1363
|
+
batch = scheduler.batchContext();
|
|
2093
1364
|
scheduler.capture(binding.record);
|
|
2094
1365
|
}
|
|
2095
1366
|
},
|
|
@@ -2114,15 +1385,25 @@ const createSources = (scheduler) => {
|
|
|
2114
1385
|
let value = initial;
|
|
2115
1386
|
let previous = initial;
|
|
2116
1387
|
let revision = 0;
|
|
1388
|
+
let detail;
|
|
1389
|
+
let cause;
|
|
1390
|
+
let batch;
|
|
1391
|
+
let reset = false;
|
|
1392
|
+
let pending = false;
|
|
2117
1393
|
const handle = Object.freeze({});
|
|
2118
1394
|
const record = {
|
|
2119
1395
|
consumers: /* @__PURE__ */ new Set(),
|
|
2120
1396
|
disposed: false,
|
|
2121
1397
|
fault: void 0,
|
|
2122
1398
|
revision: () => revision,
|
|
2123
|
-
reset: () =>
|
|
1399
|
+
reset: () => reset,
|
|
2124
1400
|
clear: () => {
|
|
2125
1401
|
previous = value;
|
|
1402
|
+
pending = false;
|
|
1403
|
+
detail = void 0;
|
|
1404
|
+
cause = void 0;
|
|
1405
|
+
batch = void 0;
|
|
1406
|
+
reset = false;
|
|
2126
1407
|
},
|
|
2127
1408
|
context: (active) => {
|
|
2128
1409
|
assertScope(active);
|
|
@@ -2131,19 +1412,29 @@ const createSources = (scheduler) => {
|
|
|
2131
1412
|
previous,
|
|
2132
1413
|
revision,
|
|
2133
1414
|
changed: !equal(previous, value),
|
|
2134
|
-
reset
|
|
1415
|
+
reset,
|
|
1416
|
+
detail,
|
|
1417
|
+
cause,
|
|
1418
|
+
batch
|
|
2135
1419
|
});
|
|
2136
1420
|
}
|
|
2137
1421
|
};
|
|
2138
1422
|
scheduler.register(handle, record);
|
|
2139
|
-
const accept = (next) => {
|
|
1423
|
+
const accept = (next, metadata) => {
|
|
2140
1424
|
scheduler.assertIdle();
|
|
2141
1425
|
const recovering = record.fault !== void 0;
|
|
2142
|
-
|
|
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;
|
|
2143
1428
|
record.fault = void 0;
|
|
1429
|
+
if (!pending) previous = value;
|
|
2144
1430
|
value = next;
|
|
2145
|
-
revision
|
|
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;
|
|
2146
1436
|
scheduler.capture(record);
|
|
1437
|
+
pending = true;
|
|
2147
1438
|
};
|
|
2148
1439
|
return {
|
|
2149
1440
|
source: handle,
|
|
@@ -2154,12 +1445,217 @@ const createSources = (scheduler) => {
|
|
|
2154
1445
|
}
|
|
2155
1446
|
};
|
|
2156
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
|
+
};
|
|
2157
1651
|
return {
|
|
2158
1652
|
dispose: () => {
|
|
2159
1653
|
documents.clear();
|
|
2160
1654
|
readables.clear();
|
|
2161
1655
|
},
|
|
2162
1656
|
document,
|
|
1657
|
+
fromSource: (source, options) => externalValueSource(source, options?.isEqual ?? Object.is),
|
|
1658
|
+
fromCollectionSource: (source) => externalCollectionSource(source),
|
|
2163
1659
|
input: (initial, options) => {
|
|
2164
1660
|
const { source, set } = valueSource(initial, options?.isEqual ?? Object.is);
|
|
2165
1661
|
return Object.freeze({
|
|
@@ -2264,9 +1760,14 @@ const createValue = (scheduler, spec, options) => {
|
|
|
2264
1760
|
let revision = 0;
|
|
2265
1761
|
let changed = false;
|
|
2266
1762
|
let reset = false;
|
|
1763
|
+
let batch;
|
|
1764
|
+
let cause;
|
|
2267
1765
|
const listeners = /* @__PURE__ */ new Set();
|
|
2268
1766
|
const owner = createNode(scheduler, spec, {
|
|
2269
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;
|
|
2270
1771
|
let candidate;
|
|
2271
1772
|
if (build || !instance) {
|
|
2272
1773
|
profile.materialized.rebuilt();
|
|
@@ -2301,7 +1802,9 @@ const createValue = (scheduler, spec, options) => {
|
|
|
2301
1802
|
previous,
|
|
2302
1803
|
changed,
|
|
2303
1804
|
revision: revision + (changed ? 1 : 0),
|
|
2304
|
-
reset
|
|
1805
|
+
reset,
|
|
1806
|
+
cause,
|
|
1807
|
+
batch
|
|
2305
1808
|
});
|
|
2306
1809
|
},
|
|
2307
1810
|
revision: () => revision,
|
|
@@ -2320,6 +1823,8 @@ const createValue = (scheduler, spec, options) => {
|
|
|
2320
1823
|
previous = value;
|
|
2321
1824
|
changed = false;
|
|
2322
1825
|
reset = false;
|
|
1826
|
+
batch = void 0;
|
|
1827
|
+
cause = void 0;
|
|
2323
1828
|
},
|
|
2324
1829
|
release: () => {
|
|
2325
1830
|
listeners.clear();
|
|
@@ -2351,13 +1856,7 @@ const createValue = (scheduler, spec, options) => {
|
|
|
2351
1856
|
};
|
|
2352
1857
|
//#endregion
|
|
2353
1858
|
//#region core/src/projection/runtime.ts
|
|
2354
|
-
const
|
|
2355
|
-
const projectionEngineDebug = (runtime) => {
|
|
2356
|
-
const read = debug.get(runtime);
|
|
2357
|
-
if (!read) throw new Error("Unknown projection engine.");
|
|
2358
|
-
return Object.freeze(read());
|
|
2359
|
-
};
|
|
2360
|
-
const createProjectionEngine = (options) => {
|
|
1859
|
+
const createRuntimeExecutor = (options) => {
|
|
2361
1860
|
const scheduler = createScheduler(options.onError);
|
|
2362
1861
|
const sources = createSources(scheduler);
|
|
2363
1862
|
function value(input, computeOrOptions, options) {
|
|
@@ -2434,6 +1933,8 @@ const createProjectionEngine = (options) => {
|
|
|
2434
1933
|
};
|
|
2435
1934
|
const runtime = {
|
|
2436
1935
|
document: sources.document,
|
|
1936
|
+
fromSource: (source, options) => sources.fromSource(source, options),
|
|
1937
|
+
fromCollectionSource: (source) => sources.fromCollectionSource(source),
|
|
2437
1938
|
input: sources.input,
|
|
2438
1939
|
fromReadable: sources.fromReadable,
|
|
2439
1940
|
value,
|
|
@@ -2448,107 +1949,274 @@ const createProjectionEngine = (options) => {
|
|
|
2448
1949
|
}
|
|
2449
1950
|
}
|
|
2450
1951
|
};
|
|
2451
|
-
debug.set(runtime, scheduler.debug);
|
|
2452
1952
|
return Object.freeze(runtime);
|
|
2453
1953
|
};
|
|
2454
1954
|
//#endregion
|
|
2455
1955
|
//#region core/src/projection/store.ts
|
|
2456
|
-
const
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
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;
|
|
2461
1965
|
};
|
|
2462
|
-
const
|
|
2463
|
-
const
|
|
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) });
|
|
2464
2041
|
const instances = /* @__PURE__ */ new WeakMap();
|
|
2465
2042
|
const inputs = /* @__PURE__ */ new WeakMap();
|
|
2043
|
+
const collections = /* @__PURE__ */ new WeakMap();
|
|
2466
2044
|
const materialize = (projection) => {
|
|
2467
2045
|
const old = instances.get(projection);
|
|
2468
2046
|
if (old) return old;
|
|
2469
2047
|
const definition = definitionOf(projection);
|
|
2470
2048
|
let instance;
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
}
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
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
|
+
}
|
|
2500
2115
|
}
|
|
2501
2116
|
instances.set(projection, instance);
|
|
2502
2117
|
return instance;
|
|
2503
2118
|
};
|
|
2504
|
-
const
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
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;
|
|
2508
2132
|
}
|
|
2509
|
-
|
|
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;
|
|
2510
2145
|
};
|
|
2511
2146
|
const store = {
|
|
2512
|
-
get
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
throw new TypeError("Projection is a source and has no published value.");
|
|
2147
|
+
get,
|
|
2148
|
+
subscribe: ((projection, listener) => {
|
|
2149
|
+
return materialize(projection).subscribe(listener);
|
|
2516
2150
|
}),
|
|
2517
|
-
set: (
|
|
2518
|
-
materialize(
|
|
2519
|
-
const target = inputs.get(
|
|
2151
|
+
set: ((input, value) => {
|
|
2152
|
+
materialize(input);
|
|
2153
|
+
const target = inputs.get(input);
|
|
2520
2154
|
if (!target) throw new TypeError("Projection is not an input.");
|
|
2521
2155
|
target.set(value);
|
|
2522
|
-
},
|
|
2523
|
-
subscribe: ((projection, listener) => {
|
|
2524
|
-
const instance = materialize(projection);
|
|
2525
|
-
if (!("subscribe" in instance)) throw new TypeError("Projection is a source and cannot be subscribed.");
|
|
2526
|
-
return instance.subscribe(listener);
|
|
2527
2156
|
}),
|
|
2528
|
-
revision: (projection) => {
|
|
2529
|
-
const instance = materialize(projection);
|
|
2530
|
-
if (!("revision" in instance) || typeof instance.revision !== "function") throw new TypeError("Projection has no published revision.");
|
|
2531
|
-
return instance.revision();
|
|
2532
|
-
},
|
|
2533
|
-
rebuild: (projection) => {
|
|
2534
|
-
const instance = materialize(projection);
|
|
2535
|
-
if (!("rebuild" in instance) || typeof instance.rebuild !== "function") throw new TypeError("Projection cannot be rebuilt.");
|
|
2536
|
-
instance.rebuild();
|
|
2537
|
-
},
|
|
2538
|
-
release: (projection) => {
|
|
2539
|
-
const instance = instances.get(projection);
|
|
2540
|
-
if (!instance) return;
|
|
2541
|
-
if ("dispose" in instance && typeof instance.dispose === "function") instance.dispose();
|
|
2542
|
-
instances.delete(projection);
|
|
2543
|
-
inputs.delete(projection);
|
|
2544
|
-
},
|
|
2545
2157
|
batch: runtime.batch,
|
|
2546
2158
|
dispose: runtime.dispose
|
|
2547
2159
|
};
|
|
2548
|
-
|
|
2160
|
+
runtimeStates.set(store, {
|
|
2161
|
+
materialize,
|
|
2162
|
+
get
|
|
2163
|
+
});
|
|
2549
2164
|
return Object.freeze(store);
|
|
2550
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
|
+
};
|
|
2551
2219
|
//#endregion
|
|
2552
|
-
export {
|
|
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 };
|
|
2553
2221
|
|
|
2554
|
-
//# sourceMappingURL=store-
|
|
2222
|
+
//# sourceMappingURL=store-CtgvCVFg.js.map
|