doxum 0.1.7 → 0.1.9
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 +217 -296
- package/dist/contract-BNStLbSE.d.ts +441 -0
- package/dist/contract-CIU5FCC1.d.cts +441 -0
- package/dist/driver-BlR81Dqg.js +200 -0
- package/dist/driver-BlR81Dqg.js.map +1 -0
- package/dist/driver-xOIkwrB8.cjs +241 -0
- package/dist/driver-xOIkwrB8.cjs.map +1 -0
- package/dist/index.cjs +666 -2222
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -20
- package/dist/index.d.ts +10 -18
- package/dist/index.js +634 -2189
- package/dist/index.js.map +1 -1
- package/dist/{integration-CZwCwFBS.cjs → integration-C87tjRop.cjs} +4 -9
- package/dist/integration-C87tjRop.cjs.map +1 -0
- package/dist/{integration-B56u1l9V.js → integration-D5XCBLJ8.js} +2 -7
- package/dist/integration-D5XCBLJ8.js.map +1 -0
- package/dist/integration.cjs +11 -11
- package/dist/integration.d.cts +10 -4
- package/dist/integration.d.ts +10 -4
- package/dist/integration.js +4 -3
- package/dist/issue-DVaGQGeP.js +576 -0
- package/dist/issue-DVaGQGeP.js.map +1 -0
- package/dist/issue-DhrNdQNg.cjs +797 -0
- package/dist/issue-DhrNdQNg.cjs.map +1 -0
- package/dist/local-sync.cjs +67 -39
- package/dist/local-sync.cjs.map +1 -1
- package/dist/local-sync.d.cts +10 -10
- package/dist/local-sync.d.ts +10 -10
- package/dist/local-sync.js +66 -38
- package/dist/local-sync.js.map +1 -1
- package/dist/react.cjs +23 -4
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +8 -3
- package/dist/react.d.ts +8 -3
- package/dist/react.js +21 -6
- package/dist/react.js.map +1 -1
- package/dist/store-1Uob0Ghk.cjs +2750 -0
- package/dist/store-1Uob0Ghk.cjs.map +1 -0
- package/dist/store-CD0KdGsq.d.cts +262 -0
- package/dist/store-D7QH6Rzw.js +2511 -0
- package/dist/store-D7QH6Rzw.js.map +1 -0
- package/dist/store-cp5CpfCy.d.ts +262 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +26 -44
- package/skills/doxum-runtime/references/guide.en.md +95 -359
- package/skills/doxum-runtime/references/guide.zh-CN.md +80 -285
- package/skills/doxum-runtime/references/invariants.en.md +42 -165
- package/skills/doxum-runtime/references/invariants.zh-CN.md +34 -97
- package/skills/doxum-runtime/references/patterns.en.md +73 -225
- package/skills/doxum-runtime/references/patterns.zh-CN.md +73 -179
- package/dist/chunk-pbuEa-1d.js +0 -13
- package/dist/contract-DNZ4D53r.d.ts +0 -563
- package/dist/contract-j3SLGAwh.d.cts +0 -563
- package/dist/driver-CNxqMVFH.cjs +0 -69
- package/dist/driver-CNxqMVFH.cjs.map +0 -1
- package/dist/driver-CbzfW5MR.js +0 -46
- package/dist/driver-CbzfW5MR.js.map +0 -1
- package/dist/integration-B56u1l9V.js.map +0 -1
- package/dist/integration-CZwCwFBS.cjs.map +0 -1
- package/dist/ownership-CY0nPXGF.cjs +0 -304
- package/dist/ownership-CY0nPXGF.cjs.map +0 -1
- package/dist/ownership-CduRygE7.js +0 -245
- package/dist/ownership-CduRygE7.js.map +0 -1
- package/dist/runtime-0mOFbe_H.cjs +0 -2439
- package/dist/runtime-0mOFbe_H.cjs.map +0 -1
- package/dist/runtime-B22tuj9A.d.cts +0 -150
- package/dist/runtime-BFmhzPpZ.d.ts +0 -150
- package/dist/runtime-DF1q9Gje.js +0 -2129
- package/dist/runtime-DF1q9Gje.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,92 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const require_driver = require("./driver-
|
|
5
|
-
//#region core/src/impact.ts
|
|
6
|
-
const emptyCollectionImpact = Object.freeze({
|
|
7
|
-
kind: "incremental",
|
|
8
|
-
added: /* @__PURE__ */ new Set(),
|
|
9
|
-
removed: /* @__PURE__ */ new Set(),
|
|
10
|
-
updated: /* @__PURE__ */ new Set(),
|
|
11
|
-
orderChanged: false
|
|
12
|
-
});
|
|
13
|
-
const resetCollectionImpact = Object.freeze({ kind: "reset" });
|
|
14
|
-
const createImpact = (input) => {
|
|
15
|
-
const kind = input.reset ? "reset" : "incremental";
|
|
16
|
-
const paths = input.paths ?? input.operations.map((operation) => operation.at);
|
|
17
|
-
let pathBuckets;
|
|
18
|
-
const somePath = (address, test) => {
|
|
19
|
-
if (address.length === 0 || paths.length <= 8) return paths.some(test);
|
|
20
|
-
if (!pathBuckets) {
|
|
21
|
-
pathBuckets = /* @__PURE__ */ new Map();
|
|
22
|
-
for (const changed of paths) {
|
|
23
|
-
const first = changed[0];
|
|
24
|
-
const bucket = pathBuckets.get(first) ?? [];
|
|
25
|
-
bucket.push(changed);
|
|
26
|
-
pathBuckets.set(first, bucket);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (pathBuckets.get(void 0)?.some(test)) return true;
|
|
30
|
-
return pathBuckets.get(address[0])?.some(test) ?? false;
|
|
31
|
-
};
|
|
32
|
-
const pathsAffect = (address) => address.length === 0 ? paths.length > 0 : somePath(address, (changed) => require_runtime.overlaps(changed, address));
|
|
33
|
-
let collectionCache;
|
|
34
|
-
const findCollection = (address) => input.collections?.find((entry) => entry.address.length === address.length && entry.address.every((segment, index) => segment === address[index]));
|
|
35
|
-
return {
|
|
36
|
-
kind,
|
|
37
|
-
operations: input.operations,
|
|
38
|
-
affects: (value) => {
|
|
39
|
-
require_ownership.profile.impact.affects();
|
|
40
|
-
if (kind === "reset") return true;
|
|
41
|
-
if (!require_runtime.belongs(value, input.schema)) return false;
|
|
42
|
-
if (pathsAffect(require_runtime.address(value))) return true;
|
|
43
|
-
if (value.kind === "collection") {
|
|
44
|
-
const collection = findCollection(require_runtime.address(value));
|
|
45
|
-
if (!collection) return false;
|
|
46
|
-
const id$1 = require_runtime.id(value);
|
|
47
|
-
if (id$1 !== void 0) return collection.added.has(id$1) || collection.removed.has(id$1) || collection.updated.has(id$1);
|
|
48
|
-
return collection.added.size > 0 || collection.removed.size > 0 || collection.updated.size > 0 || collection.orderChanged;
|
|
49
|
-
}
|
|
50
|
-
return false;
|
|
51
|
-
},
|
|
52
|
-
collection: (selector) => {
|
|
53
|
-
if (selector.schema !== input.schema) throw new Error("Collection selector belongs to another schema.");
|
|
54
|
-
if (kind === "reset") return resetCollectionImpact;
|
|
55
|
-
const cached = collectionCache?.find((entry) => entry.address.length === selector.address.length && entry.address.every((segment, index) => segment === selector.address[index]))?.value;
|
|
56
|
-
if (cached) return cached;
|
|
57
|
-
const exact = findCollection(selector.address);
|
|
58
|
-
if (somePath(selector.address, (changed) => require_runtime.contains(changed, selector.address) && changed.length < selector.address.length)) {
|
|
59
|
-
const reset = { kind: "reset" };
|
|
60
|
-
(collectionCache ??= []).push({
|
|
61
|
-
address: selector.address,
|
|
62
|
-
value: reset
|
|
63
|
-
});
|
|
64
|
-
return reset;
|
|
65
|
-
}
|
|
66
|
-
if (!exact) {
|
|
67
|
-
const empty = emptyCollectionImpact;
|
|
68
|
-
(collectionCache ??= []).push({
|
|
69
|
-
address: selector.address,
|
|
70
|
-
value: empty
|
|
71
|
-
});
|
|
72
|
-
return empty;
|
|
73
|
-
}
|
|
74
|
-
const incremental = {
|
|
75
|
-
kind: "incremental",
|
|
76
|
-
added: exact.added,
|
|
77
|
-
removed: exact.removed,
|
|
78
|
-
updated: exact.updated,
|
|
79
|
-
orderChanged: exact.orderChanged
|
|
80
|
-
};
|
|
81
|
-
(collectionCache ??= []).push({
|
|
82
|
-
address: selector.address,
|
|
83
|
-
value: incremental
|
|
84
|
-
});
|
|
85
|
-
return incremental;
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
//#endregion
|
|
2
|
+
const require_issue = require("./issue-DhrNdQNg.cjs");
|
|
3
|
+
const require_store = require("./store-1Uob0Ghk.cjs");
|
|
4
|
+
const require_driver = require("./driver-xOIkwrB8.cjs");
|
|
90
5
|
//#region core/src/history.ts
|
|
91
6
|
const createHistory = (input) => {
|
|
92
7
|
let undos = [];
|
|
@@ -150,8 +65,8 @@ const createHistory = (input) => {
|
|
|
150
65
|
} else to.push(entry);
|
|
151
66
|
let committed = false;
|
|
152
67
|
try {
|
|
153
|
-
const
|
|
154
|
-
const result = input.apply(
|
|
68
|
+
const changes = direction === "redo" ? entry.batches : entry.batches.slice().reverse();
|
|
69
|
+
const result = input.apply(changes, direction === "redo" ? "forward" : "backward");
|
|
155
70
|
committed = result.status !== "rejected";
|
|
156
71
|
if (committed && direction === "cancel") group = void 0;
|
|
157
72
|
if (result.status === "unchanged") {
|
|
@@ -175,7 +90,7 @@ const createHistory = (input) => {
|
|
|
175
90
|
current: () => state,
|
|
176
91
|
revision: () => revision,
|
|
177
92
|
subscribe: (listener) => {
|
|
178
|
-
if (disposed) throw new
|
|
93
|
+
if (disposed) throw new require_store.DocumentDisposedError();
|
|
179
94
|
listeners.add(listener);
|
|
180
95
|
return () => {
|
|
181
96
|
listeners.delete(listener);
|
|
@@ -225,12 +140,8 @@ const createHistory = (input) => {
|
|
|
225
140
|
});
|
|
226
141
|
}
|
|
227
142
|
},
|
|
228
|
-
record: (
|
|
143
|
+
record: (batch) => {
|
|
229
144
|
if (input.capacity <= 0) return;
|
|
230
|
-
const batch = {
|
|
231
|
-
operations,
|
|
232
|
-
inverse
|
|
233
|
-
};
|
|
234
145
|
if (group?.entry) group.entry.batches.push(batch);
|
|
235
146
|
else {
|
|
236
147
|
const entry = { batches: [batch] };
|
|
@@ -256,1990 +167,609 @@ const createHistory = (input) => {
|
|
|
256
167
|
};
|
|
257
168
|
};
|
|
258
169
|
//#endregion
|
|
259
|
-
//#region core/src/
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
};
|
|
275
|
-
if (node.kind === "dict") return {
|
|
276
|
-
set: (key, value) => emit(sink, {
|
|
277
|
-
type: "dict.set",
|
|
278
|
-
at: address,
|
|
279
|
-
key,
|
|
280
|
-
value
|
|
281
|
-
}),
|
|
282
|
-
delete: (key) => emit(sink, {
|
|
283
|
-
type: "dict.delete",
|
|
284
|
-
at: address,
|
|
285
|
-
key
|
|
286
|
-
}),
|
|
287
|
-
replace: (value) => emit(sink, {
|
|
288
|
-
type: "dict.replace",
|
|
289
|
-
at: address,
|
|
290
|
-
value
|
|
291
|
-
}),
|
|
292
|
-
...node.optional ? { clear: () => emit(sink, {
|
|
293
|
-
type: "value.clear",
|
|
294
|
-
at: address
|
|
295
|
-
}) } : {}
|
|
296
|
-
};
|
|
297
|
-
if (node.kind === "list") return {
|
|
298
|
-
insert: (value, anchor) => emit(sink, {
|
|
299
|
-
type: "list.insert",
|
|
300
|
-
at: address,
|
|
301
|
-
key: node.keyOf(value),
|
|
302
|
-
value,
|
|
303
|
-
anchor
|
|
304
|
-
}),
|
|
305
|
-
move: (key, anchor) => emit(sink, {
|
|
306
|
-
type: "list.move",
|
|
307
|
-
at: address,
|
|
308
|
-
key,
|
|
309
|
-
anchor
|
|
310
|
-
}),
|
|
311
|
-
remove: (key) => emit(sink, {
|
|
312
|
-
type: "list.remove",
|
|
313
|
-
at: address,
|
|
314
|
-
key
|
|
315
|
-
}),
|
|
316
|
-
replace: (value) => emit(sink, {
|
|
317
|
-
type: "list.replace",
|
|
318
|
-
at: address,
|
|
319
|
-
value,
|
|
320
|
-
keys: value.map(node.keyOf)
|
|
321
|
-
}),
|
|
322
|
-
...node.optional ? { clear: () => emit(sink, {
|
|
323
|
-
type: "value.clear",
|
|
324
|
-
at: address
|
|
325
|
-
}) } : {}
|
|
326
|
-
};
|
|
327
|
-
if (node.kind === "tree") return {
|
|
328
|
-
insert: (treeNodeId, value, position) => emit(sink, {
|
|
329
|
-
type: "tree.insert",
|
|
330
|
-
at: address,
|
|
331
|
-
treeNodeId,
|
|
332
|
-
value,
|
|
333
|
-
parentId: position?.parentId,
|
|
334
|
-
index: position?.index
|
|
335
|
-
}),
|
|
336
|
-
move: (treeNodeId, position) => emit(sink, {
|
|
337
|
-
type: "tree.move",
|
|
338
|
-
at: address,
|
|
339
|
-
treeNodeId,
|
|
340
|
-
parentId: position?.parentId,
|
|
341
|
-
index: position?.index
|
|
342
|
-
}),
|
|
343
|
-
remove: (treeNodeId) => emit(sink, {
|
|
344
|
-
type: "tree.remove",
|
|
345
|
-
at: address,
|
|
346
|
-
treeNodeId
|
|
347
|
-
}),
|
|
348
|
-
set: (treeNodeId, value) => emit(sink, {
|
|
349
|
-
type: "tree.set",
|
|
350
|
-
at: address,
|
|
351
|
-
treeNodeId,
|
|
352
|
-
value
|
|
353
|
-
}),
|
|
354
|
-
replace: (value) => emit(sink, {
|
|
355
|
-
type: "tree.replace",
|
|
356
|
-
at: address,
|
|
357
|
-
value
|
|
358
|
-
}),
|
|
359
|
-
...node.optional ? { clear: () => emit(sink, {
|
|
360
|
-
type: "value.clear",
|
|
361
|
-
at: address
|
|
362
|
-
}) } : {}
|
|
363
|
-
};
|
|
364
|
-
if (node.kind === "table") {
|
|
365
|
-
let items;
|
|
366
|
-
return {
|
|
367
|
-
create: (input, anchor) => {
|
|
368
|
-
emit(sink, {
|
|
369
|
-
type: "entity.create",
|
|
370
|
-
at: address,
|
|
371
|
-
entries: (Array.isArray(input) ? input : [input]).map(({ id, value }) => ({
|
|
372
|
-
id,
|
|
373
|
-
value
|
|
374
|
-
})),
|
|
375
|
-
anchor
|
|
376
|
-
});
|
|
377
|
-
},
|
|
378
|
-
item: (id) => {
|
|
379
|
-
const cached = items?.get(id);
|
|
380
|
-
if (cached) return cached;
|
|
381
|
-
const writer = writerFor(node.value, [...address, id], sink);
|
|
382
|
-
(items ??= /* @__PURE__ */ new Map()).set(id, writer);
|
|
383
|
-
return writer;
|
|
384
|
-
},
|
|
385
|
-
remove: (input) => emit(sink, {
|
|
386
|
-
type: "entity.remove",
|
|
387
|
-
at: address,
|
|
388
|
-
ids: Array.isArray(input) ? [...input] : [input]
|
|
389
|
-
}),
|
|
390
|
-
move: (id, anchor) => emit(sink, {
|
|
391
|
-
type: "entity.move",
|
|
392
|
-
at: address,
|
|
393
|
-
id,
|
|
394
|
-
anchor
|
|
395
|
-
})
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
if (node.kind === "map") {
|
|
399
|
-
let items;
|
|
400
|
-
return {
|
|
401
|
-
create: (input) => {
|
|
402
|
-
emit(sink, {
|
|
403
|
-
type: "entity.create",
|
|
404
|
-
at: address,
|
|
405
|
-
entries: (Array.isArray(input) ? input : [input]).map(({ id, value }) => ({
|
|
406
|
-
id,
|
|
407
|
-
value
|
|
408
|
-
}))
|
|
409
|
-
});
|
|
410
|
-
},
|
|
411
|
-
item: (id) => {
|
|
412
|
-
const cached = items?.get(id);
|
|
413
|
-
if (cached) return cached;
|
|
414
|
-
const writer = writerFor(node.value, [...address, id], sink);
|
|
415
|
-
(items ??= /* @__PURE__ */ new Map()).set(id, writer);
|
|
416
|
-
return writer;
|
|
417
|
-
},
|
|
418
|
-
remove: (input) => emit(sink, {
|
|
419
|
-
type: "entity.remove",
|
|
420
|
-
at: address,
|
|
421
|
-
ids: Array.isArray(input) ? [...input] : [input]
|
|
422
|
-
})
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
if (node.kind === "variant") return {
|
|
426
|
-
replace: (value) => emit(sink, {
|
|
427
|
-
type: "variant.replace",
|
|
428
|
-
at: address,
|
|
429
|
-
value
|
|
430
|
-
}),
|
|
431
|
-
...node.optional ? { clear: () => emit(sink, {
|
|
432
|
-
type: "value.clear",
|
|
433
|
-
at: address
|
|
434
|
-
}) } : {}
|
|
435
|
-
};
|
|
436
|
-
let children;
|
|
437
|
-
return new Proxy({}, { get: (_target, property) => {
|
|
438
|
-
if (typeof property !== "string" || !node.shape[property]) return void 0;
|
|
439
|
-
const cached = children?.get(property);
|
|
440
|
-
if (cached) return cached;
|
|
441
|
-
const writer = writerFor(node.shape[property], [...address, property], sink);
|
|
442
|
-
(children ??= /* @__PURE__ */ new Map()).set(property, writer);
|
|
443
|
-
return writer;
|
|
444
|
-
} });
|
|
445
|
-
};
|
|
446
|
-
const documentWriter = (schema, sink) => writerFor(schemaRoot(schema), [], sink);
|
|
447
|
-
//#endregion
|
|
448
|
-
//#region core/src/mutation/issue.ts
|
|
449
|
-
const ownAddress$1 = (address) => Object.freeze(address.slice());
|
|
450
|
-
const at = (address, code, message, operation) => Object.freeze({
|
|
451
|
-
source: "mutation",
|
|
452
|
-
code,
|
|
453
|
-
address: ownAddress$1(address),
|
|
454
|
-
message,
|
|
455
|
-
...operation === void 0 ? {} : { operation }
|
|
456
|
-
});
|
|
457
|
-
const from = (operation, code, message) => at(operation.at, code, message, operation.type);
|
|
458
|
-
const input = (code, message) => at([], code, message);
|
|
459
|
-
//#endregion
|
|
460
|
-
//#region core/src/mutation/execute-entity.ts
|
|
461
|
-
const resolveEntityCollection = (node, value) => {
|
|
462
|
-
if (node.kind === "table") {
|
|
463
|
-
if (!require_ownership.isRecord(value) || !Array.isArray(value.ids) || !require_ownership.isRecord(value.byId)) return { status: "invalid" };
|
|
464
|
-
return {
|
|
465
|
-
status: "resolved",
|
|
466
|
-
collection: {
|
|
467
|
-
kind: "table",
|
|
468
|
-
ids: value.ids,
|
|
469
|
-
byId: value.byId
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
return require_ownership.isRecord(value) ? {
|
|
474
|
-
status: "resolved",
|
|
475
|
-
collection: {
|
|
476
|
-
kind: "map",
|
|
477
|
-
byId: value
|
|
478
|
-
}
|
|
479
|
-
} : { status: "invalid" };
|
|
170
|
+
//#region core/src/mutation/state.ts
|
|
171
|
+
/** Install validated values, or captured before values during rollback. */
|
|
172
|
+
const installMember = (parent, key, present, value) => {
|
|
173
|
+
if (Array.isArray(parent)) {
|
|
174
|
+
const index = Number(key);
|
|
175
|
+
if (present) if (index < 0) require_issue.insert(parent, parent.length, value);
|
|
176
|
+
else parent[index] = value;
|
|
177
|
+
else if (index >= 0) require_issue.remove(parent, index);
|
|
178
|
+
} else if (!present) delete parent[key];
|
|
179
|
+
else if (Object.hasOwn(parent, key)) parent[key] = value;
|
|
180
|
+
else Object.defineProperty(parent, key, {
|
|
181
|
+
value,
|
|
182
|
+
writable: true,
|
|
183
|
+
configurable: true,
|
|
184
|
+
enumerable: true
|
|
185
|
+
});
|
|
480
186
|
};
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
const executeEntity = (target, operation, copyPayload) => {
|
|
486
|
-
if (target.node.kind !== "table" && target.node.kind !== "map") return rejected$4(operation, "invalid-address", "Collection target is invalid.");
|
|
487
|
-
const resolution = resolveEntityCollection(target.node, target.value);
|
|
488
|
-
if (resolution.status === "invalid") return rejected$4(operation, "invalid-address", "Collection target is invalid.");
|
|
489
|
-
const collection = resolution.collection;
|
|
490
|
-
const ids = collection.kind === "table" ? collection.ids : void 0;
|
|
491
|
-
if (operation.type === "entity.move" && collection.kind === "map") return rejected$4(operation, "invalid-operation", "Map entries are unordered.");
|
|
492
|
-
const byId = collection.byId;
|
|
493
|
-
if (operation.type === "entity.create") {
|
|
494
|
-
if (operation.entries.length === 0) return { status: "unchanged" };
|
|
495
|
-
const seen = /* @__PURE__ */ new Set();
|
|
496
|
-
for (const entry of operation.entries) {
|
|
497
|
-
if (seen.has(entry.id)) return rejected$4(operation, "duplicate-entity", "Entity ids must be unique within a batch.");
|
|
498
|
-
seen.add(entry.id);
|
|
499
|
-
if (Object.prototype.hasOwnProperty.call(byId, entry.id)) return rejected$4(operation, "duplicate-entity", `Entity '${entry.id}' already exists.`);
|
|
500
|
-
}
|
|
501
|
-
if (collection.kind === "map" && (operation.anchor || operation.positions)) return rejected$4(operation, "invalid-anchor", "Map entries do not support ordering metadata.");
|
|
502
|
-
if (ids && operation.positions) {
|
|
503
|
-
if (operation.positions.length !== operation.entries.length || !require_runtime.validPositions(ids.length, operation.positions)) return rejected$4(operation, "invalid-operation", "Entity restore positions are invalid.");
|
|
504
|
-
} else if (ids && !require_runtime.valid(ids, operation.anchor)) return rejected$4(operation, "invalid-anchor", "Collection anchor is invalid.");
|
|
505
|
-
for (const entry of operation.entries) byId[entry.id] = copyPayload ? require_ownership.cloneValue(entry.value, "canonical") : require_ownership.transferPayload(entry.value);
|
|
506
|
-
if (ids) if (operation.positions) require_runtime.restore(ids, operation.entries.map((entry) => entry.id), operation.positions);
|
|
507
|
-
else if (!operation.anchor || "at" in operation.anchor && operation.anchor.at === "end") for (const entry of operation.entries) ids.push(entry.id);
|
|
508
|
-
else {
|
|
509
|
-
const insertion = require_runtime.index(ids, operation.anchor);
|
|
510
|
-
const currentLength = ids.length;
|
|
511
|
-
ids.length = currentLength + operation.entries.length;
|
|
512
|
-
for (let index = currentLength - 1; index >= insertion; index -= 1) ids[index + operation.entries.length] = ids[index];
|
|
513
|
-
for (let index = 0; index < operation.entries.length; index += 1) ids[insertion + index] = operation.entries[index].id;
|
|
514
|
-
}
|
|
515
|
-
return {
|
|
516
|
-
status: "changed",
|
|
517
|
-
inverse: [{
|
|
518
|
-
type: "entity.remove",
|
|
519
|
-
at: operation.at,
|
|
520
|
-
ids: operation.entries.map((entry) => entry.id)
|
|
521
|
-
}]
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
if (operation.type === "entity.remove") {
|
|
525
|
-
if (operation.ids.length === 0) return { status: "unchanged" };
|
|
526
|
-
const seen = /* @__PURE__ */ new Set();
|
|
527
|
-
for (const id of operation.ids) {
|
|
528
|
-
if (seen.has(id)) return rejected$4(operation, "duplicate-entity", "Entity ids must be unique within a batch.");
|
|
529
|
-
seen.add(id);
|
|
530
|
-
if (!Object.prototype.hasOwnProperty.call(byId, id)) return rejected$4(operation, "missing-entity", `Entity '${id}' does not exist.`);
|
|
531
|
-
}
|
|
532
|
-
const entries = [];
|
|
533
|
-
const positions = [];
|
|
534
|
-
if (ids) {
|
|
535
|
-
const positionsById = /* @__PURE__ */ new Map();
|
|
536
|
-
for (let index = 0; index < ids.length; index += 1) {
|
|
537
|
-
const id = ids[index];
|
|
538
|
-
if (seen.has(id)) positionsById.set(id, index);
|
|
539
|
-
}
|
|
540
|
-
for (const id of operation.ids) if (!positionsById.has(id)) return rejected$4(operation, "invalid-collection", `Collection index is missing '${id}'.`);
|
|
541
|
-
let writeIndex = 0;
|
|
542
|
-
for (let index = 0; index < ids.length; index += 1) {
|
|
543
|
-
const id = ids[index];
|
|
544
|
-
if (seen.has(id)) {
|
|
545
|
-
entries.push({
|
|
546
|
-
id,
|
|
547
|
-
value: require_ownership.cloneValue(byId[id], "inverse")
|
|
548
|
-
});
|
|
549
|
-
positions.push(index);
|
|
550
|
-
delete byId[id];
|
|
551
|
-
} else ids[writeIndex++] = id;
|
|
552
|
-
}
|
|
553
|
-
ids.length = writeIndex;
|
|
554
|
-
} else for (const id of operation.ids) {
|
|
555
|
-
entries.push({
|
|
556
|
-
id,
|
|
557
|
-
value: require_ownership.cloneValue(byId[id], "inverse")
|
|
558
|
-
});
|
|
559
|
-
delete byId[id];
|
|
560
|
-
}
|
|
561
|
-
return {
|
|
562
|
-
status: "changed",
|
|
563
|
-
inverse: [{
|
|
564
|
-
type: "entity.create",
|
|
565
|
-
at: operation.at,
|
|
566
|
-
entries,
|
|
567
|
-
...ids ? { positions } : {}
|
|
568
|
-
}]
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
const exists = Object.prototype.hasOwnProperty.call(byId, operation.id);
|
|
572
|
-
const position = ids?.indexOf(operation.id) ?? -1;
|
|
573
|
-
if (!exists) return rejected$4(operation, "missing-entity", `Entity '${operation.id}' does not exist.`);
|
|
574
|
-
if (!ids || position < 0) return rejected$4(operation, "invalid-collection", `Collection index is missing '${operation.id}'.`);
|
|
575
|
-
if (!require_runtime.valid(ids, operation.anchor)) return rejected$4(operation, "invalid-anchor", "Collection anchor is invalid.");
|
|
576
|
-
if (operation.anchor && ("before" in operation.anchor && operation.anchor.before === operation.id || "after" in operation.anchor && operation.anchor.after === operation.id)) return rejected$4(operation, "invalid-anchor", "An entity cannot be moved relative to itself.");
|
|
577
|
-
const nextIndex = require_runtime.afterRemove(ids, position, operation.anchor);
|
|
578
|
-
if (nextIndex === position) return { status: "unchanged" };
|
|
579
|
-
const inverse = {
|
|
580
|
-
type: "entity.move",
|
|
581
|
-
at: operation.at,
|
|
582
|
-
id: operation.id,
|
|
583
|
-
anchor: require_runtime.at(ids, position)
|
|
584
|
-
};
|
|
585
|
-
ids.splice(position, 1);
|
|
586
|
-
ids.splice(nextIndex, 0, operation.id);
|
|
587
|
-
return {
|
|
588
|
-
status: "changed",
|
|
589
|
-
inverse: [inverse]
|
|
590
|
-
};
|
|
187
|
+
const orderOf = (node, value) => {
|
|
188
|
+
if (node.kind === "list") return value.map(node.keyOf);
|
|
189
|
+
if (node.kind === "table") return [...value.ids];
|
|
190
|
+
throw new Error("Order requires a table or list.");
|
|
591
191
|
};
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
issue: from(operation, code, message)
|
|
597
|
-
});
|
|
598
|
-
const executeList = (target, operation, copyPayload) => {
|
|
599
|
-
if (target.node.kind !== "list") return rejected$3(operation, "invalid-address", "List target is invalid.");
|
|
600
|
-
const absent = target.value === void 0 && "optional" in target.node && target.node.optional === true;
|
|
601
|
-
if (!Array.isArray(target.value) && !absent) return rejected$3(operation, "invalid-address", "List target is invalid.");
|
|
602
|
-
if (absent && operation.type !== "list.replace") return rejected$3(operation, "invalid-address", "Only list replacement can initialize an optional list.");
|
|
603
|
-
const list = Array.isArray(target.value) ? target.value : [];
|
|
604
|
-
const orderedKeys = require_runtime.keys(list, target.node.keyOf);
|
|
605
|
-
const own = (value) => copyPayload ? require_ownership.ownPayload(value) : value;
|
|
606
|
-
if (operation.type === "list.replace") {
|
|
607
|
-
if (operation.keys.length !== operation.value.length) return rejected$3(operation, "invalid-list-keys", "List replacement keys are invalid.");
|
|
608
|
-
const seen = /* @__PURE__ */ new Set();
|
|
609
|
-
for (let index = 0; index < operation.value.length; index += 1) {
|
|
610
|
-
const key = operation.keys[index];
|
|
611
|
-
if (seen.has(key) || target.node.keyOf(operation.value[index]) !== key) return rejected$3(operation, "invalid-list-keys", "List replacement keys are invalid.");
|
|
612
|
-
seen.add(key);
|
|
613
|
-
}
|
|
614
|
-
if (!absent && require_ownership.sameStructuralValue(list, operation.value)) return { status: "unchanged" };
|
|
615
|
-
const keys = list.map(target.node.keyOf);
|
|
616
|
-
const inverse = absent ? {
|
|
617
|
-
type: "value.clear",
|
|
618
|
-
at: operation.at
|
|
619
|
-
} : {
|
|
620
|
-
type: "list.replace",
|
|
621
|
-
at: operation.at,
|
|
622
|
-
value: require_ownership.cloneValue(list, "inverse"),
|
|
623
|
-
keys
|
|
624
|
-
};
|
|
625
|
-
const owned = own(operation.value);
|
|
626
|
-
if (absent) target.parent[target.key] = owned;
|
|
627
|
-
else {
|
|
628
|
-
list.length = owned.length;
|
|
629
|
-
for (let index = 0; index < owned.length; index += 1) list[index] = owned[index];
|
|
630
|
-
}
|
|
631
|
-
return {
|
|
632
|
-
status: "changed",
|
|
633
|
-
inverse: [inverse]
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
if (operation.type === "list.insert") {
|
|
637
|
-
if (target.node.keyOf(operation.value) !== operation.key) return rejected$3(operation, "invalid-list-key", "List item key does not match schema.");
|
|
638
|
-
if (orderedKeys.index(operation.key) >= 0) return rejected$3(operation, "duplicate-list-item", `List item '${operation.key}' already exists.`);
|
|
639
|
-
if (!require_runtime.valid(orderedKeys, operation.anchor)) return rejected$3(operation, "invalid-anchor", "List anchor is invalid.");
|
|
640
|
-
list.splice(require_runtime.index(orderedKeys, operation.anchor), 0, own(operation.value));
|
|
641
|
-
return {
|
|
642
|
-
status: "changed",
|
|
643
|
-
inverse: [{
|
|
644
|
-
type: "list.remove",
|
|
645
|
-
at: operation.at,
|
|
646
|
-
key: operation.key
|
|
647
|
-
}]
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
const position = orderedKeys.index(operation.key);
|
|
651
|
-
if (position < 0) return rejected$3(operation, "missing-list-item", `List item '${operation.key}' does not exist.`);
|
|
652
|
-
if (operation.type === "list.remove") {
|
|
653
|
-
const inverse = {
|
|
654
|
-
type: "list.insert",
|
|
655
|
-
at: operation.at,
|
|
656
|
-
key: operation.key,
|
|
657
|
-
value: require_ownership.cloneValue(list[position], "inverse"),
|
|
658
|
-
anchor: require_runtime.at(orderedKeys, position)
|
|
659
|
-
};
|
|
660
|
-
list.splice(position, 1);
|
|
661
|
-
return {
|
|
662
|
-
status: "changed",
|
|
663
|
-
inverse: [inverse]
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
if (!require_runtime.valid(orderedKeys, operation.anchor)) return rejected$3(operation, "invalid-anchor", "List anchor is invalid.");
|
|
667
|
-
if (operation.anchor && ("before" in operation.anchor && operation.anchor.before === operation.key || "after" in operation.anchor && operation.anchor.after === operation.key)) return rejected$3(operation, "invalid-anchor", "A list item cannot be moved relative to itself.");
|
|
668
|
-
const nextIndex = require_runtime.afterRemove(orderedKeys, position, operation.anchor);
|
|
669
|
-
if (nextIndex === position) return { status: "unchanged" };
|
|
670
|
-
const inverse = {
|
|
671
|
-
type: "list.move",
|
|
672
|
-
at: operation.at,
|
|
673
|
-
key: operation.key,
|
|
674
|
-
anchor: require_runtime.at(orderedKeys, position)
|
|
675
|
-
};
|
|
676
|
-
const [entry] = list.splice(position, 1);
|
|
677
|
-
list.splice(nextIndex, 0, entry);
|
|
678
|
-
return {
|
|
679
|
-
status: "changed",
|
|
680
|
-
inverse: [inverse]
|
|
681
|
-
};
|
|
192
|
+
const installOrder = (node, value, order) => {
|
|
193
|
+
if (node.kind === "list") require_issue.install(value, node.keyOf, order);
|
|
194
|
+
else if (node.kind === "table") value.ids = [...order];
|
|
195
|
+
else throw new Error("Order requires a table or list.");
|
|
682
196
|
};
|
|
683
197
|
//#endregion
|
|
684
|
-
//#region core/src/mutation/
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
198
|
+
//#region core/src/mutation/recorder.ts
|
|
199
|
+
const firstMember = (group, key) => {
|
|
200
|
+
if (group.layout === "dynamic") return group.members?.get(key);
|
|
201
|
+
const member = group.definitions.get(key);
|
|
202
|
+
return member && group.members[member.slot];
|
|
203
|
+
};
|
|
204
|
+
const forgetMember = (group, key) => {
|
|
205
|
+
if (group.layout === "dynamic") group.members?.delete(key);
|
|
206
|
+
else {
|
|
207
|
+
const definition = group.definitions.get(key);
|
|
208
|
+
if (definition) group.members[definition.slot] = void 0;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const emptyGroup = (group) => !group.order && (group.layout === "fixed" ? !group.members.some(Boolean) : !group.members?.size);
|
|
212
|
+
const copyNode = (node) => ({
|
|
213
|
+
...node,
|
|
214
|
+
children: [...node.children]
|
|
691
215
|
});
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
return
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
};
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
} : { status: "absent" };
|
|
216
|
+
const transition = (node, key, beforePresent, before, afterPresent, after) => {
|
|
217
|
+
if (beforePresent === afterPresent && (!beforePresent || Object.is(before, after))) return;
|
|
218
|
+
if (node.kind !== "field" && beforePresent === afterPresent && require_store.equalValue(node, before, after)) return;
|
|
219
|
+
const value = afterPresent ? node.kind === "field" ? after : require_store.copyValue(node, after) : void 0;
|
|
220
|
+
require_issue.profile.recorder("transitions");
|
|
221
|
+
return !beforePresent ? {
|
|
222
|
+
key,
|
|
223
|
+
kind: "added",
|
|
224
|
+
after: value
|
|
225
|
+
} : !afterPresent ? {
|
|
226
|
+
key,
|
|
227
|
+
kind: "removed",
|
|
228
|
+
before
|
|
229
|
+
} : {
|
|
230
|
+
key,
|
|
231
|
+
kind: "updated",
|
|
232
|
+
before,
|
|
233
|
+
after: value
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
const publishMembers = (changes, at, members, order) => {
|
|
237
|
+
if (!members.length && !order) return;
|
|
238
|
+
for (let i = 1; i < members.length; i++) if (members[i - 1].key > members[i].key) {
|
|
239
|
+
members.sort((a, b) => lexical(a.key, b.key));
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
changes.push(order ? {
|
|
243
|
+
kind: "members",
|
|
244
|
+
at,
|
|
245
|
+
members,
|
|
246
|
+
order
|
|
247
|
+
} : {
|
|
248
|
+
kind: "members",
|
|
249
|
+
at,
|
|
250
|
+
members
|
|
251
|
+
});
|
|
729
252
|
};
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
253
|
+
/** Diff only the captured subtree; this algorithm has no recorder or session state. */
|
|
254
|
+
const diffMember = (changes, members, node, at, key, beforePresent, before, afterPresent, after) => {
|
|
255
|
+
if (beforePresent === afterPresent && (!beforePresent || Object.is(before, after))) return;
|
|
256
|
+
if (beforePresent && afterPresent && before !== void 0 && after !== void 0) {
|
|
257
|
+
if (node.kind === "object" || node.kind === "variant" && before[node.tag] === after[node.tag]) {
|
|
258
|
+
const left = before, right = after;
|
|
259
|
+
const childAt = at.concat(key);
|
|
260
|
+
const children = [];
|
|
261
|
+
for (const [child, definition] of require_issue.compiledShape(node, before).members) diffMember(changes, children, definition.node, childAt, child, Object.hasOwn(left, child), left[child], Object.hasOwn(right, child), right[child]);
|
|
262
|
+
publishMembers(changes, childAt, children);
|
|
263
|
+
return;
|
|
738
264
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
};
|
|
758
|
-
};
|
|
759
|
-
const removedNodePresences = (inverses) => {
|
|
760
|
-
const children = /* @__PURE__ */ new Map();
|
|
761
|
-
for (const inverse of inverses) {
|
|
762
|
-
if (inverse.parentId === void 0) continue;
|
|
763
|
-
const entries = children.get(inverse.parentId);
|
|
764
|
-
const child = {
|
|
765
|
-
id: inverse.treeNodeId,
|
|
766
|
-
index: inverse.index ?? Number.MAX_SAFE_INTEGER
|
|
767
|
-
};
|
|
768
|
-
if (entries) entries.push(child);
|
|
769
|
-
else children.set(inverse.parentId, [child]);
|
|
770
|
-
}
|
|
771
|
-
const result = /* @__PURE__ */ new Map();
|
|
772
|
-
for (const inverse of inverses) {
|
|
773
|
-
const ordered = children.get(inverse.treeNodeId) ?? [];
|
|
774
|
-
ordered.sort((left, right) => left.index - right.index);
|
|
775
|
-
result.set(inverse.treeNodeId, {
|
|
776
|
-
status: "present",
|
|
777
|
-
...inverse.parentId === void 0 ? {} : { parentId: inverse.parentId },
|
|
778
|
-
children: ordered.map((entry) => entry.id),
|
|
779
|
-
value: inverse.value
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
|
-
return result;
|
|
783
|
-
};
|
|
784
|
-
const recordChange = (previous, current, forward, inverses) => {
|
|
785
|
-
const replacement = inverses[0];
|
|
786
|
-
if (replacement?.type === "tree.replace") return wholeChange(replacement, previous);
|
|
787
|
-
if (previous?.mode === "whole") return previous;
|
|
788
|
-
const change = previous ?? {
|
|
789
|
-
mode: "incremental",
|
|
790
|
-
root: forward.type === "tree.insert" && forward.parentId === void 0 ? { status: "absent" } : forward.type === "tree.remove" && inverses[0]?.type === "tree.insert" ? rootPresence(inverses[0].parentId === void 0 ? inverses[0].treeNodeId : current.rootId) : rootPresence(current.rootId),
|
|
791
|
-
nodes: /* @__PURE__ */ new Map()
|
|
792
|
-
};
|
|
793
|
-
if (forward.type === "tree.insert") {
|
|
794
|
-
capture(change, forward.treeNodeId, { status: "absent" });
|
|
795
|
-
if (forward.parentId !== void 0) {
|
|
796
|
-
const parentNode = node(current, forward.parentId);
|
|
797
|
-
if (parentNode) capture(change, forward.parentId, parentPresence(current, forward.parentId, without(parentNode.children, forward.treeNodeId)));
|
|
265
|
+
if (node.kind === "map" || node.kind === "table") {
|
|
266
|
+
const a = before, b = after;
|
|
267
|
+
const left = node.kind === "table" ? a.byId : a;
|
|
268
|
+
const right = node.kind === "table" ? b.byId : b;
|
|
269
|
+
const childAt = at.concat(key);
|
|
270
|
+
const children = [];
|
|
271
|
+
for (const id of Object.keys(left)) diffMember(changes, children, node.value, childAt, id, true, left[id], Object.hasOwn(right, id), right[id]);
|
|
272
|
+
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]);
|
|
273
|
+
let order;
|
|
274
|
+
if (node.kind === "table" && !require_issue.equal(a.ids, b.ids)) {
|
|
275
|
+
require_issue.profile.recorder("publishedOrderItems", b.ids.length);
|
|
276
|
+
order = {
|
|
277
|
+
before: a.ids,
|
|
278
|
+
after: [...b.ids]
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
publishMembers(changes, childAt, children, order);
|
|
282
|
+
return;
|
|
798
283
|
}
|
|
799
|
-
return change;
|
|
800
284
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
285
|
+
const member = transition(node, key, beforePresent, before, afterPresent, after);
|
|
286
|
+
if (member) members.push(member);
|
|
287
|
+
};
|
|
288
|
+
const restore = (fact, schema, root, skip = 0) => {
|
|
289
|
+
const at = skip ? fact.at.slice(skip) : fact.at;
|
|
290
|
+
if (fact.kind === "member") {
|
|
291
|
+
const member = fact.member;
|
|
292
|
+
const value = member.present ? require_store.copyValue(member.node, member.value) : void 0;
|
|
293
|
+
if (!at.length) return value;
|
|
294
|
+
const location = require_issue.resolveLocated(schema, root, at);
|
|
295
|
+
if (!location) throw new Error("Cannot restore an unresolved member.");
|
|
296
|
+
installMember(location.parent, location.key, member.present, value);
|
|
297
|
+
return root;
|
|
298
|
+
}
|
|
299
|
+
const location = require_issue.resolveValue(schema, root, at);
|
|
300
|
+
if (!location) throw new Error("Cannot restore an unresolved subtree.");
|
|
301
|
+
if (fact.kind === "order") installOrder(location.node, location.value, fact.group.order);
|
|
302
|
+
else {
|
|
303
|
+
const tree = location.value;
|
|
304
|
+
if (fact.before === null) delete tree.rootId;
|
|
305
|
+
else tree.rootId = fact.before;
|
|
306
|
+
for (const [id, node] of fact.nodes) installMember(tree.nodes, id, node !== void 0, node && copyNode(node));
|
|
307
|
+
}
|
|
308
|
+
return root;
|
|
309
|
+
};
|
|
310
|
+
const reconstructBefore = (node, current, records, offset) => {
|
|
311
|
+
let before = require_store.copyValue(node, current);
|
|
312
|
+
for (let i = records.length - 1; i >= 0; i--) if (records[i].kind !== "order") before = restore(records[i], node, before, offset);
|
|
313
|
+
for (const record of records) if (record.kind === "order") before = restore(record, node, before, offset);
|
|
314
|
+
return before;
|
|
315
|
+
};
|
|
316
|
+
const sealMembers = (fact, changes) => {
|
|
317
|
+
const members = [];
|
|
318
|
+
for (const member of fact.members?.values() ?? []) {
|
|
319
|
+
if (!member) continue;
|
|
320
|
+
const key = require_issue.memberKey(fact, member.key);
|
|
321
|
+
const present = Object.hasOwn(fact.parent, key);
|
|
322
|
+
const value = fact.parent[key];
|
|
323
|
+
if (member.node.kind === "field") {
|
|
324
|
+
const change = transition(member.node, member.key, member.present, member.value, present, value);
|
|
325
|
+
if (change) members.push(change);
|
|
326
|
+
} else diffMember(changes, members, member.node, fact.at, member.key, member.present, member.value, present, value);
|
|
327
|
+
}
|
|
328
|
+
let order;
|
|
329
|
+
if (fact.order) {
|
|
330
|
+
const current = fact.node.kind === "list" ? require_issue.keys(fact.value, fact.node.keyOf) : fact.value.ids;
|
|
331
|
+
if (!require_issue.equal(fact.order, current)) {
|
|
332
|
+
const after = orderOf(fact.node, fact.value);
|
|
333
|
+
require_issue.profile.recorder("publishedOrderItems", after.length);
|
|
334
|
+
order = {
|
|
335
|
+
before: fact.order,
|
|
336
|
+
after
|
|
337
|
+
};
|
|
809
338
|
}
|
|
810
|
-
return change;
|
|
811
|
-
}
|
|
812
|
-
if (forward.type === "tree.set") {
|
|
813
|
-
const inverse = inverses[0];
|
|
814
|
-
const entry = node(current, forward.treeNodeId);
|
|
815
|
-
if (entry && inverse?.type === "tree.set") capture(change, forward.treeNodeId, {
|
|
816
|
-
status: "present",
|
|
817
|
-
...entry.parentId === void 0 ? {} : { parentId: entry.parentId },
|
|
818
|
-
children: [...entry.children],
|
|
819
|
-
value: inverse.value
|
|
820
|
-
});
|
|
821
|
-
return change;
|
|
822
339
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
340
|
+
publishMembers(changes, fact.at, members, order);
|
|
341
|
+
};
|
|
342
|
+
const sealTree = (fact, state, changes) => {
|
|
343
|
+
const location = require_issue.resolveValue(state.schema, state.document, fact.at);
|
|
344
|
+
const tree = location?.value;
|
|
345
|
+
const node = location?.node;
|
|
346
|
+
if (node?.kind !== "tree") throw new Error("Tree schema disappeared before sealing.");
|
|
347
|
+
const nodes = [];
|
|
348
|
+
for (const [id, before] of fact.nodes) {
|
|
349
|
+
const after = Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : void 0;
|
|
350
|
+
if (!before && !after) continue;
|
|
351
|
+
if (before && after && before.parentId === after.parentId && require_issue.equal(before.children, after.children) && Object.hasOwn(before, "value") === Object.hasOwn(after, "value") && require_store.equalValue(node.value, before.value, after.value)) continue;
|
|
352
|
+
nodes.push(!before ? {
|
|
353
|
+
id,
|
|
354
|
+
kind: "added",
|
|
355
|
+
after: copyNode(after)
|
|
356
|
+
} : !after ? {
|
|
357
|
+
id,
|
|
358
|
+
kind: "removed",
|
|
359
|
+
before
|
|
360
|
+
} : {
|
|
361
|
+
id,
|
|
362
|
+
kind: "updated",
|
|
363
|
+
before,
|
|
364
|
+
after: copyNode(after)
|
|
832
365
|
});
|
|
833
|
-
const oldParent = node(current, inverse.parentId);
|
|
834
|
-
if (oldParent) {
|
|
835
|
-
const children = inverse.parentId === forward.parentId ? moved(oldParent.children, forward.treeNodeId, inverse.index ?? oldParent.children.length) : inserted(oldParent.children, forward.treeNodeId, inverse.index ?? oldParent.children.length);
|
|
836
|
-
capture(change, inverse.parentId, parentPresence(current, inverse.parentId, children));
|
|
837
|
-
}
|
|
838
|
-
if (forward.parentId !== void 0 && forward.parentId !== inverse.parentId) {
|
|
839
|
-
const nextParent = node(current, forward.parentId);
|
|
840
|
-
if (nextParent) capture(change, forward.parentId, parentPresence(current, forward.parentId, without(nextParent.children, forward.treeNodeId)));
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
return change;
|
|
844
|
-
};
|
|
845
|
-
const sameNodePresence = (before, value) => {
|
|
846
|
-
if (before.status === "absent") return value === void 0;
|
|
847
|
-
return value !== void 0 && before.parentId === value.parentId && sameOrder$1(before.children, value.children) && require_ownership.sameStructuralValue(before.value, value.value);
|
|
848
|
-
};
|
|
849
|
-
const changeChanged = (change, value) => {
|
|
850
|
-
if (change.mode === "whole") return !require_ownership.sameStructuralValue(change.value, value);
|
|
851
|
-
if (!is(value)) return true;
|
|
852
|
-
if ((change.root.status === "present" ? change.root.id : void 0) !== value.rootId) return true;
|
|
853
|
-
for (const [id, before] of change.nodes) if (!sameNodePresence(before, node(value, id))) return true;
|
|
854
|
-
return false;
|
|
855
|
-
};
|
|
856
|
-
const is = (value) => require_ownership.isRecord(value) && require_ownership.isRecord(value.nodes);
|
|
857
|
-
const contains = (tree, id) => has$1(tree.nodes, id);
|
|
858
|
-
const validate = (value) => {
|
|
859
|
-
if (!is(value)) return false;
|
|
860
|
-
const ids = Object.keys(value.nodes);
|
|
861
|
-
if (ids.length === 0) return value.rootId === void 0;
|
|
862
|
-
if (typeof value.rootId !== "string" || !has$1(value.nodes, value.rootId)) return false;
|
|
863
|
-
for (const id of ids) {
|
|
864
|
-
const entry = value.nodes[id];
|
|
865
|
-
if (!require_ownership.isRecord(entry) || !Array.isArray(entry.children)) return false;
|
|
866
|
-
if (entry.children.some((child) => typeof child !== "string") || new Set(entry.children).size !== entry.children.length) return false;
|
|
867
|
-
if (entry.parentId !== void 0 && typeof entry.parentId !== "string") return false;
|
|
868
|
-
if (id === value.rootId) {
|
|
869
|
-
if (entry.parentId !== void 0) return false;
|
|
870
|
-
} else if (entry.parentId === void 0 || !has$1(value.nodes, entry.parentId)) return false;
|
|
871
|
-
for (const child of entry.children) {
|
|
872
|
-
const childNode = value.nodes[child];
|
|
873
|
-
if (!childNode || childNode.parentId !== id) return false;
|
|
874
|
-
}
|
|
875
366
|
}
|
|
876
|
-
const
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
for (const child of entry.children) stack.push(child);
|
|
885
|
-
}
|
|
886
|
-
return seen.size === ids.length;
|
|
367
|
+
const after = tree.rootId ?? null;
|
|
368
|
+
if (nodes.length || fact.before !== after) changes.push({
|
|
369
|
+
kind: "tree",
|
|
370
|
+
at: fact.at,
|
|
371
|
+
before: fact.before,
|
|
372
|
+
after,
|
|
373
|
+
nodes: nodes.sort((a, b) => lexical(a.id, b.id))
|
|
374
|
+
});
|
|
887
375
|
};
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
if (!
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
if (
|
|
917
|
-
|
|
376
|
+
/** One first-touch record per owning container, never a per-write operation log. */
|
|
377
|
+
var ChangeRecorder = class {
|
|
378
|
+
facts = /* @__PURE__ */ new Set();
|
|
379
|
+
groups = /* @__PURE__ */ new WeakMap();
|
|
380
|
+
index;
|
|
381
|
+
indexedGroups = false;
|
|
382
|
+
resetBefore;
|
|
383
|
+
constructor(state) {
|
|
384
|
+
this.state = state;
|
|
385
|
+
}
|
|
386
|
+
indexGroups() {
|
|
387
|
+
const index = this.index ??= new require_issue.AddressIndex();
|
|
388
|
+
if (!this.indexedGroups) {
|
|
389
|
+
this.indexedGroups = true;
|
|
390
|
+
for (const fact of this.facts) if (fact.kind === "members") {
|
|
391
|
+
index.add(fact.at, fact);
|
|
392
|
+
require_issue.profile.recorder("indexedGroups");
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return index;
|
|
396
|
+
}
|
|
397
|
+
covered(at) {
|
|
398
|
+
return this.resetBefore !== void 0 || this.indexedGroups && this.index.someAncestor(at, (fact) => fact.kind === "members" && fact.at.length < at.length && firstMember(fact, at[fact.at.length]) !== void 0);
|
|
399
|
+
}
|
|
400
|
+
absorb(at, node, value, present) {
|
|
401
|
+
const index = this.indexGroups();
|
|
402
|
+
const children = [];
|
|
403
|
+
index.query((fact) => {
|
|
404
|
+
if (fact.kind === "members") {
|
|
405
|
+
if (fact.at.length < at.length) {
|
|
406
|
+
if (fact.at.length + 1 === at.length) {
|
|
407
|
+
const member = firstMember(fact, at[fact.at.length]);
|
|
408
|
+
if (member) children.push({
|
|
409
|
+
kind: "member",
|
|
410
|
+
at,
|
|
411
|
+
group: fact,
|
|
412
|
+
member
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (fact.order && require_issue.contains(at, fact.at)) children.push({
|
|
418
|
+
kind: "order",
|
|
419
|
+
at: fact.at,
|
|
420
|
+
group: fact
|
|
421
|
+
});
|
|
422
|
+
for (const member of fact.members?.values() ?? []) {
|
|
423
|
+
if (!member) continue;
|
|
424
|
+
const memberAt = fact.at.concat(member.key);
|
|
425
|
+
if (require_issue.contains(at, memberAt)) children.push({
|
|
426
|
+
kind: "member",
|
|
427
|
+
at: memberAt,
|
|
428
|
+
group: fact,
|
|
429
|
+
member
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
} else if (require_issue.contains(at, fact.at)) children.push(fact);
|
|
433
|
+
})(at);
|
|
434
|
+
if (present) value = reconstructBefore(node, value, children, at.length);
|
|
435
|
+
this.releaseCovered(children);
|
|
436
|
+
return value;
|
|
918
437
|
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
if (
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
438
|
+
releaseCovered(children) {
|
|
439
|
+
let changedGroups;
|
|
440
|
+
for (const child of children) {
|
|
441
|
+
require_issue.profile.recorder("absorbed");
|
|
442
|
+
if (child.kind === "member") {
|
|
443
|
+
const { group, member } = child;
|
|
444
|
+
forgetMember(group, member.key);
|
|
445
|
+
(changedGroups ??= /* @__PURE__ */ new Set()).add(group);
|
|
446
|
+
} else if (child.kind === "order") {
|
|
447
|
+
child.group.order = void 0;
|
|
448
|
+
(changedGroups ??= /* @__PURE__ */ new Set()).add(child.group);
|
|
449
|
+
} else this.unregister(child);
|
|
450
|
+
}
|
|
451
|
+
for (const group of changedGroups ?? []) if (emptyGroup(group)) this.unregister(group);
|
|
452
|
+
}
|
|
453
|
+
register(fact) {
|
|
454
|
+
this.facts.add(fact);
|
|
455
|
+
if (fact.kind === "members") {
|
|
456
|
+
this.groups.set(fact.parent, fact);
|
|
457
|
+
require_issue.profile.recorder("groups");
|
|
458
|
+
if (!this.indexedGroups) return;
|
|
459
|
+
require_issue.profile.recorder("indexedGroups");
|
|
460
|
+
}
|
|
461
|
+
(this.index ??= new require_issue.AddressIndex()).add(fact.at, fact);
|
|
462
|
+
}
|
|
463
|
+
unregister(fact) {
|
|
464
|
+
this.facts.delete(fact);
|
|
465
|
+
this.index?.delete(fact.at, fact);
|
|
466
|
+
if (fact.kind === "members") this.groups.delete(fact.parent);
|
|
467
|
+
}
|
|
468
|
+
member(container, key, definition, physicalKey) {
|
|
469
|
+
if (this.resetBefore) return;
|
|
470
|
+
const { node } = definition;
|
|
471
|
+
let group = this.groups.get(container.parent);
|
|
472
|
+
if (group?.layout === "dynamic" && group.members?.has(key)) return;
|
|
473
|
+
if (group?.layout === "fixed" && definition.kind === "fixed" && group.members[definition.slot]) return;
|
|
474
|
+
const at = this.indexedGroups || node.kind !== "field" ? container.at.concat(key) : void 0;
|
|
475
|
+
if (at && this.covered(at)) return;
|
|
476
|
+
const present = Object.hasOwn(container.parent, physicalKey);
|
|
477
|
+
let value = container.parent[physicalKey];
|
|
478
|
+
if (node.kind !== "field") value = this.absorb(at, node, value, present);
|
|
479
|
+
if (!group || !this.facts.has(group)) group = this.createGroup(container);
|
|
480
|
+
const member = {
|
|
481
|
+
key,
|
|
482
|
+
node,
|
|
483
|
+
present,
|
|
953
484
|
value
|
|
954
485
|
};
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
return {
|
|
978
|
-
status: "changed",
|
|
979
|
-
inverse: [inverse]
|
|
980
|
-
};
|
|
981
|
-
};
|
|
982
|
-
const remove = (tree, operation) => {
|
|
983
|
-
const first = node(tree, operation.treeNodeId);
|
|
984
|
-
if (!first) return rejected$2("missing-tree-node", `Tree node '${operation.treeNodeId}' does not exist.`);
|
|
985
|
-
const parentId = first.parentId;
|
|
986
|
-
const parentNode = parentId === void 0 ? void 0 : node(tree, parentId);
|
|
987
|
-
const originalIndex = parentNode ? childIndex(parentNode, operation.treeNodeId) : 0;
|
|
988
|
-
if (parentId !== void 0 && (!parentNode || originalIndex < 0) || parentId === void 0 && tree.rootId !== operation.treeNodeId) return rejected$2("invalid-tree", "Tree parent links are inconsistent.");
|
|
989
|
-
const entries = [];
|
|
990
|
-
const seen = /* @__PURE__ */ new Set();
|
|
991
|
-
const stack = [{
|
|
992
|
-
id: operation.treeNodeId,
|
|
993
|
-
parentId,
|
|
994
|
-
index: originalIndex
|
|
995
|
-
}];
|
|
996
|
-
while (stack.length) {
|
|
997
|
-
const frame = stack.pop();
|
|
998
|
-
const entry = node(tree, frame.id);
|
|
999
|
-
if (!entry || seen.has(frame.id) || entry.parentId !== frame.parentId) return rejected$2("invalid-tree", "Tree contains an invalid descendant.");
|
|
1000
|
-
seen.add(frame.id);
|
|
1001
|
-
entries.push({
|
|
1002
|
-
id: frame.id,
|
|
1003
|
-
parentId: frame.parentId,
|
|
1004
|
-
index: frame.index,
|
|
1005
|
-
value: require_ownership.cloneValue(entry.value, "inverse")
|
|
1006
|
-
});
|
|
1007
|
-
for (let index = entry.children.length - 1; index >= 0; index -= 1) {
|
|
1008
|
-
const childId = entry.children[index];
|
|
1009
|
-
const child = node(tree, childId);
|
|
1010
|
-
if (!child || child.parentId !== frame.id) return rejected$2("invalid-tree", "Tree child links are inconsistent.");
|
|
1011
|
-
stack.push({
|
|
1012
|
-
id: childId,
|
|
1013
|
-
parentId: frame.id,
|
|
1014
|
-
index
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
if (parentNode) parentNode.children.splice(originalIndex, 1);
|
|
1019
|
-
for (let index = entries.length - 1; index >= 0; index -= 1) delete tree.nodes[entries[index].id];
|
|
1020
|
-
if (tree.rootId === operation.treeNodeId) delete tree.rootId;
|
|
1021
|
-
return {
|
|
1022
|
-
status: "changed",
|
|
1023
|
-
inverse: entries.map((entry) => ({
|
|
1024
|
-
type: "tree.insert",
|
|
1025
|
-
at: operation.at,
|
|
1026
|
-
treeNodeId: entry.id,
|
|
1027
|
-
...entry.parentId === void 0 ? {} : { parentId: entry.parentId },
|
|
1028
|
-
index: entry.index,
|
|
1029
|
-
value: entry.value
|
|
1030
|
-
}))
|
|
1031
|
-
};
|
|
1032
|
-
};
|
|
1033
|
-
const move = (tree, operation) => {
|
|
1034
|
-
const entry = node(tree, operation.treeNodeId);
|
|
1035
|
-
if (!entry) return rejected$2("missing-tree-node", `Tree node '${operation.treeNodeId}' does not exist.`);
|
|
1036
|
-
if (!validIndex(operation.index)) return rejected$2("invalid-tree-index", "Tree index is invalid.");
|
|
1037
|
-
if (tree.rootId === operation.treeNodeId) return rejected$2("invalid-tree", "The root cannot be moved.");
|
|
1038
|
-
if (operation.parentId === void 0) return rejected$2("invalid-tree", "Only the root can have no parent.");
|
|
1039
|
-
const nextParent = node(tree, operation.parentId);
|
|
1040
|
-
if (!nextParent) return rejected$2("missing-tree-parent", `Tree parent '${operation.parentId}' does not exist.`);
|
|
1041
|
-
if (operation.parentId === operation.treeNodeId) return rejected$2("tree-cycle", "A tree node cannot parent itself.");
|
|
1042
|
-
const descendants = [operation.treeNodeId];
|
|
1043
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1044
|
-
while (descendants.length) {
|
|
1045
|
-
const id = descendants.pop();
|
|
1046
|
-
if (seen.has(id)) return rejected$2("invalid-tree", "Tree contains a cycle.");
|
|
1047
|
-
seen.add(id);
|
|
1048
|
-
const current = node(tree, id);
|
|
1049
|
-
if (!current) return rejected$2("invalid-tree", "Tree contains a missing node.");
|
|
1050
|
-
for (const childId of current.children) {
|
|
1051
|
-
const child = node(tree, childId);
|
|
1052
|
-
if (!child || child.parentId !== id) return rejected$2("invalid-tree", "Tree child links are inconsistent.");
|
|
1053
|
-
if (childId === operation.parentId) return rejected$2("tree-cycle", "Tree move would create a cycle.");
|
|
1054
|
-
descendants.push(childId);
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
const parentId = entry.parentId;
|
|
1058
|
-
const currentParent = parentId === void 0 ? void 0 : node(tree, parentId);
|
|
1059
|
-
if (!currentParent || parentId === void 0) return rejected$2("invalid-tree", "Tree parent links are inconsistent.");
|
|
1060
|
-
const originalIndex = childIndex(currentParent, operation.treeNodeId);
|
|
1061
|
-
if (originalIndex < 0) return rejected$2("invalid-tree", "Tree parent links are inconsistent.");
|
|
1062
|
-
currentParent.children.splice(originalIndex, 1);
|
|
1063
|
-
const nextIndex = Math.min(operation.index ?? nextParent.children.length, nextParent.children.length);
|
|
1064
|
-
if (parentId === operation.parentId && nextIndex === originalIndex) {
|
|
1065
|
-
currentParent.children.splice(originalIndex, 0, operation.treeNodeId);
|
|
1066
|
-
return { status: "unchanged" };
|
|
1067
|
-
}
|
|
1068
|
-
nextParent.children.splice(nextIndex, 0, operation.treeNodeId);
|
|
1069
|
-
entry.parentId = operation.parentId;
|
|
1070
|
-
return {
|
|
1071
|
-
status: "changed",
|
|
1072
|
-
inverse: [{
|
|
1073
|
-
type: "tree.move",
|
|
1074
|
-
at: operation.at,
|
|
1075
|
-
treeNodeId: operation.treeNodeId,
|
|
1076
|
-
parentId,
|
|
1077
|
-
index: originalIndex
|
|
1078
|
-
}]
|
|
1079
|
-
};
|
|
1080
|
-
};
|
|
1081
|
-
//#endregion
|
|
1082
|
-
//#region core/src/mutation/execute-tree.ts
|
|
1083
|
-
const outcome = (operation, value) => value.status === "rejected" ? {
|
|
1084
|
-
status: "rejected",
|
|
1085
|
-
issue: from(operation, value.code, value.message)
|
|
1086
|
-
} : value;
|
|
1087
|
-
const executeTree = (root, target, operation, copyPayload) => {
|
|
1088
|
-
if (target.node.kind !== "tree") return {
|
|
1089
|
-
status: "rejected",
|
|
1090
|
-
issue: from(operation, "invalid-address", "Tree target is invalid.")
|
|
1091
|
-
};
|
|
1092
|
-
const own = (value) => copyPayload ? require_ownership.ownPayload(value) : value;
|
|
1093
|
-
const absent = target.value === void 0 && "optional" in target.node && target.node.optional === true;
|
|
1094
|
-
if (operation.type === "tree.replace") {
|
|
1095
|
-
if (absent) {
|
|
1096
|
-
if (!validate(operation.value)) return {
|
|
1097
|
-
status: "rejected",
|
|
1098
|
-
issue: from(operation, "invalid-tree", "Tree replacement is malformed.")
|
|
1099
|
-
};
|
|
1100
|
-
target.parent[target.key] = own(operation.value);
|
|
1101
|
-
return {
|
|
1102
|
-
status: "changed",
|
|
1103
|
-
inverse: [{
|
|
1104
|
-
type: "value.clear",
|
|
1105
|
-
at: operation.at
|
|
1106
|
-
}]
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
if (!is(target.value)) return {
|
|
1110
|
-
status: "rejected",
|
|
1111
|
-
issue: from(operation, "invalid-address", "Tree target is invalid.")
|
|
1112
|
-
};
|
|
1113
|
-
const result = replace(target.value, operation);
|
|
1114
|
-
if (result.status === "changed") require_runtime.set(root, operation.at, require_ownership.ownPayload(operation.value));
|
|
1115
|
-
return outcome(operation, result);
|
|
1116
|
-
}
|
|
1117
|
-
if (!is(target.value)) return {
|
|
1118
|
-
status: "rejected",
|
|
1119
|
-
issue: from(operation, "invalid-address", "Tree target is invalid.")
|
|
1120
|
-
};
|
|
1121
|
-
if (operation.type === "tree.insert") return outcome(operation, insert(target.value, operation, own(operation.value)));
|
|
1122
|
-
if (operation.type === "tree.set") return outcome(operation, set(target.value, operation, own(operation.value)));
|
|
1123
|
-
if (operation.type === "tree.remove") return outcome(operation, remove(target.value, operation));
|
|
1124
|
-
return outcome(operation, move(target.value, operation));
|
|
1125
|
-
};
|
|
1126
|
-
//#endregion
|
|
1127
|
-
//#region core/src/mutation/execute-value.ts
|
|
1128
|
-
const rejected$1 = (operation, code, message) => ({
|
|
1129
|
-
status: "rejected",
|
|
1130
|
-
issue: from(operation, code, message)
|
|
1131
|
-
});
|
|
1132
|
-
const executeValue = (target, operation, copyPayload) => {
|
|
1133
|
-
const own = (value) => copyPayload ? require_ownership.ownPayload(value) : value;
|
|
1134
|
-
if (operation.type === "field.set" || operation.type === "field.clear") {
|
|
1135
|
-
const existed = Object.prototype.hasOwnProperty.call(target.parent, target.key);
|
|
1136
|
-
if (operation.type === "field.clear") {
|
|
1137
|
-
if (target.node.kind !== "field" || !target.node.optional) return rejected$1(operation, "required-field", "Only optional fields can be cleared.");
|
|
1138
|
-
if (!existed) return { status: "unchanged" };
|
|
1139
|
-
const inverse = {
|
|
1140
|
-
type: "field.set",
|
|
1141
|
-
at: operation.at,
|
|
1142
|
-
value: target.value
|
|
1143
|
-
};
|
|
1144
|
-
delete target.parent[target.key];
|
|
1145
|
-
return {
|
|
1146
|
-
status: "changed",
|
|
1147
|
-
inverse: [inverse]
|
|
1148
|
-
};
|
|
1149
|
-
}
|
|
1150
|
-
if (existed && Object.is(target.value, operation.value)) return { status: "unchanged" };
|
|
1151
|
-
const inverse = existed ? {
|
|
1152
|
-
type: "field.set",
|
|
1153
|
-
at: operation.at,
|
|
1154
|
-
value: target.value
|
|
486
|
+
if (group.layout === "dynamic") (group.members ??= /* @__PURE__ */ new Map()).set(key, member);
|
|
487
|
+
else if (definition.kind === "fixed") group.members[definition.slot] = member;
|
|
488
|
+
else throw new Error("A fixed member group requires a fixed schema member.");
|
|
489
|
+
require_issue.profile.recorder("facts");
|
|
490
|
+
}
|
|
491
|
+
reset() {
|
|
492
|
+
if (this.resetBefore) return;
|
|
493
|
+
const value = this.absorb([], this.state.schema, this.state.document, true);
|
|
494
|
+
this.resetBefore = { value };
|
|
495
|
+
this.groups = /* @__PURE__ */ new WeakMap();
|
|
496
|
+
require_issue.profile.recorder("facts");
|
|
497
|
+
}
|
|
498
|
+
createGroup(container) {
|
|
499
|
+
const group = container.layout.kind === "fixed" ? {
|
|
500
|
+
kind: "members",
|
|
501
|
+
at: container.at,
|
|
502
|
+
node: container.node,
|
|
503
|
+
parent: container.parent,
|
|
504
|
+
value: container.value,
|
|
505
|
+
layout: "fixed",
|
|
506
|
+
definitions: container.layout.members,
|
|
507
|
+
members: new Array(container.layout.members.size)
|
|
1155
508
|
} : {
|
|
1156
|
-
|
|
1157
|
-
at:
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
};
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
509
|
+
kind: "members",
|
|
510
|
+
at: container.at,
|
|
511
|
+
node: container.node,
|
|
512
|
+
parent: container.parent,
|
|
513
|
+
value: container.value,
|
|
514
|
+
layout: "dynamic",
|
|
515
|
+
members: void 0
|
|
516
|
+
};
|
|
517
|
+
this.register(group);
|
|
518
|
+
return group;
|
|
519
|
+
}
|
|
520
|
+
order(container) {
|
|
521
|
+
require_issue.profile.recorder("orderCaptures");
|
|
522
|
+
let group = this.groups.get(container.parent);
|
|
523
|
+
if (group?.order || this.covered(container.at)) return;
|
|
524
|
+
group ??= this.createGroup(container);
|
|
525
|
+
group.order = orderOf(container.node, container.value);
|
|
526
|
+
require_issue.profile.recorder("orderSnapshots");
|
|
527
|
+
require_issue.profile.recorder("orderItems", group.order.length);
|
|
528
|
+
}
|
|
529
|
+
tree(container, id) {
|
|
530
|
+
const { at, value: tree } = container;
|
|
531
|
+
if (this.covered(at)) return;
|
|
532
|
+
const index = this.index ??= new require_issue.AddressIndex();
|
|
533
|
+
let fact;
|
|
534
|
+
for (const item of index.exact(at) ?? []) if (item.kind === "tree") fact = item;
|
|
535
|
+
if (!fact) {
|
|
536
|
+
fact = {
|
|
537
|
+
kind: "tree",
|
|
538
|
+
at,
|
|
539
|
+
before: tree.rootId ?? null,
|
|
540
|
+
nodes: /* @__PURE__ */ new Map()
|
|
1175
541
|
};
|
|
542
|
+
this.register(fact);
|
|
1176
543
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
value: require_ownership.ownPayload(target.value, "inverse")
|
|
1181
|
-
};
|
|
1182
|
-
target.parent[target.key] = own(operation.value);
|
|
1183
|
-
return {
|
|
1184
|
-
status: "changed",
|
|
1185
|
-
inverse: [inverse]
|
|
1186
|
-
};
|
|
1187
|
-
}
|
|
1188
|
-
if (operation.type === "dict.replace" && target.value === void 0) {
|
|
1189
|
-
if (!("optional" in target.node) || !target.node.optional) return rejected$1(operation, "invalid-address", "Dictionary target is invalid.");
|
|
1190
|
-
const inverse = {
|
|
1191
|
-
type: "value.clear",
|
|
1192
|
-
at: operation.at
|
|
1193
|
-
};
|
|
1194
|
-
target.parent[target.key] = own(operation.value);
|
|
1195
|
-
return {
|
|
1196
|
-
status: "changed",
|
|
1197
|
-
inverse: [inverse]
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
if (!require_ownership.isRecord(target.value)) return rejected$1(operation, "invalid-address", "Dictionary target is invalid.");
|
|
1201
|
-
if (operation.type === "dict.replace") {
|
|
1202
|
-
if (require_ownership.sameStructuralValue(target.value, operation.value)) return { status: "unchanged" };
|
|
1203
|
-
const inverse = {
|
|
1204
|
-
type: "dict.replace",
|
|
1205
|
-
at: operation.at,
|
|
1206
|
-
value: require_ownership.cloneValue(target.value, "inverse")
|
|
1207
|
-
};
|
|
1208
|
-
target.parent[target.key] = own(operation.value);
|
|
1209
|
-
return {
|
|
1210
|
-
status: "changed",
|
|
1211
|
-
inverse: [inverse]
|
|
1212
|
-
};
|
|
1213
|
-
}
|
|
1214
|
-
const existed = Object.prototype.hasOwnProperty.call(target.value, operation.key);
|
|
1215
|
-
if (operation.type === "dict.delete") {
|
|
1216
|
-
if (!existed) return { status: "unchanged" };
|
|
1217
|
-
const inverse = {
|
|
1218
|
-
type: "dict.set",
|
|
1219
|
-
at: operation.at,
|
|
1220
|
-
key: operation.key,
|
|
1221
|
-
value: target.value[operation.key]
|
|
1222
|
-
};
|
|
1223
|
-
delete target.value[operation.key];
|
|
1224
|
-
return {
|
|
1225
|
-
status: "changed",
|
|
1226
|
-
inverse: [inverse]
|
|
1227
|
-
};
|
|
1228
|
-
}
|
|
1229
|
-
if (existed && Object.is(target.value[operation.key], operation.value)) return { status: "unchanged" };
|
|
1230
|
-
const inverse = existed ? {
|
|
1231
|
-
type: "dict.set",
|
|
1232
|
-
at: operation.at,
|
|
1233
|
-
key: operation.key,
|
|
1234
|
-
value: target.value[operation.key]
|
|
1235
|
-
} : {
|
|
1236
|
-
type: "dict.delete",
|
|
1237
|
-
at: operation.at,
|
|
1238
|
-
key: operation.key
|
|
1239
|
-
};
|
|
1240
|
-
target.value[operation.key] = operation.value;
|
|
1241
|
-
return {
|
|
1242
|
-
status: "changed",
|
|
1243
|
-
inverse: [inverse]
|
|
1244
|
-
};
|
|
1245
|
-
};
|
|
1246
|
-
//#endregion
|
|
1247
|
-
//#region core/src/mutation/execute.ts
|
|
1248
|
-
const hasOwn$1 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1249
|
-
const executeClear = (target, operation) => {
|
|
1250
|
-
if (!("optional" in target.node) || !target.node.optional) return {
|
|
1251
|
-
status: "rejected",
|
|
1252
|
-
issue: from(operation, "required-field", "Only optional values can be cleared.")
|
|
1253
|
-
};
|
|
1254
|
-
if (!hasOwn$1(target.parent, target.key)) return { status: "unchanged" };
|
|
1255
|
-
if (target.node.kind !== "field" && target.node.kind !== "variant" && target.node.kind !== "dict" && target.node.kind !== "list" && target.node.kind !== "tree") return {
|
|
1256
|
-
status: "rejected",
|
|
1257
|
-
issue: from(operation, "invalid-operation", "This optional value has no clear operation.")
|
|
1258
|
-
};
|
|
1259
|
-
const inverse = (() => {
|
|
1260
|
-
switch (target.node.kind) {
|
|
1261
|
-
case "field": return {
|
|
1262
|
-
type: "field.set",
|
|
1263
|
-
at: operation.at,
|
|
1264
|
-
value: target.value
|
|
1265
|
-
};
|
|
1266
|
-
case "variant": return {
|
|
1267
|
-
type: "variant.replace",
|
|
1268
|
-
at: operation.at,
|
|
1269
|
-
value: require_ownership.cloneValue(target.value, "inverse")
|
|
1270
|
-
};
|
|
1271
|
-
case "dict": return {
|
|
1272
|
-
type: "dict.replace",
|
|
1273
|
-
at: operation.at,
|
|
1274
|
-
value: require_ownership.cloneValue(target.value, "inverse")
|
|
1275
|
-
};
|
|
1276
|
-
case "list":
|
|
1277
|
-
if (!Array.isArray(target.value)) return {
|
|
1278
|
-
type: "list.replace",
|
|
1279
|
-
at: operation.at,
|
|
1280
|
-
value: [],
|
|
1281
|
-
keys: []
|
|
1282
|
-
};
|
|
1283
|
-
return {
|
|
1284
|
-
type: "list.replace",
|
|
1285
|
-
at: operation.at,
|
|
1286
|
-
value: require_ownership.cloneValue(target.value, "inverse"),
|
|
1287
|
-
keys: target.value.map(target.node.keyOf)
|
|
1288
|
-
};
|
|
1289
|
-
case "tree": return {
|
|
1290
|
-
type: "tree.replace",
|
|
1291
|
-
at: operation.at,
|
|
1292
|
-
value: require_ownership.cloneValue(target.value, "inverse")
|
|
1293
|
-
};
|
|
544
|
+
if (id !== void 0 && !fact.nodes.has(id)) {
|
|
545
|
+
require_issue.profile.recorder("treeNodes");
|
|
546
|
+
fact.nodes.set(id, Object.hasOwn(tree.nodes, id) ? copyNode(tree.nodes[id]) : void 0);
|
|
1294
547
|
}
|
|
1295
|
-
})();
|
|
1296
|
-
delete target.parent[target.key];
|
|
1297
|
-
return {
|
|
1298
|
-
status: "changed",
|
|
1299
|
-
inverse: [inverse]
|
|
1300
|
-
};
|
|
1301
|
-
};
|
|
1302
|
-
const invalidTarget = (operation) => ({
|
|
1303
|
-
status: "rejected",
|
|
1304
|
-
issue: from(operation, "invalid-address", `Operation '${operation.type}' does not match the schema address.`)
|
|
1305
|
-
});
|
|
1306
|
-
const execute = (root, operation, target, copyPayload) => {
|
|
1307
|
-
if (operation.type === "value.clear") return !target ? invalidTarget(operation) : executeClear(target, operation);
|
|
1308
|
-
if (operation.type === "field.set" || operation.type === "field.clear") return !target || target.node.kind !== "field" ? invalidTarget(operation) : executeValue(target, operation, copyPayload);
|
|
1309
|
-
if (operation.type === "variant.replace") return !target || target.node.kind !== "variant" ? invalidTarget(operation) : executeValue(target, operation, copyPayload);
|
|
1310
|
-
if (operation.type === "dict.set" || operation.type === "dict.delete" || operation.type === "dict.replace") return !target || target.node.kind !== "dict" ? invalidTarget(operation) : executeValue(target, operation, copyPayload);
|
|
1311
|
-
if (operation.type === "entity.create" || operation.type === "entity.remove" || operation.type === "entity.move") return !target || target.node.kind !== "table" && target.node.kind !== "map" ? invalidTarget(operation) : executeEntity(target, operation, copyPayload);
|
|
1312
|
-
if (operation.type === "list.insert" || operation.type === "list.move" || operation.type === "list.remove" || operation.type === "list.replace") return !target || target.node.kind !== "list" ? invalidTarget(operation) : executeList(target, operation, copyPayload);
|
|
1313
|
-
if (operation.type === "tree.insert" || operation.type === "tree.move" || operation.type === "tree.remove" || operation.type === "tree.set" || operation.type === "tree.replace") return !target || target.node.kind !== "tree" ? invalidTarget(operation) : executeTree(root, target, operation, copyPayload);
|
|
1314
|
-
return {
|
|
1315
|
-
status: "rejected",
|
|
1316
|
-
issue: from(operation, "unknown-operation", "Unknown document operation.")
|
|
1317
|
-
};
|
|
1318
|
-
};
|
|
1319
|
-
//#endregion
|
|
1320
|
-
//#region core/src/mutation/journal.ts
|
|
1321
|
-
const indexNode = () => ({ children: /* @__PURE__ */ new Map() });
|
|
1322
|
-
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1323
|
-
const sameAddress = (left, right) => {
|
|
1324
|
-
if (left.length !== right.length) return false;
|
|
1325
|
-
for (let index = 0; index < left.length; index += 1) if (left[index] !== right[index]) return false;
|
|
1326
|
-
return true;
|
|
1327
|
-
};
|
|
1328
|
-
const sameOrder = (left, right) => {
|
|
1329
|
-
if (left.length !== right.length) return false;
|
|
1330
|
-
for (let index = 0; index < left.length; index += 1) if (left[index] !== right[index]) return false;
|
|
1331
|
-
return true;
|
|
1332
|
-
};
|
|
1333
|
-
const replaceArray = (target, value) => {
|
|
1334
|
-
target.length = value.length;
|
|
1335
|
-
for (let index = 0; index < value.length; index += 1) target[index] = value[index];
|
|
1336
|
-
};
|
|
1337
|
-
const present = (value) => ({
|
|
1338
|
-
status: "present",
|
|
1339
|
-
value
|
|
1340
|
-
});
|
|
1341
|
-
const absent = Object.freeze({ status: "absent" });
|
|
1342
|
-
const inverseMismatch = (operation) => {
|
|
1343
|
-
throw new Error(`Mutation inverse does not match '${operation.type}'.`);
|
|
1344
|
-
};
|
|
1345
|
-
const ownsDescendant = (subject, address) => {
|
|
1346
|
-
if (subject.address.length >= address.length) return false;
|
|
1347
|
-
if (subject.kind === "variant") return true;
|
|
1348
|
-
if (subject.kind !== "entity") return false;
|
|
1349
|
-
return subject.entries.has(address[subject.address.length]);
|
|
1350
|
-
};
|
|
1351
|
-
const nodeAt = (root, address) => {
|
|
1352
|
-
let node = root;
|
|
1353
|
-
for (const segment of address) {
|
|
1354
|
-
node = node.children.get(segment);
|
|
1355
|
-
if (!node) return void 0;
|
|
1356
|
-
}
|
|
1357
|
-
return node;
|
|
1358
|
-
};
|
|
1359
|
-
const indexedSubjects = (root, address) => {
|
|
1360
|
-
const start = nodeAt(root, address);
|
|
1361
|
-
if (!start) return [];
|
|
1362
|
-
const subjects = [];
|
|
1363
|
-
const stack = [start];
|
|
1364
|
-
while (stack.length) {
|
|
1365
|
-
const node = stack.pop();
|
|
1366
|
-
if (!node) break;
|
|
1367
|
-
if (node.subject) subjects.push(node.subject);
|
|
1368
|
-
for (const child of node.children.values()) stack.push(child);
|
|
1369
|
-
}
|
|
1370
|
-
return subjects;
|
|
1371
|
-
};
|
|
1372
|
-
const writePresence = (root, address, before) => {
|
|
1373
|
-
const target = require_runtime.locate(root, address);
|
|
1374
|
-
if (!target) return;
|
|
1375
|
-
if (before.status === "present") target.parent[target.key] = before.value;
|
|
1376
|
-
else if (Array.isArray(target.parent)) target.parent.splice(Number(target.key), 1);
|
|
1377
|
-
else delete target.parent[String(target.key)];
|
|
1378
|
-
};
|
|
1379
|
-
const writeContainer = (root, address, value) => {
|
|
1380
|
-
const target = require_runtime.locate(root, address);
|
|
1381
|
-
if (!target) return;
|
|
1382
|
-
target.parent[target.key] = value;
|
|
1383
|
-
};
|
|
1384
|
-
const restoreDict = (value, state) => {
|
|
1385
|
-
if (state.mode === "whole") {
|
|
1386
|
-
for (const key of Object.keys(value)) delete value[key];
|
|
1387
|
-
for (const [key, entry] of Object.entries(state.value)) value[key] = entry;
|
|
1388
|
-
return;
|
|
1389
|
-
}
|
|
1390
|
-
for (const [key, before] of state.entries) if (before.status === "present") value[key] = before.value;
|
|
1391
|
-
else delete value[key];
|
|
1392
|
-
};
|
|
1393
|
-
const restoreList = (value, subject) => {
|
|
1394
|
-
if (subject.state.mode === "whole") {
|
|
1395
|
-
replaceArray(value, subject.state.value);
|
|
1396
|
-
return;
|
|
1397
548
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
else entries.delete(key);
|
|
1402
|
-
const restored = new Array(subject.state.order.length);
|
|
1403
|
-
for (let index = 0; index < subject.state.order.length; index += 1) restored[index] = entries.get(subject.state.order[index]);
|
|
1404
|
-
replaceArray(value, restored);
|
|
1405
|
-
};
|
|
1406
|
-
const entityCollection = (subject, value) => resolveEntityCollection(subject.node, value);
|
|
1407
|
-
const restoreEntity = (value, subject) => {
|
|
1408
|
-
const resolution = entityCollection(subject, value);
|
|
1409
|
-
if (resolution.status === "invalid") return;
|
|
1410
|
-
const collection = resolution.collection;
|
|
1411
|
-
for (const [id, before] of subject.entries) if (before.status === "present") collection.byId[id] = before.value;
|
|
1412
|
-
else delete collection.byId[id];
|
|
1413
|
-
if (collection.kind !== "table") return;
|
|
1414
|
-
if (subject.order.kind === "tracked") {
|
|
1415
|
-
replaceArray(collection.ids, subject.order.before);
|
|
1416
|
-
return;
|
|
1417
|
-
}
|
|
1418
|
-
if (subject.order.kind === "stable") restoreEntityOrder(collection.ids, subject.order.inverses);
|
|
1419
|
-
};
|
|
1420
|
-
const restoreRootSubject = (value, subject) => {
|
|
1421
|
-
if (subject.kind === "value") return subject.before.status === "present" ? subject.before.value : void 0;
|
|
1422
|
-
if (subject.kind === "variant") return subject.before.status === "present" ? subject.before.value : void 0;
|
|
1423
|
-
if (subject.kind === "dict") {
|
|
1424
|
-
if (require_ownership.isRecord(value)) restoreDict(value, subject.state);
|
|
1425
|
-
return value;
|
|
1426
|
-
}
|
|
1427
|
-
if (subject.kind === "entity") {
|
|
1428
|
-
restoreEntity(value, subject);
|
|
1429
|
-
return value;
|
|
1430
|
-
}
|
|
1431
|
-
if (subject.kind === "list") {
|
|
1432
|
-
if (Array.isArray(value)) restoreList(value, subject);
|
|
1433
|
-
return value;
|
|
1434
|
-
}
|
|
1435
|
-
return restoreChange(value, subject.state);
|
|
1436
|
-
};
|
|
1437
|
-
const restoreSubject = (value, prefix, subject) => {
|
|
1438
|
-
if (sameAddress(prefix, subject.address)) return restoreRootSubject(value, subject);
|
|
1439
|
-
const address = subject.address.slice(prefix.length);
|
|
1440
|
-
if (subject.kind === "value") {
|
|
1441
|
-
writePresence(value, address, subject.before);
|
|
1442
|
-
return value;
|
|
1443
|
-
}
|
|
1444
|
-
if (subject.kind === "variant") {
|
|
1445
|
-
writePresence(value, address, subject.before);
|
|
1446
|
-
return value;
|
|
1447
|
-
}
|
|
1448
|
-
const current = require_runtime.read(value, address);
|
|
1449
|
-
if (subject.kind === "dict") {
|
|
1450
|
-
if (require_ownership.isRecord(current)) restoreDict(current, subject.state);
|
|
1451
|
-
return value;
|
|
1452
|
-
}
|
|
1453
|
-
if (subject.kind === "entity") {
|
|
1454
|
-
restoreEntity(current, subject);
|
|
1455
|
-
return value;
|
|
1456
|
-
}
|
|
1457
|
-
if (subject.kind === "list") {
|
|
1458
|
-
if (Array.isArray(current)) restoreList(current, subject);
|
|
1459
|
-
return value;
|
|
1460
|
-
}
|
|
1461
|
-
const restored = restoreChange(current, subject.state);
|
|
1462
|
-
if (restored !== current) writeContainer(value, address, restored);
|
|
1463
|
-
return value;
|
|
1464
|
-
};
|
|
1465
|
-
const valueChanged = (root, subject) => {
|
|
1466
|
-
const target = require_runtime.locate(root, subject.address);
|
|
1467
|
-
const exists = !!target && hasOwn(target.parent, target.key);
|
|
1468
|
-
if (subject.before.status === "absent") return exists;
|
|
1469
|
-
return !exists || !Object.is(subject.before.value, target?.value);
|
|
1470
|
-
};
|
|
1471
|
-
const dictChanged = (root, subject) => {
|
|
1472
|
-
const value = require_runtime.read(root, subject.address);
|
|
1473
|
-
if (!require_ownership.isRecord(value)) return true;
|
|
1474
|
-
if (subject.state.mode === "whole") return !require_ownership.sameStructuralValue(subject.state.value, value);
|
|
1475
|
-
for (const [key, before] of subject.state.entries) {
|
|
1476
|
-
const exists = hasOwn(value, key);
|
|
1477
|
-
if (before.status === "absent" ? exists : !exists || !Object.is(before.value, value[key])) return true;
|
|
1478
|
-
}
|
|
1479
|
-
return false;
|
|
1480
|
-
};
|
|
1481
|
-
const listChanged = (root, subject) => {
|
|
1482
|
-
const value = require_runtime.read(root, subject.address);
|
|
1483
|
-
if (!Array.isArray(value)) return true;
|
|
1484
|
-
if (subject.state.mode === "whole") return !require_ownership.sameStructuralValue(subject.state.value, value);
|
|
1485
|
-
const positions = /* @__PURE__ */ new Map();
|
|
1486
|
-
const order = new Array(value.length);
|
|
1487
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
1488
|
-
const key = subject.node.keyOf(value[index]);
|
|
1489
|
-
order[index] = key;
|
|
1490
|
-
positions.set(key, index);
|
|
1491
|
-
}
|
|
1492
|
-
if (!sameOrder(subject.state.order, order)) return true;
|
|
1493
|
-
for (const [key, before] of subject.state.items) {
|
|
1494
|
-
const position = positions.get(key);
|
|
1495
|
-
if (before.status === "absent") {
|
|
1496
|
-
if (position !== void 0) return true;
|
|
1497
|
-
} else if (position === void 0 || !require_ownership.sameStructuralValue(before.value, value[position])) return true;
|
|
1498
|
-
}
|
|
1499
|
-
return false;
|
|
1500
|
-
};
|
|
1501
|
-
const commonOrderChanged = (before, after) => {
|
|
1502
|
-
const finalPositions = /* @__PURE__ */ new Map();
|
|
1503
|
-
for (let index = 0; index < after.length; index += 1) finalPositions.set(after[index], index);
|
|
1504
|
-
let previous = -1;
|
|
1505
|
-
for (const id of before) {
|
|
1506
|
-
const position = finalPositions.get(id);
|
|
1507
|
-
if (position === void 0) continue;
|
|
1508
|
-
if (position < previous) return true;
|
|
1509
|
-
previous = position;
|
|
1510
|
-
}
|
|
1511
|
-
return false;
|
|
1512
|
-
};
|
|
1513
|
-
const analyzeEntity = (root, subject) => {
|
|
1514
|
-
const resolution = entityCollection(subject, require_runtime.read(root, subject.address));
|
|
1515
|
-
const added = /* @__PURE__ */ new Set();
|
|
1516
|
-
const removed = /* @__PURE__ */ new Set();
|
|
1517
|
-
const updated = /* @__PURE__ */ new Set();
|
|
1518
|
-
if (resolution.status === "invalid") return {
|
|
1519
|
-
changed: true,
|
|
1520
|
-
added,
|
|
1521
|
-
removed,
|
|
1522
|
-
updated,
|
|
1523
|
-
orderChanged: false
|
|
1524
|
-
};
|
|
1525
|
-
const collection = resolution.collection;
|
|
1526
|
-
for (const [id, before] of subject.entries) {
|
|
1527
|
-
const exists = hasOwn(collection.byId, id);
|
|
1528
|
-
if (before.status === "absent") {
|
|
1529
|
-
if (exists) added.add(id);
|
|
1530
|
-
} else if (!exists) removed.add(id);
|
|
1531
|
-
else if (!require_ownership.sameStructuralValue(before.value, collection.byId[id])) updated.add(id);
|
|
1532
|
-
}
|
|
1533
|
-
const beforeOrder = subject.order.kind === "tracked" ? subject.order.before : void 0;
|
|
1534
|
-
const finalOrder = collection.kind === "table" ? collection.ids : [];
|
|
1535
|
-
const netOrderChanged = beforeOrder !== void 0 && !sameOrder(beforeOrder, finalOrder);
|
|
1536
|
-
return {
|
|
1537
|
-
changed: added.size > 0 || removed.size > 0 || updated.size > 0 || netOrderChanged,
|
|
1538
|
-
added,
|
|
1539
|
-
removed,
|
|
1540
|
-
updated,
|
|
1541
|
-
orderChanged: beforeOrder !== void 0 && commonOrderChanged(beforeOrder, finalOrder)
|
|
1542
|
-
};
|
|
1543
|
-
};
|
|
1544
|
-
const subjectChanged = (root, subject) => {
|
|
1545
|
-
if (subject.kind === "value") return valueChanged(root, subject);
|
|
1546
|
-
if (subject.kind === "variant") {
|
|
1547
|
-
const target = require_runtime.locate(root, subject.address);
|
|
1548
|
-
const exists = !!target && hasOwn(target.parent, target.key);
|
|
1549
|
-
return subject.before.status === "absent" ? exists : !exists || !require_ownership.sameStructuralValue(subject.before.value, target?.value);
|
|
1550
|
-
}
|
|
1551
|
-
if (subject.kind === "dict") return dictChanged(root, subject);
|
|
1552
|
-
if (subject.kind === "list") return listChanged(root, subject);
|
|
1553
|
-
return changeChanged(subject.state, require_runtime.read(root, subject.address));
|
|
1554
|
-
};
|
|
1555
|
-
const listOrderBefore = (value, node, inverse) => {
|
|
1556
|
-
const order = value.map(node.keyOf);
|
|
1557
|
-
if (inverse.type === "list.remove") return order.filter((key) => key !== inverse.key);
|
|
1558
|
-
if (inverse.type === "list.insert") {
|
|
1559
|
-
order.splice(require_runtime.index(order, inverse.anchor), 0, inverse.key);
|
|
1560
|
-
return order;
|
|
1561
|
-
}
|
|
1562
|
-
if (inverse.type === "list.move") {
|
|
1563
|
-
const position = order.indexOf(inverse.key);
|
|
1564
|
-
if (position >= 0) order.splice(position, 1);
|
|
1565
|
-
order.splice(require_runtime.index(order, inverse.anchor), 0, inverse.key);
|
|
1566
|
-
}
|
|
1567
|
-
return order;
|
|
1568
|
-
};
|
|
1569
|
-
const recordListPresence = (state, inverse) => {
|
|
1570
|
-
if (inverse.type === "list.remove" && !state.items.has(inverse.key)) state.items.set(inverse.key, absent);
|
|
1571
|
-
else if (inverse.type === "list.insert" && !state.items.has(inverse.key)) state.items.set(inverse.key, present(inverse.value));
|
|
1572
|
-
};
|
|
1573
|
-
const entityOrderInverse = (operation, inverse) => {
|
|
1574
|
-
if (inverse.type === "entity.remove") return {
|
|
1575
|
-
kind: "remove",
|
|
1576
|
-
ids: inverse.ids
|
|
1577
|
-
};
|
|
1578
|
-
if (inverse.type === "entity.create") {
|
|
1579
|
-
if (!inverse.positions || inverse.positions.length !== inverse.entries.length) return inverseMismatch(operation);
|
|
1580
|
-
return {
|
|
1581
|
-
kind: "restore",
|
|
1582
|
-
ids: inverse.entries.map((entry) => entry.id),
|
|
1583
|
-
positions: inverse.positions
|
|
1584
|
-
};
|
|
1585
|
-
}
|
|
1586
|
-
if (inverse.type === "entity.move") return {
|
|
1587
|
-
kind: "move",
|
|
1588
|
-
id: inverse.id,
|
|
1589
|
-
...inverse.anchor ? { anchor: inverse.anchor } : {}
|
|
1590
|
-
};
|
|
1591
|
-
return inverseMismatch(operation);
|
|
1592
|
-
};
|
|
1593
|
-
const removeEntityIds = (order, ids) => {
|
|
1594
|
-
const removed = new Set(ids);
|
|
1595
|
-
let writeIndex = 0;
|
|
1596
|
-
for (const id of order) if (!removed.has(id)) order[writeIndex++] = id;
|
|
1597
|
-
order.length = writeIndex;
|
|
1598
|
-
};
|
|
1599
|
-
const restoreEntityOrder = (order, inverses) => {
|
|
1600
|
-
for (let index = inverses.length - 1; index >= 0; index -= 1) {
|
|
1601
|
-
const inverse = inverses[index];
|
|
1602
|
-
if (inverse.kind === "remove") {
|
|
1603
|
-
removeEntityIds(order, inverse.ids);
|
|
1604
|
-
continue;
|
|
1605
|
-
}
|
|
1606
|
-
if (inverse.kind === "restore") {
|
|
1607
|
-
require_runtime.restore(order, inverse.ids, inverse.positions);
|
|
1608
|
-
continue;
|
|
1609
|
-
}
|
|
1610
|
-
const position = order.indexOf(inverse.id);
|
|
1611
|
-
if (position < 0 || !require_runtime.valid(order, inverse.anchor)) throw new Error(`Entity order inverse cannot restore '${inverse.id}'.`);
|
|
1612
|
-
const nextIndex = require_runtime.afterRemove(order, position, inverse.anchor);
|
|
1613
|
-
order.splice(position, 1);
|
|
1614
|
-
order.splice(nextIndex, 0, inverse.id);
|
|
1615
|
-
}
|
|
1616
|
-
};
|
|
1617
|
-
const treeOperation = (operation) => operation.type.startsWith("tree.");
|
|
1618
|
-
const createChangeJournal = (root) => {
|
|
1619
|
-
const active = /* @__PURE__ */ new Set();
|
|
1620
|
-
const index = indexNode();
|
|
1621
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
1622
|
-
const add = (subject) => {
|
|
1623
|
-
active.add(subject);
|
|
1624
|
-
const candidates = buckets.get(subject.addressHash);
|
|
1625
|
-
if (candidates) candidates.push(subject);
|
|
1626
|
-
else buckets.set(subject.addressHash, [subject]);
|
|
1627
|
-
let node = index;
|
|
1628
|
-
for (const segment of subject.address) {
|
|
1629
|
-
const existing = node.children.get(segment);
|
|
1630
|
-
if (existing) node = existing;
|
|
1631
|
-
else {
|
|
1632
|
-
const created = indexNode();
|
|
1633
|
-
node.children.set(segment, created);
|
|
1634
|
-
node = created;
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
if (node.subject) throw new Error(`Change journal already owns '${subject.address.join(".")}'.`);
|
|
1638
|
-
node.subject = subject;
|
|
1639
|
-
require_ownership.profile.journal.subject();
|
|
1640
|
-
return subject;
|
|
1641
|
-
};
|
|
1642
|
-
const remove = (subject) => {
|
|
1643
|
-
if (!active.delete(subject)) return;
|
|
1644
|
-
const candidates = buckets.get(subject.addressHash);
|
|
1645
|
-
if (candidates) {
|
|
1646
|
-
const position = candidates.indexOf(subject);
|
|
1647
|
-
if (position >= 0) candidates.splice(position, 1);
|
|
1648
|
-
if (candidates.length === 0) buckets.delete(subject.addressHash);
|
|
1649
|
-
}
|
|
1650
|
-
const nodes = [index];
|
|
1651
|
-
let node = index;
|
|
1652
|
-
for (const segment of subject.address) {
|
|
1653
|
-
const child = node.children.get(segment);
|
|
1654
|
-
if (!child) return;
|
|
1655
|
-
nodes.push(child);
|
|
1656
|
-
node = child;
|
|
1657
|
-
}
|
|
1658
|
-
if (node.subject === subject) delete node.subject;
|
|
1659
|
-
for (let position = subject.address.length - 1; position >= 0; position -= 1) {
|
|
1660
|
-
const child = nodes[position + 1];
|
|
1661
|
-
if (child.subject || child.children.size > 0) break;
|
|
1662
|
-
nodes[position].children.delete(subject.address[position]);
|
|
1663
|
-
}
|
|
1664
|
-
};
|
|
1665
|
-
function find(kind, address, addressHash) {
|
|
1666
|
-
const candidates = buckets.get(addressHash) ?? [];
|
|
1667
|
-
for (const subject of candidates) {
|
|
1668
|
-
require_ownership.profile.journal.comparison();
|
|
1669
|
-
if (subject.kind === kind && sameAddress(subject.address, address)) return subject;
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
const ownerOf = (address) => {
|
|
1673
|
-
let node = index;
|
|
1674
|
-
for (let position = 0; position < address.length; position += 1) {
|
|
1675
|
-
if (node.subject && ownsDescendant(node.subject, address)) return node.subject;
|
|
1676
|
-
const child = node.children.get(address[position]);
|
|
1677
|
-
if (!child) return void 0;
|
|
1678
|
-
node = child;
|
|
1679
|
-
}
|
|
1680
|
-
};
|
|
1681
|
-
const absorb = (value, address) => {
|
|
1682
|
-
const subjects = indexedSubjects(index, address);
|
|
1683
|
-
if (subjects.length === 0) return value;
|
|
1684
|
-
subjects.sort((left, right) => left.address.length - right.address.length);
|
|
1685
|
-
let before = require_ownership.ownPayload(value, "journal");
|
|
1686
|
-
for (const subject of subjects) before = restoreSubject(before, address, subject);
|
|
1687
|
-
for (const subject of subjects) {
|
|
1688
|
-
remove(subject);
|
|
1689
|
-
require_ownership.profile.journal.absorbed();
|
|
1690
|
-
}
|
|
1691
|
-
return before;
|
|
1692
|
-
};
|
|
1693
|
-
const base = (resolved, operation) => ({
|
|
1694
|
-
address: operation.at,
|
|
1695
|
-
addressHash: resolved.addressHash,
|
|
1696
|
-
...resolved.collection ? { collection: resolved.collection } : {}
|
|
1697
|
-
});
|
|
1698
|
-
const recordValue = (resolved, operation, inverse) => {
|
|
1699
|
-
if (find("value", operation.at, resolved.addressHash)) return;
|
|
1700
|
-
const first = inverse[0];
|
|
1701
|
-
if (first?.type !== "field.set" && first?.type !== "field.clear" && first?.type !== "value.clear" && !("value" in (first ?? {}))) return inverseMismatch(operation);
|
|
1702
|
-
const before = first.type === "field.clear" || first.type === "value.clear" ? absent : "value" in first ? present(first.value) : inverseMismatch(operation);
|
|
1703
|
-
add({
|
|
1704
|
-
...base(resolved, operation),
|
|
1705
|
-
kind: "value",
|
|
1706
|
-
before
|
|
1707
|
-
});
|
|
1708
|
-
};
|
|
1709
|
-
const recordVariant = (resolved, operation, inverse) => {
|
|
1710
|
-
if (find("variant", operation.at, resolved.addressHash)) return;
|
|
1711
|
-
const first = inverse[0];
|
|
1712
|
-
if (first?.type !== "variant.replace" && first?.type !== "value.clear") return inverseMismatch(operation);
|
|
1713
|
-
add({
|
|
1714
|
-
...base(resolved, operation),
|
|
1715
|
-
kind: "variant",
|
|
1716
|
-
before: first.type === "value.clear" ? absent : present(absorb(first.value, operation.at))
|
|
1717
|
-
});
|
|
1718
|
-
};
|
|
1719
|
-
const recordDict = (resolved, operation, inverse) => {
|
|
1720
|
-
const first = inverse[0];
|
|
1721
|
-
if (!first) return inverseMismatch(operation);
|
|
1722
|
-
const subject = find("dict", operation.at, resolved.addressHash) ?? add({
|
|
1723
|
-
...base(resolved, operation),
|
|
1724
|
-
kind: "dict",
|
|
1725
|
-
state: {
|
|
1726
|
-
mode: "incremental",
|
|
1727
|
-
entries: /* @__PURE__ */ new Map()
|
|
1728
|
-
}
|
|
1729
|
-
});
|
|
1730
|
-
if (subject.state.mode === "whole") return;
|
|
1731
|
-
if (first.type === "dict.replace") {
|
|
1732
|
-
const value = require_ownership.ownPayload(first.value, "journal");
|
|
1733
|
-
restoreDict(value, subject.state);
|
|
1734
|
-
subject.state = {
|
|
1735
|
-
mode: "whole",
|
|
1736
|
-
value
|
|
1737
|
-
};
|
|
1738
|
-
return;
|
|
1739
|
-
}
|
|
1740
|
-
if (first.type !== "dict.set" && first.type !== "dict.delete") return inverseMismatch(operation);
|
|
1741
|
-
if (!subject.state.entries.has(first.key)) subject.state.entries.set(first.key, first.type === "dict.set" ? present(first.value) : absent);
|
|
1742
|
-
};
|
|
1743
|
-
const recordList = (resolved, operation, inverse) => {
|
|
1744
|
-
if (resolved.node.kind !== "list") return inverseMismatch(operation);
|
|
1745
|
-
const first = inverse[0];
|
|
1746
|
-
if (!first) return inverseMismatch(operation);
|
|
1747
|
-
let subject = find("list", operation.at, resolved.addressHash);
|
|
1748
|
-
if (!subject) {
|
|
1749
|
-
if (first.type === "value.clear") {
|
|
1750
|
-
add({
|
|
1751
|
-
...base(resolved, operation),
|
|
1752
|
-
kind: "list",
|
|
1753
|
-
node: resolved.node,
|
|
1754
|
-
state: {
|
|
1755
|
-
mode: "whole",
|
|
1756
|
-
value: void 0
|
|
1757
|
-
}
|
|
1758
|
-
});
|
|
1759
|
-
return;
|
|
1760
|
-
}
|
|
1761
|
-
if (first.type === "list.replace") {
|
|
1762
|
-
add({
|
|
1763
|
-
...base(resolved, operation),
|
|
1764
|
-
kind: "list",
|
|
1765
|
-
node: resolved.node,
|
|
1766
|
-
state: {
|
|
1767
|
-
mode: "whole",
|
|
1768
|
-
value: first.value
|
|
1769
|
-
}
|
|
1770
|
-
});
|
|
1771
|
-
return;
|
|
1772
|
-
}
|
|
1773
|
-
const value = require_runtime.read(root, operation.at);
|
|
1774
|
-
if (!Array.isArray(value)) return inverseMismatch(operation);
|
|
1775
|
-
subject = add({
|
|
1776
|
-
...base(resolved, operation),
|
|
1777
|
-
kind: "list",
|
|
1778
|
-
node: resolved.node,
|
|
1779
|
-
state: {
|
|
1780
|
-
mode: "incremental",
|
|
1781
|
-
items: /* @__PURE__ */ new Map(),
|
|
1782
|
-
order: listOrderBefore(value, resolved.node, first)
|
|
1783
|
-
}
|
|
1784
|
-
});
|
|
1785
|
-
}
|
|
1786
|
-
if (subject.state.mode === "whole") return;
|
|
1787
|
-
if (first.type === "list.replace") {
|
|
1788
|
-
const value = Array.from(require_ownership.ownPayload(first.value, "journal"));
|
|
1789
|
-
restoreList(value, subject);
|
|
1790
|
-
subject.state = {
|
|
1791
|
-
mode: "whole",
|
|
1792
|
-
value
|
|
1793
|
-
};
|
|
1794
|
-
return;
|
|
1795
|
-
}
|
|
1796
|
-
if (first.type !== "list.insert" && first.type !== "list.move" && first.type !== "list.remove") return inverseMismatch(operation);
|
|
1797
|
-
recordListPresence(subject.state, first);
|
|
1798
|
-
};
|
|
1799
|
-
const trackEntityOrder = (subject, collection) => {
|
|
1800
|
-
if (subject.order.kind !== "stable") return;
|
|
1801
|
-
require_ownership.profile.journal.orderSnapshot(collection.ids.length);
|
|
1802
|
-
const before = [...collection.ids];
|
|
1803
|
-
restoreEntityOrder(before, subject.order.inverses);
|
|
1804
|
-
subject.order = {
|
|
1805
|
-
kind: "tracked",
|
|
1806
|
-
before
|
|
1807
|
-
};
|
|
1808
|
-
};
|
|
1809
|
-
const recordEntity = (resolved, operation, inverse) => {
|
|
1810
|
-
if (resolved.node.kind !== "table" && resolved.node.kind !== "map") return inverseMismatch(operation);
|
|
1811
|
-
const resolution = resolveEntityCollection(resolved.node, require_runtime.read(root, operation.at));
|
|
1812
|
-
if (resolution.status === "invalid") return inverseMismatch(operation);
|
|
1813
|
-
const collection = resolution.collection;
|
|
1814
|
-
const subject = find("entity", operation.at, resolved.addressHash) ?? add({
|
|
1815
|
-
...base(resolved, operation),
|
|
1816
|
-
kind: "entity",
|
|
1817
|
-
node: resolved.node,
|
|
1818
|
-
entries: /* @__PURE__ */ new Map(),
|
|
1819
|
-
order: collection.kind === "table" ? {
|
|
1820
|
-
kind: "stable",
|
|
1821
|
-
inverses: []
|
|
1822
|
-
} : { kind: "unordered" }
|
|
1823
|
-
});
|
|
1824
|
-
const first = inverse[0];
|
|
1825
|
-
if (!first) return inverseMismatch(operation);
|
|
1826
|
-
if (collection.kind === "table" && subject.order.kind === "stable") subject.order.inverses.push(entityOrderInverse(operation, first));
|
|
1827
|
-
if (first.type === "entity.remove") {
|
|
1828
|
-
for (const id of first.ids) if (!subject.entries.has(id)) subject.entries.set(id, absent);
|
|
1829
|
-
if (collection.kind === "table" && first.ids.some((id) => subject.entries.get(id)?.status === "present")) trackEntityOrder(subject, collection);
|
|
549
|
+
rollback() {
|
|
550
|
+
if (this.resetBefore) {
|
|
551
|
+
this.state.document = this.resetBefore.value;
|
|
1830
552
|
return;
|
|
1831
553
|
}
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
554
|
+
const facts = [...this.facts];
|
|
555
|
+
for (let i = facts.length - 1; i >= 0; i--) {
|
|
556
|
+
const fact = facts[i];
|
|
557
|
+
if (fact.kind === "members") for (const member of fact.members?.values() ?? []) {
|
|
558
|
+
if (!member) continue;
|
|
559
|
+
installMember(fact.parent, require_issue.memberKey(fact, member.key), member.present, member.present ? require_store.copyValue(member.node, member.value) : void 0);
|
|
1837
560
|
}
|
|
1838
|
-
|
|
1839
|
-
}
|
|
1840
|
-
if (first.type === "entity.move") {
|
|
1841
|
-
if (collection.kind === "table" && subject.entries.get(first.id)?.status !== "absent") trackEntityOrder(subject, collection);
|
|
1842
|
-
return;
|
|
1843
|
-
}
|
|
1844
|
-
return inverseMismatch(operation);
|
|
1845
|
-
};
|
|
1846
|
-
const recordTree = (resolved, operation, inverse) => {
|
|
1847
|
-
const current = require_runtime.read(root, operation.at);
|
|
1848
|
-
if (inverse.length === 1 && inverse[0].type === "value.clear" && operation.type === "tree.replace") {
|
|
1849
|
-
const subject = find("tree", operation.at, resolved.addressHash);
|
|
1850
|
-
if (subject) subject.state = {
|
|
1851
|
-
mode: "whole",
|
|
1852
|
-
value: void 0
|
|
1853
|
-
};
|
|
1854
|
-
else add({
|
|
1855
|
-
...base(resolved, operation),
|
|
1856
|
-
kind: "tree",
|
|
1857
|
-
state: {
|
|
1858
|
-
mode: "whole",
|
|
1859
|
-
value: void 0
|
|
1860
|
-
}
|
|
1861
|
-
});
|
|
1862
|
-
return;
|
|
561
|
+
else if (fact.kind === "tree") restore(fact, this.state.schema, this.state.document);
|
|
1863
562
|
}
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
};
|
|
1876
|
-
return {
|
|
1877
|
-
record: (resolved, operation, inverse) => {
|
|
1878
|
-
require_ownership.profile.batch.journalRecord();
|
|
1879
|
-
if (ownerOf(operation.at)) return;
|
|
1880
|
-
if (operation.type === "value.clear" && resolved.node.kind === "variant") {
|
|
1881
|
-
recordVariant(resolved, operation, inverse);
|
|
1882
|
-
return;
|
|
1883
|
-
}
|
|
1884
|
-
if (operation.type === "field.set" || operation.type === "field.clear" || operation.type === "value.clear") {
|
|
1885
|
-
recordValue(resolved, operation, inverse);
|
|
1886
|
-
return;
|
|
1887
|
-
}
|
|
1888
|
-
if (operation.type === "variant.replace") {
|
|
1889
|
-
recordVariant(resolved, operation, inverse);
|
|
1890
|
-
return;
|
|
1891
|
-
}
|
|
1892
|
-
if (operation.type === "dict.set" || operation.type === "dict.delete" || operation.type === "dict.replace") {
|
|
1893
|
-
if (operation.type === "dict.replace" && inverse[0]?.type === "value.clear") recordValue(resolved, operation, inverse);
|
|
1894
|
-
else recordDict(resolved, operation, inverse);
|
|
1895
|
-
return;
|
|
1896
|
-
}
|
|
1897
|
-
if (operation.type === "entity.create" || operation.type === "entity.remove" || operation.type === "entity.move") {
|
|
1898
|
-
recordEntity(resolved, operation, inverse);
|
|
1899
|
-
return;
|
|
1900
|
-
}
|
|
1901
|
-
if (operation.type === "list.insert" || operation.type === "list.move" || operation.type === "list.remove" || operation.type === "list.replace") {
|
|
1902
|
-
recordList(resolved, operation, inverse);
|
|
1903
|
-
return;
|
|
1904
|
-
}
|
|
1905
|
-
recordTree(resolved, operation, inverse);
|
|
1906
|
-
},
|
|
1907
|
-
finish: () => {
|
|
1908
|
-
const changed = [];
|
|
1909
|
-
const entityChanges = /* @__PURE__ */ new Map();
|
|
1910
|
-
for (const subject of active) {
|
|
1911
|
-
require_ownership.profile.journal.comparison();
|
|
1912
|
-
if (subject.kind === "entity") {
|
|
1913
|
-
const analysis = analyzeEntity(root, subject);
|
|
1914
|
-
if (!analysis.changed) continue;
|
|
1915
|
-
entityChanges.set(subject, analysis);
|
|
1916
|
-
changed.push(subject);
|
|
1917
|
-
} else if (subjectChanged(root, subject)) changed.push(subject);
|
|
1918
|
-
}
|
|
1919
|
-
if (changed.length === 0) return { status: "unchanged" };
|
|
1920
|
-
const collections = [];
|
|
1921
|
-
const collectionBuckets = /* @__PURE__ */ new Map();
|
|
1922
|
-
const changeAt = (address, addressHash) => {
|
|
1923
|
-
const candidates = collectionBuckets.get(addressHash) ?? [];
|
|
1924
|
-
collectionBuckets.set(addressHash, candidates);
|
|
1925
|
-
let change = candidates.find((entry) => sameAddress(entry.address, address));
|
|
1926
|
-
if (!change) {
|
|
1927
|
-
change = {
|
|
1928
|
-
address,
|
|
1929
|
-
added: /* @__PURE__ */ new Set(),
|
|
1930
|
-
removed: /* @__PURE__ */ new Set(),
|
|
1931
|
-
updated: /* @__PURE__ */ new Set(),
|
|
1932
|
-
orderChanged: false
|
|
1933
|
-
};
|
|
1934
|
-
candidates.push(change);
|
|
1935
|
-
collections.push(change);
|
|
1936
|
-
}
|
|
1937
|
-
return change;
|
|
1938
|
-
};
|
|
1939
|
-
for (const subject of changed) {
|
|
1940
|
-
if (subject.kind === "entity") {
|
|
1941
|
-
const analysis = entityChanges.get(subject);
|
|
1942
|
-
if (analysis) {
|
|
1943
|
-
const change = changeAt(subject.address, subject.addressHash);
|
|
1944
|
-
analysis.added.forEach((id) => change.added.add(id));
|
|
1945
|
-
analysis.removed.forEach((id) => change.removed.add(id));
|
|
1946
|
-
analysis.updated.forEach((id) => change.updated.add(id));
|
|
1947
|
-
if (analysis.orderChanged) change.orderChanged = true;
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
1950
|
-
for (let collection = subject.collection; collection; collection = collection.parent) changeAt(collection.address, collection.addressHash).updated.add(collection.id);
|
|
1951
|
-
}
|
|
1952
|
-
for (const change of collections) {
|
|
1953
|
-
change.added.forEach((id) => change.updated.delete(id));
|
|
1954
|
-
change.removed.forEach((id) => change.updated.delete(id));
|
|
1955
|
-
}
|
|
1956
|
-
return {
|
|
1957
|
-
status: "changed",
|
|
1958
|
-
paths: Object.freeze(changed.map((subject) => subject.address)),
|
|
1959
|
-
collections
|
|
1960
|
-
};
|
|
563
|
+
for (const fact of facts) if (fact.kind === "members" && fact.order) installOrder(fact.node, fact.value, fact.order);
|
|
564
|
+
}
|
|
565
|
+
seal() {
|
|
566
|
+
if (this.resetBefore) {
|
|
567
|
+
if (require_store.equalValue(this.state.schema, this.resetBefore.value, this.state.document)) return require_driver.sealChanges([]);
|
|
568
|
+
require_issue.profile.recorder("sealed");
|
|
569
|
+
return require_driver.sealChanges([{
|
|
570
|
+
kind: "reset",
|
|
571
|
+
before: this.resetBefore.value,
|
|
572
|
+
after: require_store.copyValue(this.state.schema, this.state.document)
|
|
573
|
+
}]);
|
|
1961
574
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
const positions = (value) => Array.isArray(value) && value.every((entry) => Number.isInteger(entry) && entry >= 0);
|
|
1969
|
-
const address = (value) => stringArray(value);
|
|
1970
|
-
const anchor = (value) => {
|
|
1971
|
-
if (!require_ownership.isRecord(value)) return false;
|
|
1972
|
-
if (Number(has(value, "at")) + Number(has(value, "before")) + Number(has(value, "after")) !== 1) return false;
|
|
1973
|
-
if (has(value, "at")) return value.at === "start" || value.at === "end";
|
|
1974
|
-
return typeof (has(value, "before") ? value.before : value.after) === "string";
|
|
1975
|
-
};
|
|
1976
|
-
const optionalAnchor = (value) => !has(value, "anchor") || value.anchor === void 0 || anchor(value.anchor);
|
|
1977
|
-
const optionalParent = (value) => !has(value, "parentId") || value.parentId === void 0 || typeof value.parentId === "string";
|
|
1978
|
-
const optionalIndex = (value) => !has(value, "index") || value.index === void 0 || Number.isInteger(value.index) && value.index >= 0;
|
|
1979
|
-
const entries = (value) => Array.isArray(value) && value.every((entry) => require_ownership.isRecord(entry) && typeof entry.id === "string" && has(entry, "value"));
|
|
1980
|
-
const decoded = (operation) => ({
|
|
1981
|
-
status: "decoded",
|
|
1982
|
-
operation
|
|
1983
|
-
});
|
|
1984
|
-
const rejected = (code, message) => ({
|
|
1985
|
-
status: "rejected",
|
|
1986
|
-
issue: input(code, message)
|
|
1987
|
-
});
|
|
1988
|
-
const decode = (input) => {
|
|
1989
|
-
if (!require_ownership.isRecord(input)) return rejected("invalid-operation", "Operation must be an object.");
|
|
1990
|
-
if (!address(input.at)) return rejected("invalid-address", "Operation address is malformed.");
|
|
1991
|
-
if (typeof input.type !== "string") return rejected("invalid-operation", "Operation type must be a string.");
|
|
1992
|
-
switch (input.type) {
|
|
1993
|
-
case "field.set":
|
|
1994
|
-
case "variant.replace": return has(input, "value") ? decoded(input) : rejected("invalid-operation", "Operation value is required.");
|
|
1995
|
-
case "field.clear":
|
|
1996
|
-
case "value.clear": return decoded(input);
|
|
1997
|
-
case "dict.set": return typeof input.key === "string" && has(input, "value") ? decoded(input) : rejected("invalid-operation", "Dictionary set requires a string key and value.");
|
|
1998
|
-
case "dict.delete": return typeof input.key === "string" ? decoded(input) : rejected("invalid-operation", "Dictionary delete requires a string key.");
|
|
1999
|
-
case "dict.replace": return require_ownership.isRecord(input.value) ? decoded(input) : rejected("invalid-operation", "Dictionary replacement must be an object.");
|
|
2000
|
-
case "entity.create": return entries(input.entries) && optionalAnchor(input) && (!has(input, "positions") || input.positions === void 0 || positions(input.positions)) ? decoded(input) : rejected("invalid-operation", "Entity create payload is malformed.");
|
|
2001
|
-
case "entity.remove": return stringArray(input.ids) ? decoded(input) : rejected("invalid-operation", "Entity remove requires string ids.");
|
|
2002
|
-
case "entity.move": return typeof input.id === "string" && optionalAnchor(input) ? decoded(input) : rejected("invalid-operation", "Entity move payload is malformed.");
|
|
2003
|
-
case "list.insert": return typeof input.key === "string" && has(input, "value") && optionalAnchor(input) ? decoded(input) : rejected("invalid-operation", "List insert payload is malformed.");
|
|
2004
|
-
case "list.move": return typeof input.key === "string" && optionalAnchor(input) ? decoded(input) : rejected("invalid-operation", "List move payload is malformed.");
|
|
2005
|
-
case "list.remove": return typeof input.key === "string" ? decoded(input) : rejected("invalid-operation", "List remove requires a string key.");
|
|
2006
|
-
case "list.replace": return Array.isArray(input.value) && stringArray(input.keys) ? decoded(input) : rejected("invalid-operation", "List replacement payload is malformed.");
|
|
2007
|
-
case "tree.insert": return typeof input.treeNodeId === "string" && optionalParent(input) && optionalIndex(input) ? decoded(input) : rejected("invalid-operation", "Tree insert payload is malformed.");
|
|
2008
|
-
case "tree.move": return typeof input.treeNodeId === "string" && optionalParent(input) && optionalIndex(input) ? decoded(input) : rejected("invalid-operation", "Tree move payload is malformed.");
|
|
2009
|
-
case "tree.remove": return typeof input.treeNodeId === "string" ? decoded(input) : rejected("invalid-operation", "Tree remove requires a node id.");
|
|
2010
|
-
case "tree.set": return typeof input.treeNodeId === "string" && has(input, "value") ? decoded(input) : rejected("invalid-operation", "Tree set payload is malformed.");
|
|
2011
|
-
case "tree.replace": return has(input, "value") ? decoded(input) : rejected("invalid-operation", "Tree replacement value is required.");
|
|
2012
|
-
default: return rejected("unknown-operation", "Unknown document operation.");
|
|
575
|
+
const changes = [];
|
|
576
|
+
for (const fact of this.facts) if (fact.kind === "members") sealMembers(fact, changes);
|
|
577
|
+
else sealTree(fact, this.state, changes);
|
|
578
|
+
const result = require_driver.sealChanges(changes);
|
|
579
|
+
require_issue.profile.recorder("sealed", result.changes.length);
|
|
580
|
+
return result;
|
|
2013
581
|
}
|
|
2014
582
|
};
|
|
2015
|
-
const
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
583
|
+
const lexical = (a, b) => a < b ? -1 : a === b ? 0 : 1;
|
|
584
|
+
//#endregion
|
|
585
|
+
//#region core/src/mutation/session.ts
|
|
586
|
+
var MutationSession = class {
|
|
587
|
+
identity = {};
|
|
588
|
+
generation = 0;
|
|
589
|
+
recorder;
|
|
590
|
+
resolver;
|
|
591
|
+
resolvedRoot;
|
|
592
|
+
constructor(state) {
|
|
593
|
+
this.state = state;
|
|
594
|
+
this.recorder = new ChangeRecorder(state);
|
|
595
|
+
this.resolver = require_issue.createAddressResolver(state.schema, state.document);
|
|
596
|
+
this.resolvedRoot = state.document;
|
|
597
|
+
}
|
|
598
|
+
validate(node, value, at) {
|
|
599
|
+
const issue = require_store.checkValue(node, value, at);
|
|
600
|
+
if (issue) require_issue.invalidValue(issue);
|
|
601
|
+
}
|
|
602
|
+
resolveContainer(at) {
|
|
603
|
+
return this.resolver.container(this.identity, this.generation, at) ?? require_issue.fail(at, "invalid-address", "Container does not exist.");
|
|
604
|
+
}
|
|
605
|
+
/** Bind access-resolved canonical facts without walking the address again. */
|
|
606
|
+
bind(at, node, value) {
|
|
607
|
+
return require_issue.resolveContainer(this.identity, this.generation, at, node, value) ?? require_issue.fail(at, "invalid-address", "Container does not exist.");
|
|
608
|
+
}
|
|
609
|
+
bindTree(at, node, value) {
|
|
610
|
+
return require_issue.resolveTreeContainer(this.identity, this.generation, at, node, value) ?? require_issue.fail(at, "invalid-tree", "Expected a tree.");
|
|
611
|
+
}
|
|
612
|
+
resolveTree(at) {
|
|
613
|
+
const location = require_issue.resolveValue(this.state.schema, this.state.document, at);
|
|
614
|
+
return this.bindTree(at, location?.node, location?.value);
|
|
615
|
+
}
|
|
616
|
+
replace(at, value) {
|
|
617
|
+
if (!at.length) {
|
|
618
|
+
this.validate(this.state.schema, value, at);
|
|
619
|
+
if (Object.is(this.state.document, value)) return;
|
|
620
|
+
this.recorder.reset();
|
|
621
|
+
this.state.document = require_store.copyValue(this.state.schema, value);
|
|
622
|
+
this.invalidate();
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
this.writeMember(this.resolveContainer(at.slice(0, -1)), at[at.length - 1], value, "set");
|
|
626
|
+
}
|
|
627
|
+
assignMember(container, key, value) {
|
|
628
|
+
container = this.refresh(container);
|
|
629
|
+
const member = this.definition(container, key);
|
|
630
|
+
if (container.layout.kind === "fixed" && member.node.kind !== "field" && member.node.kind !== "variant" && !member.node.optional) return require_issue.fail(container.at.concat(key), "invalid-value", "Replace fields, variants, or collection entries; edit object members individually.");
|
|
631
|
+
this.writeLocatedMember(container, key, member, require_issue.memberKey(container, key), value, "set");
|
|
632
|
+
}
|
|
633
|
+
removeMember(container, key) {
|
|
634
|
+
this.writeMember(this.refresh(container), key, void 0, "remove");
|
|
635
|
+
}
|
|
636
|
+
refresh(container) {
|
|
637
|
+
if (container.owner !== this.identity) return require_issue.fail(container.at, "invalid-address", "Container belongs to another mutation session.");
|
|
638
|
+
return container.generation === this.generation ? container : this.resolveContainer(container.at);
|
|
639
|
+
}
|
|
640
|
+
definition(container, key) {
|
|
641
|
+
const layout = container.layout;
|
|
642
|
+
const member = layout.kind === "dynamic" ? layout.entry : layout.members.get(key);
|
|
643
|
+
if (!member) return require_issue.fail(container.at.concat(key), "invalid-address", "Address does not exist.");
|
|
644
|
+
return member;
|
|
645
|
+
}
|
|
646
|
+
/** Consume a current command-local container; bulk writes retain its storage. */
|
|
647
|
+
writeMember(container, key, value, operation, physicalKey = require_issue.memberKey(container, key)) {
|
|
648
|
+
const member = this.definition(container, key);
|
|
649
|
+
return this.writeLocatedMember(container, key, member, physicalKey, value, operation);
|
|
650
|
+
}
|
|
651
|
+
/** Location and definition are resolved once by the calling operation. */
|
|
652
|
+
writeLocatedMember(container, key, member, physicalKey, value, operation) {
|
|
653
|
+
const parentNode = container.node;
|
|
654
|
+
const node = member.node;
|
|
655
|
+
const present = operation === "set";
|
|
656
|
+
if (parentNode.kind === "variant" && parentNode.tag === key) return require_issue.fail(container.at.concat(key), "invalid-value", "Variant discriminants are read-only.");
|
|
657
|
+
const entry = container.layout.kind === "dynamic";
|
|
658
|
+
if (!present && !node.optional && !entry) return require_issue.fail(container.at.concat(key), "required-field", "A required value cannot be removed.");
|
|
659
|
+
const existed = Object.hasOwn(container.parent, physicalKey);
|
|
660
|
+
const previous = container.parent[physicalKey];
|
|
661
|
+
if (existed === present && (!present || Object.is(previous, value))) return false;
|
|
662
|
+
if (parentNode.kind === "map" || parentNode.kind === "table") {
|
|
663
|
+
const issue = require_store.checkKey(parentNode.key, key, []);
|
|
664
|
+
if (issue) return require_issue.fail(container.at.concat(key, ...issue.address), "invalid-key", issue.message);
|
|
665
|
+
}
|
|
666
|
+
if (present) {
|
|
667
|
+
if (node.kind === "field") {
|
|
668
|
+
const issue = require_store.checkValue(node, value, []);
|
|
669
|
+
if (issue) return require_issue.invalidValue(issue, container.at.concat(key, ...issue.address));
|
|
670
|
+
} else this.validate(node, value, container.at.concat(key));
|
|
671
|
+
if (parentNode.kind === "list" && parentNode.keyOf(value) !== key) return require_issue.fail(container.at.concat(key), "invalid-list-key", "Replacing an item must retain its addressed key.");
|
|
672
|
+
}
|
|
673
|
+
const membershipChanged = entry && existed !== present;
|
|
674
|
+
if (membershipChanged && (parentNode.kind === "list" || parentNode.kind === "table")) this.recorder.order(container);
|
|
675
|
+
this.recorder.member(container, key, member, physicalKey);
|
|
676
|
+
const next = present ? node.kind === "field" ? value : require_store.copyValue(node, value) : void 0;
|
|
677
|
+
installMember(container.parent, physicalKey, present, next);
|
|
678
|
+
if (node.kind !== "field" || membershipChanged) this.invalidate();
|
|
679
|
+
return membershipChanged;
|
|
680
|
+
}
|
|
681
|
+
invalidate() {
|
|
682
|
+
this.generation++;
|
|
683
|
+
if (this.resolvedRoot !== this.state.document) {
|
|
684
|
+
this.resolvedRoot = this.state.document;
|
|
685
|
+
this.resolver = require_issue.createAddressResolver(this.state.schema, this.state.document);
|
|
686
|
+
} else this.resolver.invalidate();
|
|
687
|
+
}
|
|
688
|
+
finish() {
|
|
689
|
+
return this.recorder.seal();
|
|
690
|
+
}
|
|
691
|
+
rollback() {
|
|
692
|
+
this.recorder.rollback();
|
|
693
|
+
this.invalidate();
|
|
2069
694
|
}
|
|
2070
|
-
return Object.freeze(copy);
|
|
2071
695
|
};
|
|
2072
696
|
//#endregion
|
|
2073
|
-
//#region core/src/mutation/
|
|
2074
|
-
|
|
2075
|
-
const
|
|
2076
|
-
const
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
697
|
+
//#region core/src/mutation/operations/replay.ts
|
|
698
|
+
function apply(session, changes, direction) {
|
|
699
|
+
const side = direction === "forward" ? "after" : "before";
|
|
700
|
+
for (const change of changes.changes) if (change.kind === "reset") session.replace([], change[side]);
|
|
701
|
+
else if (change.kind === "members") {
|
|
702
|
+
const container = session.resolveContainer(change.at);
|
|
703
|
+
const { node, value: current } = container;
|
|
704
|
+
if (change.order) {
|
|
705
|
+
if (node.kind !== "table" && node.kind !== "list") return require_issue.fail(change.at, "invalid-changes", "Order requires an ordered container.");
|
|
706
|
+
session.recorder.order(container);
|
|
707
|
+
}
|
|
708
|
+
let membershipChanged = false;
|
|
709
|
+
for (const member of change.members) {
|
|
710
|
+
const present = side === "after" ? member.kind !== "removed" : member.kind !== "added";
|
|
711
|
+
const value = side === "after" ? member.kind !== "removed" ? member.after : void 0 : member.kind !== "added" ? member.before : void 0;
|
|
712
|
+
membershipChanged = session.writeMember(container, member.key, value, present ? "set" : "remove") || membershipChanged;
|
|
713
|
+
}
|
|
714
|
+
if (change.order) {
|
|
715
|
+
const keys = node.kind === "table" ? Object.keys(container.parent) : orderOf(node, current);
|
|
716
|
+
if (!require_issue.matches(change.order[side], keys)) return require_issue.fail(change.at, "invalid-changes", "Order must contain exactly the resulting keys.");
|
|
717
|
+
installOrder(node, current, change.order[side]);
|
|
718
|
+
session.invalidate();
|
|
719
|
+
} else if (membershipChanged && node.kind === "table") {
|
|
720
|
+
const ids = current.ids;
|
|
721
|
+
if (!require_issue.matches(ids, Object.keys(container.parent))) return require_issue.fail(change.at, "invalid-changes", "Table membership changes require a matching order.");
|
|
2083
722
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
const resolved = require_runtime.resolveLocated(schema, root, normalized.at);
|
|
2098
|
-
if (resolved?.collection) require_ownership.profile.batch.collectionResolved();
|
|
2099
|
-
require_ownership.profile.mutation.executed();
|
|
2100
|
-
const result = execute(root, normalized, resolved, options.copyPayload === true || requiresPayloadCopy(normalized));
|
|
2101
|
-
if (result.status === "rejected") {
|
|
2102
|
-
require_ownership.profile.batch.rejected();
|
|
2103
|
-
return result.issue;
|
|
2104
|
-
}
|
|
2105
|
-
if (result.status === "unchanged") return void 0;
|
|
2106
|
-
const published = publish(normalized);
|
|
2107
|
-
const group = [];
|
|
2108
|
-
for (const rawInverse of result.inverse) {
|
|
2109
|
-
const inverse$1 = inverse(rawInverse);
|
|
2110
|
-
group.push(inverse$1);
|
|
2111
|
-
require_ownership.profile.mutation.inverse();
|
|
2112
|
-
require_ownership.profile.batch.inverse();
|
|
2113
|
-
}
|
|
2114
|
-
steps.push({
|
|
2115
|
-
forward: published,
|
|
2116
|
-
inverse: Object.freeze(group)
|
|
723
|
+
} else {
|
|
724
|
+
const container = session.resolveTree(change.at);
|
|
725
|
+
const { value: current, node } = container;
|
|
726
|
+
session.recorder.tree(container);
|
|
727
|
+
for (const item of change.nodes) session.recorder.tree(container, item.id);
|
|
728
|
+
const root = change[side];
|
|
729
|
+
if (root === null) delete current.rootId;
|
|
730
|
+
else current.rootId = root;
|
|
731
|
+
for (const item of change.nodes) {
|
|
732
|
+
const next = side === "after" ? item.kind !== "removed" ? item.after : void 0 : item.kind !== "added" ? item.before : void 0;
|
|
733
|
+
installMember(current.nodes, item.id, next !== void 0, next && {
|
|
734
|
+
...next,
|
|
735
|
+
children: [...next.children]
|
|
2117
736
|
});
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
} catch (error) {
|
|
2122
|
-
rollback();
|
|
2123
|
-
throw error;
|
|
2124
|
-
}
|
|
2125
|
-
},
|
|
2126
|
-
finish: () => {
|
|
2127
|
-
if (state !== "active") throw new Error("Mutation session is closed.");
|
|
2128
|
-
const changes = journal.finish();
|
|
2129
|
-
state = "finished";
|
|
2130
|
-
if (changes.status === "unchanged") return { status: "unchanged" };
|
|
2131
|
-
let inverseCount = 0;
|
|
2132
|
-
for (const step of steps) inverseCount += step.inverse.length;
|
|
2133
|
-
const inverse = new Array(inverseCount);
|
|
2134
|
-
let writeIndex = 0;
|
|
2135
|
-
for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) for (const entry of steps[stepIndex].inverse) inverse[writeIndex++] = entry;
|
|
2136
|
-
return {
|
|
2137
|
-
status: "changed",
|
|
2138
|
-
operations: Object.freeze(steps.map((step) => step.forward)),
|
|
2139
|
-
inverse: Object.freeze(inverse),
|
|
2140
|
-
paths: changes.paths,
|
|
2141
|
-
collections: changes.collections
|
|
2142
|
-
};
|
|
2143
|
-
},
|
|
2144
|
-
rollback
|
|
2145
|
-
};
|
|
2146
|
-
};
|
|
2147
|
-
const mutateOperations = (root, schema, operations, options = {}) => {
|
|
2148
|
-
const decodedBatch = decodeBatch(operations);
|
|
2149
|
-
if (decodedBatch.status === "rejected") return {
|
|
2150
|
-
status: "rejected",
|
|
2151
|
-
issues: Object.freeze([decodedBatch.issue])
|
|
2152
|
-
};
|
|
2153
|
-
const session = createMutationSession(root, schema, options);
|
|
2154
|
-
for (const input of decodedBatch.operations) {
|
|
2155
|
-
const issue = session.apply(input);
|
|
2156
|
-
if (!issue) continue;
|
|
2157
|
-
session.rollback();
|
|
2158
|
-
return {
|
|
2159
|
-
status: "rejected",
|
|
2160
|
-
issues: Object.freeze([issue])
|
|
2161
|
-
};
|
|
737
|
+
}
|
|
738
|
+
session.validate(node, current, change.at);
|
|
739
|
+
session.invalidate();
|
|
2162
740
|
}
|
|
2163
|
-
|
|
2164
|
-
};
|
|
741
|
+
}
|
|
2165
742
|
//#endregion
|
|
2166
743
|
//#region core/src/runtime.ts
|
|
2167
|
-
var RejectedUpdate = class extends Error {
|
|
2168
|
-
issues;
|
|
2169
|
-
constructor(issues) {
|
|
2170
|
-
super("Document update rejected.");
|
|
2171
|
-
this.issues = Object.freeze(issues.slice());
|
|
2172
|
-
}
|
|
2173
|
-
};
|
|
2174
|
-
const EMPTY = Object.freeze([]);
|
|
2175
|
-
const publishDiagnostic = (value) => Object.freeze({
|
|
2176
|
-
...value,
|
|
2177
|
-
source: "application",
|
|
2178
|
-
...value.address === void 0 ? {} : { address: Object.freeze(value.address.slice()) }
|
|
2179
|
-
});
|
|
2180
744
|
const createDocument = (input) => {
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
745
|
+
if (input.schema.kind !== "object") throw new TypeError("Document schema must be a root object node.");
|
|
746
|
+
const invalid = require_store.checkValue(input.schema, input.initial);
|
|
747
|
+
if (invalid) throw new require_store.ParseError(invalid);
|
|
2184
748
|
const state = {
|
|
2185
749
|
schema: input.schema,
|
|
2186
|
-
document:
|
|
750
|
+
document: require_store.copyValue(input.schema, input.initial),
|
|
2187
751
|
disposed: false
|
|
2188
752
|
};
|
|
2189
|
-
let revision = 0;
|
|
2190
|
-
let
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
if (state.disposed) throw new require_runtime.DocumentDisposedError();
|
|
2195
|
-
if (busy) throw new require_runtime.DocumentReentrancyError();
|
|
2196
|
-
if (require_runtime.accessOf(runtime).projectionLocks) throw new require_runtime.DocumentReentrancyError();
|
|
2197
|
-
require_driver.assertRuntimeWritable(runtime, intent);
|
|
753
|
+
let revision = 0, busy = false;
|
|
754
|
+
let runtime, notification;
|
|
755
|
+
const idle = () => {
|
|
756
|
+
if (state.disposed) throw new require_store.DocumentDisposedError();
|
|
757
|
+
if (busy || require_store.accessOf(runtime).projectionLocks) throw new require_store.DocumentReentrancyError();
|
|
2198
758
|
};
|
|
2199
|
-
const
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
const transaction = {
|
|
2203
|
-
read: require_runtime.documentReader(input.schema, () => state.document, () => active),
|
|
2204
|
-
write: documentWriter(input.schema, (operation) => {
|
|
2205
|
-
if (!active) throw new Error("Document writer is no longer active.");
|
|
2206
|
-
const rejected = session.apply(operation);
|
|
2207
|
-
if (rejected) throw new RejectedUpdate([rejected]);
|
|
2208
|
-
}),
|
|
2209
|
-
reject: (diagnostic) => {
|
|
2210
|
-
throw new RejectedUpdate((Array.isArray(diagnostic) ? diagnostic : [diagnostic]).map((entry) => publishDiagnostic(entry)));
|
|
2211
|
-
},
|
|
2212
|
-
report: (diagnostic) => {
|
|
2213
|
-
if (!active) throw new Error("Document transaction is no longer active.");
|
|
2214
|
-
(reports ??= []).push(publishDiagnostic(diagnostic));
|
|
2215
|
-
}
|
|
2216
|
-
};
|
|
2217
|
-
try {
|
|
2218
|
-
const value = run(transaction);
|
|
2219
|
-
if (value !== null && typeof value === "object" && typeof value.then === "function") throw new TypeError("Document update callback must be synchronous.");
|
|
2220
|
-
return {
|
|
2221
|
-
value,
|
|
2222
|
-
reports: Object.freeze(reports ? reports.slice() : EMPTY)
|
|
2223
|
-
};
|
|
2224
|
-
} finally {
|
|
2225
|
-
active = false;
|
|
2226
|
-
}
|
|
759
|
+
const writable = (intent) => {
|
|
760
|
+
idle();
|
|
761
|
+
require_driver.assertRuntimeWritable(runtime, intent);
|
|
2227
762
|
};
|
|
2228
|
-
const impactFor = (batch, kind) => createImpact({
|
|
2229
|
-
schema: input.schema,
|
|
2230
|
-
operations: batch.operations,
|
|
2231
|
-
paths: batch.paths,
|
|
2232
|
-
collections: batch.collections,
|
|
2233
|
-
reset: kind === "replace"
|
|
2234
|
-
});
|
|
2235
763
|
const history = createHistory({
|
|
2236
764
|
capacity: input.history === false ? 0 : Math.max(0, input.history?.capacity ?? 100),
|
|
2237
765
|
revision: () => revision,
|
|
2238
|
-
apply: (
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
766
|
+
apply: (changes, direction) => mutate({
|
|
767
|
+
kind: "apply",
|
|
768
|
+
source: "history"
|
|
769
|
+
}, false, (session) => {
|
|
770
|
+
for (const change of changes) apply(session, change, direction);
|
|
771
|
+
}),
|
|
772
|
+
assertIdle: idle,
|
|
2243
773
|
notify: (run) => {
|
|
2244
774
|
busy = true;
|
|
2245
775
|
try {
|
|
@@ -2249,210 +779,125 @@ const createDocument = (input) => {
|
|
|
2249
779
|
}
|
|
2250
780
|
}
|
|
2251
781
|
});
|
|
2252
|
-
const publish = (
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
revision,
|
|
2256
|
-
kind,
|
|
782
|
+
const publish = (changes, source, recordHistory) => {
|
|
783
|
+
const commit = {
|
|
784
|
+
revision: ++revision,
|
|
2257
785
|
source,
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
if (
|
|
2263
|
-
else if (recordHistory && (source === "local" || source === "system")) history.record(batch.operations, batch.inverse);
|
|
786
|
+
changes,
|
|
787
|
+
impact: require_store.createImpact(input.schema, changes)
|
|
788
|
+
};
|
|
789
|
+
if (source === "remote") history.invalidate();
|
|
790
|
+
else if (recordHistory && (source === "local" || source === "system")) history.record(changes);
|
|
2264
791
|
else if (source === "history") history.publish();
|
|
2265
792
|
else history.endGroup();
|
|
2266
|
-
busy = true;
|
|
2267
|
-
let observerErrors;
|
|
2268
|
-
try {
|
|
2269
|
-
observerErrors = require_runtime.notify(notification, commit, history.flush);
|
|
2270
|
-
} finally {
|
|
2271
|
-
busy = false;
|
|
2272
|
-
}
|
|
2273
793
|
return {
|
|
2274
794
|
status: "committed",
|
|
2275
795
|
commit,
|
|
2276
|
-
observerErrors
|
|
796
|
+
observerErrors: require_store.notify(notification, commit, history.flush)
|
|
2277
797
|
};
|
|
2278
798
|
};
|
|
2279
|
-
function
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
if (batch.status === "rejected") return {
|
|
2286
|
-
status: "rejected",
|
|
2287
|
-
issues: batch.issues,
|
|
2288
|
-
revision
|
|
2289
|
-
};
|
|
2290
|
-
if (batch.status === "unchanged") return {
|
|
2291
|
-
status: "unchanged",
|
|
2292
|
-
revision
|
|
2293
|
-
};
|
|
2294
|
-
return publish(batch, source, "operations", recordHistory);
|
|
2295
|
-
}
|
|
2296
|
-
runtime = {
|
|
2297
|
-
schema: input.schema,
|
|
2298
|
-
address: {
|
|
2299
|
-
resolve: (address) => require_runtime.resolveAddress(input.schema, address, state.document),
|
|
2300
|
-
read: (address) => require_runtime.read(state.document, address),
|
|
2301
|
-
contains: require_runtime.contains,
|
|
2302
|
-
overlaps: require_runtime.overlaps,
|
|
2303
|
-
debugKey: require_runtime.debugKey
|
|
2304
|
-
},
|
|
2305
|
-
revision: () => revision,
|
|
2306
|
-
update: (run, options) => {
|
|
2307
|
-
const source = options?.source ?? "local";
|
|
2308
|
-
assertWritable({
|
|
2309
|
-
kind: "update",
|
|
2310
|
-
source
|
|
2311
|
-
});
|
|
2312
|
-
busy = true;
|
|
2313
|
-
const session = createMutationSession(state.document, input.schema);
|
|
2314
|
-
let committed = false;
|
|
799
|
+
function mutate(intent, recordHistory, execute) {
|
|
800
|
+
writable(intent);
|
|
801
|
+
busy = true;
|
|
802
|
+
try {
|
|
803
|
+
const session = new MutationSession(state);
|
|
804
|
+
let changes;
|
|
2315
805
|
try {
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
revision,
|
|
2322
|
-
reports
|
|
2323
|
-
};
|
|
2324
|
-
if (batch.status === "rejected") return {
|
|
806
|
+
execute(session);
|
|
807
|
+
changes = session.finish();
|
|
808
|
+
} catch (error) {
|
|
809
|
+
session.rollback();
|
|
810
|
+
if (error instanceof require_issue.MutationRejected) return {
|
|
2325
811
|
status: "rejected",
|
|
2326
|
-
issues:
|
|
812
|
+
issues: [error.issue],
|
|
2327
813
|
revision
|
|
2328
814
|
};
|
|
2329
|
-
|
|
2330
|
-
committed = true;
|
|
2331
|
-
return {
|
|
2332
|
-
status: "committed",
|
|
2333
|
-
value,
|
|
2334
|
-
commit: result.commit,
|
|
2335
|
-
reports,
|
|
2336
|
-
observerErrors: result.observerErrors
|
|
2337
|
-
};
|
|
2338
|
-
} catch (error) {
|
|
2339
|
-
try {
|
|
2340
|
-
if (!committed) session.rollback();
|
|
2341
|
-
} finally {
|
|
2342
|
-
busy = false;
|
|
2343
|
-
}
|
|
2344
|
-
if (error instanceof RejectedUpdate) return {
|
|
815
|
+
if (intent.kind === "update" && error instanceof require_store.TransactionRejected) return {
|
|
2345
816
|
status: "rejected",
|
|
2346
817
|
issues: error.issues,
|
|
2347
818
|
revision
|
|
2348
819
|
};
|
|
2349
820
|
throw error;
|
|
2350
|
-
} finally {
|
|
2351
|
-
busy = false;
|
|
2352
821
|
}
|
|
822
|
+
if (!changes.changes.length) return {
|
|
823
|
+
status: "unchanged",
|
|
824
|
+
revision
|
|
825
|
+
};
|
|
826
|
+
return publish(changes, intent.source, recordHistory);
|
|
827
|
+
} finally {
|
|
828
|
+
busy = false;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
runtime = {
|
|
832
|
+
schema: input.schema,
|
|
833
|
+
address: {
|
|
834
|
+
resolve: (at) => require_issue.resolveAddress(input.schema, at, state.document),
|
|
835
|
+
read: (at) => require_issue.read(state.document, at, input.schema),
|
|
836
|
+
contains: require_issue.contains,
|
|
837
|
+
overlaps: require_issue.overlaps,
|
|
838
|
+
debugKey: require_issue.debugKey
|
|
2353
839
|
},
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
status: "rejected",
|
|
2373
|
-
issues: batch.issues
|
|
2374
|
-
};
|
|
2375
|
-
}
|
|
2376
|
-
const impact = impactFor(batch, "operations");
|
|
2377
|
-
session.rollback();
|
|
2378
|
-
return {
|
|
2379
|
-
status: "prepared",
|
|
2380
|
-
value,
|
|
2381
|
-
operations: batch.operations,
|
|
2382
|
-
inverse: batch.inverse,
|
|
2383
|
-
impact,
|
|
2384
|
-
footprint: require_runtime.commandFootprint(batch.operations),
|
|
2385
|
-
reports
|
|
2386
|
-
};
|
|
2387
|
-
} catch (error) {
|
|
2388
|
-
try {
|
|
2389
|
-
session.rollback();
|
|
2390
|
-
} finally {
|
|
2391
|
-
busy = false;
|
|
2392
|
-
}
|
|
2393
|
-
if (error instanceof RejectedUpdate) return {
|
|
2394
|
-
status: "rejected",
|
|
2395
|
-
issues: error.issues
|
|
2396
|
-
};
|
|
2397
|
-
throw error;
|
|
2398
|
-
} finally {
|
|
2399
|
-
busy = false;
|
|
2400
|
-
}
|
|
840
|
+
revision: () => revision,
|
|
841
|
+
update: (run, options) => {
|
|
842
|
+
const source = options?.source ?? "local";
|
|
843
|
+
let value;
|
|
844
|
+
const result = mutate({
|
|
845
|
+
kind: "update",
|
|
846
|
+
source
|
|
847
|
+
}, options?.history ?? true, (session) => {
|
|
848
|
+
value = run(require_store.createAccess({
|
|
849
|
+
state,
|
|
850
|
+
session
|
|
851
|
+
}));
|
|
852
|
+
if (value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function") throw new TypeError("Document updates must be synchronous.");
|
|
853
|
+
});
|
|
854
|
+
return result.status === "rejected" ? result : {
|
|
855
|
+
...result,
|
|
856
|
+
value
|
|
857
|
+
};
|
|
2401
858
|
},
|
|
2402
|
-
apply: (
|
|
2403
|
-
|
|
859
|
+
apply: (inputChanges, options) => mutate({
|
|
860
|
+
kind: "apply",
|
|
861
|
+
source: options?.source ?? "local"
|
|
862
|
+
}, options?.history ?? true, (session) => {
|
|
863
|
+
if (!options || options.expectedRevision !== revision) require_issue.fail([], "baseline-mismatch", "apply requires the current expectedRevision.");
|
|
864
|
+
apply(session, require_driver.decodeChanges(inputChanges), "forward");
|
|
865
|
+
}),
|
|
866
|
+
replace: (value, options) => {
|
|
2404
867
|
const source = options?.source ?? "system";
|
|
2405
|
-
|
|
868
|
+
return mutate({
|
|
2406
869
|
kind: "replace",
|
|
2407
870
|
source
|
|
2408
|
-
});
|
|
2409
|
-
const invalidTree = invalidDocument(input.schema, document);
|
|
2410
|
-
if (invalidTree) return {
|
|
2411
|
-
status: "rejected",
|
|
2412
|
-
issues: [at(invalidTree, "invalid-tree", "Document replacement contains an invalid tree.")],
|
|
2413
|
-
revision
|
|
2414
|
-
};
|
|
2415
|
-
if (require_ownership.deepEqual(state.document, document)) return {
|
|
2416
|
-
status: "unchanged",
|
|
2417
|
-
revision
|
|
2418
|
-
};
|
|
2419
|
-
state.document = require_ownership.cloneValue(document, "replace");
|
|
2420
|
-
return publish({
|
|
2421
|
-
status: "changed",
|
|
2422
|
-
operations: EMPTY,
|
|
2423
|
-
inverse: EMPTY,
|
|
2424
|
-
paths: EMPTY,
|
|
2425
|
-
collections: EMPTY
|
|
2426
|
-
}, source, "replace", false);
|
|
871
|
+
}, source !== "remote", (session) => session.replace([], value));
|
|
2427
872
|
},
|
|
2428
873
|
snapshot: () => {
|
|
2429
|
-
if (state.disposed) throw new
|
|
2430
|
-
return
|
|
874
|
+
if (state.disposed) throw new require_store.DocumentDisposedError();
|
|
875
|
+
return require_store.copyValue(input.schema, state.document);
|
|
2431
876
|
},
|
|
2432
|
-
subscribe: ((
|
|
2433
|
-
if (state.disposed) throw new
|
|
2434
|
-
if (
|
|
2435
|
-
const
|
|
2436
|
-
if (
|
|
2437
|
-
return
|
|
877
|
+
subscribe: ((pick, listener) => {
|
|
878
|
+
if (state.disposed) throw new require_store.DocumentDisposedError();
|
|
879
|
+
if (!listener) return require_store.subscribeRoot(notification, pick);
|
|
880
|
+
const picks = Array.isArray(pick) ? pick : [pick];
|
|
881
|
+
if (!picks.length) throw new TypeError("Expected at least one subscription path.");
|
|
882
|
+
return require_store.subscribeTargets(notification, picks.map((p) => require_store.compilePath(input.schema, "value", p)), listener);
|
|
2438
883
|
}),
|
|
2439
884
|
history: history.api,
|
|
2440
885
|
dispose: () => {
|
|
2441
886
|
if (state.disposed) return;
|
|
2442
|
-
|
|
887
|
+
idle();
|
|
2443
888
|
state.disposed = true;
|
|
2444
889
|
try {
|
|
2445
|
-
|
|
890
|
+
require_store.disposeNotification(notification);
|
|
2446
891
|
} finally {
|
|
2447
892
|
history.dispose();
|
|
2448
893
|
require_driver.disposeRuntimeDriver(runtime);
|
|
2449
894
|
}
|
|
2450
895
|
}
|
|
2451
896
|
};
|
|
2452
|
-
|
|
897
|
+
require_store.bindRuntimeAccess(runtime, state);
|
|
2453
898
|
require_driver.bindRuntimeDriver(runtime);
|
|
2454
|
-
notification =
|
|
2455
|
-
|
|
899
|
+
notification = require_store.createNotification(runtime);
|
|
900
|
+
require_store.bindDocumentReadable(history.api, runtime);
|
|
2456
901
|
return runtime;
|
|
2457
902
|
};
|
|
2458
903
|
//#endregion
|
|
@@ -2463,37 +908,36 @@ const asReadable = (runtime) => {
|
|
|
2463
908
|
revision: runtime.revision,
|
|
2464
909
|
subscribe: runtime.subscribe
|
|
2465
910
|
};
|
|
2466
|
-
|
|
2467
|
-
|
|
911
|
+
require_store.bindRuntimeAccess(readable, require_store.accessOf(runtime));
|
|
912
|
+
require_store.shareNotification(runtime, readable);
|
|
2468
913
|
return Object.freeze(readable);
|
|
2469
914
|
};
|
|
2470
915
|
//#endregion
|
|
2471
916
|
//#region core/src/projection/select.ts
|
|
2472
|
-
const select = (runtime, selector) =>
|
|
917
|
+
const select = (runtime, selector) => require_store.readWith(runtime, selector);
|
|
2473
918
|
//#endregion
|
|
2474
|
-
exports.DocumentDisposedError =
|
|
2475
|
-
exports.DocumentReentrancyError =
|
|
2476
|
-
exports.
|
|
2477
|
-
exports.
|
|
919
|
+
exports.DocumentDisposedError = require_store.DocumentDisposedError;
|
|
920
|
+
exports.DocumentReentrancyError = require_store.DocumentReentrancyError;
|
|
921
|
+
exports.ParseError = require_store.ParseError;
|
|
922
|
+
exports.ProjectionDisposedError = require_store.ProjectionDisposedError;
|
|
923
|
+
exports.ProjectionError = require_store.ProjectionError;
|
|
924
|
+
exports.TransactionRejected = require_store.TransactionRejected;
|
|
2478
925
|
exports.asReadable = asReadable;
|
|
926
|
+
exports.assign = require_store.assign;
|
|
2479
927
|
exports.createDocument = createDocument;
|
|
2480
|
-
exports.
|
|
2481
|
-
exports.
|
|
2482
|
-
exports.
|
|
2483
|
-
exports.list =
|
|
2484
|
-
exports.map =
|
|
2485
|
-
exports.object =
|
|
2486
|
-
exports.optional =
|
|
2487
|
-
exports.
|
|
928
|
+
exports.createProjectionStore = require_store.createProjectionStore;
|
|
929
|
+
exports.field = require_store.field;
|
|
930
|
+
exports.input = require_store.input;
|
|
931
|
+
exports.list = require_store.list;
|
|
932
|
+
exports.map = require_store.map;
|
|
933
|
+
exports.object = require_store.object;
|
|
934
|
+
exports.optional = require_store.optional;
|
|
935
|
+
exports.parse = require_store.parse;
|
|
936
|
+
exports.project = require_store.project;
|
|
2488
937
|
exports.select = select;
|
|
2489
|
-
exports.
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
return require_runtime.impact_target_exports;
|
|
2494
|
-
}
|
|
2495
|
-
});
|
|
2496
|
-
exports.tree = require_runtime.tree;
|
|
2497
|
-
exports.variant = require_runtime.variant;
|
|
938
|
+
exports.snapshot = require_store.snapshot;
|
|
939
|
+
exports.table = require_store.table;
|
|
940
|
+
exports.tree = require_store.tree;
|
|
941
|
+
exports.variant = require_store.variant;
|
|
2498
942
|
|
|
2499
943
|
//# sourceMappingURL=index.cjs.map
|