doxum 0.1.6 → 0.1.7
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 +38 -13
- package/dist/{contract-I99DUMbN.d.ts → contract-DNZ4D53r.d.ts} +87 -76
- package/dist/{contract-uYJTmT8o.d.cts → contract-j3SLGAwh.d.cts} +87 -76
- package/dist/index.cjs +221 -349
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +209 -327
- package/dist/index.js.map +1 -1
- package/dist/{integration-xupBIKjU.js → integration-B56u1l9V.js} +2 -2
- package/dist/integration-B56u1l9V.js.map +1 -0
- package/dist/{integration-Bs3pOk65.cjs → integration-CZwCwFBS.cjs} +2 -2
- package/dist/integration-CZwCwFBS.cjs.map +1 -0
- package/dist/integration.cjs +10 -2
- package/dist/integration.d.cts +3 -3
- package/dist/integration.d.ts +3 -3
- package/dist/integration.js +3 -3
- package/dist/local-sync.cjs +56 -17
- package/dist/local-sync.cjs.map +1 -1
- package/dist/local-sync.d.cts +2 -2
- package/dist/local-sync.d.ts +2 -2
- package/dist/local-sync.js +56 -17
- package/dist/local-sync.js.map +1 -1
- package/dist/react.cjs +27 -19
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +27 -19
- package/dist/react.js.map +1 -1
- package/dist/{runtime-CRijXjYp.cjs → runtime-0mOFbe_H.cjs} +899 -451
- package/dist/runtime-0mOFbe_H.cjs.map +1 -0
- package/dist/{runtime-CUcvhFNP.d.ts → runtime-B22tuj9A.d.cts} +18 -20
- package/dist/{runtime-C_vKHmhy.d.cts → runtime-BFmhzPpZ.d.ts} +18 -20
- package/dist/{runtime-BJRXF0gq.js → runtime-DF1q9Gje.js} +823 -429
- package/dist/runtime-DF1q9Gje.js.map +1 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +2 -1
- package/skills/doxum-runtime/references/guide.en.md +18 -18
- package/skills/doxum-runtime/references/guide.zh-CN.md +12 -17
- package/skills/doxum-runtime/references/invariants.en.md +1 -1
- package/skills/doxum-runtime/references/invariants.zh-CN.md +1 -1
- package/skills/doxum-runtime/references/patterns.en.md +9 -20
- package/skills/doxum-runtime/references/patterns.zh-CN.md +5 -18
- package/dist/integration-Bs3pOk65.cjs.map +0 -1
- package/dist/integration-xupBIKjU.js.map +0 -1
- package/dist/runtime-BJRXF0gq.js.map +0 -1
- package/dist/runtime-CRijXjYp.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_runtime = require("./runtime-
|
|
2
|
+
const require_runtime = require("./runtime-0mOFbe_H.cjs");
|
|
3
3
|
const require_ownership = require("./ownership-CY0nPXGF.cjs");
|
|
4
4
|
const require_driver = require("./driver-CNxqMVFH.cjs");
|
|
5
5
|
//#region core/src/impact.ts
|
|
@@ -89,78 +89,170 @@ const createImpact = (input) => {
|
|
|
89
89
|
//#endregion
|
|
90
90
|
//#region core/src/history.ts
|
|
91
91
|
const createHistory = (input) => {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
let undos = [];
|
|
93
|
+
let redos = [];
|
|
94
94
|
const listeners = /* @__PURE__ */ new Set();
|
|
95
95
|
let state = Object.freeze({
|
|
96
96
|
undoDepth: 0,
|
|
97
97
|
redoDepth: 0
|
|
98
98
|
});
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
let revision = 0;
|
|
100
|
+
let pending = false;
|
|
101
|
+
let disposed = false;
|
|
102
|
+
let group;
|
|
103
|
+
const unchanged = () => ({
|
|
104
|
+
status: "unchanged",
|
|
105
|
+
revision: input.revision()
|
|
106
|
+
});
|
|
107
|
+
const publish = () => {
|
|
108
|
+
if (state.undoDepth === undos.length && state.redoDepth === redos.length) return;
|
|
109
|
+
state = Object.freeze({
|
|
101
110
|
undoDepth: undos.length,
|
|
102
111
|
redoDepth: redos.length
|
|
103
112
|
});
|
|
104
|
-
|
|
113
|
+
revision++;
|
|
114
|
+
pending = true;
|
|
105
115
|
};
|
|
106
|
-
const
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
116
|
+
const flush = () => {
|
|
117
|
+
if (!pending) return [];
|
|
118
|
+
pending = false;
|
|
119
|
+
const errors = [];
|
|
120
|
+
for (const listener of [...listeners]) {
|
|
121
|
+
if (!listeners.has(listener)) continue;
|
|
122
|
+
try {
|
|
123
|
+
listener();
|
|
124
|
+
} catch (error) {
|
|
125
|
+
errors.push({
|
|
126
|
+
phase: "listener",
|
|
127
|
+
error
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return errors;
|
|
110
132
|
};
|
|
111
133
|
const clear = () => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
134
|
+
group = void 0;
|
|
135
|
+
undos.length = redos.length = 0;
|
|
136
|
+
publish();
|
|
137
|
+
};
|
|
138
|
+
const travel = (direction) => {
|
|
139
|
+
input.assertIdle();
|
|
140
|
+
const from = direction === "redo" ? redos : undos;
|
|
141
|
+
const to = direction === "redo" ? undos : redos;
|
|
142
|
+
const previousUndos = undos;
|
|
143
|
+
const previousRedos = redos;
|
|
144
|
+
const entry = direction === "cancel" ? from.at(-1) : from.pop();
|
|
145
|
+
if (!entry) return unchanged();
|
|
146
|
+
if (direction === "cancel") {
|
|
147
|
+
if (!group || group.entry !== entry) throw new Error("History group no longer owns its entry.");
|
|
148
|
+
undos = group.undos;
|
|
149
|
+
redos = group.redos;
|
|
150
|
+
} else to.push(entry);
|
|
151
|
+
let committed = false;
|
|
152
|
+
try {
|
|
153
|
+
const operations = direction === "redo" ? entry.batches.flatMap((batch) => batch.operations) : entry.batches.slice().reverse().flatMap((batch) => batch.inverse);
|
|
154
|
+
const result = input.apply(operations);
|
|
155
|
+
committed = result.status !== "rejected";
|
|
156
|
+
if (committed && direction === "cancel") group = void 0;
|
|
157
|
+
if (result.status === "unchanged") {
|
|
158
|
+
publish();
|
|
159
|
+
const errors = input.notify(flush);
|
|
160
|
+
if (errors.length) throw new AggregateError(errors.map((entry) => entry.error), "History listeners failed.");
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
} finally {
|
|
164
|
+
if (!committed) if (direction === "cancel") {
|
|
165
|
+
undos = previousUndos;
|
|
166
|
+
redos = previousRedos;
|
|
167
|
+
} else {
|
|
168
|
+
to.pop();
|
|
169
|
+
from.push(entry);
|
|
170
|
+
}
|
|
116
171
|
}
|
|
117
172
|
};
|
|
118
173
|
return {
|
|
119
174
|
api: {
|
|
120
|
-
current,
|
|
175
|
+
current: () => state,
|
|
176
|
+
revision: () => revision,
|
|
121
177
|
subscribe: (listener) => {
|
|
178
|
+
if (disposed) throw new require_runtime.DocumentDisposedError();
|
|
122
179
|
listeners.add(listener);
|
|
123
|
-
return () =>
|
|
180
|
+
return () => {
|
|
181
|
+
listeners.delete(listener);
|
|
182
|
+
};
|
|
124
183
|
},
|
|
125
184
|
undo: () => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
revision: input.revision()
|
|
130
|
-
};
|
|
131
|
-
const result = input.apply(entry.inverse);
|
|
132
|
-
if (result.status === "committed") {
|
|
133
|
-
redos.push(entry);
|
|
134
|
-
emit();
|
|
135
|
-
} else undos.push(entry);
|
|
136
|
-
return result;
|
|
185
|
+
input.assertIdle();
|
|
186
|
+
group = void 0;
|
|
187
|
+
return travel("undo");
|
|
137
188
|
},
|
|
138
189
|
redo: () => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
revision: input.revision()
|
|
143
|
-
};
|
|
144
|
-
const result = input.apply(entry.operations);
|
|
145
|
-
if (result.status === "committed") {
|
|
146
|
-
undos.push(entry);
|
|
147
|
-
emit();
|
|
148
|
-
} else redos.push(entry);
|
|
149
|
-
return result;
|
|
190
|
+
input.assertIdle();
|
|
191
|
+
group = void 0;
|
|
192
|
+
return travel("redo");
|
|
150
193
|
},
|
|
151
|
-
clear
|
|
194
|
+
clear: () => {
|
|
195
|
+
input.assertIdle();
|
|
196
|
+
clear();
|
|
197
|
+
const errors = input.notify(flush);
|
|
198
|
+
if (errors.length) throw new AggregateError(errors.map((entry) => entry.error), "History listeners failed.");
|
|
199
|
+
},
|
|
200
|
+
group: () => {
|
|
201
|
+
input.assertIdle();
|
|
202
|
+
if (group) throw new Error("A history group is already active.");
|
|
203
|
+
if (input.capacity <= 0) throw new Error("History is disabled.");
|
|
204
|
+
const scope = {
|
|
205
|
+
undos: undos.slice(),
|
|
206
|
+
redos: redos.slice()
|
|
207
|
+
};
|
|
208
|
+
group = scope;
|
|
209
|
+
return Object.freeze({
|
|
210
|
+
end: () => {
|
|
211
|
+
input.assertIdle();
|
|
212
|
+
if (group === scope) group = void 0;
|
|
213
|
+
},
|
|
214
|
+
cancel: () => {
|
|
215
|
+
input.assertIdle();
|
|
216
|
+
if (group !== scope) return unchanged();
|
|
217
|
+
if (!scope.entry) {
|
|
218
|
+
group = void 0;
|
|
219
|
+
return unchanged();
|
|
220
|
+
}
|
|
221
|
+
const result = travel("cancel");
|
|
222
|
+
if (result.status !== "rejected") group = void 0;
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
152
227
|
},
|
|
153
228
|
record: (operations, inverse) => {
|
|
154
229
|
if (input.capacity <= 0) return;
|
|
155
|
-
|
|
230
|
+
const batch = {
|
|
156
231
|
operations,
|
|
157
232
|
inverse
|
|
158
|
-
}
|
|
159
|
-
if (
|
|
233
|
+
};
|
|
234
|
+
if (group?.entry) group.entry.batches.push(batch);
|
|
235
|
+
else {
|
|
236
|
+
const entry = { batches: [batch] };
|
|
237
|
+
undos.push(entry);
|
|
238
|
+
if (group) group.entry = entry;
|
|
239
|
+
if (undos.length > input.capacity) undos.shift();
|
|
240
|
+
}
|
|
160
241
|
redos.length = 0;
|
|
161
|
-
|
|
242
|
+
publish();
|
|
243
|
+
},
|
|
244
|
+
publish,
|
|
245
|
+
flush,
|
|
246
|
+
endGroup: () => {
|
|
247
|
+
group = void 0;
|
|
162
248
|
},
|
|
163
|
-
invalidate: clear
|
|
249
|
+
invalidate: clear,
|
|
250
|
+
dispose: () => {
|
|
251
|
+
disposed = true;
|
|
252
|
+
listeners.clear();
|
|
253
|
+
clear();
|
|
254
|
+
pending = false;
|
|
255
|
+
}
|
|
164
256
|
};
|
|
165
257
|
};
|
|
166
258
|
//#endregion
|
|
@@ -180,7 +272,7 @@ const writerFor = (node, inputAddress, sink) => {
|
|
|
180
272
|
at: address
|
|
181
273
|
}) : void 0
|
|
182
274
|
};
|
|
183
|
-
if (node.kind === "dict"
|
|
275
|
+
if (node.kind === "dict") return {
|
|
184
276
|
set: (key, value) => emit(sink, {
|
|
185
277
|
type: "dict.set",
|
|
186
278
|
at: address,
|
|
@@ -233,20 +325,20 @@ const writerFor = (node, inputAddress, sink) => {
|
|
|
233
325
|
}) } : {}
|
|
234
326
|
};
|
|
235
327
|
if (node.kind === "tree") return {
|
|
236
|
-
insert: (treeNodeId, value,
|
|
328
|
+
insert: (treeNodeId, value, position) => emit(sink, {
|
|
237
329
|
type: "tree.insert",
|
|
238
330
|
at: address,
|
|
239
331
|
treeNodeId,
|
|
240
332
|
value,
|
|
241
|
-
parentId,
|
|
242
|
-
index
|
|
333
|
+
parentId: position?.parentId,
|
|
334
|
+
index: position?.index
|
|
243
335
|
}),
|
|
244
|
-
move: (treeNodeId,
|
|
336
|
+
move: (treeNodeId, position) => emit(sink, {
|
|
245
337
|
type: "tree.move",
|
|
246
338
|
at: address,
|
|
247
339
|
treeNodeId,
|
|
248
|
-
parentId,
|
|
249
|
-
index
|
|
340
|
+
parentId: position?.parentId,
|
|
341
|
+
index: position?.index
|
|
250
342
|
}),
|
|
251
343
|
remove: (treeNodeId) => emit(sink, {
|
|
252
344
|
type: "tree.remove",
|
|
@@ -330,7 +422,6 @@ const writerFor = (node, inputAddress, sink) => {
|
|
|
330
422
|
})
|
|
331
423
|
};
|
|
332
424
|
}
|
|
333
|
-
if (node.kind === "single") return writerFor(node.value, address, sink);
|
|
334
425
|
if (node.kind === "variant") return {
|
|
335
426
|
replace: (value) => emit(sink, {
|
|
336
427
|
type: "variant.replace",
|
|
@@ -356,69 +447,15 @@ const documentWriter = (schema, sink) => writerFor(schemaRoot(schema), [], sink)
|
|
|
356
447
|
//#endregion
|
|
357
448
|
//#region core/src/mutation/issue.ts
|
|
358
449
|
const ownAddress$1 = (address) => Object.freeze(address.slice());
|
|
359
|
-
const at
|
|
450
|
+
const at = (address, code, message, operation) => Object.freeze({
|
|
360
451
|
source: "mutation",
|
|
361
452
|
code,
|
|
362
453
|
address: ownAddress$1(address),
|
|
363
454
|
message,
|
|
364
455
|
...operation === void 0 ? {} : { operation }
|
|
365
456
|
});
|
|
366
|
-
const from = (operation, code, message) => at
|
|
367
|
-
const input = (code, message) => at
|
|
368
|
-
//#endregion
|
|
369
|
-
//#region core/src/mutation/anchor.ts
|
|
370
|
-
const length = (keys) => keys.length;
|
|
371
|
-
const keyAt = (keys, index) => Array.isArray(keys) ? keys[index] : keys.at(index);
|
|
372
|
-
const keyIndex = (keys, key) => Array.isArray(keys) ? keys.indexOf(key) : keys.index(key);
|
|
373
|
-
const keys = (values, keyOf) => ({
|
|
374
|
-
length: values.length,
|
|
375
|
-
at: (index) => index >= 0 && index < values.length ? keyOf(values[index]) : void 0,
|
|
376
|
-
index: (key) => {
|
|
377
|
-
for (let index = 0; index < values.length; index += 1) if (keyOf(values[index]) === key) return index;
|
|
378
|
-
return -1;
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
const index = (keys, anchor) => {
|
|
382
|
-
if (!anchor) return length(keys);
|
|
383
|
-
if ("at" in anchor) return anchor.at === "start" ? 0 : length(keys);
|
|
384
|
-
if ("before" in anchor) return keyIndex(keys, anchor.before);
|
|
385
|
-
return keyIndex(keys, anchor.after) + 1;
|
|
386
|
-
};
|
|
387
|
-
const at = (keys, position) => position <= 0 ? { at: "start" } : { after: keyAt(keys, position - 1) };
|
|
388
|
-
const valid = (keys, anchor) => {
|
|
389
|
-
if (!anchor) return true;
|
|
390
|
-
if (typeof anchor !== "object" || anchor === null) return false;
|
|
391
|
-
if (Number("at" in anchor) + Number("before" in anchor) + Number("after" in anchor) !== 1) return false;
|
|
392
|
-
if ("at" in anchor) return anchor.at === "start" || anchor.at === "end";
|
|
393
|
-
return keyIndex(keys, "before" in anchor ? anchor.before : anchor.after) >= 0;
|
|
394
|
-
};
|
|
395
|
-
const afterRemove = (keys, removedIndex, anchor) => {
|
|
396
|
-
const position = index(keys, anchor);
|
|
397
|
-
return position > removedIndex ? position - 1 : position;
|
|
398
|
-
};
|
|
399
|
-
const validPositions = (length, positions) => {
|
|
400
|
-
let previous = -1;
|
|
401
|
-
for (let index = 0; index < positions.length; index += 1) {
|
|
402
|
-
const position = positions[index];
|
|
403
|
-
if (!Number.isInteger(position) || position < 0 || position <= previous || position > length + index) return false;
|
|
404
|
-
previous = position;
|
|
405
|
-
}
|
|
406
|
-
return true;
|
|
407
|
-
};
|
|
408
|
-
const restore = (keys, inserted, positions) => {
|
|
409
|
-
const currentLength = keys.length;
|
|
410
|
-
if (inserted.length !== positions.length || !validPositions(currentLength, positions)) throw new Error("Ordered restore positions are invalid.");
|
|
411
|
-
let readIndex = currentLength - 1;
|
|
412
|
-
let insertedIndex = inserted.length - 1;
|
|
413
|
-
keys.length = currentLength + inserted.length;
|
|
414
|
-
for (let writeIndex = keys.length - 1; writeIndex >= 0; writeIndex -= 1) if (insertedIndex >= 0 && positions[insertedIndex] === writeIndex) {
|
|
415
|
-
keys[writeIndex] = inserted[insertedIndex];
|
|
416
|
-
insertedIndex -= 1;
|
|
417
|
-
} else {
|
|
418
|
-
keys[writeIndex] = keys[readIndex];
|
|
419
|
-
readIndex -= 1;
|
|
420
|
-
}
|
|
421
|
-
};
|
|
457
|
+
const from = (operation, code, message) => at(operation.at, code, message, operation.type);
|
|
458
|
+
const input = (code, message) => at([], code, message);
|
|
422
459
|
//#endregion
|
|
423
460
|
//#region core/src/mutation/execute-entity.ts
|
|
424
461
|
const resolveEntityCollection = (node, value) => {
|
|
@@ -463,13 +500,13 @@ const executeEntity = (target, operation, copyPayload) => {
|
|
|
463
500
|
}
|
|
464
501
|
if (collection.kind === "map" && (operation.anchor || operation.positions)) return rejected$4(operation, "invalid-anchor", "Map entries do not support ordering metadata.");
|
|
465
502
|
if (ids && operation.positions) {
|
|
466
|
-
if (operation.positions.length !== operation.entries.length || !validPositions(ids.length, operation.positions)) return rejected$4(operation, "invalid-operation", "Entity restore positions are invalid.");
|
|
467
|
-
} else if (ids && !valid(ids, operation.anchor)) return rejected$4(operation, "invalid-anchor", "Collection anchor is invalid.");
|
|
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.");
|
|
468
505
|
for (const entry of operation.entries) byId[entry.id] = copyPayload ? require_ownership.cloneValue(entry.value, "canonical") : require_ownership.transferPayload(entry.value);
|
|
469
|
-
if (ids) if (operation.positions) restore(ids, operation.entries.map((entry) => entry.id), operation.positions);
|
|
506
|
+
if (ids) if (operation.positions) require_runtime.restore(ids, operation.entries.map((entry) => entry.id), operation.positions);
|
|
470
507
|
else if (!operation.anchor || "at" in operation.anchor && operation.anchor.at === "end") for (const entry of operation.entries) ids.push(entry.id);
|
|
471
508
|
else {
|
|
472
|
-
const insertion = index(ids, operation.anchor);
|
|
509
|
+
const insertion = require_runtime.index(ids, operation.anchor);
|
|
473
510
|
const currentLength = ids.length;
|
|
474
511
|
ids.length = currentLength + operation.entries.length;
|
|
475
512
|
for (let index = currentLength - 1; index >= insertion; index -= 1) ids[index + operation.entries.length] = ids[index];
|
|
@@ -535,15 +572,15 @@ const executeEntity = (target, operation, copyPayload) => {
|
|
|
535
572
|
const position = ids?.indexOf(operation.id) ?? -1;
|
|
536
573
|
if (!exists) return rejected$4(operation, "missing-entity", `Entity '${operation.id}' does not exist.`);
|
|
537
574
|
if (!ids || position < 0) return rejected$4(operation, "invalid-collection", `Collection index is missing '${operation.id}'.`);
|
|
538
|
-
if (!valid(ids, operation.anchor)) return rejected$4(operation, "invalid-anchor", "Collection anchor is invalid.");
|
|
575
|
+
if (!require_runtime.valid(ids, operation.anchor)) return rejected$4(operation, "invalid-anchor", "Collection anchor is invalid.");
|
|
539
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.");
|
|
540
|
-
const nextIndex = afterRemove(ids, position, operation.anchor);
|
|
577
|
+
const nextIndex = require_runtime.afterRemove(ids, position, operation.anchor);
|
|
541
578
|
if (nextIndex === position) return { status: "unchanged" };
|
|
542
579
|
const inverse = {
|
|
543
580
|
type: "entity.move",
|
|
544
581
|
at: operation.at,
|
|
545
582
|
id: operation.id,
|
|
546
|
-
anchor: at(ids, position)
|
|
583
|
+
anchor: require_runtime.at(ids, position)
|
|
547
584
|
};
|
|
548
585
|
ids.splice(position, 1);
|
|
549
586
|
ids.splice(nextIndex, 0, operation.id);
|
|
@@ -564,7 +601,7 @@ const executeList = (target, operation, copyPayload) => {
|
|
|
564
601
|
if (!Array.isArray(target.value) && !absent) return rejected$3(operation, "invalid-address", "List target is invalid.");
|
|
565
602
|
if (absent && operation.type !== "list.replace") return rejected$3(operation, "invalid-address", "Only list replacement can initialize an optional list.");
|
|
566
603
|
const list = Array.isArray(target.value) ? target.value : [];
|
|
567
|
-
const orderedKeys = keys(list, target.node.keyOf);
|
|
604
|
+
const orderedKeys = require_runtime.keys(list, target.node.keyOf);
|
|
568
605
|
const own = (value) => copyPayload ? require_ownership.ownPayload(value) : value;
|
|
569
606
|
if (operation.type === "list.replace") {
|
|
570
607
|
if (operation.keys.length !== operation.value.length) return rejected$3(operation, "invalid-list-keys", "List replacement keys are invalid.");
|
|
@@ -599,8 +636,8 @@ const executeList = (target, operation, copyPayload) => {
|
|
|
599
636
|
if (operation.type === "list.insert") {
|
|
600
637
|
if (target.node.keyOf(operation.value) !== operation.key) return rejected$3(operation, "invalid-list-key", "List item key does not match schema.");
|
|
601
638
|
if (orderedKeys.index(operation.key) >= 0) return rejected$3(operation, "duplicate-list-item", `List item '${operation.key}' already exists.`);
|
|
602
|
-
if (!valid(orderedKeys, operation.anchor)) return rejected$3(operation, "invalid-anchor", "List anchor is invalid.");
|
|
603
|
-
list.splice(index(orderedKeys, operation.anchor), 0, own(operation.value));
|
|
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));
|
|
604
641
|
return {
|
|
605
642
|
status: "changed",
|
|
606
643
|
inverse: [{
|
|
@@ -618,7 +655,7 @@ const executeList = (target, operation, copyPayload) => {
|
|
|
618
655
|
at: operation.at,
|
|
619
656
|
key: operation.key,
|
|
620
657
|
value: require_ownership.cloneValue(list[position], "inverse"),
|
|
621
|
-
anchor: at(orderedKeys, position)
|
|
658
|
+
anchor: require_runtime.at(orderedKeys, position)
|
|
622
659
|
};
|
|
623
660
|
list.splice(position, 1);
|
|
624
661
|
return {
|
|
@@ -626,15 +663,15 @@ const executeList = (target, operation, copyPayload) => {
|
|
|
626
663
|
inverse: [inverse]
|
|
627
664
|
};
|
|
628
665
|
}
|
|
629
|
-
if (!valid(orderedKeys, operation.anchor)) return rejected$3(operation, "invalid-anchor", "List anchor is invalid.");
|
|
666
|
+
if (!require_runtime.valid(orderedKeys, operation.anchor)) return rejected$3(operation, "invalid-anchor", "List anchor is invalid.");
|
|
630
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.");
|
|
631
|
-
const nextIndex = afterRemove(orderedKeys, position, operation.anchor);
|
|
668
|
+
const nextIndex = require_runtime.afterRemove(orderedKeys, position, operation.anchor);
|
|
632
669
|
if (nextIndex === position) return { status: "unchanged" };
|
|
633
670
|
const inverse = {
|
|
634
671
|
type: "list.move",
|
|
635
672
|
at: operation.at,
|
|
636
673
|
key: operation.key,
|
|
637
|
-
anchor: at(orderedKeys, position)
|
|
674
|
+
anchor: require_runtime.at(orderedKeys, position)
|
|
638
675
|
};
|
|
639
676
|
const [entry] = list.splice(position, 1);
|
|
640
677
|
list.splice(nextIndex, 0, entry);
|
|
@@ -817,7 +854,7 @@ const changeChanged = (change, value) => {
|
|
|
817
854
|
return false;
|
|
818
855
|
};
|
|
819
856
|
const is = (value) => require_ownership.isRecord(value) && require_ownership.isRecord(value.nodes);
|
|
820
|
-
const contains
|
|
857
|
+
const contains = (tree, id) => has$1(tree.nodes, id);
|
|
821
858
|
const validate = (value) => {
|
|
822
859
|
if (!is(value)) return false;
|
|
823
860
|
const ids = Object.keys(value.nodes);
|
|
@@ -859,7 +896,6 @@ const documentTreeError = (node, value, address) => {
|
|
|
859
896
|
}
|
|
860
897
|
return;
|
|
861
898
|
}
|
|
862
|
-
if (node.kind === "single") return documentTreeError(node.value, value, address);
|
|
863
899
|
if (node.kind === "variant") {
|
|
864
900
|
if (!require_ownership.isRecord(value) || typeof value[node.tag] !== "string") return void 0;
|
|
865
901
|
const variant = node.variants[value[node.tag]];
|
|
@@ -898,7 +934,7 @@ const replace = (current, operation) => {
|
|
|
898
934
|
};
|
|
899
935
|
};
|
|
900
936
|
const insert = (tree, operation, value) => {
|
|
901
|
-
if (contains
|
|
937
|
+
if (contains(tree, operation.treeNodeId)) return rejected$2("duplicate-tree-node", `Tree node '${operation.treeNodeId}' already exists.`);
|
|
902
938
|
if (!validIndex(operation.index)) return rejected$2("invalid-tree-index", "Tree index is invalid.");
|
|
903
939
|
if (tree.rootId === void 0) {
|
|
904
940
|
if (operation.parentId !== void 0) return rejected$2("missing-tree-parent", `Tree parent '${operation.parentId}' does not exist.`);
|
|
@@ -1023,9 +1059,8 @@ const move = (tree, operation) => {
|
|
|
1023
1059
|
if (!currentParent || parentId === void 0) return rejected$2("invalid-tree", "Tree parent links are inconsistent.");
|
|
1024
1060
|
const originalIndex = childIndex(currentParent, operation.treeNodeId);
|
|
1025
1061
|
if (originalIndex < 0) return rejected$2("invalid-tree", "Tree parent links are inconsistent.");
|
|
1026
|
-
let nextIndex = Math.min(operation.index ?? nextParent.children.length, nextParent.children.length);
|
|
1027
1062
|
currentParent.children.splice(originalIndex, 1);
|
|
1028
|
-
|
|
1063
|
+
const nextIndex = Math.min(operation.index ?? nextParent.children.length, nextParent.children.length);
|
|
1029
1064
|
if (parentId === operation.parentId && nextIndex === originalIndex) {
|
|
1030
1065
|
currentParent.children.splice(originalIndex, 0, operation.treeNodeId);
|
|
1031
1066
|
return { status: "unchanged" };
|
|
@@ -1129,6 +1164,16 @@ const executeValue = (target, operation, copyPayload) => {
|
|
|
1129
1164
|
}
|
|
1130
1165
|
if (operation.type === "variant.replace") {
|
|
1131
1166
|
if (require_ownership.sameStructuralValue(target.value, operation.value)) return { status: "unchanged" };
|
|
1167
|
+
if (target.value === void 0 && target.node.optional) {
|
|
1168
|
+
target.parent[target.key] = own(operation.value);
|
|
1169
|
+
return {
|
|
1170
|
+
status: "changed",
|
|
1171
|
+
inverse: [{
|
|
1172
|
+
type: "value.clear",
|
|
1173
|
+
at: operation.at
|
|
1174
|
+
}]
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1132
1177
|
const inverse = {
|
|
1133
1178
|
type: "variant.replace",
|
|
1134
1179
|
at: operation.at,
|
|
@@ -1207,7 +1252,7 @@ const executeClear = (target, operation) => {
|
|
|
1207
1252
|
issue: from(operation, "required-field", "Only optional values can be cleared.")
|
|
1208
1253
|
};
|
|
1209
1254
|
if (!hasOwn$1(target.parent, target.key)) return { status: "unchanged" };
|
|
1210
|
-
if (target.node.kind !== "field" && target.node.kind !== "variant" && target.node.kind !== "dict" && target.node.kind !== "
|
|
1255
|
+
if (target.node.kind !== "field" && target.node.kind !== "variant" && target.node.kind !== "dict" && target.node.kind !== "list" && target.node.kind !== "tree") return {
|
|
1211
1256
|
status: "rejected",
|
|
1212
1257
|
issue: from(operation, "invalid-operation", "This optional value has no clear operation.")
|
|
1213
1258
|
};
|
|
@@ -1223,8 +1268,7 @@ const executeClear = (target, operation) => {
|
|
|
1223
1268
|
at: operation.at,
|
|
1224
1269
|
value: require_ownership.cloneValue(target.value, "inverse")
|
|
1225
1270
|
};
|
|
1226
|
-
case "dict":
|
|
1227
|
-
case "record": return {
|
|
1271
|
+
case "dict": return {
|
|
1228
1272
|
type: "dict.replace",
|
|
1229
1273
|
at: operation.at,
|
|
1230
1274
|
value: require_ownership.cloneValue(target.value, "inverse")
|
|
@@ -1263,7 +1307,7 @@ const execute = (root, operation, target, copyPayload) => {
|
|
|
1263
1307
|
if (operation.type === "value.clear") return !target ? invalidTarget(operation) : executeClear(target, operation);
|
|
1264
1308
|
if (operation.type === "field.set" || operation.type === "field.clear") return !target || target.node.kind !== "field" ? invalidTarget(operation) : executeValue(target, operation, copyPayload);
|
|
1265
1309
|
if (operation.type === "variant.replace") return !target || target.node.kind !== "variant" ? invalidTarget(operation) : executeValue(target, operation, copyPayload);
|
|
1266
|
-
if (operation.type === "dict.set" || operation.type === "dict.delete" || operation.type === "dict.replace") return !target || target.node.kind !== "dict"
|
|
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);
|
|
1267
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);
|
|
1268
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);
|
|
1269
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);
|
|
@@ -1276,7 +1320,7 @@ const execute = (root, operation, target, copyPayload) => {
|
|
|
1276
1320
|
//#region core/src/mutation/journal.ts
|
|
1277
1321
|
const indexNode = () => ({ children: /* @__PURE__ */ new Map() });
|
|
1278
1322
|
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1279
|
-
const sameAddress
|
|
1323
|
+
const sameAddress = (left, right) => {
|
|
1280
1324
|
if (left.length !== right.length) return false;
|
|
1281
1325
|
for (let index = 0; index < left.length; index += 1) if (left[index] !== right[index]) return false;
|
|
1282
1326
|
return true;
|
|
@@ -1375,7 +1419,7 @@ const restoreEntity = (value, subject) => {
|
|
|
1375
1419
|
};
|
|
1376
1420
|
const restoreRootSubject = (value, subject) => {
|
|
1377
1421
|
if (subject.kind === "value") return subject.before.status === "present" ? subject.before.value : void 0;
|
|
1378
|
-
if (subject.kind === "variant") return subject.before;
|
|
1422
|
+
if (subject.kind === "variant") return subject.before.status === "present" ? subject.before.value : void 0;
|
|
1379
1423
|
if (subject.kind === "dict") {
|
|
1380
1424
|
if (require_ownership.isRecord(value)) restoreDict(value, subject.state);
|
|
1381
1425
|
return value;
|
|
@@ -1391,14 +1435,14 @@ const restoreRootSubject = (value, subject) => {
|
|
|
1391
1435
|
return restoreChange(value, subject.state);
|
|
1392
1436
|
};
|
|
1393
1437
|
const restoreSubject = (value, prefix, subject) => {
|
|
1394
|
-
if (sameAddress
|
|
1438
|
+
if (sameAddress(prefix, subject.address)) return restoreRootSubject(value, subject);
|
|
1395
1439
|
const address = subject.address.slice(prefix.length);
|
|
1396
1440
|
if (subject.kind === "value") {
|
|
1397
1441
|
writePresence(value, address, subject.before);
|
|
1398
1442
|
return value;
|
|
1399
1443
|
}
|
|
1400
1444
|
if (subject.kind === "variant") {
|
|
1401
|
-
|
|
1445
|
+
writePresence(value, address, subject.before);
|
|
1402
1446
|
return value;
|
|
1403
1447
|
}
|
|
1404
1448
|
const current = require_runtime.read(value, address);
|
|
@@ -1499,7 +1543,11 @@ const analyzeEntity = (root, subject) => {
|
|
|
1499
1543
|
};
|
|
1500
1544
|
const subjectChanged = (root, subject) => {
|
|
1501
1545
|
if (subject.kind === "value") return valueChanged(root, subject);
|
|
1502
|
-
if (subject.kind === "variant")
|
|
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
|
+
}
|
|
1503
1551
|
if (subject.kind === "dict") return dictChanged(root, subject);
|
|
1504
1552
|
if (subject.kind === "list") return listChanged(root, subject);
|
|
1505
1553
|
return changeChanged(subject.state, require_runtime.read(root, subject.address));
|
|
@@ -1508,13 +1556,13 @@ const listOrderBefore = (value, node, inverse) => {
|
|
|
1508
1556
|
const order = value.map(node.keyOf);
|
|
1509
1557
|
if (inverse.type === "list.remove") return order.filter((key) => key !== inverse.key);
|
|
1510
1558
|
if (inverse.type === "list.insert") {
|
|
1511
|
-
order.splice(index(order, inverse.anchor), 0, inverse.key);
|
|
1559
|
+
order.splice(require_runtime.index(order, inverse.anchor), 0, inverse.key);
|
|
1512
1560
|
return order;
|
|
1513
1561
|
}
|
|
1514
1562
|
if (inverse.type === "list.move") {
|
|
1515
1563
|
const position = order.indexOf(inverse.key);
|
|
1516
1564
|
if (position >= 0) order.splice(position, 1);
|
|
1517
|
-
order.splice(index(order, inverse.anchor), 0, inverse.key);
|
|
1565
|
+
order.splice(require_runtime.index(order, inverse.anchor), 0, inverse.key);
|
|
1518
1566
|
}
|
|
1519
1567
|
return order;
|
|
1520
1568
|
};
|
|
@@ -1556,12 +1604,12 @@ const restoreEntityOrder = (order, inverses) => {
|
|
|
1556
1604
|
continue;
|
|
1557
1605
|
}
|
|
1558
1606
|
if (inverse.kind === "restore") {
|
|
1559
|
-
restore(order, inverse.ids, inverse.positions);
|
|
1607
|
+
require_runtime.restore(order, inverse.ids, inverse.positions);
|
|
1560
1608
|
continue;
|
|
1561
1609
|
}
|
|
1562
1610
|
const position = order.indexOf(inverse.id);
|
|
1563
|
-
if (position < 0 || !valid(order, inverse.anchor)) throw new Error(`Entity order inverse cannot restore '${inverse.id}'.`);
|
|
1564
|
-
const nextIndex = afterRemove(order, position, inverse.anchor);
|
|
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);
|
|
1565
1613
|
order.splice(position, 1);
|
|
1566
1614
|
order.splice(nextIndex, 0, inverse.id);
|
|
1567
1615
|
}
|
|
@@ -1618,7 +1666,7 @@ const createChangeJournal = (root) => {
|
|
|
1618
1666
|
const candidates = buckets.get(addressHash) ?? [];
|
|
1619
1667
|
for (const subject of candidates) {
|
|
1620
1668
|
require_ownership.profile.journal.comparison();
|
|
1621
|
-
if (subject.kind === kind && sameAddress
|
|
1669
|
+
if (subject.kind === kind && sameAddress(subject.address, address)) return subject;
|
|
1622
1670
|
}
|
|
1623
1671
|
}
|
|
1624
1672
|
const ownerOf = (address) => {
|
|
@@ -1661,11 +1709,11 @@ const createChangeJournal = (root) => {
|
|
|
1661
1709
|
const recordVariant = (resolved, operation, inverse) => {
|
|
1662
1710
|
if (find("variant", operation.at, resolved.addressHash)) return;
|
|
1663
1711
|
const first = inverse[0];
|
|
1664
|
-
if (first?.type !== "variant.replace") return inverseMismatch(operation);
|
|
1712
|
+
if (first?.type !== "variant.replace" && first?.type !== "value.clear") return inverseMismatch(operation);
|
|
1665
1713
|
add({
|
|
1666
1714
|
...base(resolved, operation),
|
|
1667
1715
|
kind: "variant",
|
|
1668
|
-
before: absorb(first.value, operation.at)
|
|
1716
|
+
before: first.type === "value.clear" ? absent : present(absorb(first.value, operation.at))
|
|
1669
1717
|
});
|
|
1670
1718
|
};
|
|
1671
1719
|
const recordDict = (resolved, operation, inverse) => {
|
|
@@ -1829,6 +1877,10 @@ const createChangeJournal = (root) => {
|
|
|
1829
1877
|
record: (resolved, operation, inverse) => {
|
|
1830
1878
|
require_ownership.profile.batch.journalRecord();
|
|
1831
1879
|
if (ownerOf(operation.at)) return;
|
|
1880
|
+
if (operation.type === "value.clear" && resolved.node.kind === "variant") {
|
|
1881
|
+
recordVariant(resolved, operation, inverse);
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1832
1884
|
if (operation.type === "field.set" || operation.type === "field.clear" || operation.type === "value.clear") {
|
|
1833
1885
|
recordValue(resolved, operation, inverse);
|
|
1834
1886
|
return;
|
|
@@ -1870,7 +1922,7 @@ const createChangeJournal = (root) => {
|
|
|
1870
1922
|
const changeAt = (address, addressHash) => {
|
|
1871
1923
|
const candidates = collectionBuckets.get(addressHash) ?? [];
|
|
1872
1924
|
collectionBuckets.set(addressHash, candidates);
|
|
1873
|
-
let change = candidates.find((entry) => sameAddress
|
|
1925
|
+
let change = candidates.find((entry) => sameAddress(entry.address, address));
|
|
1874
1926
|
if (!change) {
|
|
1875
1927
|
change = {
|
|
1876
1928
|
address,
|
|
@@ -1914,7 +1966,7 @@ const createChangeJournal = (root) => {
|
|
|
1914
1966
|
const has = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1915
1967
|
const stringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
1916
1968
|
const positions = (value) => Array.isArray(value) && value.every((entry) => Number.isInteger(entry) && entry >= 0);
|
|
1917
|
-
const address
|
|
1969
|
+
const address = (value) => stringArray(value);
|
|
1918
1970
|
const anchor = (value) => {
|
|
1919
1971
|
if (!require_ownership.isRecord(value)) return false;
|
|
1920
1972
|
if (Number(has(value, "at")) + Number(has(value, "before")) + Number(has(value, "after")) !== 1) return false;
|
|
@@ -1935,7 +1987,7 @@ const rejected = (code, message) => ({
|
|
|
1935
1987
|
});
|
|
1936
1988
|
const decode = (input) => {
|
|
1937
1989
|
if (!require_ownership.isRecord(input)) return rejected("invalid-operation", "Operation must be an object.");
|
|
1938
|
-
if (!address
|
|
1990
|
+
if (!address(input.at)) return rejected("invalid-address", "Operation address is malformed.");
|
|
1939
1991
|
if (typeof input.type !== "string") return rejected("invalid-operation", "Operation type must be a string.");
|
|
1940
1992
|
switch (input.type) {
|
|
1941
1993
|
case "field.set":
|
|
@@ -2111,192 +2163,6 @@ const mutateOperations = (root, schema, operations, options = {}) => {
|
|
|
2111
2163
|
return session.finish();
|
|
2112
2164
|
};
|
|
2113
2165
|
//#endregion
|
|
2114
|
-
//#region core/src/mutation/footprint.ts
|
|
2115
|
-
const sameAddress = (left, right) => left.length === right.length && left.every((segment, index) => segment === right[index]);
|
|
2116
|
-
const containsAddress = (parent, child) => parent.length <= child.length && parent.every((segment, index) => segment === child[index]);
|
|
2117
|
-
const address = (value) => Array.isArray(value) && value.every((segment) => typeof segment === "string") ? Object.freeze(value.slice()) : void 0;
|
|
2118
|
-
const targetKey = (target) => {
|
|
2119
|
-
const at = target.at.join("\0");
|
|
2120
|
-
if (target.kind === "dictionary-key" || target.kind === "list-item") return `${target.kind}:${at}:${target.key}`;
|
|
2121
|
-
if (target.kind === "entity" || target.kind === "tree-node") return `${target.kind}:${at}:${target.id}`;
|
|
2122
|
-
return `${target.kind}:${at}`;
|
|
2123
|
-
};
|
|
2124
|
-
const freezeTarget = (target) => Object.freeze({
|
|
2125
|
-
...target,
|
|
2126
|
-
at: Object.freeze(target.at.slice())
|
|
2127
|
-
});
|
|
2128
|
-
const add = (targets, target) => {
|
|
2129
|
-
const stable = freezeTarget(target);
|
|
2130
|
-
targets.set(targetKey(stable), stable);
|
|
2131
|
-
};
|
|
2132
|
-
const commandFootprint = (operations) => {
|
|
2133
|
-
const targets = /* @__PURE__ */ new Map();
|
|
2134
|
-
for (const operation of operations) switch (operation.type) {
|
|
2135
|
-
case "field.set":
|
|
2136
|
-
case "field.clear":
|
|
2137
|
-
case "value.clear":
|
|
2138
|
-
case "variant.replace":
|
|
2139
|
-
add(targets, {
|
|
2140
|
-
kind: "value",
|
|
2141
|
-
at: operation.at
|
|
2142
|
-
});
|
|
2143
|
-
break;
|
|
2144
|
-
case "dict.replace":
|
|
2145
|
-
add(targets, {
|
|
2146
|
-
kind: "dictionary",
|
|
2147
|
-
at: operation.at
|
|
2148
|
-
});
|
|
2149
|
-
break;
|
|
2150
|
-
case "dict.set":
|
|
2151
|
-
case "dict.delete":
|
|
2152
|
-
add(targets, {
|
|
2153
|
-
kind: "dictionary-key",
|
|
2154
|
-
at: operation.at,
|
|
2155
|
-
key: operation.key
|
|
2156
|
-
});
|
|
2157
|
-
break;
|
|
2158
|
-
case "entity.create":
|
|
2159
|
-
add(targets, {
|
|
2160
|
-
kind: "collection",
|
|
2161
|
-
at: operation.at
|
|
2162
|
-
});
|
|
2163
|
-
for (const entry of operation.entries) add(targets, {
|
|
2164
|
-
kind: "entity",
|
|
2165
|
-
at: operation.at,
|
|
2166
|
-
id: entry.id
|
|
2167
|
-
});
|
|
2168
|
-
break;
|
|
2169
|
-
case "entity.remove":
|
|
2170
|
-
add(targets, {
|
|
2171
|
-
kind: "collection",
|
|
2172
|
-
at: operation.at
|
|
2173
|
-
});
|
|
2174
|
-
for (const id of operation.ids) add(targets, {
|
|
2175
|
-
kind: "entity",
|
|
2176
|
-
at: operation.at,
|
|
2177
|
-
id
|
|
2178
|
-
});
|
|
2179
|
-
break;
|
|
2180
|
-
case "entity.move":
|
|
2181
|
-
add(targets, {
|
|
2182
|
-
kind: "collection",
|
|
2183
|
-
at: operation.at
|
|
2184
|
-
});
|
|
2185
|
-
add(targets, {
|
|
2186
|
-
kind: "entity",
|
|
2187
|
-
at: operation.at,
|
|
2188
|
-
id: operation.id
|
|
2189
|
-
});
|
|
2190
|
-
break;
|
|
2191
|
-
case "list.insert":
|
|
2192
|
-
case "list.move":
|
|
2193
|
-
case "list.remove":
|
|
2194
|
-
add(targets, {
|
|
2195
|
-
kind: "list",
|
|
2196
|
-
at: operation.at
|
|
2197
|
-
});
|
|
2198
|
-
add(targets, {
|
|
2199
|
-
kind: "list-item",
|
|
2200
|
-
at: operation.at,
|
|
2201
|
-
key: operation.key
|
|
2202
|
-
});
|
|
2203
|
-
break;
|
|
2204
|
-
case "list.replace":
|
|
2205
|
-
add(targets, {
|
|
2206
|
-
kind: "list",
|
|
2207
|
-
at: operation.at
|
|
2208
|
-
});
|
|
2209
|
-
for (const key of operation.keys) add(targets, {
|
|
2210
|
-
kind: "list-item",
|
|
2211
|
-
at: operation.at,
|
|
2212
|
-
key
|
|
2213
|
-
});
|
|
2214
|
-
break;
|
|
2215
|
-
case "tree.insert":
|
|
2216
|
-
case "tree.move":
|
|
2217
|
-
case "tree.remove":
|
|
2218
|
-
case "tree.set":
|
|
2219
|
-
add(targets, {
|
|
2220
|
-
kind: "tree",
|
|
2221
|
-
at: operation.at
|
|
2222
|
-
});
|
|
2223
|
-
add(targets, {
|
|
2224
|
-
kind: "tree-node",
|
|
2225
|
-
at: operation.at,
|
|
2226
|
-
id: operation.treeNodeId
|
|
2227
|
-
});
|
|
2228
|
-
break;
|
|
2229
|
-
case "tree.replace":
|
|
2230
|
-
add(targets, {
|
|
2231
|
-
kind: "tree",
|
|
2232
|
-
at: operation.at
|
|
2233
|
-
});
|
|
2234
|
-
break;
|
|
2235
|
-
}
|
|
2236
|
-
return Object.freeze(Array.from(targets.values()));
|
|
2237
|
-
};
|
|
2238
|
-
const overlapsValueAndEntity = (value, entity) => containsAddress([...entity.at, entity.id], value.at) || containsAddress(value.at, [...entity.at, entity.id]);
|
|
2239
|
-
const targetsOverlap = (left, right) => {
|
|
2240
|
-
if (left.kind === "value" && right.kind === "value") return containsAddress(left.at, right.at) || containsAddress(right.at, left.at);
|
|
2241
|
-
if (left.kind === "value" && right.kind === "entity") return overlapsValueAndEntity(left, right);
|
|
2242
|
-
if (left.kind === "entity" && right.kind === "value") return overlapsValueAndEntity(right, left);
|
|
2243
|
-
if (left.kind === "dictionary" && right.kind === "dictionary") return sameAddress(left.at, right.at);
|
|
2244
|
-
if (left.kind === "dictionary" && right.kind === "dictionary-key") return sameAddress(left.at, right.at);
|
|
2245
|
-
if (left.kind === "dictionary-key" && right.kind === "dictionary") return sameAddress(left.at, right.at);
|
|
2246
|
-
if (left.kind === "dictionary-key" && right.kind === "dictionary-key") return sameAddress(left.at, right.at) && left.key === right.key;
|
|
2247
|
-
if (left.kind === "collection" && right.kind === "collection") return sameAddress(left.at, right.at);
|
|
2248
|
-
if (left.kind === "collection" && right.kind === "entity") return sameAddress(left.at, right.at);
|
|
2249
|
-
if (left.kind === "entity" && right.kind === "collection") return sameAddress(left.at, right.at);
|
|
2250
|
-
if (left.kind === "entity" && right.kind === "entity") return sameAddress(left.at, right.at) && left.id === right.id;
|
|
2251
|
-
if (left.kind === "list" && (right.kind === "list" || right.kind === "list-item")) return sameAddress(left.at, right.at);
|
|
2252
|
-
if (right.kind === "list" && left.kind === "list-item") return sameAddress(left.at, right.at);
|
|
2253
|
-
if (left.kind === "list-item" && right.kind === "list-item") return sameAddress(left.at, right.at) && left.key === right.key;
|
|
2254
|
-
if (left.kind === "tree" && (right.kind === "tree" || right.kind === "tree-node")) return sameAddress(left.at, right.at);
|
|
2255
|
-
if (right.kind === "tree" && left.kind === "tree-node") return sameAddress(left.at, right.at);
|
|
2256
|
-
if (left.kind === "tree-node" && right.kind === "tree-node") return sameAddress(left.at, right.at) && left.id === right.id;
|
|
2257
|
-
return false;
|
|
2258
|
-
};
|
|
2259
|
-
const footprintsOverlap = (left, right) => left.some((leftTarget) => right.some((rightTarget) => targetsOverlap(leftTarget, rightTarget)));
|
|
2260
|
-
const decodedTarget = (value) => {
|
|
2261
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
2262
|
-
const record = value;
|
|
2263
|
-
const at = address(record.at);
|
|
2264
|
-
if (!at || typeof record.kind !== "string") return void 0;
|
|
2265
|
-
switch (record.kind) {
|
|
2266
|
-
case "value":
|
|
2267
|
-
case "dictionary":
|
|
2268
|
-
case "collection":
|
|
2269
|
-
case "list":
|
|
2270
|
-
case "tree": return freezeTarget({
|
|
2271
|
-
kind: record.kind,
|
|
2272
|
-
at
|
|
2273
|
-
});
|
|
2274
|
-
case "dictionary-key":
|
|
2275
|
-
case "list-item": return typeof record.key === "string" ? freezeTarget({
|
|
2276
|
-
kind: record.kind,
|
|
2277
|
-
at,
|
|
2278
|
-
key: record.key
|
|
2279
|
-
}) : void 0;
|
|
2280
|
-
case "entity":
|
|
2281
|
-
case "tree-node": return typeof record.id === "string" ? freezeTarget({
|
|
2282
|
-
kind: record.kind,
|
|
2283
|
-
at,
|
|
2284
|
-
id: record.id
|
|
2285
|
-
}) : void 0;
|
|
2286
|
-
default: return;
|
|
2287
|
-
}
|
|
2288
|
-
};
|
|
2289
|
-
const decodeCommandFootprint = (value) => {
|
|
2290
|
-
if (!Array.isArray(value)) return void 0;
|
|
2291
|
-
const targets = /* @__PURE__ */ new Map();
|
|
2292
|
-
for (const entry of value) {
|
|
2293
|
-
const target = decodedTarget(entry);
|
|
2294
|
-
if (!target) return void 0;
|
|
2295
|
-
targets.set(targetKey(target), target);
|
|
2296
|
-
}
|
|
2297
|
-
return Object.freeze(Array.from(targets.values()));
|
|
2298
|
-
};
|
|
2299
|
-
//#endregion
|
|
2300
2166
|
//#region core/src/runtime.ts
|
|
2301
2167
|
var RejectedUpdate = class extends Error {
|
|
2302
2168
|
issues;
|
|
@@ -2308,6 +2174,7 @@ var RejectedUpdate = class extends Error {
|
|
|
2308
2174
|
const EMPTY = Object.freeze([]);
|
|
2309
2175
|
const publishDiagnostic = (value) => Object.freeze({
|
|
2310
2176
|
...value,
|
|
2177
|
+
source: "application",
|
|
2311
2178
|
...value.address === void 0 ? {} : { address: Object.freeze(value.address.slice()) }
|
|
2312
2179
|
});
|
|
2313
2180
|
const createDocument = (input) => {
|
|
@@ -2368,7 +2235,19 @@ const createDocument = (input) => {
|
|
|
2368
2235
|
const history = createHistory({
|
|
2369
2236
|
capacity: input.history === false ? 0 : Math.max(0, input.history?.capacity ?? 100),
|
|
2370
2237
|
revision: () => revision,
|
|
2371
|
-
apply: (operations) => applyBatch(operations, "history", false)
|
|
2238
|
+
apply: (operations) => applyBatch(operations, "history", false),
|
|
2239
|
+
assertIdle: () => {
|
|
2240
|
+
if (state.disposed) throw new require_runtime.DocumentDisposedError();
|
|
2241
|
+
if (busy || require_runtime.accessOf(runtime).projectionLocks) throw new require_runtime.DocumentReentrancyError();
|
|
2242
|
+
},
|
|
2243
|
+
notify: (run) => {
|
|
2244
|
+
busy = true;
|
|
2245
|
+
try {
|
|
2246
|
+
return run();
|
|
2247
|
+
} finally {
|
|
2248
|
+
busy = false;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2372
2251
|
});
|
|
2373
2252
|
const publish = (batch, source, kind, recordHistory) => {
|
|
2374
2253
|
revision += 1;
|
|
@@ -2382,10 +2261,12 @@ const createDocument = (input) => {
|
|
|
2382
2261
|
});
|
|
2383
2262
|
if (kind === "replace" || source === "remote") history.invalidate();
|
|
2384
2263
|
else if (recordHistory && (source === "local" || source === "system")) history.record(batch.operations, batch.inverse);
|
|
2264
|
+
else if (source === "history") history.publish();
|
|
2265
|
+
else history.endGroup();
|
|
2385
2266
|
busy = true;
|
|
2386
2267
|
let observerErrors;
|
|
2387
2268
|
try {
|
|
2388
|
-
observerErrors = require_runtime.notify(notification, commit);
|
|
2269
|
+
observerErrors = require_runtime.notify(notification, commit, history.flush);
|
|
2389
2270
|
} finally {
|
|
2390
2271
|
busy = false;
|
|
2391
2272
|
}
|
|
@@ -2500,7 +2381,7 @@ const createDocument = (input) => {
|
|
|
2500
2381
|
operations: batch.operations,
|
|
2501
2382
|
inverse: batch.inverse,
|
|
2502
2383
|
impact,
|
|
2503
|
-
footprint: commandFootprint(batch.operations),
|
|
2384
|
+
footprint: require_runtime.commandFootprint(batch.operations),
|
|
2504
2385
|
reports
|
|
2505
2386
|
};
|
|
2506
2387
|
} catch (error) {
|
|
@@ -2528,7 +2409,7 @@ const createDocument = (input) => {
|
|
|
2528
2409
|
const invalidTree = invalidDocument(input.schema, document);
|
|
2529
2410
|
if (invalidTree) return {
|
|
2530
2411
|
status: "rejected",
|
|
2531
|
-
issues: [at
|
|
2412
|
+
issues: [at(invalidTree, "invalid-tree", "Document replacement contains an invalid tree.")],
|
|
2532
2413
|
revision
|
|
2533
2414
|
};
|
|
2534
2415
|
if (require_ownership.deepEqual(state.document, document)) return {
|
|
@@ -2563,7 +2444,7 @@ const createDocument = (input) => {
|
|
|
2563
2444
|
try {
|
|
2564
2445
|
require_runtime.disposeNotification(notification);
|
|
2565
2446
|
} finally {
|
|
2566
|
-
history.
|
|
2447
|
+
history.dispose();
|
|
2567
2448
|
require_driver.disposeRuntimeDriver(runtime);
|
|
2568
2449
|
}
|
|
2569
2450
|
}
|
|
@@ -2571,6 +2452,7 @@ const createDocument = (input) => {
|
|
|
2571
2452
|
require_runtime.bindRuntimeAccess(runtime, state);
|
|
2572
2453
|
require_driver.bindRuntimeDriver(runtime);
|
|
2573
2454
|
notification = require_runtime.createNotification(runtime);
|
|
2455
|
+
require_runtime.bindDocumentReadable(history.api, runtime);
|
|
2574
2456
|
return runtime;
|
|
2575
2457
|
};
|
|
2576
2458
|
//#endregion
|
|
@@ -2594,26 +2476,16 @@ exports.DocumentReentrancyError = require_runtime.DocumentReentrancyError;
|
|
|
2594
2476
|
exports.ProjectionDisposedError = require_runtime.ProjectionDisposedError;
|
|
2595
2477
|
exports.ProjectionError = require_runtime.ProjectionError;
|
|
2596
2478
|
exports.asReadable = asReadable;
|
|
2597
|
-
exports.commandFootprint = commandFootprint;
|
|
2598
|
-
exports.contains = require_runtime.contains;
|
|
2599
2479
|
exports.createDocument = createDocument;
|
|
2600
2480
|
exports.createProjectionRuntime = require_runtime.createProjectionRuntime;
|
|
2601
|
-
exports.debugKey = require_runtime.debugKey;
|
|
2602
|
-
exports.decodeCommandFootprint = decodeCommandFootprint;
|
|
2603
2481
|
exports.dict = require_runtime.dict;
|
|
2604
2482
|
exports.field = require_runtime.field;
|
|
2605
|
-
exports.footprintsOverlap = footprintsOverlap;
|
|
2606
2483
|
exports.list = require_runtime.list;
|
|
2607
2484
|
exports.map = require_runtime.map;
|
|
2608
2485
|
exports.object = require_runtime.object;
|
|
2609
2486
|
exports.optional = require_runtime.optional;
|
|
2610
|
-
exports.overlaps = require_runtime.overlaps;
|
|
2611
|
-
exports.readAddress = require_runtime.read;
|
|
2612
|
-
exports.record = require_runtime.record;
|
|
2613
|
-
exports.resolveAddress = require_runtime.resolveAddress;
|
|
2614
2487
|
exports.schema = require_runtime.schema;
|
|
2615
2488
|
exports.select = select;
|
|
2616
|
-
exports.single = require_runtime.single;
|
|
2617
2489
|
exports.table = require_runtime.table;
|
|
2618
2490
|
Object.defineProperty(exports, "target", {
|
|
2619
2491
|
enumerable: true,
|