doxum 0.1.23 → 0.1.26
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 +208 -102
- package/dist/advanced.cjs +237 -2
- package/dist/advanced.cjs.map +1 -0
- package/dist/advanced.d.cts +90 -2
- package/dist/advanced.d.ts +90 -2
- package/dist/advanced.js +236 -1
- package/dist/advanced.js.map +1 -0
- package/dist/{contract-ULPQmiEs.d.ts → contract-CNR6BS75.d.cts} +216 -30
- package/dist/{contract-SYJFP0lM.d.cts → contract-COgxO5sv.d.ts} +216 -30
- package/dist/definition-CDOuvb5C.d.cts +111 -0
- package/dist/definition-xgk3YNBp.d.ts +111 -0
- package/dist/{driver-yFfvDMX5.js → driver-B5vdPohL.js} +59 -3
- package/dist/driver-B5vdPohL.js.map +1 -0
- package/dist/{driver-BD3DQOW6.cjs → driver-BL91gn7_.cjs} +107 -9
- package/dist/driver-BL91gn7_.cjs.map +1 -0
- package/dist/index.cjs +1637 -703
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -33
- package/dist/index.d.ts +65 -33
- package/dist/index.js +1432 -497
- package/dist/index.js.map +1 -1
- package/dist/local-sync.cjs +69 -103
- package/dist/local-sync.cjs.map +1 -1
- package/dist/local-sync.d.cts +14 -26
- package/dist/local-sync.d.ts +14 -26
- package/dist/local-sync.js +68 -96
- package/dist/local-sync.js.map +1 -1
- package/dist/react.cjs +4 -5
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +6 -8
- package/dist/react.d.ts +6 -8
- package/dist/react.js +4 -5
- package/dist/react.js.map +1 -1
- package/dist/record-B2yRhYsc.js +28 -0
- package/dist/record-B2yRhYsc.js.map +1 -0
- package/dist/record-ChYUi8PP.cjs +45 -0
- package/dist/record-ChYUi8PP.cjs.map +1 -0
- package/dist/scheduler--137L3j-.js +500 -0
- package/dist/scheduler--137L3j-.js.map +1 -0
- package/dist/scheduler-D4VthaaK.cjs +589 -0
- package/dist/scheduler-D4VthaaK.cjs.map +1 -0
- package/package.json +6 -3
- package/skills/doxum-runtime/SKILL.md +32 -7
- package/skills/doxum-runtime/agents/openai.yaml +1 -1
- package/skills/doxum-runtime/references/api.en.md +249 -0
- package/skills/doxum-runtime/references/api.zh-CN.md +242 -0
- package/skills/doxum-runtime/references/guide.en.md +18 -8
- package/skills/doxum-runtime/references/guide.zh-CN.md +14 -6
- package/skills/doxum-runtime/references/invariants.en.md +29 -5
- package/skills/doxum-runtime/references/invariants.zh-CN.md +24 -6
- package/skills/doxum-runtime/references/patterns.en.md +78 -18
- package/skills/doxum-runtime/references/patterns.zh-CN.md +76 -18
- package/skills/doxum-runtime/references/projections.en.md +209 -119
- package/skills/doxum-runtime/references/projections.zh-CN.md +200 -101
- package/dist/advanced-CLJCWkHa.d.cts +0 -176
- package/dist/advanced-CRNHfJBu.js +0 -1224
- package/dist/advanced-CRNHfJBu.js.map +0 -1
- package/dist/advanced-Cle5g3B4.cjs +0 -1517
- package/dist/advanced-Cle5g3B4.cjs.map +0 -1
- package/dist/advanced-d6J61er9.d.ts +0 -176
- package/dist/context-BcLKGfq8.cjs +0 -149
- package/dist/context-BcLKGfq8.cjs.map +0 -1
- package/dist/context-DFEaDUv5.js +0 -84
- package/dist/context-DFEaDUv5.js.map +0 -1
- package/dist/driver-BD3DQOW6.cjs.map +0 -1
- package/dist/driver-yFfvDMX5.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,16 +1,432 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const require_driver = require("./driver-
|
|
5
|
-
//#region core/src/
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
const require_scheduler = require("./scheduler-D4VthaaK.cjs");
|
|
3
|
+
const require_record = require("./record-ChYUi8PP.cjs");
|
|
4
|
+
const require_driver = require("./driver-BL91gn7_.cjs");
|
|
5
|
+
//#region core/src/schema/model.ts
|
|
6
|
+
const schemaNodeOf = (schema) => schema;
|
|
7
|
+
const collectionEntryNode = (node) => node.kind === "map" || node.kind === "table" || node.kind === "list" ? node.value : void 0;
|
|
8
|
+
const node$2 = (value) => Object.freeze(value);
|
|
9
|
+
const field = (validator) => node$2({
|
|
10
|
+
kind: "field",
|
|
11
|
+
...validator ? { validator } : {}
|
|
12
|
+
});
|
|
13
|
+
function optional(value) {
|
|
14
|
+
const concrete = schemaNodeOf(value);
|
|
15
|
+
if (![
|
|
16
|
+
"field",
|
|
17
|
+
"variant",
|
|
18
|
+
"map",
|
|
19
|
+
"list",
|
|
20
|
+
"tree"
|
|
21
|
+
].includes(concrete.kind)) throw new TypeError("Only field, variant, map, list and tree nodes support optional presence.");
|
|
22
|
+
return node$2({
|
|
23
|
+
...concrete,
|
|
24
|
+
optional: true
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const object = (shape) => node$2({
|
|
28
|
+
kind: "object",
|
|
29
|
+
shape: Object.freeze(Object.fromEntries(Object.entries(shape).map(([key, value]) => [key, schemaNodeOf(value)])))
|
|
30
|
+
});
|
|
31
|
+
const variant = (tag, variants) => node$2({
|
|
32
|
+
kind: "variant",
|
|
33
|
+
tag,
|
|
34
|
+
variants: Object.freeze(Object.fromEntries(Object.entries(variants).map(([key, value]) => [key, schemaNodeOf(value)])))
|
|
35
|
+
});
|
|
36
|
+
const table = (value, options) => node$2({
|
|
37
|
+
kind: "table",
|
|
38
|
+
value: schemaNodeOf(value),
|
|
39
|
+
...options
|
|
40
|
+
});
|
|
41
|
+
const map = (value, options) => node$2({
|
|
42
|
+
kind: "map",
|
|
43
|
+
value: schemaNodeOf(value),
|
|
44
|
+
...options
|
|
45
|
+
});
|
|
46
|
+
const list = (value, config) => node$2({
|
|
47
|
+
kind: "list",
|
|
48
|
+
keyOf: config.keyOf,
|
|
49
|
+
value: schemaNodeOf(value)
|
|
50
|
+
});
|
|
51
|
+
function tree(value) {
|
|
52
|
+
return Object.freeze({
|
|
53
|
+
kind: "tree",
|
|
54
|
+
value: schemaNodeOf(value)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region core/src/order/sequence.ts
|
|
59
|
+
const length$1 = (keys) => keys.length;
|
|
60
|
+
const keyAt = (keys, index) => Array.isArray(keys) ? keys[index] : keys.at(index);
|
|
61
|
+
const equal = (left, right) => {
|
|
62
|
+
require_scheduler.profile.equality.call();
|
|
63
|
+
if (left === right) return true;
|
|
64
|
+
require_scheduler.profile.equality.container();
|
|
65
|
+
if (left.length !== right.length) return false;
|
|
66
|
+
for (let index = 0; index < left.length; index++) if (left[index] !== keyAt(right, index)) return false;
|
|
67
|
+
return true;
|
|
68
|
+
};
|
|
69
|
+
const keys = (values, keyOf) => ({
|
|
70
|
+
length: values.length,
|
|
71
|
+
at: (index) => index >= 0 && index < values.length ? keyOf(values[index]) : void 0,
|
|
72
|
+
index: (key) => {
|
|
73
|
+
for (let index = 0; index < values.length; index += 1) if (keyOf(values[index]) === key) return index;
|
|
74
|
+
return -1;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
const listIndexes = /* @__PURE__ */ new WeakMap();
|
|
78
|
+
const knownKeys = (values) => {
|
|
79
|
+
let positions;
|
|
80
|
+
return {
|
|
81
|
+
length: values.length,
|
|
82
|
+
at: (index) => values[index],
|
|
83
|
+
index: (key) => {
|
|
84
|
+
if (!positions) {
|
|
85
|
+
positions = /* @__PURE__ */ new Map();
|
|
86
|
+
for (let index = 0; index < values.length; index++) positions.set(values[index], index);
|
|
87
|
+
require_scheduler.profile.address.listIndex(values.length);
|
|
88
|
+
}
|
|
89
|
+
return positions.get(key) ?? -1;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
const cacheKnownKeys = (values, keyOf, order) => {
|
|
94
|
+
listIndexes.set(values, {
|
|
95
|
+
keyOf,
|
|
96
|
+
order: knownKeys(order)
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
/** Canonical lists keep their key positions until a structural write invalidates them. */
|
|
100
|
+
const indexedKeys = (values, keyOf) => {
|
|
101
|
+
const cached = listIndexes.get(values);
|
|
102
|
+
if (cached?.keyOf === keyOf) return cached.order;
|
|
103
|
+
let positions;
|
|
104
|
+
const order = {
|
|
105
|
+
length: values.length,
|
|
106
|
+
at: (index) => index >= 0 && index < values.length ? keyOf(values[index]) : void 0,
|
|
107
|
+
index: (key) => {
|
|
108
|
+
if (!positions) {
|
|
109
|
+
positions = /* @__PURE__ */ new Map();
|
|
110
|
+
for (let index = 0; index < values.length; index++) positions.set(keyOf(values[index]), index);
|
|
111
|
+
require_scheduler.profile.address.listIndex(values.length);
|
|
112
|
+
}
|
|
113
|
+
return positions.get(key) ?? -1;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
listIndexes.set(values, {
|
|
117
|
+
keyOf,
|
|
118
|
+
order
|
|
119
|
+
});
|
|
120
|
+
return order;
|
|
121
|
+
};
|
|
122
|
+
const toArray = (keys) => {
|
|
123
|
+
const result = new Array(length$1(keys));
|
|
124
|
+
for (let index = 0; index < result.length; index++) result[index] = keyAt(keys, index);
|
|
125
|
+
return result;
|
|
126
|
+
};
|
|
127
|
+
/** One operation-local list scan provides both stable keys and item association. */
|
|
128
|
+
const listSequence = (values, keyOf) => {
|
|
129
|
+
const current = indexedKeys(values, keyOf);
|
|
130
|
+
const order = new Array(values.length);
|
|
131
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
132
|
+
for (let index = 0; index < values.length; index++) {
|
|
133
|
+
const key = current.at(index);
|
|
134
|
+
order[index] = key;
|
|
135
|
+
byKey.set(key, values[index]);
|
|
136
|
+
}
|
|
137
|
+
cacheKnownKeys(values, keyOf, order);
|
|
138
|
+
return {
|
|
139
|
+
order,
|
|
140
|
+
values: byKey
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
/** Structural sequence writes own invalidation, including rollback writes. */
|
|
144
|
+
const insert$2 = (values, index, value) => {
|
|
145
|
+
values.splice(index, 0, value);
|
|
146
|
+
listIndexes.delete(values);
|
|
147
|
+
};
|
|
148
|
+
const remove$3 = (values, index) => {
|
|
149
|
+
values.splice(index, 1);
|
|
150
|
+
listIndexes.delete(values);
|
|
151
|
+
};
|
|
152
|
+
/** Installs an exact string-key sequence such as table ids in one structural write. */
|
|
153
|
+
const installKeys = (values, order) => {
|
|
154
|
+
values.length = order.length;
|
|
155
|
+
for (let index = 0; index < order.length; index++) values[index] = order[index];
|
|
156
|
+
};
|
|
157
|
+
/** Installs a pre-resolved list sequence without running keyOf again. */
|
|
158
|
+
const installList = (values, keyOf, sequence, order) => {
|
|
159
|
+
values.length = order.length;
|
|
160
|
+
for (let index = 0; index < order.length; index++) values[index] = sequence.values.get(order[index]);
|
|
161
|
+
cacheKnownKeys(values, keyOf, order);
|
|
162
|
+
};
|
|
163
|
+
const install = (values, keyOf, order) => {
|
|
164
|
+
installList(values, keyOf, listSequence(values, keyOf), order);
|
|
165
|
+
};
|
|
166
|
+
const matches = (order, members) => {
|
|
167
|
+
if (order.length !== members.length) return false;
|
|
168
|
+
const ids = new Set(order);
|
|
169
|
+
return ids.size === order.length && members.every((id) => ids.has(id));
|
|
170
|
+
};
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region core/src/schema/layout.ts
|
|
173
|
+
const compiledObjects = /* @__PURE__ */ new WeakMap();
|
|
174
|
+
const compiledEntries = /* @__PURE__ */ new WeakMap();
|
|
175
|
+
const variantBranch = (node, discriminator) => node.variants[discriminator ?? Object.keys(node.variants)[0] ?? ""];
|
|
176
|
+
const objectBranch = (node, value) => {
|
|
177
|
+
if (node.kind === "object") return node;
|
|
178
|
+
if (node.kind !== "variant" || typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
179
|
+
const rawTag = value[node.tag];
|
|
180
|
+
return variantBranch(node, typeof rawTag === "string" ? rawTag : void 0);
|
|
181
|
+
};
|
|
182
|
+
const compiledShape = (node, value) => {
|
|
183
|
+
const branch = objectBranch(node, value);
|
|
184
|
+
if (!branch) return void 0;
|
|
185
|
+
const shape = branch.shape;
|
|
186
|
+
let layout = compiledObjects.get(shape);
|
|
187
|
+
if (!layout) {
|
|
188
|
+
const members = /* @__PURE__ */ new Map();
|
|
189
|
+
for (const key of Object.keys(shape).sort()) {
|
|
190
|
+
require_scheduler.profile.address.schemaStep();
|
|
191
|
+
members.set(key, {
|
|
192
|
+
kind: "fixed",
|
|
193
|
+
key,
|
|
194
|
+
node: shape[key],
|
|
195
|
+
field: shape[key].kind === "field",
|
|
196
|
+
slot: members.size
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
layout = {
|
|
200
|
+
kind: "fixed",
|
|
201
|
+
members
|
|
202
|
+
};
|
|
203
|
+
compiledObjects.set(shape, layout);
|
|
204
|
+
}
|
|
205
|
+
return layout;
|
|
206
|
+
};
|
|
207
|
+
const compiledLayout = (node, value) => {
|
|
208
|
+
if (node.kind !== "map" && node.kind !== "table" && node.kind !== "list") return compiledShape(node, value);
|
|
209
|
+
let layout = compiledEntries.get(node);
|
|
210
|
+
if (!layout) {
|
|
211
|
+
layout = {
|
|
212
|
+
kind: "dynamic",
|
|
213
|
+
entry: {
|
|
214
|
+
kind: "entry",
|
|
215
|
+
node: node.value,
|
|
216
|
+
field: node.value.kind === "field"
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
compiledEntries.set(node, layout);
|
|
220
|
+
}
|
|
221
|
+
return layout;
|
|
222
|
+
};
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region core/src/schema/value.ts
|
|
225
|
+
var ParseError = class extends TypeError {
|
|
226
|
+
issues;
|
|
227
|
+
constructor(issue) {
|
|
228
|
+
super(`${issue.message} at ${JSON.stringify(issue.address)}`);
|
|
229
|
+
this.name = "ParseError";
|
|
230
|
+
this.issues = Object.freeze([Object.freeze({
|
|
231
|
+
...issue,
|
|
232
|
+
address: Object.freeze([...issue.address])
|
|
233
|
+
})]);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const issue = (address, message, code = "invalid-value") => ({
|
|
237
|
+
address,
|
|
238
|
+
message,
|
|
239
|
+
code
|
|
240
|
+
});
|
|
241
|
+
const checkScalar = (validator, value, address, strict) => {
|
|
242
|
+
if (!validator) return strict ? issue(address, "A validator is required to parse an opaque value.", "missing-validator") : void 0;
|
|
243
|
+
try {
|
|
244
|
+
const result = typeof validator === "function" ? validator(value) : validator["~standard"].validate(value);
|
|
245
|
+
if (result && (typeof result === "object" || typeof result === "function") && "then" in result && typeof result.then === "function") return issue(address, "Validators must be synchronous.");
|
|
246
|
+
if (typeof validator === "function") {
|
|
247
|
+
if (result === false) return issue(address, "Value validation failed.");
|
|
248
|
+
if (result !== void 0 && result !== true) return issue(address, "Function validators must return boolean or undefined.");
|
|
249
|
+
} else {
|
|
250
|
+
if (!require_record.isRecord(result)) return issue(address, "Malformed validator result.");
|
|
251
|
+
if (Array.isArray(result.issues)) {
|
|
252
|
+
const first = result.issues[0];
|
|
253
|
+
return issue([...address, ...(first?.path ?? []).map((part) => String(typeof part === "object" ? part.key : part))], first?.message ?? "Value validation failed.");
|
|
254
|
+
}
|
|
255
|
+
if (!Object.hasOwn(result, "value")) return issue(address, "Malformed validator result.");
|
|
256
|
+
if (!Object.is(result.value, value)) return issue(address, "Validators must not transform values.");
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return issue(address, error instanceof Error ? error.message : "Value validation failed.");
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const checkKey = (validator, value, address) => {
|
|
264
|
+
if (typeof value !== "string") return issue(address, "Collection keys must be strings.", "invalid-key");
|
|
265
|
+
const failure = checkScalar(validator, value, address, false);
|
|
266
|
+
return failure ? {
|
|
267
|
+
...failure,
|
|
268
|
+
code: "invalid-key"
|
|
269
|
+
} : void 0;
|
|
270
|
+
};
|
|
271
|
+
const checkValue = (node, value, address = [], strict = false) => {
|
|
272
|
+
if (node.optional && value === void 0) return void 0;
|
|
273
|
+
if (node.kind === "field") return checkScalar(node.validator, value, address, strict);
|
|
274
|
+
if (node.kind === "object" || node.kind === "variant") {
|
|
275
|
+
if (!require_record.isPlainObject(value)) return issue(address, "Expected an object.");
|
|
276
|
+
if (node.kind === "variant") {
|
|
277
|
+
const tag = value[node.tag];
|
|
278
|
+
if (!Object.hasOwn(value, node.tag) || typeof tag !== "string" || !Object.hasOwn(node.variants, tag)) return issue([...address, node.tag], "Unknown variant tag.");
|
|
279
|
+
}
|
|
280
|
+
const { members } = compiledShape(node, value);
|
|
281
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
282
|
+
if (node.kind === "variant" && key === node.tag) continue;
|
|
283
|
+
if (typeof key !== "string" || !members.has(key)) return issue([...address, String(key)], "Property is not declared in the object schema.");
|
|
284
|
+
}
|
|
285
|
+
for (const [key, member] of members) {
|
|
286
|
+
const child = member.node;
|
|
287
|
+
if (!Object.prototype.hasOwnProperty.call(value, key) && !child.optional) return issue([...address, key], "Required property is missing.");
|
|
288
|
+
const failure = checkValue(child, value[key], [...address, key], strict);
|
|
289
|
+
if (failure) return failure;
|
|
290
|
+
}
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (node.kind === "table" || node.kind === "map") {
|
|
294
|
+
if (!require_record.isPlainObject(value)) return issue(address, "Expected a collection object.");
|
|
295
|
+
const entries = node.kind === "table" ? value.byId : value;
|
|
296
|
+
if (!require_record.isPlainObject(entries)) return issue(address, "Expected a byId object.");
|
|
297
|
+
if (node.kind === "table") {
|
|
298
|
+
if (!Array.isArray(value.ids) || value.ids.length !== Object.keys(entries).length) return issue(address, "Table ids and entries must agree.");
|
|
299
|
+
const seen = /* @__PURE__ */ new Set();
|
|
300
|
+
for (const id of value.ids) {
|
|
301
|
+
const failure = checkKey(node.key, id, address);
|
|
302
|
+
if (failure) return failure;
|
|
303
|
+
if (seen.has(id) || !Object.prototype.hasOwnProperty.call(entries, id)) return issue(address, "Table ids must be unique and reference existing entries.");
|
|
304
|
+
seen.add(id);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
for (const id of Object.keys(entries)) {
|
|
308
|
+
const failure = checkKey(node.key, id, [...address, id]) ?? checkValue(node.value, entries[id], [...address, id], strict);
|
|
309
|
+
if (failure) return failure;
|
|
310
|
+
}
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (node.kind === "list") {
|
|
314
|
+
if (!Array.isArray(value)) return issue(address, "Expected a list.");
|
|
315
|
+
const seen = /* @__PURE__ */ new Set();
|
|
316
|
+
const order = keys(value, node.keyOf);
|
|
317
|
+
for (let i = 0; i < value.length; i++) {
|
|
318
|
+
const failure = checkValue(node.value, value[i], [...address, String(i)], strict);
|
|
319
|
+
if (failure) return failure;
|
|
320
|
+
let key;
|
|
321
|
+
try {
|
|
322
|
+
key = order.at(i);
|
|
323
|
+
} catch {
|
|
324
|
+
return issue(address, "List keyOf failed.");
|
|
325
|
+
}
|
|
326
|
+
if (typeof key !== "string" || seen.has(key)) return issue(address, "List keys must be unique strings.", "invalid-key");
|
|
327
|
+
seen.add(key);
|
|
328
|
+
}
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (!require_driver.validate(value)) return issue(address, "Invalid tree structure.", "invalid-tree");
|
|
332
|
+
for (const id of Object.keys(value.nodes)) {
|
|
333
|
+
const failure = checkTreePayload(node.value, value.nodes[id], [...address, id], strict);
|
|
334
|
+
if (failure) return failure;
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
const checkTreePayload = (node, value, address, strict = false) => {
|
|
338
|
+
if (!require_record.isRecord(value)) return issue(address, "Invalid tree node.", "invalid-tree");
|
|
339
|
+
const present = Object.prototype.hasOwnProperty.call(value, "value");
|
|
340
|
+
if (!present && !node.optional) return issue(address, "Tree node value is required.");
|
|
341
|
+
return present ? checkValue(node, value.value, address, strict) : void 0;
|
|
342
|
+
};
|
|
343
|
+
const copyTreeNode = (value) => {
|
|
344
|
+
require_scheduler.profile.copy.treeNode();
|
|
345
|
+
return {
|
|
346
|
+
...value,
|
|
347
|
+
children: [...value.children]
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
const equalTreeNode = (left, right) => {
|
|
351
|
+
if (left === right) return true;
|
|
352
|
+
if (!left || !right) return false;
|
|
353
|
+
return left.parentId === right.parentId && equal(left.children, right.children) && Object.hasOwn(left, "value") === Object.hasOwn(right, "value") && Object.is(left.value, right.value);
|
|
354
|
+
};
|
|
355
|
+
/** Copy editable structure; atomic payloads retain their immutable ownership contract. */
|
|
356
|
+
const copyValue = (node, value) => {
|
|
357
|
+
if (value === void 0 || node.kind === "field") return value;
|
|
358
|
+
require_scheduler.profile.copy.structure();
|
|
359
|
+
if ((node.kind === "object" || node.kind === "variant") && require_record.isRecord(value)) {
|
|
360
|
+
const result = Object.create(Object.getPrototypeOf(value));
|
|
361
|
+
const { members } = compiledShape(node, value);
|
|
362
|
+
for (const key of Object.getOwnPropertyNames(value)) Object.defineProperty(result, key, {
|
|
363
|
+
value: node.kind === "variant" && key === node.tag ? value[key] : copyValue(members.get(key).node, value[key]),
|
|
364
|
+
writable: true,
|
|
365
|
+
enumerable: Object.getOwnPropertyDescriptor(value, key)?.enumerable,
|
|
366
|
+
configurable: true
|
|
367
|
+
});
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
if ((node.kind === "table" || node.kind === "map") && require_record.isRecord(value)) {
|
|
371
|
+
const entries = node.kind === "table" ? value.byId : value;
|
|
372
|
+
const result = Object.fromEntries(Object.keys(entries).map((id) => [id, copyValue(node.value, entries[id])]));
|
|
373
|
+
return node.kind === "table" ? {
|
|
374
|
+
ids: [...value.ids],
|
|
375
|
+
byId: result
|
|
376
|
+
} : result;
|
|
377
|
+
}
|
|
378
|
+
if (node.kind === "list") return [...value];
|
|
379
|
+
if (node.kind === "tree" && require_record.isRecord(value)) {
|
|
380
|
+
const entries = value.nodes;
|
|
381
|
+
return {
|
|
382
|
+
...value,
|
|
383
|
+
nodes: Object.fromEntries(Object.entries(entries).map(([id, n]) => [id, copyTreeNode(n)]))
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
return value;
|
|
387
|
+
};
|
|
388
|
+
const equalValue = (node, left, right) => {
|
|
389
|
+
if (Object.is(left, right)) return true;
|
|
390
|
+
if (node.kind === "field" || left === void 0 || right === void 0) return false;
|
|
391
|
+
if ((node.kind === "object" || node.kind === "variant") && require_record.isRecord(left) && require_record.isRecord(right)) {
|
|
392
|
+
if (node.kind === "variant" && left[node.tag] !== right[node.tag]) return false;
|
|
393
|
+
for (const [key, member] of compiledShape(node, left).members) {
|
|
394
|
+
if (Object.hasOwn(left, key) !== Object.hasOwn(right, key)) return false;
|
|
395
|
+
if (!equalValue(member.node, left[key], right[key])) return false;
|
|
396
|
+
}
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
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]));
|
|
400
|
+
if (node.kind === "table" && require_record.isRecord(left) && require_record.isRecord(right)) return equal(left.ids, right.ids) && equalValue({
|
|
401
|
+
kind: "map",
|
|
402
|
+
value: node.value
|
|
403
|
+
}, left.byId, right.byId);
|
|
404
|
+
if (node.kind === "tree" && require_record.isRecord(left) && require_record.isRecord(right)) {
|
|
405
|
+
if (left.rootId !== right.rootId) return false;
|
|
406
|
+
const a = left.nodes, b = right.nodes;
|
|
407
|
+
return Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((id) => Object.hasOwn(b, id) && equalTreeNode(a[id], b[id]));
|
|
408
|
+
}
|
|
409
|
+
if (node.kind === "map" && require_record.isRecord(left) && require_record.isRecord(right)) {
|
|
410
|
+
const keys = Object.keys(left);
|
|
411
|
+
return keys.length === Object.keys(right).length && keys.every((key) => Object.hasOwn(right, key) && equalValue(node.value, left[key], right[key]));
|
|
412
|
+
}
|
|
413
|
+
return false;
|
|
9
414
|
};
|
|
415
|
+
const parse = (schema, input) => {
|
|
416
|
+
const valueNode = schemaNodeOf(schema);
|
|
417
|
+
const failure = checkValue(valueNode, input, [], true);
|
|
418
|
+
if (failure) throw new ParseError(failure);
|
|
419
|
+
return copyValue(valueNode, input);
|
|
420
|
+
};
|
|
421
|
+
//#endregion
|
|
422
|
+
//#region core/src/address/resolve.ts
|
|
10
423
|
const step = (nodeInput, value, segment) => {
|
|
11
424
|
let node = nodeInput;
|
|
12
425
|
if (!node) return void 0;
|
|
13
|
-
if (node.kind === "variant")
|
|
426
|
+
if (node.kind === "variant") {
|
|
427
|
+
const rawTag = require_record.isRecord(value) ? value[node.tag] : void 0;
|
|
428
|
+
node = variantBranch(node, typeof rawTag === "string" ? rawTag : void 0);
|
|
429
|
+
}
|
|
14
430
|
if (!node) return void 0;
|
|
15
431
|
if (node.kind === "object") return Object.hasOwn(node.shape, segment) ? node.shape[segment] : void 0;
|
|
16
432
|
if (node.kind === "table" || node.kind === "map") return node.value;
|
|
@@ -28,17 +444,17 @@ const nodeAt = (schema, address, document) => {
|
|
|
28
444
|
return node;
|
|
29
445
|
};
|
|
30
446
|
const readSegment = (value, segment, node) => {
|
|
31
|
-
if (!
|
|
32
|
-
if (node?.kind === "list" && Array.isArray(value)) return value[
|
|
33
|
-
if (node?.kind === "table" &&
|
|
447
|
+
if (!require_record.isRecord(value) && !Array.isArray(value)) return void 0;
|
|
448
|
+
if (node?.kind === "list" && Array.isArray(value)) return value[indexedKeys(value, node.keyOf).index(segment)];
|
|
449
|
+
if (node?.kind === "table" && require_record.isRecord(value) && require_record.isRecord(value.byId)) return Object.hasOwn(value.byId, segment) ? value.byId[segment] : void 0;
|
|
34
450
|
return Object.prototype.hasOwnProperty.call(value, segment) ? value[segment] : void 0;
|
|
35
451
|
};
|
|
36
452
|
const resolveContainer = (owner, generation, at, node, value) => {
|
|
37
|
-
if (!node || !
|
|
38
|
-
const parent = node.kind === "table" &&
|
|
39
|
-
if (!
|
|
453
|
+
if (!node || !require_record.isRecord(value) && !Array.isArray(value)) return void 0;
|
|
454
|
+
const parent = node.kind === "table" && require_record.isRecord(value) ? value.byId : value;
|
|
455
|
+
if (!require_record.isRecord(parent) && !Array.isArray(parent)) return void 0;
|
|
40
456
|
if (node.kind === "tree") return void 0;
|
|
41
|
-
const layout =
|
|
457
|
+
const layout = compiledLayout(node, value);
|
|
42
458
|
if (!layout) return void 0;
|
|
43
459
|
return {
|
|
44
460
|
owner,
|
|
@@ -50,14 +466,14 @@ const resolveContainer = (owner, generation, at, node, value) => {
|
|
|
50
466
|
layout
|
|
51
467
|
};
|
|
52
468
|
};
|
|
53
|
-
const resolveTreeContainer = (owner, generation, at, node, value) => node?.kind === "tree" &&
|
|
469
|
+
const resolveTreeContainer = (owner, generation, at, node, value) => node?.kind === "tree" && require_driver.is(value) ? {
|
|
54
470
|
owner,
|
|
55
471
|
generation,
|
|
56
472
|
at,
|
|
57
473
|
node,
|
|
58
474
|
value
|
|
59
475
|
} : void 0;
|
|
60
|
-
const memberKey = (container, key) => container.node.kind === "list" ?
|
|
476
|
+
const memberKey = (container, key) => container.node.kind === "list" ? indexedKeys(container.parent, container.node.keyOf).index(key) : key;
|
|
61
477
|
/** One recent path per transaction; structural writes invalidate all retained locations. */
|
|
62
478
|
const createAddressResolver = (schema, root) => {
|
|
63
479
|
const prefix = [];
|
|
@@ -103,8 +519,8 @@ const resolveWithPrefix = (schema, root, address, prefix) => {
|
|
|
103
519
|
}
|
|
104
520
|
let cacheable = true;
|
|
105
521
|
for (let index = start; index < address.length - 1; index += 1) {
|
|
106
|
-
|
|
107
|
-
|
|
522
|
+
require_scheduler.profile.address.schemaStep();
|
|
523
|
+
require_scheduler.profile.address.documentStep();
|
|
108
524
|
const resolved = step(node, current, address[index]);
|
|
109
525
|
if (!resolved) return void 0;
|
|
110
526
|
if (node?.kind !== "object" && node?.kind !== "table" && node?.kind !== "map") cacheable = false;
|
|
@@ -122,11 +538,11 @@ const resolveWithPrefix = (schema, root, address, prefix) => {
|
|
|
122
538
|
};
|
|
123
539
|
/** Resolves one member from an already current schema/container pair. */
|
|
124
540
|
const resolveChild = (node, current, last) => {
|
|
125
|
-
|
|
126
|
-
|
|
541
|
+
require_scheduler.profile.address.schemaStep();
|
|
542
|
+
require_scheduler.profile.address.documentStep();
|
|
127
543
|
const resolved = step(node, current, last);
|
|
128
|
-
if (!resolved || !
|
|
129
|
-
if (node?.kind === "table" &&
|
|
544
|
+
if (!resolved || !require_record.isRecord(current) && !Array.isArray(current)) return void 0;
|
|
545
|
+
if (node?.kind === "table" && require_record.isRecord(current) && require_record.isRecord(current.byId)) return {
|
|
130
546
|
node: resolved,
|
|
131
547
|
parent: current.byId,
|
|
132
548
|
key: last
|
|
@@ -134,20 +550,10 @@ const resolveChild = (node, current, last) => {
|
|
|
134
550
|
return {
|
|
135
551
|
node: resolved,
|
|
136
552
|
parent: current,
|
|
137
|
-
key: node?.kind === "list" && Array.isArray(current) ?
|
|
553
|
+
key: node?.kind === "list" && Array.isArray(current) ? indexedKeys(current, node.keyOf).index(last) : last
|
|
138
554
|
};
|
|
139
555
|
};
|
|
140
556
|
//#endregion
|
|
141
|
-
//#region core/src/mutation/operations/map.ts
|
|
142
|
-
function put(session, container, id, value) {
|
|
143
|
-
if (container.node.kind !== "map") return require_driver.fail(container.at, "invalid-collection", "Expected a map.");
|
|
144
|
-
session.writeMember(container, id, value, "set");
|
|
145
|
-
}
|
|
146
|
-
function remove$3(session, container, id) {
|
|
147
|
-
if (container.node.kind !== "map") return require_driver.fail(container.at, "invalid-collection", "Expected a map.");
|
|
148
|
-
session.writeMember(container, id, void 0, "remove");
|
|
149
|
-
}
|
|
150
|
-
//#endregion
|
|
151
557
|
//#region core/src/order/anchor.ts
|
|
152
558
|
const length = (keys) => keys.length;
|
|
153
559
|
const keyIndex = (keys, key) => Array.isArray(keys) ? keys.indexOf(key) : keys.index(key);
|
|
@@ -235,25 +641,25 @@ function insert$1(session, container, value, position) {
|
|
|
235
641
|
session.validate(node.value, value, at);
|
|
236
642
|
const id = node.keyOf(value);
|
|
237
643
|
if (typeof id !== "string") return require_driver.fail(at, "invalid-list-key", "List key must be a string.");
|
|
238
|
-
const items = current, keys$1 =
|
|
644
|
+
const items = current, keys$1 = keys(items, node.keyOf);
|
|
239
645
|
if (keys$1.index(id) >= 0) return require_driver.fail(at, "duplicate-list-item", "List key already exists.");
|
|
240
646
|
if (!valid(keys$1, position)) return require_driver.fail(at, "invalid-anchor", "Unknown list anchor.");
|
|
241
647
|
session.recorder.order(container);
|
|
242
648
|
session.recorder.member(container, id, session.definition(container, id), -1);
|
|
243
|
-
|
|
649
|
+
insert$2(items, index(keys$1, position), value);
|
|
244
650
|
session.invalidate();
|
|
245
651
|
}
|
|
246
652
|
function replace$2(session, container, id, value) {
|
|
247
653
|
const { at, node, value: items } = container;
|
|
248
654
|
if (node.kind !== "list") return require_driver.fail(at, "invalid-list-key", "Expected a list.");
|
|
249
|
-
const index =
|
|
655
|
+
const index = indexedKeys(items, node.keyOf).index(id);
|
|
250
656
|
if (index < 0) return require_driver.fail(at, "missing-list-item", "List key does not exist.");
|
|
251
657
|
session.writeMember(container, id, value, "set", index);
|
|
252
658
|
}
|
|
253
659
|
function remove$1(session, container, id) {
|
|
254
660
|
const { at, node, value } = container;
|
|
255
661
|
if (node.kind !== "list") return require_driver.fail(at, "missing-list-item", "List key does not exist.");
|
|
256
|
-
const index =
|
|
662
|
+
const index = keys(value, node.keyOf).index(id);
|
|
257
663
|
if (index < 0) return require_driver.fail(at, "missing-list-item", "List key does not exist.");
|
|
258
664
|
session.writeMember(container, id, void 0, "remove", index);
|
|
259
665
|
}
|
|
@@ -267,7 +673,7 @@ const positionIndex = (index, length, at) => {
|
|
|
267
673
|
const insert = (session, container, id, value, position) => {
|
|
268
674
|
const { at, node: schema, value: tree } = container;
|
|
269
675
|
if (typeof id !== "string") return require_driver.fail(at, "invalid-key", "Tree keys must be strings.");
|
|
270
|
-
if (
|
|
676
|
+
if (require_driver.contains(tree, id)) return require_driver.fail(at, "duplicate-tree-node", "Tree node already exists.");
|
|
271
677
|
const parentId = position?.parentId;
|
|
272
678
|
const parent = parentId === void 0 ? void 0 : node$1(tree, parentId);
|
|
273
679
|
if (tree.rootId !== void 0 && !parent || tree.rootId === void 0 && parentId !== void 0) return require_driver.fail(at, "missing-tree-parent", "A nonempty tree requires an existing parent.");
|
|
@@ -322,7 +728,7 @@ const move$1 = (session, container, id, position) => {
|
|
|
322
728
|
const { at, value: tree } = container;
|
|
323
729
|
const entry = node$1(tree, id), parentId = position?.parentId;
|
|
324
730
|
if (!entry) return require_driver.fail(at, "missing-tree-node", "Tree node does not exist.");
|
|
325
|
-
if (entry.parentId === void 0 || parentId === void 0 || !
|
|
731
|
+
if (entry.parentId === void 0 || parentId === void 0 || !require_driver.contains(tree, parentId)) return require_driver.fail(at, "missing-tree-parent", "Moving requires a non-root node and an existing parent.");
|
|
326
732
|
for (let ancestor = parentId; ancestor !== void 0; ancestor = tree.nodes[ancestor].parentId) if (ancestor === id) return require_driver.fail(at, "tree-cycle", "Cannot move a node under its descendant.");
|
|
327
733
|
const old = tree.nodes[entry.parentId], next = tree.nodes[parentId];
|
|
328
734
|
const index = positionIndex(position?.index, next.children.length - (old === next ? 1 : 0), at);
|
|
@@ -359,16 +765,16 @@ function move(session, container, ids, position) {
|
|
|
359
765
|
if (node.kind === "table") {
|
|
360
766
|
const order = container.value.ids;
|
|
361
767
|
const next = plannedOrder(container.at, order, selection, position);
|
|
362
|
-
if (
|
|
768
|
+
if (equal(order, next)) return;
|
|
363
769
|
session.recorder.order(container);
|
|
364
|
-
|
|
770
|
+
installKeys(order, next);
|
|
365
771
|
} else {
|
|
366
772
|
const items = container.value;
|
|
367
|
-
const current =
|
|
773
|
+
const current = listSequence(items, node.keyOf);
|
|
368
774
|
const next = plannedOrder(container.at, current.order, selection, position);
|
|
369
|
-
if (
|
|
775
|
+
if (equal(current.order, next)) return;
|
|
370
776
|
session.recorder.order(container);
|
|
371
|
-
|
|
777
|
+
installList(items, node.keyOf, current, next);
|
|
372
778
|
}
|
|
373
779
|
session.invalidate();
|
|
374
780
|
}
|
|
@@ -376,17 +782,17 @@ function reorder(session, container, next) {
|
|
|
376
782
|
const node = assertOrdered(container);
|
|
377
783
|
if (node.kind === "table") {
|
|
378
784
|
const current = container.value.ids;
|
|
379
|
-
if (!
|
|
380
|
-
if (
|
|
785
|
+
if (!matches(next, current)) return require_driver.fail(container.at, "invalid-collection", "Order must contain every key exactly once.");
|
|
786
|
+
if (equal(current, next)) return;
|
|
381
787
|
session.recorder.order(container);
|
|
382
|
-
|
|
788
|
+
installKeys(current, next);
|
|
383
789
|
} else {
|
|
384
790
|
const items = container.value;
|
|
385
|
-
const current =
|
|
386
|
-
if (!
|
|
387
|
-
if (
|
|
791
|
+
const current = listSequence(items, node.keyOf);
|
|
792
|
+
if (!matches(next, current.order)) return require_driver.fail(container.at, "invalid-collection", "Order must contain every key exactly once.");
|
|
793
|
+
if (equal(current.order, next)) return;
|
|
388
794
|
session.recorder.order(container);
|
|
389
|
-
|
|
795
|
+
installList(items, node.keyOf, current, next);
|
|
390
796
|
}
|
|
391
797
|
session.invalidate();
|
|
392
798
|
}
|
|
@@ -411,13 +817,17 @@ const sameTreeTarget = (left, right) => {
|
|
|
411
817
|
return left.kind !== "node" || right.kind === "node" && left.id === right.id;
|
|
412
818
|
};
|
|
413
819
|
const same = (left, right) => {
|
|
820
|
+
require_scheduler.profile.dependency.comparison();
|
|
414
821
|
if ("schema" in left && "schema" in right && left.schema !== right.schema) return false;
|
|
415
822
|
if (!sameTreeTarget(treeTarget(left), treeTarget(right))) return false;
|
|
416
823
|
if (left.kind !== right.kind) return false;
|
|
417
824
|
const leftAddress = address(left);
|
|
418
825
|
const rightAddress = address(right);
|
|
419
826
|
if (leftAddress.length !== rightAddress.length) return false;
|
|
420
|
-
for (let index = 0; index < leftAddress.length; index += 1)
|
|
827
|
+
for (let index = 0; index < leftAddress.length; index += 1) {
|
|
828
|
+
require_scheduler.profile.dependency.segmentCompared();
|
|
829
|
+
if (leftAddress[index] !== rightAddress[index]) return false;
|
|
830
|
+
}
|
|
421
831
|
return left.kind !== "collection" || id(left) === id(right);
|
|
422
832
|
};
|
|
423
833
|
const indexedAddress = (target) => {
|
|
@@ -445,6 +855,12 @@ const indexedAddress = (target) => {
|
|
|
445
855
|
key
|
|
446
856
|
];
|
|
447
857
|
};
|
|
858
|
+
/**
|
|
859
|
+
* Coarse identity for dependency de-duplication. Schema is intentionally excluded because
|
|
860
|
+
* schema-bound and schema-free targets may compare equal; callers must still use same()
|
|
861
|
+
* inside a bucket for the final equality decision.
|
|
862
|
+
*/
|
|
863
|
+
const dependencyBucket = (target) => JSON.stringify([target.kind, indexedAddress(target)]);
|
|
448
864
|
const visitChangedLocations = (changes, value, order) => {
|
|
449
865
|
for (const change of changes.changes) {
|
|
450
866
|
if (change.kind === "reset") continue;
|
|
@@ -522,7 +938,7 @@ const locations = /* @__PURE__ */ new WeakMap();
|
|
|
522
938
|
const locationOf = (value) => locations.get(value);
|
|
523
939
|
const addressOf = (target) => {
|
|
524
940
|
if (target.at) return target.at;
|
|
525
|
-
|
|
941
|
+
require_scheduler.profile.access("addresses");
|
|
526
942
|
return target.at = addressOf(target.parent).concat(target.key);
|
|
527
943
|
};
|
|
528
944
|
const assertActive = (context) => {
|
|
@@ -544,11 +960,11 @@ const snapshot = (value) => {
|
|
|
544
960
|
if (!location) return value;
|
|
545
961
|
const { context } = location;
|
|
546
962
|
const at = addressOf(location.target);
|
|
547
|
-
|
|
963
|
+
require_scheduler.profile.access("snapshots");
|
|
548
964
|
collect(context, at);
|
|
549
965
|
const resolved = resolve(context, location.target);
|
|
550
966
|
if (!resolved.node) throw new Error("The selected schema address no longer exists.");
|
|
551
|
-
return
|
|
967
|
+
return copyValue(resolved.node, resolved.value);
|
|
552
968
|
};
|
|
553
969
|
const replace = (container, key, value) => {
|
|
554
970
|
if (!locationOf(container)) throw new TypeError("replace requires scoped document access.");
|
|
@@ -559,7 +975,7 @@ const resolve = (context, target) => {
|
|
|
559
975
|
assertActive(context);
|
|
560
976
|
const generation = context.session?.generation ?? 0;
|
|
561
977
|
if (target.generation !== generation) {
|
|
562
|
-
|
|
978
|
+
require_scheduler.profile.access("resolutions");
|
|
563
979
|
if (!target.parent && !target.at.length) {
|
|
564
980
|
target.node = context.state.schema;
|
|
565
981
|
target.value = context.state.document;
|
|
@@ -574,7 +990,7 @@ const resolve = (context, target) => {
|
|
|
574
990
|
return target;
|
|
575
991
|
};
|
|
576
992
|
const createAccess = (context, initial = []) => {
|
|
577
|
-
|
|
993
|
+
require_scheduler.profile.access("scopes");
|
|
578
994
|
const mutable = () => {
|
|
579
995
|
assertActive(context);
|
|
580
996
|
if (!context.session) throw new TypeError("Cannot modify read-only document access.");
|
|
@@ -585,13 +1001,13 @@ const createAccess = (context, initial = []) => {
|
|
|
585
1001
|
return target.container = session.bind(addressOf(target), target.node, target.value);
|
|
586
1002
|
};
|
|
587
1003
|
const childAt = (target, key) => {
|
|
588
|
-
|
|
1004
|
+
require_scheduler.profile.access("addresses");
|
|
589
1005
|
return addressOf(target).concat(key);
|
|
590
1006
|
};
|
|
591
1007
|
const access = (target) => {
|
|
592
1008
|
if (target.proxy) return target.proxy;
|
|
593
1009
|
const proxy = new Proxy(target, handler);
|
|
594
|
-
|
|
1010
|
+
require_scheduler.profile.access("proxies");
|
|
595
1011
|
locations.set(proxy, {
|
|
596
1012
|
context,
|
|
597
1013
|
target
|
|
@@ -621,7 +1037,7 @@ const createAccess = (context, initial = []) => {
|
|
|
621
1037
|
if (children?.kind !== "fixed" || children.schema !== target.node) children = {
|
|
622
1038
|
kind: "fixed",
|
|
623
1039
|
schema: target.node,
|
|
624
|
-
slots: new Array(
|
|
1040
|
+
slots: new Array(compiledShape(target.node, target.value).members.size)
|
|
625
1041
|
};
|
|
626
1042
|
child = children.slots[member.slot] ??= createTarget(void 0, target, key);
|
|
627
1043
|
} else {
|
|
@@ -683,12 +1099,14 @@ const createAccess = (context, initial = []) => {
|
|
|
683
1099
|
return childAccess(target, id, node.value, current[id]);
|
|
684
1100
|
};
|
|
685
1101
|
if (property === "put") return (id, value) => {
|
|
1102
|
+
const session = mutable();
|
|
686
1103
|
const writable = writableCollection(target, node);
|
|
687
|
-
|
|
1104
|
+
session.writeMember(writable, id, value, "set");
|
|
688
1105
|
};
|
|
689
1106
|
if (property === "remove") return (id) => {
|
|
1107
|
+
const session = mutable();
|
|
690
1108
|
const writable = writableCollection(target, node);
|
|
691
|
-
|
|
1109
|
+
session.writeMember(writable, id, void 0, "remove");
|
|
692
1110
|
};
|
|
693
1111
|
if (property === "replace") return (value) => replaceCollection(target, node, value);
|
|
694
1112
|
};
|
|
@@ -702,7 +1120,7 @@ const createAccess = (context, initial = []) => {
|
|
|
702
1120
|
if (property === "has") return (id) => {
|
|
703
1121
|
const current = collectionValue(target, node);
|
|
704
1122
|
collect(context, at, "collection", id);
|
|
705
|
-
return node.kind === "table" ? Object.hasOwn(current.byId, id) :
|
|
1123
|
+
return node.kind === "table" ? Object.hasOwn(current.byId, id) : indexedKeys(current, node.keyOf).index(id) >= 0;
|
|
706
1124
|
};
|
|
707
1125
|
if (property === "get") return (id) => {
|
|
708
1126
|
const current = collectionValue(target, node);
|
|
@@ -788,28 +1206,28 @@ const createAccess = (context, initial = []) => {
|
|
|
788
1206
|
id
|
|
789
1207
|
});
|
|
790
1208
|
const value = current();
|
|
791
|
-
return
|
|
1209
|
+
return require_driver.contains(value, id) ? value.nodes[id].value : void 0;
|
|
792
1210
|
};
|
|
793
1211
|
if (property === "has") return (id) => {
|
|
794
1212
|
collectTree(context, at, {
|
|
795
1213
|
kind: "node",
|
|
796
1214
|
id
|
|
797
1215
|
});
|
|
798
|
-
return
|
|
1216
|
+
return require_driver.contains(current(), id);
|
|
799
1217
|
};
|
|
800
1218
|
if (property === "parent") return (id) => {
|
|
801
1219
|
collectTree(context, at, {
|
|
802
1220
|
kind: "node",
|
|
803
1221
|
id
|
|
804
1222
|
});
|
|
805
|
-
return
|
|
1223
|
+
return require_driver.parent(current(), id);
|
|
806
1224
|
};
|
|
807
1225
|
if (property === "children") return (id) => {
|
|
808
1226
|
collectTree(context, at, {
|
|
809
1227
|
kind: "node",
|
|
810
1228
|
id
|
|
811
1229
|
});
|
|
812
|
-
const children$1 =
|
|
1230
|
+
const children$1 = require_driver.children(current(), id);
|
|
813
1231
|
return children$1 ? [...children$1] : void 0;
|
|
814
1232
|
};
|
|
815
1233
|
};
|
|
@@ -826,7 +1244,7 @@ const createAccess = (context, initial = []) => {
|
|
|
826
1244
|
collect(context, childAt(target, property));
|
|
827
1245
|
return value[property];
|
|
828
1246
|
}
|
|
829
|
-
const member =
|
|
1247
|
+
const member = compiledShape(node, value)?.members.get(property);
|
|
830
1248
|
if (!member) return void 0;
|
|
831
1249
|
if (member.field) {
|
|
832
1250
|
if (context.dependencies) collect(context, childAt(target, property));
|
|
@@ -889,12 +1307,68 @@ const createAccess = (context, initial = []) => {
|
|
|
889
1307
|
return access(root);
|
|
890
1308
|
};
|
|
891
1309
|
//#endregion
|
|
1310
|
+
//#region core/src/schema/path.ts
|
|
1311
|
+
const paths = /* @__PURE__ */ new WeakMap();
|
|
1312
|
+
const pathProxy = (nodes, address, owner, treeSelection) => {
|
|
1313
|
+
const proxy = new Proxy({}, { get: (_target, property) => {
|
|
1314
|
+
if (typeof property !== "string") return void 0;
|
|
1315
|
+
const trees = nodes.every((node) => node.kind === "tree");
|
|
1316
|
+
if (!treeSelection && trees && property === "rootId") return pathProxy(nodes, address, owner, { kind: "root" });
|
|
1317
|
+
if (!treeSelection && trees && property === "nodes") return pathProxy(nodes, address, owner, { kind: "nodes" });
|
|
1318
|
+
if (treeSelection?.kind === "nodes" && property === "item") return (id) => {
|
|
1319
|
+
if (typeof id !== "string") throw new TypeError("Tree node keys must be strings.");
|
|
1320
|
+
return pathProxy(nodes, address, owner, {
|
|
1321
|
+
kind: "node",
|
|
1322
|
+
id
|
|
1323
|
+
});
|
|
1324
|
+
};
|
|
1325
|
+
if (treeSelection) throw new TypeError(`Invalid tree path segment: ${property}`);
|
|
1326
|
+
if (nodes.every((node) => collectionEntryNode(node) !== void 0) && property === "item") return (id) => {
|
|
1327
|
+
if (typeof id !== "string") throw new TypeError("Collection keys must be strings.");
|
|
1328
|
+
for (const node of nodes) if (node.kind === "table" || node.kind === "map") {
|
|
1329
|
+
const invalid = checkKey(node.key, id, [...address, id]);
|
|
1330
|
+
if (invalid) throw new TypeError(invalid.message);
|
|
1331
|
+
}
|
|
1332
|
+
return pathProxy(nodes.map((node) => collectionEntryNode(node)), [...address, id], owner);
|
|
1333
|
+
};
|
|
1334
|
+
const children = nodes.flatMap((node) => {
|
|
1335
|
+
if (node.kind === "object") return Object.hasOwn(node.shape, property) ? [node.shape[property]] : [];
|
|
1336
|
+
if (node.kind !== "variant") return [];
|
|
1337
|
+
if (property === node.tag) return [Object.freeze({ kind: "field" })];
|
|
1338
|
+
return Object.values(node.variants).flatMap((branch) => Object.hasOwn(branch.shape, property) ? [branch.shape[property]] : []);
|
|
1339
|
+
});
|
|
1340
|
+
if (!children.length) throw new TypeError(`Invalid schema path segment: ${property}`);
|
|
1341
|
+
return pathProxy(children, [...address, property], owner);
|
|
1342
|
+
} });
|
|
1343
|
+
paths.set(proxy, {
|
|
1344
|
+
nodes,
|
|
1345
|
+
address,
|
|
1346
|
+
owner,
|
|
1347
|
+
...treeSelection ? { tree: treeSelection } : {}
|
|
1348
|
+
});
|
|
1349
|
+
return proxy;
|
|
1350
|
+
};
|
|
1351
|
+
const compilePath = (owner, kind, pick) => {
|
|
1352
|
+
const scope = {};
|
|
1353
|
+
const value = pick(pathProxy([owner], [], scope));
|
|
1354
|
+
const selected = typeof value === "object" && value !== null ? paths.get(value) : void 0;
|
|
1355
|
+
if (!selected || selected.owner !== scope) throw new TypeError("Selector must return a path from its callback.");
|
|
1356
|
+
const collection = selected.tree?.kind === "nodes" || selected.nodes.every((node) => collectionEntryNode(node) !== void 0);
|
|
1357
|
+
if (kind === "collection" && !collection) throw new TypeError("Collection selectors require a map, table, list or tree nodes path.");
|
|
1358
|
+
return Object.freeze({
|
|
1359
|
+
kind: kind === "auto" ? collection ? "collection" : "value" : kind,
|
|
1360
|
+
schema: owner,
|
|
1361
|
+
address: Object.freeze([...selected.address]),
|
|
1362
|
+
...selected.tree ? { tree: Object.freeze({ ...selected.tree }) } : {}
|
|
1363
|
+
});
|
|
1364
|
+
};
|
|
1365
|
+
//#endregion
|
|
892
1366
|
//#region core/src/address/relation.ts
|
|
893
1367
|
const contains = (parent, child) => {
|
|
894
|
-
|
|
1368
|
+
require_scheduler.profile.address.prefixComparison();
|
|
895
1369
|
if (parent.length > child.length) return false;
|
|
896
1370
|
for (let index = 0; index < parent.length; index += 1) {
|
|
897
|
-
|
|
1371
|
+
require_scheduler.profile.address.segmentCompared();
|
|
898
1372
|
if (parent[index] !== child[index]) return false;
|
|
899
1373
|
}
|
|
900
1374
|
return true;
|
|
@@ -907,7 +1381,7 @@ const createImpact = (schema, changes) => {
|
|
|
907
1381
|
let orders;
|
|
908
1382
|
const index = () => {
|
|
909
1383
|
if (!values) {
|
|
910
|
-
|
|
1384
|
+
require_scheduler.profile.impact.index();
|
|
911
1385
|
values = new require_driver.AddressIndex();
|
|
912
1386
|
orders = new require_driver.AddressIndex();
|
|
913
1387
|
visitChangedLocations(changes, (at) => values.add(at, true), (at) => orders.add(at, true));
|
|
@@ -916,7 +1390,7 @@ const createImpact = (schema, changes) => {
|
|
|
916
1390
|
};
|
|
917
1391
|
const cache = /* @__PURE__ */ new Map();
|
|
918
1392
|
const affects = (value) => {
|
|
919
|
-
|
|
1393
|
+
require_scheduler.profile.impact.affects();
|
|
920
1394
|
if (!belongs(value, schema)) return false;
|
|
921
1395
|
if (reset) return true;
|
|
922
1396
|
return affected(value, index(), orders);
|
|
@@ -990,8 +1464,8 @@ const createImpact = (schema, changes) => {
|
|
|
990
1464
|
};
|
|
991
1465
|
return Object.freeze({
|
|
992
1466
|
kind: reset ? "reset" : "incremental",
|
|
993
|
-
affects: (pick) => affects(
|
|
994
|
-
collection: (pick) => collection(
|
|
1467
|
+
affects: (pick) => affects(compilePath(schema, "value", pick)),
|
|
1468
|
+
collection: (pick) => collection(compilePath(schema, "collection", pick))
|
|
995
1469
|
});
|
|
996
1470
|
};
|
|
997
1471
|
//#endregion
|
|
@@ -1193,18 +1667,18 @@ const createHistory = (input) => {
|
|
|
1193
1667
|
const installMember = (parent, key, present, value) => {
|
|
1194
1668
|
if (Array.isArray(parent)) {
|
|
1195
1669
|
const index = Number(key);
|
|
1196
|
-
if (present) if (index < 0)
|
|
1670
|
+
if (present) if (index < 0) insert$2(parent, parent.length, value);
|
|
1197
1671
|
else parent[index] = value;
|
|
1198
|
-
else if (index >= 0)
|
|
1199
|
-
} else
|
|
1672
|
+
else if (index >= 0) remove$3(parent, index);
|
|
1673
|
+
} else require_record.installOwn(parent, key, present, value);
|
|
1200
1674
|
};
|
|
1201
1675
|
const orderOf = (node, value) => {
|
|
1202
|
-
if (node.kind === "list") return
|
|
1676
|
+
if (node.kind === "list") return toArray(indexedKeys(value, node.keyOf));
|
|
1203
1677
|
if (node.kind === "table") return [...value.ids];
|
|
1204
1678
|
throw new Error("Order requires a table or list.");
|
|
1205
1679
|
};
|
|
1206
1680
|
const installOrder = (node, value, order) => {
|
|
1207
|
-
if (node.kind === "list")
|
|
1681
|
+
if (node.kind === "list") install(value, node.keyOf, order);
|
|
1208
1682
|
else if (node.kind === "table") value.ids = [...order];
|
|
1209
1683
|
else throw new Error("Order requires a table or list.");
|
|
1210
1684
|
};
|
|
@@ -1225,9 +1699,9 @@ const forgetMember = (group, key) => {
|
|
|
1225
1699
|
const emptyGroup = (group) => !group.order && (group.layout === "fixed" ? !group.members.some(Boolean) : !group.members?.size);
|
|
1226
1700
|
const transition = (node, key, beforePresent, before, afterPresent, after) => {
|
|
1227
1701
|
if (beforePresent === afterPresent && (!beforePresent || Object.is(before, after))) return;
|
|
1228
|
-
if (node.kind !== "field" && beforePresent === afterPresent &&
|
|
1229
|
-
const value = afterPresent ? node.kind === "field" ? after :
|
|
1230
|
-
|
|
1702
|
+
if (node.kind !== "field" && beforePresent === afterPresent && equalValue(node, before, after)) return;
|
|
1703
|
+
const value = afterPresent ? node.kind === "field" ? after : copyValue(node, after) : void 0;
|
|
1704
|
+
require_scheduler.profile.recorder("transitions");
|
|
1231
1705
|
return !beforePresent ? {
|
|
1232
1706
|
key,
|
|
1233
1707
|
kind: "added",
|
|
@@ -1268,7 +1742,7 @@ const diffMember = (changes, members, node, at, key, beforePresent, before, afte
|
|
|
1268
1742
|
const left = before, right = after;
|
|
1269
1743
|
const childAt = at.concat(key);
|
|
1270
1744
|
const children = [];
|
|
1271
|
-
for (const [child, definition] of
|
|
1745
|
+
for (const [child, definition] of compiledShape(node, before).members) diffMember(changes, children, definition.node, childAt, child, Object.hasOwn(left, child), left[child], Object.hasOwn(right, child), right[child]);
|
|
1272
1746
|
publishMembers(changes, childAt, children);
|
|
1273
1747
|
return;
|
|
1274
1748
|
}
|
|
@@ -1281,8 +1755,8 @@ const diffMember = (changes, members, node, at, key, beforePresent, before, afte
|
|
|
1281
1755
|
for (const id of Object.keys(left)) diffMember(changes, children, node.value, childAt, id, true, left[id], Object.hasOwn(right, id), right[id]);
|
|
1282
1756
|
for (const id of Object.keys(right)) if (!Object.hasOwn(left, id)) diffMember(changes, children, node.value, childAt, id, false, void 0, true, right[id]);
|
|
1283
1757
|
let order;
|
|
1284
|
-
if (node.kind === "table" && !
|
|
1285
|
-
|
|
1758
|
+
if (node.kind === "table" && !equal(a.ids, b.ids)) {
|
|
1759
|
+
require_scheduler.profile.recorder("publishedOrderItems", b.ids.length);
|
|
1286
1760
|
order = {
|
|
1287
1761
|
before: a.ids,
|
|
1288
1762
|
after: [...b.ids]
|
|
@@ -1299,7 +1773,7 @@ const restore = (fact, schema, root, skip = 0) => {
|
|
|
1299
1773
|
const at = skip ? fact.at.slice(skip) : fact.at;
|
|
1300
1774
|
if (fact.kind === "member") {
|
|
1301
1775
|
const member = fact.member;
|
|
1302
|
-
const value = member.present ?
|
|
1776
|
+
const value = member.present ? copyValue(member.node, member.value) : void 0;
|
|
1303
1777
|
if (!at.length) return value;
|
|
1304
1778
|
const location = resolveLocated(schema, root, at);
|
|
1305
1779
|
if (!location) throw new Error("Cannot restore an unresolved member.");
|
|
@@ -1313,12 +1787,12 @@ const restore = (fact, schema, root, skip = 0) => {
|
|
|
1313
1787
|
const tree = location.value;
|
|
1314
1788
|
if (fact.before === null) delete tree.rootId;
|
|
1315
1789
|
else tree.rootId = fact.before;
|
|
1316
|
-
for (const [id, node] of fact.nodes) installMember(tree.nodes, id, node !== void 0, node &&
|
|
1790
|
+
for (const [id, node] of fact.nodes) installMember(tree.nodes, id, node !== void 0, node && copyTreeNode(node));
|
|
1317
1791
|
}
|
|
1318
1792
|
return root;
|
|
1319
1793
|
};
|
|
1320
1794
|
const reconstructBefore = (node, current, records, offset) => {
|
|
1321
|
-
let before =
|
|
1795
|
+
let before = copyValue(node, current);
|
|
1322
1796
|
for (let i = records.length - 1; i >= 0; i--) if (records[i].kind !== "order") before = restore(records[i], node, before, offset);
|
|
1323
1797
|
for (const record of records) if (record.kind === "order") before = restore(record, node, before, offset);
|
|
1324
1798
|
return before;
|
|
@@ -1337,10 +1811,10 @@ const sealMembers = (fact, changes) => {
|
|
|
1337
1811
|
}
|
|
1338
1812
|
let order;
|
|
1339
1813
|
if (fact.order) {
|
|
1340
|
-
const current = fact.node.kind === "list" ?
|
|
1341
|
-
if (!
|
|
1814
|
+
const current = fact.node.kind === "list" ? indexedKeys(fact.value, fact.node.keyOf) : fact.value.ids;
|
|
1815
|
+
if (!equal(fact.order, current)) {
|
|
1342
1816
|
const after = orderOf(fact.node, fact.value);
|
|
1343
|
-
|
|
1817
|
+
require_scheduler.profile.recorder("publishedOrderItems", after.length);
|
|
1344
1818
|
order = {
|
|
1345
1819
|
before: fact.order,
|
|
1346
1820
|
after
|
|
@@ -1357,11 +1831,11 @@ const sealTree = (fact, canonical, changes) => {
|
|
|
1357
1831
|
for (const [id, before] of fact.nodes) {
|
|
1358
1832
|
const after = Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : void 0;
|
|
1359
1833
|
if (!before && !after) continue;
|
|
1360
|
-
if (before && after &&
|
|
1834
|
+
if (before && after && equalTreeNode(before, after)) continue;
|
|
1361
1835
|
nodes.push(!before ? {
|
|
1362
1836
|
id,
|
|
1363
1837
|
kind: "added",
|
|
1364
|
-
after:
|
|
1838
|
+
after: copyTreeNode(after)
|
|
1365
1839
|
} : !after ? {
|
|
1366
1840
|
id,
|
|
1367
1841
|
kind: "removed",
|
|
@@ -1370,7 +1844,7 @@ const sealTree = (fact, canonical, changes) => {
|
|
|
1370
1844
|
id,
|
|
1371
1845
|
kind: "updated",
|
|
1372
1846
|
before,
|
|
1373
|
-
after:
|
|
1847
|
+
after: copyTreeNode(after)
|
|
1374
1848
|
});
|
|
1375
1849
|
}
|
|
1376
1850
|
const after = tree.rootId ?? null;
|
|
@@ -1398,7 +1872,7 @@ var ChangeRecorder = class {
|
|
|
1398
1872
|
this.indexedGroups = true;
|
|
1399
1873
|
for (const fact of this.facts) if (fact.kind === "members") {
|
|
1400
1874
|
index.add(fact.at, fact);
|
|
1401
|
-
|
|
1875
|
+
require_scheduler.profile.recorder("indexedGroups");
|
|
1402
1876
|
}
|
|
1403
1877
|
}
|
|
1404
1878
|
return index;
|
|
@@ -1447,7 +1921,7 @@ var ChangeRecorder = class {
|
|
|
1447
1921
|
releaseCovered(children) {
|
|
1448
1922
|
let changedGroups;
|
|
1449
1923
|
for (const child of children) {
|
|
1450
|
-
|
|
1924
|
+
require_scheduler.profile.recorder("absorbed");
|
|
1451
1925
|
if (child.kind === "member") {
|
|
1452
1926
|
const { group, member } = child;
|
|
1453
1927
|
forgetMember(group, member.key);
|
|
@@ -1463,9 +1937,9 @@ var ChangeRecorder = class {
|
|
|
1463
1937
|
this.facts.add(fact);
|
|
1464
1938
|
if (fact.kind === "members") {
|
|
1465
1939
|
this.groups.set(fact.parent, fact);
|
|
1466
|
-
|
|
1940
|
+
require_scheduler.profile.recorder("groups");
|
|
1467
1941
|
if (!this.indexedGroups) return;
|
|
1468
|
-
|
|
1942
|
+
require_scheduler.profile.recorder("indexedGroups");
|
|
1469
1943
|
}
|
|
1470
1944
|
(this.index ??= new require_driver.AddressIndex()).add(fact.at, fact);
|
|
1471
1945
|
}
|
|
@@ -1495,14 +1969,14 @@ var ChangeRecorder = class {
|
|
|
1495
1969
|
if (group.layout === "dynamic") (group.members ??= /* @__PURE__ */ new Map()).set(key, member);
|
|
1496
1970
|
else if (definition.kind === "fixed") group.members[definition.slot] = member;
|
|
1497
1971
|
else throw new Error("A fixed member group requires a fixed schema member.");
|
|
1498
|
-
|
|
1972
|
+
require_scheduler.profile.recorder("facts");
|
|
1499
1973
|
}
|
|
1500
1974
|
reset() {
|
|
1501
1975
|
if (this.resetBefore) return;
|
|
1502
1976
|
const value = this.absorb([], this.state.schema, this.state.document, true);
|
|
1503
1977
|
this.resetBefore = { value };
|
|
1504
1978
|
this.groups = /* @__PURE__ */ new WeakMap();
|
|
1505
|
-
|
|
1979
|
+
require_scheduler.profile.recorder("facts");
|
|
1506
1980
|
}
|
|
1507
1981
|
createGroup(container) {
|
|
1508
1982
|
const group = container.layout.kind === "fixed" ? {
|
|
@@ -1527,13 +2001,13 @@ var ChangeRecorder = class {
|
|
|
1527
2001
|
return group;
|
|
1528
2002
|
}
|
|
1529
2003
|
order(container) {
|
|
1530
|
-
|
|
2004
|
+
require_scheduler.profile.recorder("orderCaptures");
|
|
1531
2005
|
let group = this.groups.get(container.parent);
|
|
1532
2006
|
if (group?.order || this.covered(container.at)) return;
|
|
1533
2007
|
group ??= this.createGroup(container);
|
|
1534
2008
|
group.order = orderOf(container.node, container.value);
|
|
1535
|
-
|
|
1536
|
-
|
|
2009
|
+
require_scheduler.profile.recorder("orderSnapshots");
|
|
2010
|
+
require_scheduler.profile.recorder("orderItems", group.order.length);
|
|
1537
2011
|
}
|
|
1538
2012
|
tree(container, id) {
|
|
1539
2013
|
const { at, value: tree } = container;
|
|
@@ -1551,8 +2025,8 @@ var ChangeRecorder = class {
|
|
|
1551
2025
|
this.register(fact);
|
|
1552
2026
|
}
|
|
1553
2027
|
if (id !== void 0 && !fact.nodes.has(id)) {
|
|
1554
|
-
|
|
1555
|
-
fact.nodes.set(id, Object.hasOwn(tree.nodes, id) ?
|
|
2028
|
+
require_scheduler.profile.recorder("treeNodes");
|
|
2029
|
+
fact.nodes.set(id, Object.hasOwn(tree.nodes, id) ? copyTreeNode(tree.nodes[id]) : void 0);
|
|
1556
2030
|
}
|
|
1557
2031
|
}
|
|
1558
2032
|
rollback() {
|
|
@@ -1565,7 +2039,7 @@ var ChangeRecorder = class {
|
|
|
1565
2039
|
const fact = facts[i];
|
|
1566
2040
|
if (fact.kind === "members") for (const member of fact.members?.values() ?? []) {
|
|
1567
2041
|
if (!member) continue;
|
|
1568
|
-
installMember(fact.parent, memberKey(fact, member.key), member.present, member.present ?
|
|
2042
|
+
installMember(fact.parent, memberKey(fact, member.key), member.present, member.present ? copyValue(member.node, member.value) : void 0);
|
|
1569
2043
|
}
|
|
1570
2044
|
else if (fact.kind === "tree") restore(fact, this.state.schema, this.state.document);
|
|
1571
2045
|
}
|
|
@@ -1573,19 +2047,19 @@ var ChangeRecorder = class {
|
|
|
1573
2047
|
}
|
|
1574
2048
|
seal() {
|
|
1575
2049
|
if (this.resetBefore) {
|
|
1576
|
-
if (
|
|
1577
|
-
|
|
2050
|
+
if (equalValue(this.state.schema, this.resetBefore.value, this.state.document)) return require_driver.sealChanges([]);
|
|
2051
|
+
require_scheduler.profile.recorder("sealed");
|
|
1578
2052
|
return require_driver.sealChanges([{
|
|
1579
2053
|
kind: "reset",
|
|
1580
2054
|
before: this.resetBefore.value,
|
|
1581
|
-
after:
|
|
2055
|
+
after: copyValue(this.state.schema, this.state.document)
|
|
1582
2056
|
}]);
|
|
1583
2057
|
}
|
|
1584
2058
|
const changes = [];
|
|
1585
2059
|
for (const fact of this.facts) if (fact.kind === "members") sealMembers(fact, changes);
|
|
1586
2060
|
else sealTree(fact, this.state, changes);
|
|
1587
2061
|
const result = require_driver.sealChanges(changes);
|
|
1588
|
-
|
|
2062
|
+
require_scheduler.profile.recorder("sealed", result.changes.length);
|
|
1589
2063
|
return result;
|
|
1590
2064
|
}
|
|
1591
2065
|
};
|
|
@@ -1605,7 +2079,7 @@ var MutationSession = class {
|
|
|
1605
2079
|
this.resolvedRoot = state.document;
|
|
1606
2080
|
}
|
|
1607
2081
|
validate(node, value, at) {
|
|
1608
|
-
const failure =
|
|
2082
|
+
const failure = checkValue(node, value, at);
|
|
1609
2083
|
if (failure) require_driver.invalidValue(failure);
|
|
1610
2084
|
}
|
|
1611
2085
|
resolveContainer(at) {
|
|
@@ -1627,7 +2101,7 @@ var MutationSession = class {
|
|
|
1627
2101
|
this.validate(this.state.schema, value, at);
|
|
1628
2102
|
if (Object.is(this.state.document, value)) return;
|
|
1629
2103
|
this.recorder.reset();
|
|
1630
|
-
this.state.document =
|
|
2104
|
+
this.state.document = copyValue(this.state.schema, value);
|
|
1631
2105
|
this.invalidate();
|
|
1632
2106
|
return;
|
|
1633
2107
|
}
|
|
@@ -1669,12 +2143,12 @@ var MutationSession = class {
|
|
|
1669
2143
|
const previous = container.parent[physicalKey];
|
|
1670
2144
|
if (existed === present && (!present || Object.is(previous, value))) return false;
|
|
1671
2145
|
if (parentNode.kind === "map" || parentNode.kind === "table") {
|
|
1672
|
-
const failure =
|
|
2146
|
+
const failure = checkKey(parentNode.key, key, []);
|
|
1673
2147
|
if (failure) return require_driver.fail(container.at.concat(key, ...failure.address), "invalid-key", failure.message);
|
|
1674
2148
|
}
|
|
1675
2149
|
if (present) {
|
|
1676
2150
|
if (node.kind === "field") {
|
|
1677
|
-
const failure =
|
|
2151
|
+
const failure = checkValue(node, value, []);
|
|
1678
2152
|
if (failure) return require_driver.invalidValue(failure, container.at.concat(key, ...failure.address));
|
|
1679
2153
|
} else this.validate(node, value, container.at.concat(key));
|
|
1680
2154
|
if (parentNode.kind === "list" && parentNode.keyOf(value) !== key) return require_driver.fail(container.at.concat(key), "invalid-list-key", "Replacing an item must retain its addressed key.");
|
|
@@ -1682,7 +2156,7 @@ var MutationSession = class {
|
|
|
1682
2156
|
const membershipChanged = entry && existed !== present;
|
|
1683
2157
|
if (membershipChanged && (parentNode.kind === "list" || parentNode.kind === "table")) this.recorder.order(container);
|
|
1684
2158
|
this.recorder.member(container, key, member, physicalKey);
|
|
1685
|
-
const next = present ? node.kind === "field" ? value :
|
|
2159
|
+
const next = present ? node.kind === "field" ? value : copyValue(node, value) : void 0;
|
|
1686
2160
|
installMember(container.parent, physicalKey, present, next);
|
|
1687
2161
|
if (node.kind !== "field" || membershipChanged) this.invalidate();
|
|
1688
2162
|
return membershipChanged;
|
|
@@ -1722,12 +2196,12 @@ function apply(session, changes, direction) {
|
|
|
1722
2196
|
}
|
|
1723
2197
|
if (change.order) {
|
|
1724
2198
|
const keys = node.kind === "table" ? Object.keys(container.parent) : orderOf(node, current);
|
|
1725
|
-
if (!
|
|
2199
|
+
if (!matches(change.order[side], keys)) return require_driver.fail(change.at, "invalid-changes", "Order must contain exactly the resulting keys.");
|
|
1726
2200
|
installOrder(node, current, change.order[side]);
|
|
1727
2201
|
session.invalidate();
|
|
1728
2202
|
} else if (membershipChanged && node.kind === "table") {
|
|
1729
2203
|
const ids = current.ids;
|
|
1730
|
-
if (!
|
|
2204
|
+
if (!matches(ids, Object.keys(container.parent))) return require_driver.fail(change.at, "invalid-changes", "Table membership changes require a matching order.");
|
|
1731
2205
|
}
|
|
1732
2206
|
} else {
|
|
1733
2207
|
const container = session.resolveTree(change.at);
|
|
@@ -1740,16 +2214,26 @@ function apply(session, changes, direction) {
|
|
|
1740
2214
|
for (const item of change.nodes) {
|
|
1741
2215
|
const next = side === "after" ? item.kind !== "removed" ? item.after : void 0 : item.kind !== "added" ? item.before : void 0;
|
|
1742
2216
|
if (next !== void 0) {
|
|
1743
|
-
const diagnostic =
|
|
2217
|
+
const diagnostic = checkTreePayload(node.value, next, change.at.concat(item.id));
|
|
1744
2218
|
if (diagnostic) require_driver.invalidValue(diagnostic);
|
|
1745
2219
|
}
|
|
1746
|
-
installMember(current.nodes, item.id, next !== void 0, next &&
|
|
2220
|
+
installMember(current.nodes, item.id, next !== void 0, next && copyTreeNode(next));
|
|
1747
2221
|
}
|
|
1748
|
-
if (!
|
|
2222
|
+
if (!require_driver.validate(current)) return require_driver.fail(change.at, "invalid-tree", "Invalid tree structure.");
|
|
1749
2223
|
session.invalidate();
|
|
1750
2224
|
}
|
|
1751
2225
|
}
|
|
1752
2226
|
//#endregion
|
|
2227
|
+
//#region core/src/runtime/readable.ts
|
|
2228
|
+
const readonlyDocument = (runtime) => {
|
|
2229
|
+
const readable = {
|
|
2230
|
+
revision: runtime.revision,
|
|
2231
|
+
subscribe: runtime.subscribe
|
|
2232
|
+
};
|
|
2233
|
+
require_driver.bindContext(readable, require_driver.contextOf(runtime));
|
|
2234
|
+
return Object.freeze(readable);
|
|
2235
|
+
};
|
|
2236
|
+
//#endregion
|
|
1753
2237
|
//#region core/src/runtime/notification.ts
|
|
1754
2238
|
const attachProjection = (notification, processor) => {
|
|
1755
2239
|
const entry = {
|
|
@@ -1913,18 +2397,19 @@ const createNotificationCenter = (schema) => {
|
|
|
1913
2397
|
//#endregion
|
|
1914
2398
|
//#region core/src/runtime.ts
|
|
1915
2399
|
const createDocument = (input) => {
|
|
1916
|
-
|
|
1917
|
-
const invalid =
|
|
1918
|
-
if (invalid) throw new
|
|
2400
|
+
const schema = schemaNodeOf(input.schema);
|
|
2401
|
+
const invalid = checkValue(schema, input.initial);
|
|
2402
|
+
if (invalid) throw new ParseError(invalid);
|
|
1919
2403
|
const state = {
|
|
1920
|
-
schema
|
|
1921
|
-
document:
|
|
2404
|
+
schema,
|
|
2405
|
+
document: copyValue(schema, input.initial),
|
|
1922
2406
|
disposed: false,
|
|
1923
2407
|
projectionLocks: 0
|
|
1924
2408
|
};
|
|
1925
2409
|
let revision = 0, busy = false;
|
|
1926
|
-
const notifications = createNotificationCenter(
|
|
2410
|
+
const notifications = createNotificationCenter(schema);
|
|
1927
2411
|
let runtime, context;
|
|
2412
|
+
let readonlyAlias;
|
|
1928
2413
|
const idle = () => {
|
|
1929
2414
|
if (state.disposed) throw new DocumentDisposedError();
|
|
1930
2415
|
if (busy || state.projectionLocks) throw new DocumentReentrancyError();
|
|
@@ -1957,7 +2442,7 @@ const createDocument = (input) => {
|
|
|
1957
2442
|
revision: ++revision,
|
|
1958
2443
|
source,
|
|
1959
2444
|
changes,
|
|
1960
|
-
impact: createImpact(
|
|
2445
|
+
impact: createImpact(schema, changes)
|
|
1961
2446
|
};
|
|
1962
2447
|
if (source === "remote") history.invalidate();
|
|
1963
2448
|
else if (recordHistory && (source === "local" || source === "system")) history.record(changes);
|
|
@@ -2004,6 +2489,7 @@ const createDocument = (input) => {
|
|
|
2004
2489
|
runtime = {
|
|
2005
2490
|
schema: input.schema,
|
|
2006
2491
|
revision: () => revision,
|
|
2492
|
+
readonly: () => readonlyAlias ??= readonlyDocument(runtime),
|
|
2007
2493
|
update: (run, options) => {
|
|
2008
2494
|
const source = options?.source ?? "local";
|
|
2009
2495
|
let value;
|
|
@@ -2022,13 +2508,16 @@ const createDocument = (input) => {
|
|
|
2022
2508
|
value
|
|
2023
2509
|
};
|
|
2024
2510
|
},
|
|
2025
|
-
apply: (inputChanges, options) =>
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2511
|
+
apply: (inputChanges, options) => {
|
|
2512
|
+
const source = options?.source ?? "local";
|
|
2513
|
+
return mutate({
|
|
2514
|
+
kind: "apply",
|
|
2515
|
+
source
|
|
2516
|
+
}, source === "remote" ? false : options?.history ?? true, (session) => {
|
|
2517
|
+
if (!options || options.expectedRevision !== revision) require_driver.fail([], "baseline-mismatch", "apply requires the current expectedRevision.");
|
|
2518
|
+
apply(session, require_driver.decodeChanges(inputChanges), "forward");
|
|
2519
|
+
});
|
|
2520
|
+
},
|
|
2032
2521
|
replace: (value, options) => {
|
|
2033
2522
|
const source = options?.source ?? "system";
|
|
2034
2523
|
return mutate({
|
|
@@ -2038,14 +2527,14 @@ const createDocument = (input) => {
|
|
|
2038
2527
|
},
|
|
2039
2528
|
snapshot: () => {
|
|
2040
2529
|
if (state.disposed) throw new DocumentDisposedError();
|
|
2041
|
-
return
|
|
2530
|
+
return copyValue(schema, state.document);
|
|
2042
2531
|
},
|
|
2043
2532
|
subscribe: ((pick, listener) => {
|
|
2044
2533
|
if (state.disposed) throw new DocumentDisposedError();
|
|
2045
2534
|
if (!listener) return notifications.subscribe(pick);
|
|
2046
2535
|
const picks = Array.isArray(pick) ? pick : [pick];
|
|
2047
2536
|
if (!picks.length) throw new TypeError("Expected at least one subscription path.");
|
|
2048
|
-
return notifications.subscribeTargets(picks.map((p) =>
|
|
2537
|
+
return notifications.subscribeTargets(picks.map((p) => compilePath(schema, "value", p)), listener);
|
|
2049
2538
|
}),
|
|
2050
2539
|
history: history.api,
|
|
2051
2540
|
dispose: () => {
|
|
@@ -2067,27 +2556,25 @@ const createDocument = (input) => {
|
|
|
2067
2556
|
driver: void 0,
|
|
2068
2557
|
bypassDepth: 0
|
|
2069
2558
|
};
|
|
2070
|
-
|
|
2071
|
-
|
|
2559
|
+
require_driver.bindContext(runtime, context);
|
|
2560
|
+
require_driver.bindContext(history.api, context);
|
|
2072
2561
|
return runtime;
|
|
2073
2562
|
};
|
|
2074
2563
|
//#endregion
|
|
2075
|
-
//#region core/src/runtime/readable.ts
|
|
2076
|
-
const asReadable = (runtime) => {
|
|
2077
|
-
const readable = {
|
|
2078
|
-
revision: runtime.revision,
|
|
2079
|
-
subscribe: runtime.subscribe
|
|
2080
|
-
};
|
|
2081
|
-
require_context.bindContext(readable, require_context.contextOf(runtime));
|
|
2082
|
-
return Object.freeze(readable);
|
|
2083
|
-
};
|
|
2084
|
-
//#endregion
|
|
2085
2564
|
//#region core/src/access/dependency.ts
|
|
2086
2565
|
const createDependencyTracker = () => {
|
|
2087
2566
|
const targets = [];
|
|
2567
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
2088
2568
|
return {
|
|
2089
2569
|
record: (value) => {
|
|
2090
|
-
|
|
2570
|
+
const key = dependencyBucket(value);
|
|
2571
|
+
let entries = buckets.get(key);
|
|
2572
|
+
if (!entries) {
|
|
2573
|
+
entries = [];
|
|
2574
|
+
buckets.set(key, entries);
|
|
2575
|
+
} else if (entries.some((entry) => same(entry, value))) return;
|
|
2576
|
+
entries.push(value);
|
|
2577
|
+
targets.push(value);
|
|
2091
2578
|
},
|
|
2092
2579
|
snapshot: () => Object.freeze(targets.slice())
|
|
2093
2580
|
};
|
|
@@ -2095,7 +2582,7 @@ const createDependencyTracker = () => {
|
|
|
2095
2582
|
//#endregion
|
|
2096
2583
|
//#region core/src/runtime/access.ts
|
|
2097
2584
|
const readWith = (runtime, run, dependencies) => {
|
|
2098
|
-
const state =
|
|
2585
|
+
const state = require_driver.contextOf(runtime).state;
|
|
2099
2586
|
if (state.disposed) throw new DocumentDisposedError();
|
|
2100
2587
|
state.projectionLocks += 1;
|
|
2101
2588
|
try {
|
|
@@ -2116,7 +2603,7 @@ const sameDependencySet = (left, right) => {
|
|
|
2116
2603
|
return true;
|
|
2117
2604
|
};
|
|
2118
2605
|
const select = (document, selector, equality = Object.is) => {
|
|
2119
|
-
const context =
|
|
2606
|
+
const context = require_driver.contextOf(document);
|
|
2120
2607
|
let initialized = false;
|
|
2121
2608
|
let value;
|
|
2122
2609
|
let selectedRevision = 0;
|
|
@@ -2176,6 +2663,281 @@ const select = (document, selector, equality = Object.is) => {
|
|
|
2176
2663
|
});
|
|
2177
2664
|
};
|
|
2178
2665
|
//#endregion
|
|
2666
|
+
//#region core/src/projection/input.ts
|
|
2667
|
+
const valueInput = (initial, equality = Object.is) => require_scheduler.defineSource({
|
|
2668
|
+
kind: "value-input",
|
|
2669
|
+
initial,
|
|
2670
|
+
equality
|
|
2671
|
+
}, {
|
|
2672
|
+
kind: "value",
|
|
2673
|
+
equality
|
|
2674
|
+
});
|
|
2675
|
+
const collectionInput = (initial = /* @__PURE__ */ new Map(), equality = Object.is) => {
|
|
2676
|
+
const entries = /* @__PURE__ */ new Map();
|
|
2677
|
+
for (const [key, value] of initial) {
|
|
2678
|
+
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
2679
|
+
entries.set(key, value);
|
|
2680
|
+
}
|
|
2681
|
+
return require_scheduler.defineSource({
|
|
2682
|
+
kind: "collection-input",
|
|
2683
|
+
initial: entries,
|
|
2684
|
+
equality
|
|
2685
|
+
}, {
|
|
2686
|
+
kind: "collection",
|
|
2687
|
+
equality
|
|
2688
|
+
});
|
|
2689
|
+
};
|
|
2690
|
+
const input = Object.assign(valueInput, { collection: collectionInput });
|
|
2691
|
+
//#endregion
|
|
2692
|
+
//#region core/src/projection/observe.ts
|
|
2693
|
+
function observe(source, selector) {
|
|
2694
|
+
if (isExternalSource(source)) return source.kind === "collection" ? require_scheduler.defineSource({
|
|
2695
|
+
kind: "external-collection",
|
|
2696
|
+
source
|
|
2697
|
+
}, {
|
|
2698
|
+
kind: "collection",
|
|
2699
|
+
equality: Object.is
|
|
2700
|
+
}) : require_scheduler.defineSource({
|
|
2701
|
+
kind: "external-value",
|
|
2702
|
+
source,
|
|
2703
|
+
equality: Object.is
|
|
2704
|
+
}, {
|
|
2705
|
+
kind: "value",
|
|
2706
|
+
equality: Object.is
|
|
2707
|
+
});
|
|
2708
|
+
if ("current" in source && typeof source.current === "function") return require_scheduler.defineSource({
|
|
2709
|
+
kind: "readable",
|
|
2710
|
+
readable: source,
|
|
2711
|
+
equality: Object.is
|
|
2712
|
+
}, {
|
|
2713
|
+
kind: "value",
|
|
2714
|
+
equality: Object.is
|
|
2715
|
+
});
|
|
2716
|
+
const document = source;
|
|
2717
|
+
const state = require_driver.contextOf(document).state;
|
|
2718
|
+
const selected = selector ? compilePath(state.schema, "auto", selector) : Object.freeze({
|
|
2719
|
+
kind: "value",
|
|
2720
|
+
schema: state.schema,
|
|
2721
|
+
address: Object.freeze([])
|
|
2722
|
+
});
|
|
2723
|
+
return require_scheduler.defineSource({
|
|
2724
|
+
kind: "document",
|
|
2725
|
+
document,
|
|
2726
|
+
selector: selected
|
|
2727
|
+
}, {
|
|
2728
|
+
kind: selected.kind,
|
|
2729
|
+
equality: Object.is
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
const isExternalSource = (source) => {
|
|
2733
|
+
if (!source || typeof source !== "object") return false;
|
|
2734
|
+
const candidate = source;
|
|
2735
|
+
return (candidate.kind === "value" || candidate.kind === "collection") && typeof candidate.current === "function" && typeof candidate.revision === "function" && typeof candidate.subscribe === "function";
|
|
2736
|
+
};
|
|
2737
|
+
//#endregion
|
|
2738
|
+
//#region core/src/projection/derive.ts
|
|
2739
|
+
const sourceValue = (source) => source.kind === "value" ? source.value : require_scheduler.snapshotCollectionView(source.read);
|
|
2740
|
+
const outputKind = (projection) => {
|
|
2741
|
+
const ref = require_scheduler.projectionRef(projection);
|
|
2742
|
+
const definition = ref.producer;
|
|
2743
|
+
const output = definition.kind === "source" ? definition.output : definition.outputs[ref.output];
|
|
2744
|
+
if (!output) throw new TypeError("Projection output does not exist.");
|
|
2745
|
+
return output.kind;
|
|
2746
|
+
};
|
|
2747
|
+
const assertKeyedProjection = (projection, role) => {
|
|
2748
|
+
if (outputKind(projection) !== "collection") throw new TypeError(`${role} must be a keyed collection projection.`);
|
|
2749
|
+
};
|
|
2750
|
+
const isKeyedDependency = (value) => {
|
|
2751
|
+
if (!value || typeof value !== "object" || require_scheduler.isProjection(value)) return false;
|
|
2752
|
+
const candidate = value;
|
|
2753
|
+
return require_scheduler.isProjection(candidate.source) && typeof candidate.key === "function";
|
|
2754
|
+
};
|
|
2755
|
+
const bindKey = (index, outputKey, sourceKey) => {
|
|
2756
|
+
const previous = index.forward.get(outputKey);
|
|
2757
|
+
if (previous === sourceKey) return;
|
|
2758
|
+
if (previous !== void 0) {
|
|
2759
|
+
const dependents = index.reverse.get(previous);
|
|
2760
|
+
dependents?.delete(outputKey);
|
|
2761
|
+
if (dependents?.size === 0) index.reverse.delete(previous);
|
|
2762
|
+
index.forward.delete(outputKey);
|
|
2763
|
+
}
|
|
2764
|
+
if (sourceKey === void 0) return;
|
|
2765
|
+
index.forward.set(outputKey, sourceKey);
|
|
2766
|
+
const dependents = index.reverse.get(sourceKey);
|
|
2767
|
+
if (dependents) dependents.add(outputKey);
|
|
2768
|
+
else index.reverse.set(sourceKey, new Set([outputKey]));
|
|
2769
|
+
};
|
|
2770
|
+
const unbindKey = (indexes, outputKey) => {
|
|
2771
|
+
for (const index of indexes) bindKey(index, outputKey, void 0);
|
|
2772
|
+
};
|
|
2773
|
+
const createBindingIndex = () => ({
|
|
2774
|
+
forward: /* @__PURE__ */ new Map(),
|
|
2775
|
+
reverse: /* @__PURE__ */ new Map()
|
|
2776
|
+
});
|
|
2777
|
+
function createValueDerive(dependencies, compute, equality = Object.is) {
|
|
2778
|
+
if (!require_record.isPlainObject(dependencies) || require_scheduler.isProjection(dependencies)) throw new TypeError("Derive dependencies must be a plain object.");
|
|
2779
|
+
const names = Object.keys(dependencies);
|
|
2780
|
+
const ordered = names.map((name) => dependencies[name]);
|
|
2781
|
+
ordered.forEach(require_scheduler.projectionRef);
|
|
2782
|
+
const [projection] = require_scheduler.defineProcessor({
|
|
2783
|
+
dependencies: ordered,
|
|
2784
|
+
outputs: [{
|
|
2785
|
+
kind: "value",
|
|
2786
|
+
equality
|
|
2787
|
+
}],
|
|
2788
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
2789
|
+
const output = evaluation.outputs[0];
|
|
2790
|
+
if (output.kind !== "value") throw new Error("derive requires a value output.");
|
|
2791
|
+
const values = Object.create(null);
|
|
2792
|
+
for (let index = 0; index < names.length; index++) values[names[index]] = sourceValue(evaluation.sources[index]);
|
|
2793
|
+
const next = compute(Object.freeze(values));
|
|
2794
|
+
require_scheduler.assertSynchronous(next);
|
|
2795
|
+
output.output.set(next);
|
|
2796
|
+
} })
|
|
2797
|
+
});
|
|
2798
|
+
return projection;
|
|
2799
|
+
}
|
|
2800
|
+
function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
2801
|
+
assertKeyedProjection(source, "Keyed derive source");
|
|
2802
|
+
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
2803
|
+
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
2804
|
+
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
2805
|
+
if (typeof select !== "function") throw new TypeError("Keyed derive requires a selector.");
|
|
2806
|
+
const dependencies = [source];
|
|
2807
|
+
const dependencyNames = [];
|
|
2808
|
+
const keyed = [];
|
|
2809
|
+
if (dependencySpecs !== void 0) {
|
|
2810
|
+
if (!require_record.isPlainObject(dependencySpecs) || require_scheduler.isProjection(dependencySpecs)) throw new TypeError("Keyed derive dependencies must be a plain object.");
|
|
2811
|
+
for (const name of Reflect.ownKeys(dependencySpecs)) {
|
|
2812
|
+
if (typeof name !== "string") throw new TypeError("Keyed derive dependency names must be strings.");
|
|
2813
|
+
const descriptor = Object.getOwnPropertyDescriptor(dependencySpecs, name);
|
|
2814
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) throw new TypeError("Keyed derive dependencies must be enumerable data properties.");
|
|
2815
|
+
const dependency = descriptor.value;
|
|
2816
|
+
dependencyNames.push(name);
|
|
2817
|
+
if (require_scheduler.isProjection(dependency)) {
|
|
2818
|
+
dependencies.push(dependency);
|
|
2819
|
+
keyed.push(void 0);
|
|
2820
|
+
continue;
|
|
2821
|
+
}
|
|
2822
|
+
if (!isKeyedDependency(dependency)) throw new TypeError("Keyed derive dependencies must be projections or keyed lookups.");
|
|
2823
|
+
assertKeyedProjection(dependency.source, "Dynamic keyed dependency source");
|
|
2824
|
+
dependencies.push(dependency.source);
|
|
2825
|
+
keyed.push(dependency);
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
const [projection] = require_scheduler.defineProcessor({
|
|
2829
|
+
dependencies,
|
|
2830
|
+
outputs: [{
|
|
2831
|
+
kind: "collection",
|
|
2832
|
+
equality: equality ?? Object.is
|
|
2833
|
+
}],
|
|
2834
|
+
create: () => {
|
|
2835
|
+
const bindings = keyed.map((dependency) => dependency ? createBindingIndex() : void 0);
|
|
2836
|
+
const bindingIndexes = bindings.filter((index) => index !== void 0);
|
|
2837
|
+
const globalValues = keyed.map(() => void 0);
|
|
2838
|
+
let revisions = Object.freeze([]);
|
|
2839
|
+
const clearBindings = () => {
|
|
2840
|
+
for (const index of bindings) {
|
|
2841
|
+
index?.forward.clear();
|
|
2842
|
+
index?.reverse.clear();
|
|
2843
|
+
}
|
|
2844
|
+
};
|
|
2845
|
+
return {
|
|
2846
|
+
evaluate: (evaluation) => {
|
|
2847
|
+
const driver = evaluation.sources[0];
|
|
2848
|
+
const output = evaluation.outputs[0];
|
|
2849
|
+
if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("Keyed derive requires collection input and output.");
|
|
2850
|
+
const currentRevisions = evaluation.sources.map((current) => current.revision);
|
|
2851
|
+
for (let index = 0; index < keyed.length; index++) {
|
|
2852
|
+
if (keyed[index]) continue;
|
|
2853
|
+
if (evaluation.reset || revisions[index + 1] !== currentRevisions[index + 1]) globalValues[index] = sourceValue(evaluation.sources[index + 1]);
|
|
2854
|
+
}
|
|
2855
|
+
const project = (key) => {
|
|
2856
|
+
if (!driver.read.has(key)) return;
|
|
2857
|
+
const value = driver.read.get(key);
|
|
2858
|
+
const dependencyValues = Object.create(null);
|
|
2859
|
+
for (let index = 0; index < keyed.length; index++) {
|
|
2860
|
+
const dependency = keyed[index];
|
|
2861
|
+
if (!dependency) {
|
|
2862
|
+
dependencyValues[dependencyNames[index]] = globalValues[index];
|
|
2863
|
+
continue;
|
|
2864
|
+
}
|
|
2865
|
+
const sourceContext = evaluation.sources[index + 1];
|
|
2866
|
+
if (sourceContext.kind !== "collection") throw new TypeError("Dynamic keyed dependency resolved to a non-collection source.");
|
|
2867
|
+
const selectedKey = dependency.key(value, key);
|
|
2868
|
+
require_scheduler.assertSynchronous(selectedKey);
|
|
2869
|
+
if (selectedKey !== void 0 && typeof selectedKey !== "string") throw new TypeError("Dynamic keyed dependency keys must be strings or undefined.");
|
|
2870
|
+
bindKey(bindings[index], key, selectedKey);
|
|
2871
|
+
dependencyValues[dependencyNames[index]] = selectedKey === void 0 ? void 0 : sourceContext.read.get(selectedKey);
|
|
2872
|
+
}
|
|
2873
|
+
const next = dependencySpecs === void 0 ? select(value, key) : select(value, key, Object.freeze(dependencyValues));
|
|
2874
|
+
require_scheduler.assertSynchronous(next);
|
|
2875
|
+
output.output.set(key, next);
|
|
2876
|
+
};
|
|
2877
|
+
if (evaluation.reset) {
|
|
2878
|
+
clearBindings();
|
|
2879
|
+
const ids = driver.read.ids();
|
|
2880
|
+
for (const key of ids) project(key);
|
|
2881
|
+
output.output.order(ids);
|
|
2882
|
+
revisions = Object.freeze(currentRevisions);
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
2886
|
+
let all = false;
|
|
2887
|
+
let updateOrder = false;
|
|
2888
|
+
if (revisions[0] !== currentRevisions[0]) {
|
|
2889
|
+
const change = driver.change;
|
|
2890
|
+
if (!change || change.kind === "reset") {
|
|
2891
|
+
all = true;
|
|
2892
|
+
updateOrder = true;
|
|
2893
|
+
} else {
|
|
2894
|
+
for (const entry of change.removed) {
|
|
2895
|
+
output.output.remove(entry.key);
|
|
2896
|
+
unbindKey(bindingIndexes, entry.key);
|
|
2897
|
+
dirty.delete(entry.key);
|
|
2898
|
+
}
|
|
2899
|
+
for (const entry of change.added) dirty.add(entry.key);
|
|
2900
|
+
for (const entry of change.updated) dirty.add(entry.key);
|
|
2901
|
+
updateOrder = Boolean(change.added.length || change.removed.length || change.order);
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
for (let index = 0; index < keyed.length; index++) {
|
|
2905
|
+
if (revisions[index + 1] === currentRevisions[index + 1]) continue;
|
|
2906
|
+
if (!keyed[index]) {
|
|
2907
|
+
all = true;
|
|
2908
|
+
continue;
|
|
2909
|
+
}
|
|
2910
|
+
const sourceContext = evaluation.sources[index + 1];
|
|
2911
|
+
if (sourceContext.kind !== "collection" || !sourceContext.change) {
|
|
2912
|
+
all = true;
|
|
2913
|
+
continue;
|
|
2914
|
+
}
|
|
2915
|
+
if (sourceContext.change.kind === "reset") {
|
|
2916
|
+
all = true;
|
|
2917
|
+
continue;
|
|
2918
|
+
}
|
|
2919
|
+
const reverse = bindings[index].reverse;
|
|
2920
|
+
for (const transition of sourceContext.change.added) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
|
|
2921
|
+
for (const transition of sourceContext.change.updated) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
|
|
2922
|
+
for (const transition of sourceContext.change.removed) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
|
|
2923
|
+
}
|
|
2924
|
+
if (all) for (const key of driver.read.ids()) dirty.add(key);
|
|
2925
|
+
for (const key of dirty) if (driver.read.has(key)) project(key);
|
|
2926
|
+
if (updateOrder) output.output.order(driver.read.ids());
|
|
2927
|
+
revisions = Object.freeze(currentRevisions);
|
|
2928
|
+
},
|
|
2929
|
+
release: () => {
|
|
2930
|
+
clearBindings();
|
|
2931
|
+
revisions = Object.freeze([]);
|
|
2932
|
+
}
|
|
2933
|
+
};
|
|
2934
|
+
},
|
|
2935
|
+
name: "derive.keyed"
|
|
2936
|
+
});
|
|
2937
|
+
return projection;
|
|
2938
|
+
}
|
|
2939
|
+
const derive = Object.assign(createValueDerive, { keyed: createKeyedDerive });
|
|
2940
|
+
//#endregion
|
|
2179
2941
|
//#region core/src/projection/collection/change.ts
|
|
2180
2942
|
const commonOrderChanged = (before, after, added, removed) => {
|
|
2181
2943
|
const beforeCommon = before.filter((key) => !removed.has(key));
|
|
@@ -2239,7 +3001,7 @@ const collectionHasAnyChange = (change) => change.kind === "reset" || Boolean(ch
|
|
|
2239
3001
|
//#region core/src/projection/collection/index.ts
|
|
2240
3002
|
const height = (node) => node?.height ?? 0;
|
|
2241
3003
|
const node = (key, value, left, right) => {
|
|
2242
|
-
|
|
3004
|
+
require_scheduler.profile.collectionIndex.node();
|
|
2243
3005
|
return Object.freeze({
|
|
2244
3006
|
key,
|
|
2245
3007
|
value,
|
|
@@ -2300,7 +3062,7 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
|
2300
3062
|
}
|
|
2301
3063
|
static from(entries) {
|
|
2302
3064
|
const sorted = [...entries].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
2303
|
-
|
|
3065
|
+
require_scheduler.profile.collectionIndex.build(sorted.length);
|
|
2304
3066
|
return new PersistentKeyedIndex(fromSorted(sorted));
|
|
2305
3067
|
}
|
|
2306
3068
|
get(key) {
|
|
@@ -2374,7 +3136,7 @@ const createPublishedCollection = () => {
|
|
|
2374
3136
|
};
|
|
2375
3137
|
};
|
|
2376
3138
|
/** Owns staged keyed intent, exact transitions and published collection state. */
|
|
2377
|
-
const createCollectionOutput = () => {
|
|
3139
|
+
const createCollectionOutput = (binding) => {
|
|
2378
3140
|
const published = createPublishedCollection();
|
|
2379
3141
|
let staged = /* @__PURE__ */ new Map();
|
|
2380
3142
|
let nextIds = published.ids();
|
|
@@ -2385,6 +3147,7 @@ const createCollectionOutput = () => {
|
|
|
2385
3147
|
let explicitOrder;
|
|
2386
3148
|
let cleared = false;
|
|
2387
3149
|
const listeners = /* @__PURE__ */ new Set();
|
|
3150
|
+
const consumers = /* @__PURE__ */ new Set();
|
|
2388
3151
|
const hasNext = (key) => staged.has(key) ? staged.get(key).present : !cleared && published.has(key);
|
|
2389
3152
|
const getNext = (key) => {
|
|
2390
3153
|
const entry = staged.get(key);
|
|
@@ -2404,15 +3167,15 @@ const createCollectionOutput = () => {
|
|
|
2404
3167
|
reset = initialize;
|
|
2405
3168
|
const read = (next) => ({
|
|
2406
3169
|
get: (key) => {
|
|
2407
|
-
|
|
3170
|
+
require_scheduler.assertScope(active);
|
|
2408
3171
|
return next ? getNext(key) : published.get(key);
|
|
2409
3172
|
},
|
|
2410
3173
|
has: (key) => {
|
|
2411
|
-
|
|
3174
|
+
require_scheduler.assertScope(active);
|
|
2412
3175
|
return next ? hasNext(key) : published.has(key);
|
|
2413
3176
|
},
|
|
2414
3177
|
ids: () => {
|
|
2415
|
-
|
|
3178
|
+
require_scheduler.assertScope(active);
|
|
2416
3179
|
return next ? Object.freeze(explicitOrder ? [...explicitOrder] : [...deriveIds()]) : published.ids();
|
|
2417
3180
|
}
|
|
2418
3181
|
});
|
|
@@ -2421,7 +3184,7 @@ const createCollectionOutput = () => {
|
|
|
2421
3184
|
next: read(true),
|
|
2422
3185
|
output: {
|
|
2423
3186
|
set: (key, value) => {
|
|
2424
|
-
|
|
3187
|
+
require_scheduler.assertScope(active);
|
|
2425
3188
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
2426
3189
|
staged.set(key, {
|
|
2427
3190
|
present: true,
|
|
@@ -2429,11 +3192,11 @@ const createCollectionOutput = () => {
|
|
|
2429
3192
|
});
|
|
2430
3193
|
},
|
|
2431
3194
|
remove: (key) => {
|
|
2432
|
-
|
|
3195
|
+
require_scheduler.assertScope(active);
|
|
2433
3196
|
staged.set(key, { present: false });
|
|
2434
3197
|
},
|
|
2435
3198
|
order: (order) => {
|
|
2436
|
-
|
|
3199
|
+
require_scheduler.assertScope(active);
|
|
2437
3200
|
explicitOrder = Object.freeze([...order]);
|
|
2438
3201
|
}
|
|
2439
3202
|
}
|
|
@@ -2443,7 +3206,7 @@ const createCollectionOutput = () => {
|
|
|
2443
3206
|
if (cleared) {
|
|
2444
3207
|
for (const key of published.keys()) if (!staged.has(key)) staged.set(key, { present: false });
|
|
2445
3208
|
}
|
|
2446
|
-
|
|
3209
|
+
require_scheduler.profile.projection("touchedKeys", staged.size);
|
|
2447
3210
|
const added = /* @__PURE__ */ new Set();
|
|
2448
3211
|
const removed = /* @__PURE__ */ new Set();
|
|
2449
3212
|
const updated = /* @__PURE__ */ new Set();
|
|
@@ -2462,12 +3225,11 @@ const createCollectionOutput = () => {
|
|
|
2462
3225
|
if (new Set(order).size !== order.length || order.length !== published.size() + added.size - removed.size || order.some((key) => !hasNext(key))) throw new TypeError("Projection order must contain every key exactly once.");
|
|
2463
3226
|
nextIds = ids.length === order.length && ids.every((key, index) => key === order[index]) ? ids : Object.freeze([...order]);
|
|
2464
3227
|
} else nextIds = ids;
|
|
2465
|
-
const changedKeys = new Set(
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
require_advanced.profile.projection("changedKeys", changedKeys.size);
|
|
3228
|
+
const changedKeys = /* @__PURE__ */ new Set();
|
|
3229
|
+
added.forEach((key) => changedKeys.add(key));
|
|
3230
|
+
removed.forEach((key) => changedKeys.add(key));
|
|
3231
|
+
updated.forEach((key) => changedKeys.add(key));
|
|
3232
|
+
require_scheduler.profile.projection("changedKeys", changedKeys.size);
|
|
2471
3233
|
const addedEntries = [];
|
|
2472
3234
|
const updatedEntries = [];
|
|
2473
3235
|
const removedEntries = [];
|
|
@@ -2475,7 +3237,8 @@ const createCollectionOutput = () => {
|
|
|
2475
3237
|
const beforePresent = published.has(key);
|
|
2476
3238
|
const afterPresent = hasNext(key);
|
|
2477
3239
|
if (beforePresent === afterPresent) {
|
|
2478
|
-
if (!beforePresent
|
|
3240
|
+
if (!beforePresent) continue;
|
|
3241
|
+
if (isEqual !== Object.is && isEqual(published.get(key), getNext(key))) continue;
|
|
2479
3242
|
}
|
|
2480
3243
|
if (!beforePresent) addedEntries.push({
|
|
2481
3244
|
key,
|
|
@@ -2505,15 +3268,15 @@ const createCollectionOutput = () => {
|
|
|
2505
3268
|
kind: "collection",
|
|
2506
3269
|
read: {
|
|
2507
3270
|
get: (key) => {
|
|
2508
|
-
|
|
3271
|
+
require_scheduler.assertScope(active);
|
|
2509
3272
|
return getNext(key);
|
|
2510
3273
|
},
|
|
2511
3274
|
has: (key) => {
|
|
2512
|
-
|
|
3275
|
+
require_scheduler.assertScope(active);
|
|
2513
3276
|
return hasNext(key);
|
|
2514
3277
|
},
|
|
2515
3278
|
ids: () => {
|
|
2516
|
-
|
|
3279
|
+
require_scheduler.assertScope(active);
|
|
2517
3280
|
return nextIds;
|
|
2518
3281
|
}
|
|
2519
3282
|
},
|
|
@@ -2523,10 +3286,14 @@ const createCollectionOutput = () => {
|
|
|
2523
3286
|
cause
|
|
2524
3287
|
});
|
|
2525
3288
|
return {
|
|
3289
|
+
kind: "collection",
|
|
3290
|
+
get owner() {
|
|
3291
|
+
return binding.owner();
|
|
3292
|
+
},
|
|
2526
3293
|
begin,
|
|
2527
3294
|
seal,
|
|
2528
|
-
context,
|
|
2529
|
-
current: (
|
|
3295
|
+
context: (active) => context(active, binding.cause()),
|
|
3296
|
+
current: () => published.read(binding.check),
|
|
2530
3297
|
revision: () => revision,
|
|
2531
3298
|
reset: () => reset,
|
|
2532
3299
|
publish: () => {
|
|
@@ -2535,9 +3302,12 @@ const createCollectionOutput = () => {
|
|
|
2535
3302
|
initialized = true;
|
|
2536
3303
|
},
|
|
2537
3304
|
emit: (call) => {
|
|
2538
|
-
|
|
2539
|
-
Array.from(listeners).forEach((listener) => call(() => listener(change ?? resetEvent)));
|
|
3305
|
+
Array.from(listeners).forEach((listener) => call(listener));
|
|
2540
3306
|
},
|
|
3307
|
+
hasConsumers: () => consumers.size > 0,
|
|
3308
|
+
forEachConsumer: (run) => consumers.forEach(run),
|
|
3309
|
+
attachConsumer: (consumer) => consumers.add(consumer),
|
|
3310
|
+
detachConsumer: (consumer) => consumers.delete(consumer),
|
|
2541
3311
|
clear: () => {
|
|
2542
3312
|
staged.clear();
|
|
2543
3313
|
nextIds = published.ids();
|
|
@@ -2550,6 +3320,7 @@ const createCollectionOutput = () => {
|
|
|
2550
3320
|
published.release();
|
|
2551
3321
|
staged.clear();
|
|
2552
3322
|
listeners.clear();
|
|
3323
|
+
consumers.clear();
|
|
2553
3324
|
nextIds = Object.freeze([]);
|
|
2554
3325
|
change = void 0;
|
|
2555
3326
|
initialized = false;
|
|
@@ -2558,15 +3329,18 @@ const createCollectionOutput = () => {
|
|
|
2558
3329
|
explicitOrder = void 0;
|
|
2559
3330
|
cleared = false;
|
|
2560
3331
|
},
|
|
2561
|
-
subscribe: (listener) =>
|
|
2562
|
-
|
|
3332
|
+
subscribe: (listener) => {
|
|
3333
|
+
binding.check();
|
|
3334
|
+
listeners.add(listener);
|
|
3335
|
+
return () => listeners.delete(listener);
|
|
3336
|
+
}
|
|
2563
3337
|
};
|
|
2564
3338
|
};
|
|
2565
3339
|
//#endregion
|
|
2566
3340
|
//#region core/src/projection/output/value.ts
|
|
2567
3341
|
const unset = Symbol("projection-value-unset");
|
|
2568
3342
|
/** Owns staged and published state for one scalar Projection output. */
|
|
2569
|
-
const createValueOutput = () => {
|
|
3343
|
+
const createValueOutput = (binding) => {
|
|
2570
3344
|
let value;
|
|
2571
3345
|
let staged = unset;
|
|
2572
3346
|
let next;
|
|
@@ -2575,6 +3349,7 @@ const createValueOutput = () => {
|
|
|
2575
3349
|
let changed = false;
|
|
2576
3350
|
let reset = false;
|
|
2577
3351
|
const listeners = /* @__PURE__ */ new Set();
|
|
3352
|
+
const consumers = /* @__PURE__ */ new Set();
|
|
2578
3353
|
const begin = (active, initialize) => {
|
|
2579
3354
|
staged = unset;
|
|
2580
3355
|
next = value;
|
|
@@ -2583,12 +3358,12 @@ const createValueOutput = () => {
|
|
|
2583
3358
|
return {
|
|
2584
3359
|
previous: initialized ? value : void 0,
|
|
2585
3360
|
next: () => {
|
|
2586
|
-
|
|
3361
|
+
require_scheduler.assertScope(active);
|
|
2587
3362
|
if (staged !== unset) return staged;
|
|
2588
3363
|
return initialize ? void 0 : initialized ? value : void 0;
|
|
2589
3364
|
},
|
|
2590
3365
|
output: Object.freeze({ set: (candidate) => {
|
|
2591
|
-
|
|
3366
|
+
require_scheduler.assertScope(active);
|
|
2592
3367
|
staged = candidate;
|
|
2593
3368
|
} })
|
|
2594
3369
|
};
|
|
@@ -2608,20 +3383,24 @@ const createValueOutput = () => {
|
|
|
2608
3383
|
return changed;
|
|
2609
3384
|
};
|
|
2610
3385
|
return {
|
|
3386
|
+
kind: "value",
|
|
3387
|
+
get owner() {
|
|
3388
|
+
return binding.owner();
|
|
3389
|
+
},
|
|
2611
3390
|
begin,
|
|
2612
3391
|
seal,
|
|
2613
|
-
context: (active
|
|
2614
|
-
|
|
3392
|
+
context: (active) => {
|
|
3393
|
+
require_scheduler.assertScope(active);
|
|
2615
3394
|
return Object.freeze({
|
|
2616
3395
|
kind: "value",
|
|
2617
3396
|
value: next,
|
|
2618
3397
|
revision,
|
|
2619
3398
|
reset,
|
|
2620
|
-
cause
|
|
3399
|
+
cause: binding.cause()
|
|
2621
3400
|
});
|
|
2622
3401
|
},
|
|
2623
|
-
current: (
|
|
2624
|
-
check();
|
|
3402
|
+
current: () => {
|
|
3403
|
+
binding.check();
|
|
2625
3404
|
return value;
|
|
2626
3405
|
},
|
|
2627
3406
|
revision: () => revision,
|
|
@@ -2632,9 +3411,13 @@ const createValueOutput = () => {
|
|
|
2632
3411
|
initialized = true;
|
|
2633
3412
|
},
|
|
2634
3413
|
emit: (call) => {
|
|
2635
|
-
|
|
3414
|
+
require_scheduler.profile.materialized.notification();
|
|
2636
3415
|
Array.from(listeners).forEach((listener) => call(listener));
|
|
2637
3416
|
},
|
|
3417
|
+
hasConsumers: () => consumers.size > 0,
|
|
3418
|
+
forEachConsumer: (run) => consumers.forEach(run),
|
|
3419
|
+
attachConsumer: (consumer) => consumers.add(consumer),
|
|
3420
|
+
detachConsumer: (consumer) => consumers.delete(consumer),
|
|
2638
3421
|
clear: () => {
|
|
2639
3422
|
staged = unset;
|
|
2640
3423
|
next = value;
|
|
@@ -2643,6 +3426,7 @@ const createValueOutput = () => {
|
|
|
2643
3426
|
},
|
|
2644
3427
|
release: () => {
|
|
2645
3428
|
listeners.clear();
|
|
3429
|
+
consumers.clear();
|
|
2646
3430
|
staged = unset;
|
|
2647
3431
|
value = next = void 0;
|
|
2648
3432
|
initialized = false;
|
|
@@ -2650,8 +3434,11 @@ const createValueOutput = () => {
|
|
|
2650
3434
|
changed = false;
|
|
2651
3435
|
reset = false;
|
|
2652
3436
|
},
|
|
2653
|
-
subscribe: (listener) =>
|
|
2654
|
-
|
|
3437
|
+
subscribe: (listener) => {
|
|
3438
|
+
binding.check();
|
|
3439
|
+
listeners.add(listener);
|
|
3440
|
+
return () => listeners.delete(listener);
|
|
3441
|
+
}
|
|
2655
3442
|
};
|
|
2656
3443
|
};
|
|
2657
3444
|
//#endregion
|
|
@@ -2663,63 +3450,27 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
2663
3450
|
let instance;
|
|
2664
3451
|
let cause;
|
|
2665
3452
|
const check = () => {
|
|
2666
|
-
if (!scheduler.active || processor.disposed) throw new
|
|
3453
|
+
if (!scheduler.active || processor.disposed) throw new require_scheduler.ProjectionDisposedError();
|
|
2667
3454
|
if (processor.fault) throw processor.fault;
|
|
2668
3455
|
};
|
|
2669
3456
|
const bound = definition.outputs.map((outputDefinition) => {
|
|
2670
|
-
if (outputDefinition.kind === "value") {
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
return processor;
|
|
2680
|
-
},
|
|
2681
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
2682
|
-
context: (active) => state.context(active, cause),
|
|
2683
|
-
current: () => state.current(check),
|
|
2684
|
-
revision: state.revision,
|
|
2685
|
-
reset: state.reset,
|
|
2686
|
-
subscribe: (listener) => {
|
|
2687
|
-
check();
|
|
2688
|
-
const wrapped = () => listener();
|
|
2689
|
-
state.subscribe(wrapped);
|
|
2690
|
-
return () => state.unsubscribe(wrapped);
|
|
2691
|
-
},
|
|
2692
|
-
emit: state.emit,
|
|
2693
|
-
clear: state.clear,
|
|
2694
|
-
release: state.release
|
|
2695
|
-
}
|
|
2696
|
-
};
|
|
2697
|
-
}
|
|
2698
|
-
const state = createCollectionOutput();
|
|
3457
|
+
if (outputDefinition.kind === "value") return {
|
|
3458
|
+
kind: "value",
|
|
3459
|
+
definition: outputDefinition,
|
|
3460
|
+
state: createValueOutput({
|
|
3461
|
+
owner: () => processor,
|
|
3462
|
+
check,
|
|
3463
|
+
cause: () => cause
|
|
3464
|
+
})
|
|
3465
|
+
};
|
|
2699
3466
|
return {
|
|
2700
3467
|
kind: "collection",
|
|
2701
3468
|
definition: outputDefinition,
|
|
2702
|
-
state
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
},
|
|
2708
|
-
consumers: /* @__PURE__ */ new Set(),
|
|
2709
|
-
context: (active) => state.context(active, cause),
|
|
2710
|
-
current: () => state.current(check),
|
|
2711
|
-
revision: state.revision,
|
|
2712
|
-
reset: state.reset,
|
|
2713
|
-
subscribe: (listener) => {
|
|
2714
|
-
check();
|
|
2715
|
-
const wrapped = (change) => listener(change);
|
|
2716
|
-
state.subscribe(wrapped);
|
|
2717
|
-
return () => state.unsubscribe(wrapped);
|
|
2718
|
-
},
|
|
2719
|
-
emit: state.emit,
|
|
2720
|
-
clear: state.clear,
|
|
2721
|
-
release: state.release
|
|
2722
|
-
}
|
|
3469
|
+
state: createCollectionOutput({
|
|
3470
|
+
owner: () => processor,
|
|
3471
|
+
check,
|
|
3472
|
+
cause: () => cause
|
|
3473
|
+
})
|
|
2723
3474
|
};
|
|
2724
3475
|
});
|
|
2725
3476
|
const begin = (active, initialize) => Object.freeze(bound.map((entry) => {
|
|
@@ -2738,40 +3489,28 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
2738
3489
|
}));
|
|
2739
3490
|
const seal = (reset) => {
|
|
2740
3491
|
const changed = [];
|
|
2741
|
-
for (const entry of bound) if (entry.state.seal(reset, entry.definition.equality)) changed.push(entry.
|
|
3492
|
+
for (const entry of bound) if (entry.state.seal(reset, entry.definition.equality)) changed.push(entry.state);
|
|
2742
3493
|
return Object.freeze(changed);
|
|
2743
3494
|
};
|
|
2744
|
-
const run = (reset, runCause) => {
|
|
3495
|
+
const run = (reset, runCause, recreate = false) => {
|
|
2745
3496
|
cause = runCause;
|
|
2746
3497
|
let active = true;
|
|
2747
3498
|
try {
|
|
2748
3499
|
const scopeActive = () => active;
|
|
2749
3500
|
const sources = Object.freeze(dependencies.map((output) => output.context(scopeActive)));
|
|
2750
|
-
|
|
3501
|
+
const outputs = begin(scopeActive, reset);
|
|
3502
|
+
if (recreate && instance) {
|
|
3503
|
+
instance.release?.();
|
|
3504
|
+
instance = void 0;
|
|
3505
|
+
}
|
|
2751
3506
|
if (!instance) instance = definition.create();
|
|
2752
|
-
|
|
2753
|
-
|
|
3507
|
+
require_scheduler.profile.materialized[reset ? "rebuilt" : "updated"]();
|
|
3508
|
+
require_scheduler.assertSynchronous(instance.evaluate({
|
|
2754
3509
|
reset,
|
|
2755
3510
|
cause: runCause,
|
|
2756
3511
|
sources,
|
|
2757
3512
|
outputs
|
|
2758
|
-
});
|
|
2759
|
-
require_advanced.assertSynchronous(result);
|
|
2760
|
-
if (result?.kind === "rebuild") {
|
|
2761
|
-
if (reset) throw new TypeError("Initial or reset projection processor cannot rebuild.");
|
|
2762
|
-
instance.release?.();
|
|
2763
|
-
instance = definition.create();
|
|
2764
|
-
outputs = begin(scopeActive, true);
|
|
2765
|
-
result = instance.evaluate({
|
|
2766
|
-
reset: true,
|
|
2767
|
-
cause: runCause,
|
|
2768
|
-
sources,
|
|
2769
|
-
outputs
|
|
2770
|
-
});
|
|
2771
|
-
require_advanced.assertSynchronous(result);
|
|
2772
|
-
if (result?.kind === "rebuild") throw new TypeError("Reset projection processor cannot rebuild again.");
|
|
2773
|
-
return seal(true);
|
|
2774
|
-
}
|
|
3513
|
+
}));
|
|
2775
3514
|
return seal(reset);
|
|
2776
3515
|
} finally {
|
|
2777
3516
|
active = false;
|
|
@@ -2783,7 +3522,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
2783
3522
|
order,
|
|
2784
3523
|
name: definition.name ? `${definition.name} (${order})` : `projection-${order}`,
|
|
2785
3524
|
dependencies: Object.freeze([...dependencies]),
|
|
2786
|
-
outputs: Object.freeze(bound.map((entry) => entry.
|
|
3525
|
+
outputs: Object.freeze(bound.map((entry) => entry.state)),
|
|
2787
3526
|
fault: void 0,
|
|
2788
3527
|
disposed: false,
|
|
2789
3528
|
evaluate: run,
|
|
@@ -2793,27 +3532,38 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
2793
3532
|
cause = void 0;
|
|
2794
3533
|
},
|
|
2795
3534
|
release: () => {
|
|
2796
|
-
instance
|
|
3535
|
+
const current = instance;
|
|
2797
3536
|
instance = void 0;
|
|
2798
|
-
|
|
2799
|
-
|
|
3537
|
+
const failures = [];
|
|
3538
|
+
try {
|
|
3539
|
+
current?.release?.();
|
|
3540
|
+
} catch (error) {
|
|
3541
|
+
failures.push(error);
|
|
3542
|
+
}
|
|
3543
|
+
for (const entry of bound) try {
|
|
2800
3544
|
entry.state.release();
|
|
2801
|
-
})
|
|
3545
|
+
} catch (error) {
|
|
3546
|
+
failures.push(error);
|
|
3547
|
+
}
|
|
2802
3548
|
cause = void 0;
|
|
3549
|
+
if (failures.length) throw failures[0];
|
|
2803
3550
|
}
|
|
2804
3551
|
};
|
|
2805
3552
|
try {
|
|
2806
3553
|
scheduler.initialize(() => {
|
|
2807
3554
|
const invalid = dependencies.find((output) => output.owner.fault || output.owner.disposed);
|
|
2808
|
-
if (invalid) throw invalid.owner.fault ?? new
|
|
3555
|
+
if (invalid) throw invalid.owner.fault ?? new require_scheduler.ProjectionDisposedError();
|
|
2809
3556
|
if (processor.evaluate(true, void 0).length) processor.publish();
|
|
2810
3557
|
processor.clear();
|
|
2811
3558
|
});
|
|
3559
|
+
scheduler.addProcessor(processor);
|
|
2812
3560
|
} catch (error) {
|
|
2813
|
-
processor.
|
|
3561
|
+
processor.disposed = true;
|
|
3562
|
+
try {
|
|
3563
|
+
processor.release();
|
|
3564
|
+
} catch {}
|
|
2814
3565
|
throw error;
|
|
2815
3566
|
}
|
|
2816
|
-
scheduler.addProcessor(processor);
|
|
2817
3567
|
return processor;
|
|
2818
3568
|
};
|
|
2819
3569
|
//#endregion
|
|
@@ -2979,57 +3729,11 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
2979
3729
|
};
|
|
2980
3730
|
//#endregion
|
|
2981
3731
|
//#region core/src/projection/source/materialization.ts
|
|
2982
|
-
const createDocumentDirty = () => ({
|
|
2983
|
-
replace: false,
|
|
2984
|
-
order: false,
|
|
2985
|
-
treeRoot: false,
|
|
2986
|
-
treeNodes: /* @__PURE__ */ new Set(),
|
|
2987
|
-
children: /* @__PURE__ */ new Map()
|
|
2988
|
-
});
|
|
2989
|
-
const clearDocumentDirty = (dirty) => {
|
|
2990
|
-
dirty.replace = false;
|
|
2991
|
-
dirty.order = false;
|
|
2992
|
-
dirty.treeRoot = false;
|
|
2993
|
-
dirty.treeNodes.clear();
|
|
2994
|
-
dirty.children.clear();
|
|
2995
|
-
};
|
|
2996
3732
|
const hasDocumentDirty = (dirty) => dirty.replace || dirty.order || dirty.treeRoot || dirty.treeNodes.size > 0 || dirty.children.size > 0;
|
|
2997
|
-
const
|
|
2998
|
-
|
|
2999
|
-
for (const key of path) {
|
|
3000
|
-
if (current.replace) return void 0;
|
|
3001
|
-
let child = current.children.get(key);
|
|
3002
|
-
if (!child) {
|
|
3003
|
-
child = createDocumentDirty();
|
|
3004
|
-
current.children.set(key, child);
|
|
3005
|
-
}
|
|
3006
|
-
current = child;
|
|
3007
|
-
}
|
|
3008
|
-
return current;
|
|
3009
|
-
};
|
|
3010
|
-
const markDocumentReplace = (dirty, path) => {
|
|
3011
|
-
const current = locate(dirty, path);
|
|
3012
|
-
if (!current) return;
|
|
3013
|
-
current.replace = true;
|
|
3014
|
-
current.order = false;
|
|
3015
|
-
current.treeRoot = false;
|
|
3016
|
-
current.treeNodes.clear();
|
|
3017
|
-
current.children.clear();
|
|
3018
|
-
};
|
|
3019
|
-
const markDocumentOrder = (dirty, path) => {
|
|
3020
|
-
const current = locate(dirty, path);
|
|
3021
|
-
if (current && !current.replace) current.order = true;
|
|
3022
|
-
};
|
|
3023
|
-
const markDocumentTree = (dirty, path, root, nodes) => {
|
|
3024
|
-
const current = locate(dirty, path);
|
|
3025
|
-
if (!current || current.replace) return;
|
|
3026
|
-
current.treeRoot ||= root;
|
|
3027
|
-
for (const id of nodes) current.treeNodes.add(id);
|
|
3028
|
-
};
|
|
3029
|
-
const equivalent = (node, left, right) => node.kind === "field" ? Object.is(left, right) : require_advanced.equalValue(node, left, right);
|
|
3030
|
-
const replaceValue = (node, previous, current) => equivalent(node, previous, current) ? previous : require_advanced.copyValue(node, current);
|
|
3733
|
+
const equivalent = (node, left, right) => node.kind === "field" ? Object.is(left, right) : equalValue(node, left, right);
|
|
3734
|
+
const replaceValue = (node, previous, current) => equivalent(node, previous, current) ? previous : copyValue(node, current);
|
|
3031
3735
|
const cloneRecord = (value) => {
|
|
3032
|
-
|
|
3736
|
+
require_scheduler.profile.copy.shallowRecord(Object.getOwnPropertyNames(value).length);
|
|
3033
3737
|
return Object.create(Object.getPrototypeOf(value), Object.getOwnPropertyDescriptors(value));
|
|
3034
3738
|
};
|
|
3035
3739
|
const materializeRecordChild = (node, previous, current, key, dirty) => {
|
|
@@ -3041,7 +3745,7 @@ const materializeRecordChild = (node, previous, current, key, dirty) => {
|
|
|
3041
3745
|
present: false,
|
|
3042
3746
|
value: void 0
|
|
3043
3747
|
};
|
|
3044
|
-
const next = before ? materializeDocumentValue(node, previous[key], current[key], dirty) :
|
|
3748
|
+
const next = before ? materializeDocumentValue(node, previous[key], current[key], dirty) : copyValue(node, current[key]);
|
|
3045
3749
|
if (before && next === previous[key]) return { changed: false };
|
|
3046
3750
|
return {
|
|
3047
3751
|
changed: true,
|
|
@@ -3051,7 +3755,7 @@ const materializeRecordChild = (node, previous, current, key, dirty) => {
|
|
|
3051
3755
|
};
|
|
3052
3756
|
const materializeObject = (node, previous, current, dirty) => {
|
|
3053
3757
|
if (node.kind === "variant" && previous[node.tag] !== current[node.tag]) return replaceValue(node, previous, current);
|
|
3054
|
-
const layout =
|
|
3758
|
+
const layout = compiledShape(node, current);
|
|
3055
3759
|
if (!layout) return replaceValue(node, previous, current);
|
|
3056
3760
|
let result;
|
|
3057
3761
|
for (const [key, childDirty] of dirty.children) {
|
|
@@ -3060,7 +3764,7 @@ const materializeObject = (node, previous, current, dirty) => {
|
|
|
3060
3764
|
const child = materializeRecordChild(member.node, previous, current, key, childDirty);
|
|
3061
3765
|
if (!child.changed) continue;
|
|
3062
3766
|
result ??= cloneRecord(previous);
|
|
3063
|
-
|
|
3767
|
+
require_record.installOwn(result, key, child.present, child.value);
|
|
3064
3768
|
}
|
|
3065
3769
|
return result ?? previous;
|
|
3066
3770
|
};
|
|
@@ -3070,12 +3774,12 @@ const materializeMap = (node, previous, current, dirty) => {
|
|
|
3070
3774
|
const child = materializeRecordChild(node.value, previous, current, key, childDirty);
|
|
3071
3775
|
if (!child.changed) continue;
|
|
3072
3776
|
result ??= cloneRecord(previous);
|
|
3073
|
-
|
|
3777
|
+
require_record.installOwn(result, key, child.present, child.value);
|
|
3074
3778
|
}
|
|
3075
3779
|
return result ?? previous;
|
|
3076
3780
|
};
|
|
3077
3781
|
const materializeTable = (node, previous, current, dirty) => {
|
|
3078
|
-
if (!
|
|
3782
|
+
if (!require_record.isRecord(previous.byId) || !require_record.isRecord(current.byId)) return replaceValue(node, previous, current);
|
|
3079
3783
|
const beforeById = previous.byId;
|
|
3080
3784
|
const afterById = current.byId;
|
|
3081
3785
|
let result;
|
|
@@ -3084,28 +3788,28 @@ const materializeTable = (node, previous, current, dirty) => {
|
|
|
3084
3788
|
const child = materializeRecordChild(node.value, beforeById, afterById, key, childDirty);
|
|
3085
3789
|
if (!child.changed) continue;
|
|
3086
3790
|
nextById ??= cloneRecord(beforeById);
|
|
3087
|
-
|
|
3791
|
+
require_record.installOwn(nextById, key, child.present, child.value);
|
|
3088
3792
|
}
|
|
3089
3793
|
if (nextById) {
|
|
3090
3794
|
result = cloneRecord(previous);
|
|
3091
|
-
|
|
3795
|
+
require_record.installOwn(result, "byId", true, nextById);
|
|
3092
3796
|
}
|
|
3093
3797
|
if (dirty.order) {
|
|
3094
3798
|
const beforeIds = previous.ids;
|
|
3095
3799
|
const afterIds = current.ids;
|
|
3096
3800
|
if (!Array.isArray(beforeIds) || !Array.isArray(afterIds)) return replaceValue(node, previous, current);
|
|
3097
|
-
if (!
|
|
3801
|
+
if (!equal(beforeIds, afterIds)) {
|
|
3098
3802
|
result ??= cloneRecord(previous);
|
|
3099
|
-
|
|
3803
|
+
require_record.installOwn(result, "ids", true, [...afterIds]);
|
|
3100
3804
|
}
|
|
3101
3805
|
}
|
|
3102
3806
|
return result ?? previous;
|
|
3103
3807
|
};
|
|
3104
3808
|
const materializeList = (node, previous, current, dirty) => {
|
|
3105
3809
|
if (dirty.order) {
|
|
3106
|
-
const before =
|
|
3107
|
-
const after =
|
|
3108
|
-
let changed = !
|
|
3810
|
+
const before = listSequence(previous, node.keyOf);
|
|
3811
|
+
const after = listSequence(current, node.keyOf);
|
|
3812
|
+
let changed = !equal(before.order, after.order);
|
|
3109
3813
|
const result = new Array(after.order.length);
|
|
3110
3814
|
for (let index = 0; index < after.order.length; index++) {
|
|
3111
3815
|
const key = after.order[index];
|
|
@@ -3124,8 +3828,8 @@ const materializeList = (node, previous, current, dirty) => {
|
|
|
3124
3828
|
return changed ? result : previous;
|
|
3125
3829
|
}
|
|
3126
3830
|
let result;
|
|
3127
|
-
const before =
|
|
3128
|
-
const after =
|
|
3831
|
+
const before = indexedKeys(previous, node.keyOf);
|
|
3832
|
+
const after = indexedKeys(current, node.keyOf);
|
|
3129
3833
|
for (const [key, childDirty] of dirty.children) {
|
|
3130
3834
|
const previousIndex = before.index(key);
|
|
3131
3835
|
const currentIndex = after.index(key);
|
|
@@ -3138,14 +3842,14 @@ const materializeList = (node, previous, current, dirty) => {
|
|
|
3138
3842
|
return result ?? previous;
|
|
3139
3843
|
};
|
|
3140
3844
|
const materializeTree = (node, previous, current, dirty) => {
|
|
3141
|
-
if (!
|
|
3845
|
+
if (!require_record.isRecord(previous.nodes) || !require_record.isRecord(current.nodes)) return replaceValue(node, previous, current);
|
|
3142
3846
|
let result;
|
|
3143
3847
|
if (dirty.treeRoot) {
|
|
3144
3848
|
const beforePresent = Object.hasOwn(previous, "rootId");
|
|
3145
3849
|
const afterPresent = Object.hasOwn(current, "rootId");
|
|
3146
3850
|
if (beforePresent !== afterPresent || !Object.is(previous.rootId, current.rootId)) {
|
|
3147
3851
|
result = cloneRecord(previous);
|
|
3148
|
-
|
|
3852
|
+
require_record.installOwn(result, "rootId", afterPresent, current.rootId);
|
|
3149
3853
|
}
|
|
3150
3854
|
}
|
|
3151
3855
|
let nodes;
|
|
@@ -3154,13 +3858,13 @@ const materializeTree = (node, previous, current, dirty) => {
|
|
|
3154
3858
|
const afterPresent = Object.hasOwn(current.nodes, id);
|
|
3155
3859
|
const before = beforePresent ? previous.nodes[id] : void 0;
|
|
3156
3860
|
const after = afterPresent ? current.nodes[id] : void 0;
|
|
3157
|
-
if (beforePresent === afterPresent &&
|
|
3861
|
+
if (beforePresent === afterPresent && equalTreeNode(before, after)) continue;
|
|
3158
3862
|
nodes ??= cloneRecord(previous.nodes);
|
|
3159
|
-
|
|
3863
|
+
require_record.installOwn(nodes, id, afterPresent, after && copyTreeNode(after));
|
|
3160
3864
|
}
|
|
3161
3865
|
if (nodes) {
|
|
3162
3866
|
result ??= cloneRecord(previous);
|
|
3163
|
-
|
|
3867
|
+
require_record.installOwn(result, "nodes", true, nodes);
|
|
3164
3868
|
}
|
|
3165
3869
|
return result ?? previous;
|
|
3166
3870
|
};
|
|
@@ -3168,26 +3872,72 @@ const materializeDocumentValue = (node, previous, current, dirty) => {
|
|
|
3168
3872
|
if (!hasDocumentDirty(dirty)) return previous;
|
|
3169
3873
|
if (dirty.replace || previous === void 0 || current === void 0 || node.kind === "field") return replaceValue(node, previous, current);
|
|
3170
3874
|
if (node.kind === "object" || node.kind === "variant") {
|
|
3171
|
-
if (!
|
|
3875
|
+
if (!require_record.isRecord(previous) || !require_record.isRecord(current)) return replaceValue(node, previous, current);
|
|
3172
3876
|
return materializeObject(node, previous, current, dirty);
|
|
3173
3877
|
}
|
|
3174
3878
|
if (node.kind === "map") {
|
|
3175
|
-
if (!
|
|
3879
|
+
if (!require_record.isRecord(previous) || !require_record.isRecord(current)) return replaceValue(node, previous, current);
|
|
3176
3880
|
return materializeMap(node, previous, current, dirty);
|
|
3177
3881
|
}
|
|
3178
3882
|
if (node.kind === "table") {
|
|
3179
|
-
if (!
|
|
3883
|
+
if (!require_record.isRecord(previous) || !require_record.isRecord(current)) return replaceValue(node, previous, current);
|
|
3180
3884
|
return materializeTable(node, previous, current, dirty);
|
|
3181
3885
|
}
|
|
3182
3886
|
if (node.kind === "list") {
|
|
3183
3887
|
if (!Array.isArray(previous) || !Array.isArray(current)) return replaceValue(node, previous, current);
|
|
3184
3888
|
return materializeList(node, previous, current, dirty);
|
|
3185
3889
|
}
|
|
3186
|
-
if (!
|
|
3890
|
+
if (!require_record.isRecord(previous) || !require_record.isRecord(current)) return replaceValue(node, previous, current);
|
|
3187
3891
|
return materializeTree(node, previous, current, dirty);
|
|
3188
3892
|
};
|
|
3189
3893
|
//#endregion
|
|
3190
3894
|
//#region core/src/projection/source/dirty.ts
|
|
3895
|
+
const createDocumentDirty = () => ({
|
|
3896
|
+
replace: false,
|
|
3897
|
+
order: false,
|
|
3898
|
+
treeRoot: false,
|
|
3899
|
+
treeNodes: /* @__PURE__ */ new Set(),
|
|
3900
|
+
children: /* @__PURE__ */ new Map()
|
|
3901
|
+
});
|
|
3902
|
+
const clearDocumentDirty = (dirty) => {
|
|
3903
|
+
dirty.replace = false;
|
|
3904
|
+
dirty.order = false;
|
|
3905
|
+
dirty.treeRoot = false;
|
|
3906
|
+
dirty.treeNodes.clear();
|
|
3907
|
+
dirty.children.clear();
|
|
3908
|
+
};
|
|
3909
|
+
const locate = (dirty, path) => {
|
|
3910
|
+
let current = dirty;
|
|
3911
|
+
for (const key of path) {
|
|
3912
|
+
if (current.replace) return void 0;
|
|
3913
|
+
let child = current.children.get(key);
|
|
3914
|
+
if (!child) {
|
|
3915
|
+
child = createDocumentDirty();
|
|
3916
|
+
current.children.set(key, child);
|
|
3917
|
+
}
|
|
3918
|
+
current = child;
|
|
3919
|
+
}
|
|
3920
|
+
return current;
|
|
3921
|
+
};
|
|
3922
|
+
const markDocumentReplace = (dirty, path) => {
|
|
3923
|
+
const current = locate(dirty, path);
|
|
3924
|
+
if (!current) return;
|
|
3925
|
+
current.replace = true;
|
|
3926
|
+
current.order = false;
|
|
3927
|
+
current.treeRoot = false;
|
|
3928
|
+
current.treeNodes.clear();
|
|
3929
|
+
current.children.clear();
|
|
3930
|
+
};
|
|
3931
|
+
const markDocumentOrder = (dirty, path) => {
|
|
3932
|
+
const current = locate(dirty, path);
|
|
3933
|
+
if (current && !current.replace) current.order = true;
|
|
3934
|
+
};
|
|
3935
|
+
const markDocumentTree = (dirty, path, root, nodes) => {
|
|
3936
|
+
const current = locate(dirty, path);
|
|
3937
|
+
if (!current || current.replace) return;
|
|
3938
|
+
current.treeRoot ||= root;
|
|
3939
|
+
for (const id of nodes) current.treeNodes.add(id);
|
|
3940
|
+
};
|
|
3191
3941
|
const createCollectionPending = () => ({
|
|
3192
3942
|
reset: false,
|
|
3193
3943
|
order: false,
|
|
@@ -3289,9 +4039,8 @@ const mergeCollectionChanges = (selector, pending, changes) => {
|
|
|
3289
4039
|
};
|
|
3290
4040
|
//#endregion
|
|
3291
4041
|
//#region core/src/projection/source/boundary.ts
|
|
3292
|
-
const sourceError = (
|
|
4042
|
+
const sourceError = (cause) => new require_scheduler.ProjectionError("source", cause);
|
|
3293
4043
|
const createValueBoundary = (scheduler, name, initial, equality, prepare, clearSource) => {
|
|
3294
|
-
const state = createValueOutput();
|
|
3295
4044
|
let boundary;
|
|
3296
4045
|
let pendingReset = false;
|
|
3297
4046
|
let pendingCause;
|
|
@@ -3299,29 +4048,15 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
|
|
|
3299
4048
|
let recovering = false;
|
|
3300
4049
|
let cleanup;
|
|
3301
4050
|
const check = () => {
|
|
3302
|
-
if (!scheduler.active || boundary.disposed) throw new
|
|
4051
|
+
if (!scheduler.active || boundary.disposed) throw new require_scheduler.ProjectionDisposedError();
|
|
3303
4052
|
if (boundary.fault) throw boundary.fault;
|
|
3304
4053
|
};
|
|
3305
|
-
const
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
context: (active) => state.context(active, contextCause),
|
|
3312
|
-
current: () => state.current(check),
|
|
3313
|
-
revision: state.revision,
|
|
3314
|
-
reset: state.reset,
|
|
3315
|
-
subscribe: (listener) => {
|
|
3316
|
-
check();
|
|
3317
|
-
const wrapped = () => listener();
|
|
3318
|
-
state.subscribe(wrapped);
|
|
3319
|
-
return () => state.unsubscribe(wrapped);
|
|
3320
|
-
},
|
|
3321
|
-
emit: state.emit,
|
|
3322
|
-
clear: state.clear,
|
|
3323
|
-
release: state.release
|
|
3324
|
-
};
|
|
4054
|
+
const state = createValueOutput({
|
|
4055
|
+
owner: () => boundary,
|
|
4056
|
+
check,
|
|
4057
|
+
cause: () => contextCause
|
|
4058
|
+
});
|
|
4059
|
+
const output = state;
|
|
3325
4060
|
boundary = {
|
|
3326
4061
|
kind: "source",
|
|
3327
4062
|
name,
|
|
@@ -3357,24 +4092,42 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
|
|
|
3357
4092
|
clearSource(failed);
|
|
3358
4093
|
},
|
|
3359
4094
|
release: () => {
|
|
3360
|
-
cleanup
|
|
4095
|
+
const detach = cleanup;
|
|
3361
4096
|
cleanup = void 0;
|
|
3362
|
-
|
|
3363
|
-
|
|
4097
|
+
const failures = [];
|
|
4098
|
+
try {
|
|
4099
|
+
detach?.();
|
|
4100
|
+
} catch (error) {
|
|
4101
|
+
failures.push(error);
|
|
4102
|
+
}
|
|
4103
|
+
try {
|
|
4104
|
+
state.release();
|
|
4105
|
+
} catch (error) {
|
|
4106
|
+
failures.push(error);
|
|
4107
|
+
}
|
|
4108
|
+
if (failures.length) throw failures[0];
|
|
3364
4109
|
}
|
|
3365
4110
|
};
|
|
3366
|
-
|
|
3367
|
-
|
|
4111
|
+
try {
|
|
4112
|
+
scheduler.initialize(() => {
|
|
4113
|
+
let active = true;
|
|
4114
|
+
try {
|
|
4115
|
+
state.begin(() => active, true).output.set(initial);
|
|
4116
|
+
state.seal(true, equality);
|
|
4117
|
+
state.publish();
|
|
4118
|
+
state.clear();
|
|
4119
|
+
} finally {
|
|
4120
|
+
active = false;
|
|
4121
|
+
}
|
|
4122
|
+
});
|
|
4123
|
+
scheduler.addSource(boundary);
|
|
4124
|
+
} catch (error) {
|
|
4125
|
+
boundary.disposed = true;
|
|
3368
4126
|
try {
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
} finally {
|
|
3374
|
-
active = false;
|
|
3375
|
-
}
|
|
3376
|
-
});
|
|
3377
|
-
scheduler.addSource(boundary);
|
|
4127
|
+
boundary.release();
|
|
4128
|
+
} catch {}
|
|
4129
|
+
throw error;
|
|
4130
|
+
}
|
|
3378
4131
|
const mark = (metadata = {}) => {
|
|
3379
4132
|
scheduler.assertIdle();
|
|
3380
4133
|
pendingReset ||= metadata.reset ?? false;
|
|
@@ -3383,7 +4136,7 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
|
|
|
3383
4136
|
scheduler.capture(boundary);
|
|
3384
4137
|
};
|
|
3385
4138
|
const fail = (cause) => {
|
|
3386
|
-
boundary.fault = sourceError(
|
|
4139
|
+
boundary.fault = sourceError(cause);
|
|
3387
4140
|
recovering = false;
|
|
3388
4141
|
scheduler.capture(boundary);
|
|
3389
4142
|
};
|
|
@@ -3402,30 +4155,29 @@ const stageCollectionRead = (state, read, active, options) => {
|
|
|
3402
4155
|
const evaluation = state.begin(active, options.reset);
|
|
3403
4156
|
if (options.reset) {
|
|
3404
4157
|
const ids = read.ids();
|
|
3405
|
-
|
|
4158
|
+
require_scheduler.profile.collectionView.idsScanned(ids.length);
|
|
3406
4159
|
for (const key of ids) {
|
|
3407
|
-
|
|
4160
|
+
require_scheduler.profile.collectionView.mapped();
|
|
3408
4161
|
evaluation.output.set(key, read.get(key));
|
|
3409
4162
|
}
|
|
3410
4163
|
evaluation.output.order(ids);
|
|
3411
4164
|
} else {
|
|
3412
|
-
const previous = state.current(
|
|
4165
|
+
const previous = state.current();
|
|
3413
4166
|
const keys = options.candidates ? [...new Set(options.candidates)] : [...new Set([...previous.ids(), ...read.ids()])];
|
|
3414
|
-
if (!options.candidates)
|
|
4167
|
+
if (!options.candidates) require_scheduler.profile.collectionView.idsScanned(keys.length);
|
|
3415
4168
|
for (const key of keys) if (read.has(key)) {
|
|
3416
|
-
|
|
4169
|
+
require_scheduler.profile.collectionView.mapped();
|
|
3417
4170
|
evaluation.output.set(key, read.get(key));
|
|
3418
4171
|
} else evaluation.output.remove(key);
|
|
3419
4172
|
if (options.orderMayChange || !options.candidates) {
|
|
3420
4173
|
const ids = read.ids();
|
|
3421
|
-
if (options.candidates)
|
|
4174
|
+
if (options.candidates) require_scheduler.profile.collectionView.idsScanned(ids.length);
|
|
3422
4175
|
evaluation.output.order(ids);
|
|
3423
4176
|
}
|
|
3424
4177
|
}
|
|
3425
4178
|
return state.seal(options.reset, options.isEqual ?? Object.is);
|
|
3426
4179
|
};
|
|
3427
4180
|
const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSource) => {
|
|
3428
|
-
const state = createCollectionOutput();
|
|
3429
4181
|
let boundary;
|
|
3430
4182
|
let pendingReset = false;
|
|
3431
4183
|
let pendingCause;
|
|
@@ -3436,29 +4188,15 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
3436
4188
|
const candidates = /* @__PURE__ */ new Set();
|
|
3437
4189
|
let cleanup;
|
|
3438
4190
|
const check = () => {
|
|
3439
|
-
if (!scheduler.active || boundary.disposed) throw new
|
|
4191
|
+
if (!scheduler.active || boundary.disposed) throw new require_scheduler.ProjectionDisposedError();
|
|
3440
4192
|
if (boundary.fault) throw boundary.fault;
|
|
3441
4193
|
};
|
|
3442
|
-
const
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
context: (active) => state.context(active, contextCause),
|
|
3449
|
-
current: () => state.current(check),
|
|
3450
|
-
revision: state.revision,
|
|
3451
|
-
reset: state.reset,
|
|
3452
|
-
subscribe: (listener) => {
|
|
3453
|
-
check();
|
|
3454
|
-
const wrapped = (change) => listener(change);
|
|
3455
|
-
state.subscribe(wrapped);
|
|
3456
|
-
return () => state.unsubscribe(wrapped);
|
|
3457
|
-
},
|
|
3458
|
-
emit: state.emit,
|
|
3459
|
-
clear: state.clear,
|
|
3460
|
-
release: state.release
|
|
3461
|
-
};
|
|
4194
|
+
const state = createCollectionOutput({
|
|
4195
|
+
owner: () => boundary,
|
|
4196
|
+
check,
|
|
4197
|
+
cause: () => contextCause
|
|
4198
|
+
});
|
|
4199
|
+
const output = state;
|
|
3462
4200
|
boundary = {
|
|
3463
4201
|
kind: "source",
|
|
3464
4202
|
name,
|
|
@@ -3471,7 +4209,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
3471
4209
|
contextCause = cause;
|
|
3472
4210
|
let active = true;
|
|
3473
4211
|
try {
|
|
3474
|
-
const changed = stageCollectionRead(state, readLatest(() => active, state.current(
|
|
4212
|
+
const changed = stageCollectionRead(state, readLatest(() => active, state.current()), () => active, {
|
|
3475
4213
|
reset: pendingReset,
|
|
3476
4214
|
...fullScan ? {} : { candidates },
|
|
3477
4215
|
orderMayChange,
|
|
@@ -3500,26 +4238,44 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
3500
4238
|
clearSource(failed);
|
|
3501
4239
|
},
|
|
3502
4240
|
release: () => {
|
|
3503
|
-
cleanup
|
|
4241
|
+
const detach = cleanup;
|
|
3504
4242
|
cleanup = void 0;
|
|
3505
|
-
|
|
3506
|
-
|
|
4243
|
+
const failures = [];
|
|
4244
|
+
try {
|
|
4245
|
+
detach?.();
|
|
4246
|
+
} catch (error) {
|
|
4247
|
+
failures.push(error);
|
|
4248
|
+
}
|
|
4249
|
+
try {
|
|
4250
|
+
state.release();
|
|
4251
|
+
} catch (error) {
|
|
4252
|
+
failures.push(error);
|
|
4253
|
+
}
|
|
4254
|
+
if (failures.length) throw failures[0];
|
|
3507
4255
|
}
|
|
3508
4256
|
};
|
|
3509
|
-
|
|
3510
|
-
|
|
4257
|
+
try {
|
|
4258
|
+
scheduler.initialize(() => {
|
|
4259
|
+
let active = true;
|
|
4260
|
+
try {
|
|
4261
|
+
stageCollectionRead(state, readLatest(() => active, state.current()), () => active, {
|
|
4262
|
+
reset: true,
|
|
4263
|
+
isEqual: equality
|
|
4264
|
+
});
|
|
4265
|
+
state.publish();
|
|
4266
|
+
state.clear();
|
|
4267
|
+
} finally {
|
|
4268
|
+
active = false;
|
|
4269
|
+
}
|
|
4270
|
+
});
|
|
4271
|
+
scheduler.addSource(boundary);
|
|
4272
|
+
} catch (error) {
|
|
4273
|
+
boundary.disposed = true;
|
|
3511
4274
|
try {
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
state.publish();
|
|
3517
|
-
state.clear();
|
|
3518
|
-
} finally {
|
|
3519
|
-
active = false;
|
|
3520
|
-
}
|
|
3521
|
-
});
|
|
3522
|
-
scheduler.addSource(boundary);
|
|
4275
|
+
boundary.release();
|
|
4276
|
+
} catch {}
|
|
4277
|
+
throw error;
|
|
4278
|
+
}
|
|
3523
4279
|
const mark = (metadata = {}) => {
|
|
3524
4280
|
scheduler.assertIdle();
|
|
3525
4281
|
pendingReset ||= metadata.reset ?? false;
|
|
@@ -3531,7 +4287,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
3531
4287
|
scheduler.capture(boundary);
|
|
3532
4288
|
};
|
|
3533
4289
|
const fail = (cause) => {
|
|
3534
|
-
boundary.fault = sourceError(
|
|
4290
|
+
boundary.fault = sourceError(cause);
|
|
3535
4291
|
recovering = false;
|
|
3536
4292
|
scheduler.capture(boundary);
|
|
3537
4293
|
};
|
|
@@ -3554,7 +4310,7 @@ const documentRootTarget = Object.freeze({
|
|
|
3554
4310
|
const createDocumentSourceRegistry = (scheduler) => {
|
|
3555
4311
|
const documents = /* @__PURE__ */ new Map();
|
|
3556
4312
|
const connection = (runtime) => {
|
|
3557
|
-
const context =
|
|
4313
|
+
const context = require_driver.contextOf(runtime);
|
|
3558
4314
|
const state = context.state;
|
|
3559
4315
|
const cached = documents.get(state);
|
|
3560
4316
|
if (cached) return cached;
|
|
@@ -3565,14 +4321,19 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
3565
4321
|
const guard = (locked) => {
|
|
3566
4322
|
state.projectionLocks += locked ? 1 : -1;
|
|
3567
4323
|
};
|
|
3568
|
-
scheduler.
|
|
4324
|
+
let unregisterGuard = scheduler.registerGuard(guard);
|
|
3569
4325
|
let closed = false;
|
|
3570
|
-
let detach
|
|
4326
|
+
let detach;
|
|
3571
4327
|
const documentConnection = {
|
|
3572
4328
|
add(binding) {
|
|
3573
4329
|
if (closed) throw new Error("Document projection connection is closed.");
|
|
3574
4330
|
bindings.add(binding);
|
|
3575
|
-
|
|
4331
|
+
try {
|
|
4332
|
+
index.add(binding.target, binding);
|
|
4333
|
+
} catch (error) {
|
|
4334
|
+
bindings.delete(binding);
|
|
4335
|
+
throw error;
|
|
4336
|
+
}
|
|
3576
4337
|
let active = true;
|
|
3577
4338
|
return () => {
|
|
3578
4339
|
if (!active) return;
|
|
@@ -3585,34 +4346,65 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
3585
4346
|
close() {
|
|
3586
4347
|
if (closed) return;
|
|
3587
4348
|
closed = true;
|
|
3588
|
-
detach
|
|
4349
|
+
const stopNotifications = detach;
|
|
4350
|
+
detach = void 0;
|
|
4351
|
+
const stopGuard = unregisterGuard;
|
|
4352
|
+
unregisterGuard = () => void 0;
|
|
3589
4353
|
bindings.clear();
|
|
3590
4354
|
candidates.clear();
|
|
3591
4355
|
index.clear();
|
|
3592
|
-
scheduler.guards.delete(guard);
|
|
3593
4356
|
documents.delete(state);
|
|
4357
|
+
const failures = [];
|
|
4358
|
+
try {
|
|
4359
|
+
stopNotifications?.();
|
|
4360
|
+
} catch (error) {
|
|
4361
|
+
failures.push(error);
|
|
4362
|
+
}
|
|
4363
|
+
try {
|
|
4364
|
+
stopGuard();
|
|
4365
|
+
} catch (error) {
|
|
4366
|
+
failures.push(error);
|
|
4367
|
+
}
|
|
4368
|
+
if (failures.length) throw failures[0];
|
|
3594
4369
|
}
|
|
3595
4370
|
};
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
4371
|
+
try {
|
|
4372
|
+
detach = context.notifications.attachProjection({
|
|
4373
|
+
capture: (commit) => {
|
|
4374
|
+
candidates.clear();
|
|
4375
|
+
index.collect(commit.changes, (binding) => candidates.add(binding));
|
|
4376
|
+
candidates.forEach((binding) => binding.capture(commit));
|
|
4377
|
+
},
|
|
4378
|
+
settle: scheduler.settle,
|
|
4379
|
+
flush: scheduler.flush,
|
|
4380
|
+
dispose: () => {
|
|
4381
|
+
const failures = [];
|
|
4382
|
+
for (const binding of [...bindings]) try {
|
|
4383
|
+
binding.dispose();
|
|
4384
|
+
} catch (error) {
|
|
4385
|
+
failures.push(error);
|
|
4386
|
+
}
|
|
4387
|
+
try {
|
|
4388
|
+
scheduler.run();
|
|
4389
|
+
} catch (error) {
|
|
4390
|
+
failures.push(error);
|
|
4391
|
+
}
|
|
4392
|
+
if (failures.length) throw failures[0];
|
|
4393
|
+
}
|
|
4394
|
+
});
|
|
4395
|
+
} catch (error) {
|
|
4396
|
+
try {
|
|
4397
|
+
documentConnection.close();
|
|
4398
|
+
} catch {}
|
|
4399
|
+
throw error;
|
|
4400
|
+
}
|
|
3609
4401
|
documents.set(state, documentConnection);
|
|
3610
4402
|
return documentConnection;
|
|
3611
4403
|
};
|
|
3612
4404
|
const materialize = (definition) => {
|
|
3613
4405
|
if (definition.source.kind !== "document") throw new Error("Invalid document source.");
|
|
3614
4406
|
const { document, selector } = definition.source;
|
|
3615
|
-
const state =
|
|
4407
|
+
const state = require_driver.contextOf(document).state;
|
|
3616
4408
|
const current = () => resolveValue(state.schema, state.document, selector.address);
|
|
3617
4409
|
const disposed = () => {
|
|
3618
4410
|
if (state.disposed) throw new Error("Document has been disposed.");
|
|
@@ -3624,15 +4416,15 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
3624
4416
|
const resolved = current();
|
|
3625
4417
|
const node = resolved?.node;
|
|
3626
4418
|
const value = resolved?.value;
|
|
3627
|
-
if (selector.tree?.kind === "root") return node?.kind === "tree" &&
|
|
4419
|
+
if (selector.tree?.kind === "root") return node?.kind === "tree" && require_record.isRecord(value) && typeof value.rootId === "string" ? value.rootId : void 0;
|
|
3628
4420
|
if (selector.tree?.kind === "node") {
|
|
3629
|
-
if (node?.kind !== "tree" || !
|
|
4421
|
+
if (node?.kind !== "tree" || !require_record.isRecord(value) || !require_record.isRecord(value.nodes)) return void 0;
|
|
3630
4422
|
const entry = Object.hasOwn(value.nodes, selector.tree.id) ? value.nodes[selector.tree.id] : void 0;
|
|
3631
|
-
if (
|
|
3632
|
-
return entry ?
|
|
4423
|
+
if (equalTreeNode(previous, entry)) return previous;
|
|
4424
|
+
return entry ? copyTreeNode(entry) : void 0;
|
|
3633
4425
|
}
|
|
3634
4426
|
if (!node) return void 0;
|
|
3635
|
-
return previous === void 0 ?
|
|
4427
|
+
return previous === void 0 ? copyValue(node, value) : materializeDocumentValue(node, previous, value, dirty);
|
|
3636
4428
|
};
|
|
3637
4429
|
const initial = read();
|
|
3638
4430
|
let boundary;
|
|
@@ -3640,18 +4432,26 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
3640
4432
|
if (failed) markDocumentReplace(dirty, []);
|
|
3641
4433
|
else clearDocumentDirty(dirty);
|
|
3642
4434
|
});
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
4435
|
+
let remove;
|
|
4436
|
+
try {
|
|
4437
|
+
remove = connection(document).add({
|
|
4438
|
+
target: selector,
|
|
4439
|
+
capture: (commit) => {
|
|
4440
|
+
try {
|
|
4441
|
+
mergeValueChanges(selector, dirty, commit.changes);
|
|
4442
|
+
boundary.mark({ reset: commit.changes.changes.some((change) => change.kind === "reset") });
|
|
4443
|
+
} catch (cause) {
|
|
4444
|
+
boundary.fail(cause);
|
|
4445
|
+
}
|
|
4446
|
+
},
|
|
4447
|
+
dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
|
|
4448
|
+
});
|
|
4449
|
+
} catch (error) {
|
|
4450
|
+
try {
|
|
4451
|
+
scheduler.releaseProducer(boundary.producer);
|
|
4452
|
+
} catch {}
|
|
4453
|
+
throw error;
|
|
4454
|
+
}
|
|
3655
4455
|
boundary.detach(remove);
|
|
3656
4456
|
return boundary;
|
|
3657
4457
|
}
|
|
@@ -3662,50 +4462,50 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
3662
4462
|
const node = resolved?.node;
|
|
3663
4463
|
const value = resolved?.value;
|
|
3664
4464
|
const treeNodes = selector.tree?.kind === "nodes";
|
|
3665
|
-
const entryNode = !treeNodes && node ?
|
|
4465
|
+
const entryNode = !treeNodes && node ? collectionEntryNode(node) : void 0;
|
|
3666
4466
|
const snapshots = /* @__PURE__ */ new Map();
|
|
3667
4467
|
const replacement = createDocumentDirty();
|
|
3668
4468
|
if (pending.reset) markDocumentReplace(replacement, []);
|
|
3669
4469
|
const ids = () => {
|
|
3670
|
-
|
|
3671
|
-
if (treeNodes) return node?.kind === "tree" &&
|
|
3672
|
-
if (node?.kind === "map" &&
|
|
3673
|
-
if (node?.kind === "table" &&
|
|
3674
|
-
if (node?.kind === "list" && Array.isArray(value)) return Object.freeze(
|
|
4470
|
+
require_scheduler.assertScope(active);
|
|
4471
|
+
if (treeNodes) return node?.kind === "tree" && require_record.isRecord(value) && require_record.isRecord(value.nodes) ? Object.freeze(Object.keys(value.nodes)) : Object.freeze([]);
|
|
4472
|
+
if (node?.kind === "map" && require_record.isRecord(value)) return Object.freeze(Object.keys(value));
|
|
4473
|
+
if (node?.kind === "table" && require_record.isRecord(value) && Array.isArray(value.ids)) return Object.freeze([...value.ids]);
|
|
4474
|
+
if (node?.kind === "list" && Array.isArray(value)) return Object.freeze(toArray(indexedKeys(value, node.keyOf)));
|
|
3675
4475
|
return Object.freeze([]);
|
|
3676
4476
|
};
|
|
3677
4477
|
const has = (key) => {
|
|
3678
|
-
|
|
3679
|
-
if (treeNodes) return Boolean(node?.kind === "tree" &&
|
|
3680
|
-
if (node?.kind === "map" &&
|
|
3681
|
-
if (node?.kind === "table" &&
|
|
3682
|
-
if (node?.kind === "list" && Array.isArray(value)) return
|
|
4478
|
+
require_scheduler.assertScope(active);
|
|
4479
|
+
if (treeNodes) return Boolean(node?.kind === "tree" && require_record.isRecord(value) && require_record.isRecord(value.nodes) && Object.hasOwn(value.nodes, key));
|
|
4480
|
+
if (node?.kind === "map" && require_record.isRecord(value)) return Object.hasOwn(value, key);
|
|
4481
|
+
if (node?.kind === "table" && require_record.isRecord(value) && require_record.isRecord(value.byId)) return Object.hasOwn(value.byId, key);
|
|
4482
|
+
if (node?.kind === "list" && Array.isArray(value)) return indexedKeys(value, node.keyOf).index(key) >= 0;
|
|
3683
4483
|
return false;
|
|
3684
4484
|
};
|
|
3685
4485
|
const raw = (key) => {
|
|
3686
4486
|
if (treeNodes) {
|
|
3687
|
-
if (node?.kind !== "tree" || !
|
|
4487
|
+
if (node?.kind !== "tree" || !require_record.isRecord(value) || !require_record.isRecord(value.nodes)) return void 0;
|
|
3688
4488
|
return Object.hasOwn(value.nodes, key) ? value.nodes[key] : void 0;
|
|
3689
4489
|
}
|
|
3690
|
-
if (node?.kind === "map" &&
|
|
3691
|
-
if (node?.kind === "table" &&
|
|
4490
|
+
if (node?.kind === "map" && require_record.isRecord(value)) return Object.hasOwn(value, key) ? value[key] : void 0;
|
|
4491
|
+
if (node?.kind === "table" && require_record.isRecord(value) && require_record.isRecord(value.byId)) return Object.hasOwn(value.byId, key) ? value.byId[key] : void 0;
|
|
3692
4492
|
if (node?.kind === "list" && Array.isArray(value)) {
|
|
3693
|
-
const index =
|
|
4493
|
+
const index = indexedKeys(value, node.keyOf).index(key);
|
|
3694
4494
|
return index < 0 ? void 0 : value[index];
|
|
3695
4495
|
}
|
|
3696
4496
|
};
|
|
3697
4497
|
return Object.freeze({
|
|
3698
4498
|
get: (key) => {
|
|
3699
|
-
|
|
4499
|
+
require_scheduler.assertScope(active);
|
|
3700
4500
|
if (!snapshots.has(key)) if (!has(key)) snapshots.set(key, void 0);
|
|
3701
4501
|
else {
|
|
3702
4502
|
const current = raw(key);
|
|
3703
4503
|
if (treeNodes) {
|
|
3704
4504
|
const entry = current;
|
|
3705
4505
|
const before = previous.has(key) ? previous.get(key) : void 0;
|
|
3706
|
-
snapshots.set(key,
|
|
4506
|
+
snapshots.set(key, equalTreeNode(before, entry) ? before : copyTreeNode(entry));
|
|
3707
4507
|
} else if (!entryNode) snapshots.set(key, current);
|
|
3708
|
-
else if (!previous.has(key)) snapshots.set(key,
|
|
4508
|
+
else if (!previous.has(key)) snapshots.set(key, copyValue(entryNode, current));
|
|
3709
4509
|
else {
|
|
3710
4510
|
const dirty = pending.entries.get(key) ?? (pending.reset ? replacement : void 0);
|
|
3711
4511
|
snapshots.set(key, dirty ? materializeDocumentValue(entryNode, previous.get(key), current, dirty) : previous.get(key));
|
|
@@ -3722,22 +4522,30 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
3722
4522
|
if (failed) resetCollectionPending(pending);
|
|
3723
4523
|
else clearCollectionPending(pending);
|
|
3724
4524
|
});
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
4525
|
+
let remove;
|
|
4526
|
+
try {
|
|
4527
|
+
remove = connection(document).add({
|
|
4528
|
+
target: selector,
|
|
4529
|
+
capture: (commit) => {
|
|
4530
|
+
try {
|
|
4531
|
+
mergeCollectionChanges(selector, pending, commit.changes);
|
|
4532
|
+
boundary.mark({
|
|
4533
|
+
reset: pending.reset,
|
|
4534
|
+
...pending.reset ? {} : { candidates: pending.entries.keys() },
|
|
4535
|
+
orderMayChange: pending.order
|
|
4536
|
+
});
|
|
4537
|
+
} catch (cause) {
|
|
4538
|
+
boundary.fail(cause);
|
|
4539
|
+
}
|
|
4540
|
+
},
|
|
4541
|
+
dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
|
|
4542
|
+
});
|
|
4543
|
+
} catch (error) {
|
|
4544
|
+
try {
|
|
4545
|
+
scheduler.releaseProducer(boundary.producer);
|
|
4546
|
+
} catch {}
|
|
4547
|
+
throw error;
|
|
4548
|
+
}
|
|
3741
4549
|
boundary.detach(remove);
|
|
3742
4550
|
return boundary;
|
|
3743
4551
|
};
|
|
@@ -3751,115 +4559,23 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
3751
4559
|
});
|
|
3752
4560
|
},
|
|
3753
4561
|
dispose() {
|
|
3754
|
-
|
|
4562
|
+
const failures = [];
|
|
4563
|
+
for (const document of [...documents.values()]) try {
|
|
4564
|
+
document.close();
|
|
4565
|
+
} catch (error) {
|
|
4566
|
+
failures.push(error);
|
|
4567
|
+
}
|
|
3755
4568
|
documents.clear();
|
|
4569
|
+
if (failures.length) throw failures[0];
|
|
3756
4570
|
}
|
|
3757
4571
|
};
|
|
3758
4572
|
};
|
|
3759
4573
|
//#endregion
|
|
3760
|
-
//#region core/src/projection/source/
|
|
3761
|
-
const
|
|
3762
|
-
const documents = createDocumentSourceRegistry(scheduler);
|
|
4574
|
+
//#region core/src/projection/source/external.ts
|
|
4575
|
+
const createExternalSourceRegistry = (scheduler, documents) => {
|
|
3763
4576
|
const readables = /* @__PURE__ */ new Map();
|
|
3764
|
-
const externalValues = /* @__PURE__ */ new
|
|
3765
|
-
const externalCollections = /* @__PURE__ */ new
|
|
3766
|
-
const valueInput = (definition) => {
|
|
3767
|
-
if (definition.source.kind !== "value-input") throw new Error("Invalid value input source.");
|
|
3768
|
-
const source = definition.source;
|
|
3769
|
-
let latest = source.initial;
|
|
3770
|
-
const boundary = createValueBoundary(scheduler, "projection input", source.initial, source.equality, () => latest, () => void 0);
|
|
3771
|
-
return {
|
|
3772
|
-
...boundary,
|
|
3773
|
-
write: {
|
|
3774
|
-
kind: "value",
|
|
3775
|
-
set(value) {
|
|
3776
|
-
scheduler.assertIdle();
|
|
3777
|
-
latest = value;
|
|
3778
|
-
boundary.mark();
|
|
3779
|
-
scheduler.run();
|
|
3780
|
-
}
|
|
3781
|
-
}
|
|
3782
|
-
};
|
|
3783
|
-
};
|
|
3784
|
-
const collectionInput = (definition) => {
|
|
3785
|
-
if (definition.source.kind !== "collection-input") throw new Error("Invalid collection input source.");
|
|
3786
|
-
const values = new Map(definition.source.initial);
|
|
3787
|
-
const boundary = createCollectionBoundary(scheduler, "projection collection input", () => require_advanced.mapRead(values), definition.output.equality, () => void 0);
|
|
3788
|
-
return {
|
|
3789
|
-
...boundary,
|
|
3790
|
-
write: {
|
|
3791
|
-
kind: "collection",
|
|
3792
|
-
update(run) {
|
|
3793
|
-
scheduler.assertIdle();
|
|
3794
|
-
const staged = /* @__PURE__ */ new Map();
|
|
3795
|
-
const operations = [];
|
|
3796
|
-
let active = true;
|
|
3797
|
-
const draft = Object.freeze({
|
|
3798
|
-
get: (key) => {
|
|
3799
|
-
require_advanced.assertScope(() => active);
|
|
3800
|
-
const entry = staged.get(key);
|
|
3801
|
-
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
3802
|
-
},
|
|
3803
|
-
has: (key) => {
|
|
3804
|
-
require_advanced.assertScope(() => active);
|
|
3805
|
-
const entry = staged.get(key);
|
|
3806
|
-
return entry ? entry.present : values.has(key);
|
|
3807
|
-
},
|
|
3808
|
-
set: (key, value) => {
|
|
3809
|
-
require_advanced.assertScope(() => active);
|
|
3810
|
-
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
3811
|
-
staged.set(key, {
|
|
3812
|
-
present: true,
|
|
3813
|
-
value
|
|
3814
|
-
});
|
|
3815
|
-
operations.push({
|
|
3816
|
-
kind: "set",
|
|
3817
|
-
key,
|
|
3818
|
-
value
|
|
3819
|
-
});
|
|
3820
|
-
},
|
|
3821
|
-
remove: (key) => {
|
|
3822
|
-
require_advanced.assertScope(() => active);
|
|
3823
|
-
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
3824
|
-
staged.set(key, { present: false });
|
|
3825
|
-
operations.push({
|
|
3826
|
-
kind: "remove",
|
|
3827
|
-
key
|
|
3828
|
-
});
|
|
3829
|
-
}
|
|
3830
|
-
});
|
|
3831
|
-
try {
|
|
3832
|
-
require_advanced.assertSynchronous(run(draft));
|
|
3833
|
-
} finally {
|
|
3834
|
-
active = false;
|
|
3835
|
-
}
|
|
3836
|
-
const candidates = /* @__PURE__ */ new Set();
|
|
3837
|
-
let structural = false;
|
|
3838
|
-
let changed = false;
|
|
3839
|
-
for (const operation of operations) {
|
|
3840
|
-
const present = values.has(operation.key);
|
|
3841
|
-
if (operation.kind === "set") {
|
|
3842
|
-
if (present && Object.is(values.get(operation.key), operation.value)) continue;
|
|
3843
|
-
structural ||= !present;
|
|
3844
|
-
values.set(operation.key, operation.value);
|
|
3845
|
-
} else {
|
|
3846
|
-
if (!present) continue;
|
|
3847
|
-
structural = true;
|
|
3848
|
-
values.delete(operation.key);
|
|
3849
|
-
}
|
|
3850
|
-
candidates.add(operation.key);
|
|
3851
|
-
changed = true;
|
|
3852
|
-
}
|
|
3853
|
-
if (!changed) return;
|
|
3854
|
-
boundary.mark({
|
|
3855
|
-
candidates,
|
|
3856
|
-
orderMayChange: structural
|
|
3857
|
-
});
|
|
3858
|
-
scheduler.run();
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
|
-
};
|
|
3862
|
-
};
|
|
4577
|
+
const externalValues = /* @__PURE__ */ new Map();
|
|
4578
|
+
const externalCollections = /* @__PURE__ */ new Map();
|
|
3863
4579
|
const readable = (definition) => {
|
|
3864
4580
|
if (definition.source.kind !== "readable") throw new Error("Invalid Readable source.");
|
|
3865
4581
|
const { readable, equality } = definition.source;
|
|
@@ -3893,19 +4609,44 @@ const createSourceRegistry = (scheduler) => {
|
|
|
3893
4609
|
close() {
|
|
3894
4610
|
if (closed) return;
|
|
3895
4611
|
closed = true;
|
|
3896
|
-
unsubscribe
|
|
3897
|
-
|
|
4612
|
+
const stopReadable = unsubscribe;
|
|
4613
|
+
unsubscribe = void 0;
|
|
4614
|
+
const stopDocument = detachDocument;
|
|
4615
|
+
detachDocument = void 0;
|
|
3898
4616
|
readables.delete(readable);
|
|
4617
|
+
members.clear();
|
|
4618
|
+
const failures = [];
|
|
4619
|
+
try {
|
|
4620
|
+
stopReadable?.();
|
|
4621
|
+
} catch (error) {
|
|
4622
|
+
failures.push(error);
|
|
4623
|
+
}
|
|
4624
|
+
try {
|
|
4625
|
+
stopDocument?.();
|
|
4626
|
+
} catch (error) {
|
|
4627
|
+
failures.push(error);
|
|
4628
|
+
}
|
|
4629
|
+
if (failures.length) throw failures[0];
|
|
3899
4630
|
}
|
|
3900
4631
|
};
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
4632
|
+
try {
|
|
4633
|
+
const owner = require_driver.contextOf(readable, false)?.owner;
|
|
4634
|
+
if (owner) detachDocument = documents.attachRoot(owner, {
|
|
4635
|
+
capture: () => receive(false),
|
|
4636
|
+
dispose: () => {
|
|
4637
|
+
members.forEach((next) => next.boundary.fail(/* @__PURE__ */ new Error("Document has been disposed.")));
|
|
4638
|
+
}
|
|
4639
|
+
});
|
|
4640
|
+
unsubscribe = readable.subscribe(() => receive(true));
|
|
4641
|
+
} catch (error) {
|
|
4642
|
+
try {
|
|
4643
|
+
created.close();
|
|
4644
|
+
} catch {}
|
|
4645
|
+
try {
|
|
4646
|
+
scheduler.releaseProducer(boundary.producer);
|
|
4647
|
+
} catch {}
|
|
4648
|
+
throw error;
|
|
4649
|
+
}
|
|
3909
4650
|
connection = created;
|
|
3910
4651
|
readables.set(readable, connection);
|
|
3911
4652
|
}
|
|
@@ -3938,8 +4679,11 @@ const createSourceRegistry = (scheduler) => {
|
|
|
3938
4679
|
close() {
|
|
3939
4680
|
if (closed) return;
|
|
3940
4681
|
closed = true;
|
|
3941
|
-
unsubscribe
|
|
4682
|
+
const stop = unsubscribe;
|
|
4683
|
+
unsubscribe = void 0;
|
|
3942
4684
|
externalValues.delete(source);
|
|
4685
|
+
members.clear();
|
|
4686
|
+
stop?.();
|
|
3943
4687
|
}
|
|
3944
4688
|
};
|
|
3945
4689
|
try {
|
|
@@ -3954,9 +4698,14 @@ const createSourceRegistry = (scheduler) => {
|
|
|
3954
4698
|
}
|
|
3955
4699
|
scheduler.run();
|
|
3956
4700
|
});
|
|
3957
|
-
} catch (
|
|
3958
|
-
|
|
3959
|
-
|
|
4701
|
+
} catch (error) {
|
|
4702
|
+
try {
|
|
4703
|
+
created.close();
|
|
4704
|
+
} catch {}
|
|
4705
|
+
try {
|
|
4706
|
+
scheduler.releaseProducer(boundary.producer);
|
|
4707
|
+
} catch {}
|
|
4708
|
+
throw error;
|
|
3960
4709
|
}
|
|
3961
4710
|
connection = created;
|
|
3962
4711
|
externalValues.set(source, connection);
|
|
@@ -3989,8 +4738,11 @@ const createSourceRegistry = (scheduler) => {
|
|
|
3989
4738
|
close() {
|
|
3990
4739
|
if (closed) return;
|
|
3991
4740
|
closed = true;
|
|
3992
|
-
unsubscribe
|
|
4741
|
+
const stop = unsubscribe;
|
|
4742
|
+
unsubscribe = void 0;
|
|
3993
4743
|
externalCollections.delete(source);
|
|
4744
|
+
members.clear();
|
|
4745
|
+
stop?.();
|
|
3994
4746
|
}
|
|
3995
4747
|
};
|
|
3996
4748
|
try {
|
|
@@ -4022,9 +4774,14 @@ const createSourceRegistry = (scheduler) => {
|
|
|
4022
4774
|
}
|
|
4023
4775
|
scheduler.run();
|
|
4024
4776
|
});
|
|
4025
|
-
} catch (
|
|
4026
|
-
|
|
4027
|
-
|
|
4777
|
+
} catch (error) {
|
|
4778
|
+
try {
|
|
4779
|
+
created.close();
|
|
4780
|
+
} catch {}
|
|
4781
|
+
try {
|
|
4782
|
+
scheduler.releaseProducer(boundary.producer);
|
|
4783
|
+
} catch {}
|
|
4784
|
+
throw error;
|
|
4028
4785
|
}
|
|
4029
4786
|
connection = created;
|
|
4030
4787
|
externalCollections.set(source, connection);
|
|
@@ -4039,25 +4796,173 @@ const createSourceRegistry = (scheduler) => {
|
|
|
4039
4796
|
return {
|
|
4040
4797
|
materialize(definition) {
|
|
4041
4798
|
switch (definition.source.kind) {
|
|
4042
|
-
case "value-input": return valueInput(definition);
|
|
4043
|
-
case "collection-input": return collectionInput(definition);
|
|
4044
|
-
case "document": return documents.materialize(definition);
|
|
4045
4799
|
case "readable": return readable(definition);
|
|
4046
4800
|
case "external-value": return externalValue(definition);
|
|
4047
4801
|
case "external-collection": return externalCollection(definition);
|
|
4802
|
+
default: throw new Error("Invalid external projection source.");
|
|
4048
4803
|
}
|
|
4049
4804
|
},
|
|
4050
4805
|
dispose() {
|
|
4051
|
-
|
|
4052
|
-
|
|
4806
|
+
const failures = [];
|
|
4807
|
+
const connections = new Set([
|
|
4808
|
+
...readables.values(),
|
|
4809
|
+
...externalValues.values(),
|
|
4810
|
+
...externalCollections.values()
|
|
4811
|
+
]);
|
|
4812
|
+
for (const connection of connections) try {
|
|
4813
|
+
connection.close();
|
|
4814
|
+
} catch (error) {
|
|
4815
|
+
failures.push(error);
|
|
4816
|
+
}
|
|
4053
4817
|
readables.clear();
|
|
4818
|
+
externalValues.clear();
|
|
4819
|
+
externalCollections.clear();
|
|
4820
|
+
if (failures.length) throw failures[0];
|
|
4821
|
+
}
|
|
4822
|
+
};
|
|
4823
|
+
};
|
|
4824
|
+
//#endregion
|
|
4825
|
+
//#region core/src/projection/source/input.ts
|
|
4826
|
+
const materializeInputSource = (scheduler, definition) => {
|
|
4827
|
+
const source = definition.source;
|
|
4828
|
+
if (source.kind === "value-input") {
|
|
4829
|
+
let latest = source.initial;
|
|
4830
|
+
const boundary = createValueBoundary(scheduler, "projection input", source.initial, source.equality, () => latest, () => void 0);
|
|
4831
|
+
return {
|
|
4832
|
+
...boundary,
|
|
4833
|
+
write: {
|
|
4834
|
+
kind: "value",
|
|
4835
|
+
set(value) {
|
|
4836
|
+
scheduler.assertIdle();
|
|
4837
|
+
latest = value;
|
|
4838
|
+
boundary.mark();
|
|
4839
|
+
scheduler.run();
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
};
|
|
4843
|
+
}
|
|
4844
|
+
if (source.kind !== "collection-input") throw new Error("Invalid projection input source.");
|
|
4845
|
+
const values = new Map(source.initial);
|
|
4846
|
+
const boundary = createCollectionBoundary(scheduler, "projection collection input", () => require_scheduler.mapRead(values), definition.output.equality, () => void 0);
|
|
4847
|
+
return {
|
|
4848
|
+
...boundary,
|
|
4849
|
+
write: {
|
|
4850
|
+
kind: "collection",
|
|
4851
|
+
update(run) {
|
|
4852
|
+
scheduler.assertIdle();
|
|
4853
|
+
const staged = /* @__PURE__ */ new Map();
|
|
4854
|
+
const operations = [];
|
|
4855
|
+
let active = true;
|
|
4856
|
+
const draft = Object.freeze({
|
|
4857
|
+
get: (key) => {
|
|
4858
|
+
require_scheduler.assertScope(() => active);
|
|
4859
|
+
const entry = staged.get(key);
|
|
4860
|
+
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
4861
|
+
},
|
|
4862
|
+
has: (key) => {
|
|
4863
|
+
require_scheduler.assertScope(() => active);
|
|
4864
|
+
const entry = staged.get(key);
|
|
4865
|
+
return entry ? entry.present : values.has(key);
|
|
4866
|
+
},
|
|
4867
|
+
set: (key, value) => {
|
|
4868
|
+
require_scheduler.assertScope(() => active);
|
|
4869
|
+
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
4870
|
+
staged.set(key, {
|
|
4871
|
+
present: true,
|
|
4872
|
+
value
|
|
4873
|
+
});
|
|
4874
|
+
operations.push({
|
|
4875
|
+
kind: "set",
|
|
4876
|
+
key,
|
|
4877
|
+
value
|
|
4878
|
+
});
|
|
4879
|
+
},
|
|
4880
|
+
remove: (key) => {
|
|
4881
|
+
require_scheduler.assertScope(() => active);
|
|
4882
|
+
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
4883
|
+
staged.set(key, { present: false });
|
|
4884
|
+
operations.push({
|
|
4885
|
+
kind: "remove",
|
|
4886
|
+
key
|
|
4887
|
+
});
|
|
4888
|
+
}
|
|
4889
|
+
});
|
|
4890
|
+
try {
|
|
4891
|
+
require_scheduler.assertSynchronous(run(draft));
|
|
4892
|
+
} finally {
|
|
4893
|
+
active = false;
|
|
4894
|
+
}
|
|
4895
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
4896
|
+
let structural = false;
|
|
4897
|
+
const accepted = [];
|
|
4898
|
+
const overlay = /* @__PURE__ */ new Map();
|
|
4899
|
+
for (const operation of operations) {
|
|
4900
|
+
const overlayValue = overlay.get(operation.key);
|
|
4901
|
+
const present = overlayValue ? overlayValue.present : values.has(operation.key);
|
|
4902
|
+
if (operation.kind === "set") {
|
|
4903
|
+
const previous = overlayValue ? overlayValue.present ? overlayValue.value : void 0 : values.get(operation.key);
|
|
4904
|
+
if (present && source.equality(previous, operation.value)) continue;
|
|
4905
|
+
structural ||= !present;
|
|
4906
|
+
overlay.set(operation.key, {
|
|
4907
|
+
present: true,
|
|
4908
|
+
value: operation.value
|
|
4909
|
+
});
|
|
4910
|
+
} else {
|
|
4911
|
+
if (!present) continue;
|
|
4912
|
+
structural = true;
|
|
4913
|
+
overlay.set(operation.key, { present: false });
|
|
4914
|
+
}
|
|
4915
|
+
accepted.push(operation);
|
|
4916
|
+
candidates.add(operation.key);
|
|
4917
|
+
}
|
|
4918
|
+
if (!accepted.length) return;
|
|
4919
|
+
for (const operation of accepted) if (operation.kind === "set") values.set(operation.key, operation.value);
|
|
4920
|
+
else values.delete(operation.key);
|
|
4921
|
+
boundary.mark({
|
|
4922
|
+
candidates,
|
|
4923
|
+
orderMayChange: structural
|
|
4924
|
+
});
|
|
4925
|
+
scheduler.run();
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
};
|
|
4929
|
+
};
|
|
4930
|
+
//#endregion
|
|
4931
|
+
//#region core/src/projection/source/registry.ts
|
|
4932
|
+
const createSourceRegistry = (scheduler) => {
|
|
4933
|
+
const documents = createDocumentSourceRegistry(scheduler);
|
|
4934
|
+
const external = createExternalSourceRegistry(scheduler, documents);
|
|
4935
|
+
return {
|
|
4936
|
+
materialize(definition) {
|
|
4937
|
+
switch (definition.source.kind) {
|
|
4938
|
+
case "value-input":
|
|
4939
|
+
case "collection-input": return materializeInputSource(scheduler, definition);
|
|
4940
|
+
case "document": return documents.materialize(definition);
|
|
4941
|
+
case "readable":
|
|
4942
|
+
case "external-value":
|
|
4943
|
+
case "external-collection": return external.materialize(definition);
|
|
4944
|
+
}
|
|
4945
|
+
},
|
|
4946
|
+
dispose() {
|
|
4947
|
+
const failures = [];
|
|
4948
|
+
try {
|
|
4949
|
+
external.dispose();
|
|
4950
|
+
} catch (error) {
|
|
4951
|
+
failures.push(error);
|
|
4952
|
+
}
|
|
4953
|
+
try {
|
|
4954
|
+
documents.dispose();
|
|
4955
|
+
} catch (error) {
|
|
4956
|
+
failures.push(error);
|
|
4957
|
+
}
|
|
4958
|
+
if (failures.length) throw failures[0];
|
|
4054
4959
|
}
|
|
4055
4960
|
};
|
|
4056
4961
|
};
|
|
4057
4962
|
//#endregion
|
|
4058
4963
|
//#region core/src/projection/runtime.ts
|
|
4059
4964
|
const createProjectionRuntime = (options) => {
|
|
4060
|
-
const scheduler =
|
|
4965
|
+
const scheduler = require_scheduler.createScheduler(options?.onError ?? (() => void 0));
|
|
4061
4966
|
const sources = createSourceRegistry(scheduler);
|
|
4062
4967
|
const materialized = /* @__PURE__ */ new WeakMap();
|
|
4063
4968
|
const publicViews = /* @__PURE__ */ new WeakMap();
|
|
@@ -4067,7 +4972,7 @@ const createProjectionRuntime = (options) => {
|
|
|
4067
4972
|
const owner = definition.owner;
|
|
4068
4973
|
if (owner === void 0) return;
|
|
4069
4974
|
if (owner !== requester) throw new TypeError("Projection belongs to another scope.");
|
|
4070
|
-
if (!requester?.active) throw new
|
|
4975
|
+
if (!requester?.active) throw new require_scheduler.ProjectionDisposedError();
|
|
4071
4976
|
};
|
|
4072
4977
|
const rememberScoped = (definition, requester) => {
|
|
4073
4978
|
if (requester && definition.owner === requester) requester.materialized.push(definition);
|
|
@@ -4085,7 +4990,7 @@ const createProjectionRuntime = (options) => {
|
|
|
4085
4990
|
...source.write ? { write: source.write } : {}
|
|
4086
4991
|
});
|
|
4087
4992
|
} else {
|
|
4088
|
-
if (definition.owner === void 0 && definition.dependencies.some((dependency) =>
|
|
4993
|
+
if (definition.owner === void 0 && definition.dependencies.some((dependency) => require_scheduler.producerOf(dependency).owner !== void 0)) throw new TypeError("A root projection cannot depend on a scoped projection.");
|
|
4089
4994
|
const dependencies = definition.dependencies.map((dependency) => resolveOutput(dependency, requester));
|
|
4090
4995
|
const processor = createProcessor(scheduler, definition, Object.freeze(dependencies));
|
|
4091
4996
|
result = Object.freeze({
|
|
@@ -4093,19 +4998,20 @@ const createProjectionRuntime = (options) => {
|
|
|
4093
4998
|
outputs: processor.outputs
|
|
4094
4999
|
});
|
|
4095
5000
|
}
|
|
5001
|
+
definition.materialized = true;
|
|
4096
5002
|
materialized.set(definition, result);
|
|
4097
5003
|
rememberScoped(definition, requester);
|
|
4098
5004
|
return result;
|
|
4099
5005
|
};
|
|
4100
5006
|
const resolveOutput = (projection, requester) => {
|
|
4101
|
-
const ref =
|
|
5007
|
+
const ref = require_scheduler.projectionRef(projection);
|
|
4102
5008
|
const output = materializeProducer(ref.producer, requester).outputs[ref.output];
|
|
4103
5009
|
if (!output) throw new TypeError("Projection output does not exist.");
|
|
4104
5010
|
return output;
|
|
4105
5011
|
};
|
|
4106
5012
|
const assertOutput = (output) => {
|
|
4107
5013
|
scheduler.assertActive();
|
|
4108
|
-
if (output.owner.disposed) throw new
|
|
5014
|
+
if (output.owner.disposed) throw new require_scheduler.ProjectionDisposedError();
|
|
4109
5015
|
if (output.owner.fault) throw output.owner.fault;
|
|
4110
5016
|
};
|
|
4111
5017
|
const publicCurrent = (output) => {
|
|
@@ -4115,7 +5021,7 @@ const createProjectionRuntime = (options) => {
|
|
|
4115
5021
|
const cached = publicViews.get(output);
|
|
4116
5022
|
if (cached?.revision === revision) return cached.value;
|
|
4117
5023
|
if (output.kind === "collection") {
|
|
4118
|
-
const value =
|
|
5024
|
+
const value = require_scheduler.collectionView(current);
|
|
4119
5025
|
publicViews.set(output, {
|
|
4120
5026
|
revision,
|
|
4121
5027
|
value
|
|
@@ -4123,7 +5029,7 @@ const createProjectionRuntime = (options) => {
|
|
|
4123
5029
|
return value;
|
|
4124
5030
|
}
|
|
4125
5031
|
if (isMapLike(current)) {
|
|
4126
|
-
const value =
|
|
5032
|
+
const value = require_scheduler.snapshotCollectionView(require_scheduler.mapRead(current));
|
|
4127
5033
|
publicViews.set(output, {
|
|
4128
5034
|
revision,
|
|
4129
5035
|
value
|
|
@@ -4133,12 +5039,12 @@ const createProjectionRuntime = (options) => {
|
|
|
4133
5039
|
publicViews.delete(output);
|
|
4134
5040
|
return current;
|
|
4135
5041
|
};
|
|
4136
|
-
const
|
|
5042
|
+
const readProjection = (projection, requester) => publicCurrent(resolveOutput(projection, requester));
|
|
4137
5043
|
const readableSource = (projection, requester) => {
|
|
4138
5044
|
const output = resolveOutput(projection, requester);
|
|
4139
5045
|
return Object.freeze({
|
|
4140
5046
|
kind: output.kind,
|
|
4141
|
-
current: () =>
|
|
5047
|
+
current: () => readProjection(projection, requester),
|
|
4142
5048
|
revision: () => {
|
|
4143
5049
|
assertOutput(output);
|
|
4144
5050
|
return output.revision();
|
|
@@ -4150,45 +5056,57 @@ const createProjectionRuntime = (options) => {
|
|
|
4150
5056
|
const source = readableSource(projection, requester);
|
|
4151
5057
|
return selector ? createSelectorReadable(source, selector, equality) : createDirectReadable(source);
|
|
4152
5058
|
};
|
|
4153
|
-
function
|
|
5059
|
+
function selectProjection(projection, selector, equality) {
|
|
4154
5060
|
return makeReadable(projection, selector, equality);
|
|
4155
5061
|
}
|
|
4156
5062
|
const inputWrite = (target, requester) => {
|
|
4157
|
-
const ref =
|
|
5063
|
+
const ref = require_scheduler.projectionRef(target);
|
|
4158
5064
|
if (ref.output !== 0 || ref.producer.kind !== "source") throw new TypeError("Projection is not an input.");
|
|
4159
5065
|
if (ref.producer.source.kind !== "value-input" && ref.producer.source.kind !== "collection-input") throw new TypeError("Projection is not an input.");
|
|
4160
5066
|
const write = materializeProducer(ref.producer, requester).write;
|
|
4161
5067
|
if (!write) throw new TypeError("Projection input is not writable.");
|
|
4162
5068
|
return write;
|
|
4163
5069
|
};
|
|
4164
|
-
const
|
|
4165
|
-
const write = inputWrite(target, requester);
|
|
4166
|
-
if (write.kind !== "value") throw new TypeError("Projection is not a value input.");
|
|
4167
|
-
write.set(value);
|
|
4168
|
-
};
|
|
4169
|
-
const updateInput = (target, run, requester) => {
|
|
5070
|
+
const updateInput = (target, valueOrRun, requester) => {
|
|
4170
5071
|
const write = inputWrite(target, requester);
|
|
4171
|
-
if (write.kind
|
|
4172
|
-
|
|
5072
|
+
if (write.kind === "value") {
|
|
5073
|
+
write.set(valueOrRun);
|
|
5074
|
+
return;
|
|
5075
|
+
}
|
|
5076
|
+
if (typeof valueOrRun !== "function") throw new TypeError("Collection input update requires a draft callback.");
|
|
5077
|
+
write.update(valueOrRun);
|
|
4173
5078
|
};
|
|
4174
|
-
function
|
|
4175
|
-
|
|
5079
|
+
function update(target, valueOrRun) {
|
|
5080
|
+
updateInput(target, valueOrRun);
|
|
4176
5081
|
}
|
|
5082
|
+
const batch = (run, options) => scheduler.batch(run, options);
|
|
4177
5083
|
const disposeScope = (state) => {
|
|
4178
5084
|
if (!state.active) return;
|
|
4179
5085
|
scheduler.assertIdle();
|
|
4180
5086
|
state.active = false;
|
|
4181
|
-
|
|
5087
|
+
const failures = [];
|
|
5088
|
+
const subscriptions = [...state.subscriptions];
|
|
4182
5089
|
state.subscriptions.clear();
|
|
5090
|
+
for (const unsubscribe of subscriptions) try {
|
|
5091
|
+
unsubscribe();
|
|
5092
|
+
} catch (error) {
|
|
5093
|
+
failures.push(error);
|
|
5094
|
+
}
|
|
4183
5095
|
for (let index = state.materialized.length - 1; index >= 0; index--) {
|
|
4184
5096
|
const definition = state.materialized[index];
|
|
4185
5097
|
const current = materialized.get(definition);
|
|
4186
5098
|
if (!current) continue;
|
|
4187
|
-
|
|
4188
|
-
|
|
5099
|
+
try {
|
|
5100
|
+
scheduler.releaseProducer(current.record);
|
|
5101
|
+
} catch (error) {
|
|
5102
|
+
failures.push(error);
|
|
5103
|
+
} finally {
|
|
5104
|
+
materialized.delete(definition);
|
|
5105
|
+
}
|
|
4189
5106
|
}
|
|
4190
5107
|
state.materialized.length = 0;
|
|
4191
5108
|
scopes.delete(state);
|
|
5109
|
+
if (failures.length) throw failures[0];
|
|
4192
5110
|
};
|
|
4193
5111
|
const createScope = () => {
|
|
4194
5112
|
scheduler.assertIdle();
|
|
@@ -4199,26 +5117,33 @@ const createProjectionRuntime = (options) => {
|
|
|
4199
5117
|
};
|
|
4200
5118
|
scopes.add(state);
|
|
4201
5119
|
const assertActive = () => {
|
|
4202
|
-
if (!state.active) throw new
|
|
5120
|
+
if (!state.active) throw new require_scheduler.ProjectionDisposedError();
|
|
4203
5121
|
scheduler.assertActive();
|
|
4204
5122
|
};
|
|
4205
|
-
|
|
5123
|
+
function own(target) {
|
|
4206
5124
|
assertActive();
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
5125
|
+
const projections = [];
|
|
5126
|
+
const collect = (value) => {
|
|
5127
|
+
if (require_scheduler.isProjection(value)) {
|
|
5128
|
+
projections.push(value);
|
|
5129
|
+
return;
|
|
5130
|
+
}
|
|
5131
|
+
if (!require_record.isPlainObject(value)) throw new TypeError("Projection ownership tree must be a plain object.");
|
|
5132
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
5133
|
+
if (typeof key !== "string") throw new TypeError("Projection ownership tree keys must be strings.");
|
|
5134
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
5135
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) throw new TypeError("Projection ownership tree must use enumerable data properties.");
|
|
5136
|
+
const child = descriptor.value;
|
|
5137
|
+
if (!require_scheduler.isProjection(child) && !require_record.isPlainObject(child)) throw new TypeError("Projection ownership tree leaves must be projections.");
|
|
5138
|
+
collect(child);
|
|
5139
|
+
}
|
|
5140
|
+
};
|
|
5141
|
+
collect(target);
|
|
5142
|
+
if (!projections.length) throw new TypeError("Projection ownership tree must contain a projection.");
|
|
5143
|
+
require_scheduler.ownProjections(projections, state);
|
|
5144
|
+
return target;
|
|
5145
|
+
}
|
|
5146
|
+
function scopeSelect(projection, selector, equality) {
|
|
4222
5147
|
assertActive();
|
|
4223
5148
|
const source = makeReadable(projection, selector, equality, state);
|
|
4224
5149
|
return Object.freeze({
|
|
@@ -4245,78 +5170,87 @@ const createProjectionRuntime = (options) => {
|
|
|
4245
5170
|
}
|
|
4246
5171
|
});
|
|
4247
5172
|
}
|
|
4248
|
-
function
|
|
5173
|
+
function scopeUpdate(target, valueOrRun) {
|
|
5174
|
+
assertActive();
|
|
5175
|
+
updateInput(target, valueOrRun, state);
|
|
5176
|
+
}
|
|
5177
|
+
function scopeBatch(run, options) {
|
|
4249
5178
|
assertActive();
|
|
4250
|
-
return
|
|
5179
|
+
return batch(run, options);
|
|
4251
5180
|
}
|
|
4252
5181
|
return Object.freeze({
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
incremental: scopedIncremental,
|
|
4256
|
-
get: (projection) => {
|
|
5182
|
+
own,
|
|
5183
|
+
read: (projection) => {
|
|
4257
5184
|
assertActive();
|
|
4258
|
-
return
|
|
4259
|
-
},
|
|
4260
|
-
readable: scopeReadable,
|
|
4261
|
-
set: (target, value) => {
|
|
4262
|
-
assertActive();
|
|
4263
|
-
setInput(target, value, state);
|
|
4264
|
-
},
|
|
4265
|
-
update: (target, run) => {
|
|
4266
|
-
assertActive();
|
|
4267
|
-
updateInput(target, run, state);
|
|
5185
|
+
return readProjection(projection, state);
|
|
4268
5186
|
},
|
|
5187
|
+
select: scopeSelect,
|
|
5188
|
+
update: scopeUpdate,
|
|
4269
5189
|
batch: scopeBatch,
|
|
4270
5190
|
dispose: () => disposeScope(state)
|
|
4271
5191
|
});
|
|
4272
5192
|
};
|
|
4273
5193
|
return Object.freeze({
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
update: updateInput,
|
|
5194
|
+
read: readProjection,
|
|
5195
|
+
select: selectProjection,
|
|
5196
|
+
update,
|
|
4278
5197
|
batch,
|
|
4279
5198
|
scope: createScope,
|
|
4280
5199
|
dispose: () => {
|
|
4281
5200
|
if (!scheduler.active) return;
|
|
4282
5201
|
scheduler.assertIdle();
|
|
5202
|
+
const failures = [];
|
|
4283
5203
|
for (const scope of scopes) {
|
|
4284
5204
|
scope.active = false;
|
|
4285
|
-
scope.subscriptions
|
|
5205
|
+
const subscriptions = [...scope.subscriptions];
|
|
4286
5206
|
scope.subscriptions.clear();
|
|
5207
|
+
for (const unsubscribe of subscriptions) try {
|
|
5208
|
+
unsubscribe();
|
|
5209
|
+
} catch (error) {
|
|
5210
|
+
failures.push(error);
|
|
5211
|
+
}
|
|
4287
5212
|
scope.materialized.length = 0;
|
|
4288
5213
|
}
|
|
4289
5214
|
scopes.clear();
|
|
4290
|
-
|
|
4291
|
-
|
|
5215
|
+
try {
|
|
5216
|
+
sources.dispose();
|
|
5217
|
+
} catch (error) {
|
|
5218
|
+
failures.push(error);
|
|
5219
|
+
}
|
|
5220
|
+
try {
|
|
5221
|
+
scheduler.dispose();
|
|
5222
|
+
} catch (error) {
|
|
5223
|
+
failures.push(error);
|
|
5224
|
+
}
|
|
5225
|
+
if (failures.length === 1) throw failures[0];
|
|
5226
|
+
if (failures.length) throw new AggregateError(failures, "Projection cleanup failed.");
|
|
4292
5227
|
}
|
|
4293
5228
|
});
|
|
4294
5229
|
};
|
|
4295
5230
|
//#endregion
|
|
4296
5231
|
exports.DocumentDisposedError = DocumentDisposedError;
|
|
4297
5232
|
exports.DocumentReentrancyError = DocumentReentrancyError;
|
|
4298
|
-
exports.ParseError =
|
|
4299
|
-
exports.ProjectionDisposedError =
|
|
4300
|
-
exports.ProjectionError =
|
|
5233
|
+
exports.ParseError = ParseError;
|
|
5234
|
+
exports.ProjectionDisposedError = require_scheduler.ProjectionDisposedError;
|
|
5235
|
+
exports.ProjectionError = require_scheduler.ProjectionError;
|
|
4301
5236
|
exports.TransactionRejected = TransactionRejected;
|
|
4302
|
-
exports.asReadable = asReadable;
|
|
4303
5237
|
exports.createDocument = createDocument;
|
|
4304
5238
|
exports.createProjectionRuntime = createProjectionRuntime;
|
|
4305
|
-
exports.derive =
|
|
4306
|
-
exports.field =
|
|
4307
|
-
exports.input =
|
|
4308
|
-
exports.list =
|
|
4309
|
-
exports.map =
|
|
4310
|
-
exports.object =
|
|
4311
|
-
exports.observe =
|
|
4312
|
-
exports.optional =
|
|
4313
|
-
exports.parse =
|
|
5239
|
+
exports.derive = derive;
|
|
5240
|
+
exports.field = field;
|
|
5241
|
+
exports.input = input;
|
|
5242
|
+
exports.list = list;
|
|
5243
|
+
exports.map = map;
|
|
5244
|
+
exports.object = object;
|
|
5245
|
+
exports.observe = observe;
|
|
5246
|
+
exports.optional = optional;
|
|
5247
|
+
exports.parse = parse;
|
|
4314
5248
|
exports.read = read;
|
|
4315
5249
|
exports.replace = replace;
|
|
4316
5250
|
exports.select = select;
|
|
4317
5251
|
exports.snapshot = snapshot;
|
|
4318
|
-
exports.table =
|
|
4319
|
-
exports.tree =
|
|
4320
|
-
exports.variant =
|
|
5252
|
+
exports.table = table;
|
|
5253
|
+
exports.tree = tree;
|
|
5254
|
+
exports.variant = variant;
|
|
4321
5255
|
|
|
4322
5256
|
//# sourceMappingURL=index.cjs.map
|