@markless/runtime 0.3.1 → 0.3.3
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/dist/{graph-DoSjSC5T.js → graph-B77v_FUC.js} +23 -198
- package/dist/graph-async-Db6SqB5x.js +197 -0
- package/dist/graph-reconcile-DoBBfYsT.d.ts +274 -0
- package/dist/graph-reconcile.d.ts +2 -0
- package/dist/graph-reconcile.js +324 -0
- package/dist/graph.d.ts +2 -145
- package/dist/graph.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +6 -2
|
@@ -1,69 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
function findLastSharedReturnProperty(properties, propertyName) {
|
|
3
|
-
if (!properties) return void 0;
|
|
4
|
-
for (let index = properties.length - 1; index >= 0; index--) {
|
|
5
|
-
const property = properties[index];
|
|
6
|
-
if (property?.name === propertyName) return property;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
function readPath(value, path) {
|
|
10
|
-
let current = value;
|
|
11
|
-
for (const segment of path) {
|
|
12
|
-
if (current == null) return void 0;
|
|
13
|
-
current = current[segment];
|
|
14
|
-
}
|
|
15
|
-
return current;
|
|
16
|
-
}
|
|
17
|
-
function writePath(value, path, nextValue) {
|
|
18
|
-
if (path.length === 0) return nextValue;
|
|
19
|
-
const root = isObject(value) ? value : {};
|
|
20
|
-
let current = root;
|
|
21
|
-
for (const segment of path.slice(0, -1)) {
|
|
22
|
-
const child = current[segment];
|
|
23
|
-
if (!isObject(child)) current[segment] = {};
|
|
24
|
-
current = current[segment];
|
|
25
|
-
}
|
|
26
|
-
current[path[path.length - 1]] = nextValue;
|
|
27
|
-
return root;
|
|
28
|
-
}
|
|
29
|
-
function dirtyPathForGraphWrite(value, path) {
|
|
30
|
-
if (path[path.length - 1] !== "length") return path;
|
|
31
|
-
const parentPath = path.slice(0, -1);
|
|
32
|
-
const parent = readPath(value, parentPath);
|
|
33
|
-
if (!Array.isArray(parent)) return path;
|
|
34
|
-
return parentPath;
|
|
35
|
-
}
|
|
36
|
-
function deletePath(value, path) {
|
|
37
|
-
if (path.length === 0) throw new TypeError("Cannot delete a graph node root. Delete a property path instead.");
|
|
38
|
-
let current = value;
|
|
39
|
-
for (const segment of path.slice(0, -1)) {
|
|
40
|
-
if (current == null) throw new TypeError(`Cannot delete graph path "${path.join(".")}".`);
|
|
41
|
-
current = current[segment];
|
|
42
|
-
}
|
|
43
|
-
if (current == null) throw new TypeError(`Cannot delete graph path "${path.join(".")}".`);
|
|
44
|
-
if (!isObject(current)) return {
|
|
45
|
-
result: true,
|
|
46
|
-
mutated: false
|
|
47
|
-
};
|
|
48
|
-
const key = path[path.length - 1];
|
|
49
|
-
const hadProperty = Object.prototype.hasOwnProperty.call(current, key);
|
|
50
|
-
const result = delete current[key];
|
|
51
|
-
return {
|
|
52
|
-
result,
|
|
53
|
-
mutated: hadProperty && result
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function pathsIntersect(a, b) {
|
|
57
|
-
return isPrefix(a, b) || isPrefix(b, a);
|
|
58
|
-
}
|
|
59
|
-
function isPrefix(prefix, value) {
|
|
60
|
-
if (prefix.length > value.length) return false;
|
|
61
|
-
return prefix.every((segment, index) => value[index] === segment);
|
|
62
|
-
}
|
|
63
|
-
function isObject(value) {
|
|
64
|
-
return typeof value === "object" && value !== null;
|
|
65
|
-
}
|
|
66
|
-
//#endregion
|
|
1
|
+
import { a as readAsyncComputedNode, c as findLastSharedReturnProperty, d as writePath, i as invalidateAsyncComputed, l as pathsIntersect, n as createRuntimeAsyncComputedNodes, o as deletePath, r as demandAsyncComputed, s as dirtyPathForGraphWrite, u as readPath } from "./graph-async-Db6SqB5x.js";
|
|
67
2
|
//#region src/graph-collections.ts
|
|
68
3
|
function applyCollectionCall(target, method, args) {
|
|
69
4
|
if (!isSupportedCollectionTarget(target)) throw new TypeError(`Cannot call collection method "${method}" because the graph path is not an Array, Map, Set, or Date.`);
|
|
@@ -144,134 +79,6 @@ function arraySlotsEqual(before, after) {
|
|
|
144
79
|
});
|
|
145
80
|
}
|
|
146
81
|
//#endregion
|
|
147
|
-
//#region src/graph-async.ts
|
|
148
|
-
const ASYNC_SNAPSHOT_META_KEYS = new Set([
|
|
149
|
-
"status",
|
|
150
|
-
"version",
|
|
151
|
-
"key",
|
|
152
|
-
"error",
|
|
153
|
-
"value"
|
|
154
|
-
]);
|
|
155
|
-
function createRuntimeAsyncComputedNodes(asyncComputedInput) {
|
|
156
|
-
const asyncComputedNodes = /* @__PURE__ */ new Map();
|
|
157
|
-
for (const asyncComputed of asyncComputedInput ?? []) {
|
|
158
|
-
const initialSnapshot = asyncComputed.initialSnapshot ?? {
|
|
159
|
-
status: "idle",
|
|
160
|
-
version: 0
|
|
161
|
-
};
|
|
162
|
-
asyncComputedNodes.set(asyncComputed.graphNodeId, {
|
|
163
|
-
...asyncComputed,
|
|
164
|
-
gate: initialSnapshot.status === "pending" ? 1 : 0,
|
|
165
|
-
snapshot: initialSnapshot,
|
|
166
|
-
version: initialSnapshot.version
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
return asyncComputedNodes;
|
|
170
|
-
}
|
|
171
|
-
function readAsyncComputedNode(node, path, demand) {
|
|
172
|
-
demand();
|
|
173
|
-
const head = path[0];
|
|
174
|
-
if (head === void 0 || ASYNC_SNAPSHOT_META_KEYS.has(head)) return readPath(node.snapshot, path);
|
|
175
|
-
return readPath(node.snapshot.value, path);
|
|
176
|
-
}
|
|
177
|
-
function demandAsyncComputed(input) {
|
|
178
|
-
if (input.node.gate !== 1 && input.node.snapshot.status !== "idle") return;
|
|
179
|
-
advanceAsyncComputed(input);
|
|
180
|
-
}
|
|
181
|
-
function invalidateAsyncComputed(input) {
|
|
182
|
-
if (input.node.snapshot.status === "idle") return;
|
|
183
|
-
advanceAsyncComputed(input);
|
|
184
|
-
}
|
|
185
|
-
function advanceAsyncComputed(input) {
|
|
186
|
-
const visited = new Set([input.node.graphNodeId]);
|
|
187
|
-
let blocked;
|
|
188
|
-
const visit = (graphNodeId) => {
|
|
189
|
-
if (visited.has(graphNodeId)) return;
|
|
190
|
-
visited.add(graphNodeId);
|
|
191
|
-
const dependency = input.asyncComputedNodes.get(graphNodeId);
|
|
192
|
-
if (dependency) {
|
|
193
|
-
if (dependency.snapshot.status === "idle" || dependency.snapshot.status === "pending") {
|
|
194
|
-
blocked = dependency;
|
|
195
|
-
input.demandAsyncComputed(dependency);
|
|
196
|
-
} else if (!blocked && dependency.snapshot.status === "rejected") blocked = dependency;
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
for (const dependency of input.computedNodes.get(graphNodeId)?.dependencies ?? []) visit(dependency.graphNodeId);
|
|
200
|
-
};
|
|
201
|
-
for (const dependency of input.node.dependencies) visit(dependency.graphNodeId);
|
|
202
|
-
if (blocked) {
|
|
203
|
-
commitDependencyBlock(input, blocked.snapshot.status === "rejected" ? "rejected" : "pending", blocked.snapshot.error);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
const nextKey = input.node.key(input.readGraph);
|
|
207
|
-
if (!input.node.gate && input.node.snapshot.status !== "idle" && Object.is(input.node.snapshot.key, nextKey)) return;
|
|
208
|
-
startAsyncComputed({
|
|
209
|
-
...input,
|
|
210
|
-
key: nextKey
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
function commitDependencyBlock(input, status, error) {
|
|
214
|
-
if (input.node.gate === 2 && (status === "pending" || input.node.snapshot.status === "rejected" && Object.is(input.node.snapshot.error, error))) return;
|
|
215
|
-
input.node.controller?.abort();
|
|
216
|
-
input.node.controller = void 0;
|
|
217
|
-
input.node.gate = 2;
|
|
218
|
-
const version = ++input.node.version;
|
|
219
|
-
const key = input.node.snapshot.key;
|
|
220
|
-
publish(input, status === "pending" ? {
|
|
221
|
-
status,
|
|
222
|
-
version,
|
|
223
|
-
key,
|
|
224
|
-
value: input.node.snapshot.value
|
|
225
|
-
} : {
|
|
226
|
-
status,
|
|
227
|
-
version,
|
|
228
|
-
key,
|
|
229
|
-
error
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
function startAsyncComputed(input) {
|
|
233
|
-
input.node.controller?.abort();
|
|
234
|
-
input.node.gate = 0;
|
|
235
|
-
const controller = new AbortController();
|
|
236
|
-
const version = ++input.node.version;
|
|
237
|
-
input.node.controller = controller;
|
|
238
|
-
publish(input, {
|
|
239
|
-
status: "pending",
|
|
240
|
-
version,
|
|
241
|
-
key: input.key,
|
|
242
|
-
value: input.node.snapshot.value
|
|
243
|
-
});
|
|
244
|
-
const commit = (snapshot) => {
|
|
245
|
-
if (input.node.version !== version) return;
|
|
246
|
-
publish(input, snapshot);
|
|
247
|
-
};
|
|
248
|
-
const commitRejected = (error) => commit({
|
|
249
|
-
status: "rejected",
|
|
250
|
-
version,
|
|
251
|
-
key: input.key,
|
|
252
|
-
error
|
|
253
|
-
});
|
|
254
|
-
try {
|
|
255
|
-
Promise.resolve(input.node.run({
|
|
256
|
-
key: input.key,
|
|
257
|
-
signal: controller.signal,
|
|
258
|
-
read: input.readGraph
|
|
259
|
-
})).then((value) => commit({
|
|
260
|
-
status: "fulfilled",
|
|
261
|
-
version,
|
|
262
|
-
key: input.key,
|
|
263
|
-
value
|
|
264
|
-
}), commitRejected);
|
|
265
|
-
} catch (error) {
|
|
266
|
-
commitRejected(error);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
function publish(input, snapshot) {
|
|
270
|
-
input.node.snapshot = snapshot;
|
|
271
|
-
input.markDirtyPath(input.node.graphNodeId, []);
|
|
272
|
-
input.scheduleFlush();
|
|
273
|
-
}
|
|
274
|
-
//#endregion
|
|
275
82
|
//#region src/graph-computed.ts
|
|
276
83
|
function createRuntimeComputedNodes(computedInput) {
|
|
277
84
|
const computedNodes = /* @__PURE__ */ new Map();
|
|
@@ -282,11 +89,13 @@ function createRuntimeComputedNodes(computedInput) {
|
|
|
282
89
|
});
|
|
283
90
|
return computedNodes;
|
|
284
91
|
}
|
|
285
|
-
function readComputedNode(computed, readGraph, path) {
|
|
92
|
+
function readComputedNode(computed, readGraph, path, reconcile) {
|
|
286
93
|
const { compute } = computed;
|
|
287
94
|
if (computed.dirty && compute) {
|
|
95
|
+
const previous = computed.value;
|
|
288
96
|
computed.value = compute(readGraph);
|
|
289
97
|
computed.dirty = false;
|
|
98
|
+
reconcile?.(computed, previous);
|
|
290
99
|
}
|
|
291
100
|
return readPath(computed.value, path);
|
|
292
101
|
}
|
|
@@ -296,7 +105,8 @@ function markComputedDirty(input) {
|
|
|
296
105
|
const computed = input.computedNodes.get(input.graphNodeId);
|
|
297
106
|
if (!computed?.compute) return;
|
|
298
107
|
computed.dirty = true;
|
|
299
|
-
input.
|
|
108
|
+
if (input.reconcile) input.reconcile.invalidateComputed(computed);
|
|
109
|
+
else input.dirtyPaths.push({
|
|
300
110
|
graphNodeId: input.graphNodeId,
|
|
301
111
|
path: []
|
|
302
112
|
});
|
|
@@ -447,7 +257,7 @@ function createRuntimeGraph(input) {
|
|
|
447
257
|
}
|
|
448
258
|
const readGraph = (graphNodeId, path = []) => {
|
|
449
259
|
const computed = computedNodes.get(graphNodeId);
|
|
450
|
-
if (computed?.compute) return readComputedNode(computed, readGraph, path);
|
|
260
|
+
if (computed?.compute) return readComputedNode(computed, readGraph, path, plane?.reconcileComputed);
|
|
451
261
|
const asyncComputed = asyncComputedNodes.get(graphNodeId);
|
|
452
262
|
if (asyncComputed) return readAsyncComputedNode(asyncComputed, path, () => demandAsyncComputed$1(asyncComputed));
|
|
453
263
|
const initialize = readInitializers.get(graphNodeId);
|
|
@@ -470,12 +280,14 @@ function createRuntimeGraph(input) {
|
|
|
470
280
|
graphNodeId,
|
|
471
281
|
path
|
|
472
282
|
});
|
|
283
|
+
plane?.recordWrite(graphNodeId, path);
|
|
473
284
|
markDirtyComputedDependencies({
|
|
474
285
|
graphNodeId,
|
|
475
286
|
path,
|
|
476
287
|
computedNodes,
|
|
477
288
|
asyncComputedNodes,
|
|
478
289
|
dirtyPaths,
|
|
290
|
+
reconcile: plane,
|
|
479
291
|
invalidateAsyncComputed: invalidateAsyncComputed$1
|
|
480
292
|
});
|
|
481
293
|
};
|
|
@@ -486,13 +298,23 @@ function createRuntimeGraph(input) {
|
|
|
486
298
|
flush();
|
|
487
299
|
});
|
|
488
300
|
};
|
|
301
|
+
const plane = input.reconcile?.({
|
|
302
|
+
cells,
|
|
303
|
+
computedNodes,
|
|
304
|
+
subscriptions,
|
|
305
|
+
dirtyPaths,
|
|
306
|
+
readGraph,
|
|
307
|
+
markDirtyPath,
|
|
308
|
+
scheduleFlush
|
|
309
|
+
});
|
|
489
310
|
const asyncComputedInput = () => ({
|
|
490
311
|
computedNodes,
|
|
491
312
|
asyncComputedNodes,
|
|
492
313
|
demandAsyncComputed: demandAsyncComputed$1,
|
|
493
314
|
readGraph,
|
|
494
315
|
markDirtyPath,
|
|
495
|
-
scheduleFlush
|
|
316
|
+
scheduleFlush,
|
|
317
|
+
reconcile: plane
|
|
496
318
|
});
|
|
497
319
|
const demandAsyncComputed$1 = (node) => demandAsyncComputed({
|
|
498
320
|
node,
|
|
@@ -520,6 +342,7 @@ function createRuntimeGraph(input) {
|
|
|
520
342
|
try {
|
|
521
343
|
try {
|
|
522
344
|
while (dirtyPaths.length > 0) {
|
|
345
|
+
plane?.recomputeSubscribed();
|
|
523
346
|
const pending = dirtyPaths.splice(0);
|
|
524
347
|
const ranSubscriptions = /* @__PURE__ */ new Set();
|
|
525
348
|
for (const subscription of subscriptions) {
|
|
@@ -530,6 +353,7 @@ function createRuntimeGraph(input) {
|
|
|
530
353
|
}
|
|
531
354
|
}
|
|
532
355
|
} finally {
|
|
356
|
+
plane?.settleBaselines();
|
|
533
357
|
flushing = false;
|
|
534
358
|
}
|
|
535
359
|
await notifyJournalListeners();
|
|
@@ -555,6 +379,7 @@ function createRuntimeGraph(input) {
|
|
|
555
379
|
write(write) {
|
|
556
380
|
const path = write.path ?? [];
|
|
557
381
|
const current = cells.get(write.graphNodeId);
|
|
382
|
+
if (plane?.commitDerived(write, path)) return;
|
|
558
383
|
if (Object.is(readPath(current, path), write.value)) return;
|
|
559
384
|
cells.set(write.graphNodeId, writePath(current, path, write.value));
|
|
560
385
|
markDirtyPath(write.graphNodeId, dirtyPathForGraphWrite(current, path));
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
//#region src/graph-core.ts
|
|
2
|
+
function findLastSharedReturnProperty(properties, propertyName) {
|
|
3
|
+
if (!properties) return void 0;
|
|
4
|
+
for (let index = properties.length - 1; index >= 0; index--) {
|
|
5
|
+
const property = properties[index];
|
|
6
|
+
if (property?.name === propertyName) return property;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function readPath(value, path) {
|
|
10
|
+
let current = value;
|
|
11
|
+
for (const segment of path) {
|
|
12
|
+
if (current == null) return void 0;
|
|
13
|
+
current = current[segment];
|
|
14
|
+
}
|
|
15
|
+
return current;
|
|
16
|
+
}
|
|
17
|
+
function writePath(value, path, nextValue) {
|
|
18
|
+
if (path.length === 0) return nextValue;
|
|
19
|
+
const root = isObject(value) ? value : {};
|
|
20
|
+
let current = root;
|
|
21
|
+
for (const segment of path.slice(0, -1)) {
|
|
22
|
+
const child = current[segment];
|
|
23
|
+
if (!isObject(child)) current[segment] = {};
|
|
24
|
+
current = current[segment];
|
|
25
|
+
}
|
|
26
|
+
current[path[path.length - 1]] = nextValue;
|
|
27
|
+
return root;
|
|
28
|
+
}
|
|
29
|
+
function dirtyPathForGraphWrite(value, path) {
|
|
30
|
+
if (path[path.length - 1] !== "length") return path;
|
|
31
|
+
const parentPath = path.slice(0, -1);
|
|
32
|
+
const parent = readPath(value, parentPath);
|
|
33
|
+
if (!Array.isArray(parent)) return path;
|
|
34
|
+
return parentPath;
|
|
35
|
+
}
|
|
36
|
+
function deletePath(value, path) {
|
|
37
|
+
if (path.length === 0) throw new TypeError("Cannot delete a graph node root. Delete a property path instead.");
|
|
38
|
+
let current = value;
|
|
39
|
+
for (const segment of path.slice(0, -1)) {
|
|
40
|
+
if (current == null) throw new TypeError(`Cannot delete graph path "${path.join(".")}".`);
|
|
41
|
+
current = current[segment];
|
|
42
|
+
}
|
|
43
|
+
if (current == null) throw new TypeError(`Cannot delete graph path "${path.join(".")}".`);
|
|
44
|
+
if (!isObject(current)) return {
|
|
45
|
+
result: true,
|
|
46
|
+
mutated: false
|
|
47
|
+
};
|
|
48
|
+
const key = path[path.length - 1];
|
|
49
|
+
const hadProperty = Object.prototype.hasOwnProperty.call(current, key);
|
|
50
|
+
const result = delete current[key];
|
|
51
|
+
return {
|
|
52
|
+
result,
|
|
53
|
+
mutated: hadProperty && result
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function pathsIntersect(a, b) {
|
|
57
|
+
return isPrefix(a, b) || isPrefix(b, a);
|
|
58
|
+
}
|
|
59
|
+
function isPrefix(prefix, value) {
|
|
60
|
+
if (prefix.length > value.length) return false;
|
|
61
|
+
return prefix.every((segment, index) => value[index] === segment);
|
|
62
|
+
}
|
|
63
|
+
function isObject(value) {
|
|
64
|
+
return typeof value === "object" && value !== null;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/graph-async.ts
|
|
68
|
+
const ASYNC_SNAPSHOT_META_KEYS = new Set([
|
|
69
|
+
"status",
|
|
70
|
+
"version",
|
|
71
|
+
"key",
|
|
72
|
+
"error",
|
|
73
|
+
"value"
|
|
74
|
+
]);
|
|
75
|
+
function createRuntimeAsyncComputedNodes(asyncComputedInput) {
|
|
76
|
+
const asyncComputedNodes = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const asyncComputed of asyncComputedInput ?? []) {
|
|
78
|
+
const initialSnapshot = asyncComputed.initialSnapshot ?? {
|
|
79
|
+
status: "idle",
|
|
80
|
+
version: 0
|
|
81
|
+
};
|
|
82
|
+
asyncComputedNodes.set(asyncComputed.graphNodeId, {
|
|
83
|
+
...asyncComputed,
|
|
84
|
+
gate: initialSnapshot.status === "pending" ? 1 : 0,
|
|
85
|
+
snapshot: initialSnapshot,
|
|
86
|
+
version: initialSnapshot.version
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return asyncComputedNodes;
|
|
90
|
+
}
|
|
91
|
+
function readAsyncComputedNode(node, path, demand) {
|
|
92
|
+
demand();
|
|
93
|
+
const head = path[0];
|
|
94
|
+
if (head === void 0 || ASYNC_SNAPSHOT_META_KEYS.has(head)) return readPath(node.snapshot, path);
|
|
95
|
+
return readPath(node.snapshot.value, path);
|
|
96
|
+
}
|
|
97
|
+
function demandAsyncComputed(input) {
|
|
98
|
+
if (input.node.gate !== 1 && input.node.snapshot.status !== "idle") return;
|
|
99
|
+
advanceAsyncComputed(input);
|
|
100
|
+
}
|
|
101
|
+
function invalidateAsyncComputed(input) {
|
|
102
|
+
if (input.node.snapshot.status === "idle") return;
|
|
103
|
+
advanceAsyncComputed(input);
|
|
104
|
+
}
|
|
105
|
+
function advanceAsyncComputed(input) {
|
|
106
|
+
const visited = new Set([input.node.graphNodeId]);
|
|
107
|
+
let blocked;
|
|
108
|
+
const visit = (graphNodeId) => {
|
|
109
|
+
if (visited.has(graphNodeId)) return;
|
|
110
|
+
visited.add(graphNodeId);
|
|
111
|
+
const dependency = input.asyncComputedNodes.get(graphNodeId);
|
|
112
|
+
if (dependency) {
|
|
113
|
+
if (dependency.snapshot.status === "idle" || dependency.snapshot.status === "pending") {
|
|
114
|
+
blocked = dependency;
|
|
115
|
+
input.demandAsyncComputed(dependency);
|
|
116
|
+
} else if (!blocked && dependency.snapshot.status === "rejected") blocked = dependency;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const dependency of input.computedNodes.get(graphNodeId)?.dependencies ?? []) visit(dependency.graphNodeId);
|
|
120
|
+
};
|
|
121
|
+
for (const dependency of input.node.dependencies) visit(dependency.graphNodeId);
|
|
122
|
+
if (blocked) {
|
|
123
|
+
commitDependencyBlock(input, blocked.snapshot.status === "rejected" ? "rejected" : "pending", blocked.snapshot.error);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const nextKey = input.node.key(input.readGraph);
|
|
127
|
+
if (!input.node.gate && input.node.snapshot.status !== "idle" && Object.is(input.node.snapshot.key, nextKey)) return;
|
|
128
|
+
startAsyncComputed({
|
|
129
|
+
...input,
|
|
130
|
+
key: nextKey
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function commitDependencyBlock(input, status, error) {
|
|
134
|
+
if (input.node.gate === 2 && (status === "pending" || input.node.snapshot.status === "rejected" && Object.is(input.node.snapshot.error, error))) return;
|
|
135
|
+
input.node.controller?.abort();
|
|
136
|
+
input.node.controller = void 0;
|
|
137
|
+
input.node.gate = 2;
|
|
138
|
+
const version = ++input.node.version;
|
|
139
|
+
const key = input.node.snapshot.key;
|
|
140
|
+
publish(input, status === "pending" ? {
|
|
141
|
+
status,
|
|
142
|
+
version,
|
|
143
|
+
key,
|
|
144
|
+
value: input.node.snapshot.value
|
|
145
|
+
} : {
|
|
146
|
+
status,
|
|
147
|
+
version,
|
|
148
|
+
key,
|
|
149
|
+
error
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function startAsyncComputed(input) {
|
|
153
|
+
input.node.controller?.abort();
|
|
154
|
+
input.node.gate = 0;
|
|
155
|
+
const controller = new AbortController();
|
|
156
|
+
const version = ++input.node.version;
|
|
157
|
+
input.node.controller = controller;
|
|
158
|
+
publish(input, {
|
|
159
|
+
status: "pending",
|
|
160
|
+
version,
|
|
161
|
+
key: input.key,
|
|
162
|
+
value: input.node.snapshot.value
|
|
163
|
+
});
|
|
164
|
+
const commit = (snapshot) => {
|
|
165
|
+
if (input.node.version !== version) return;
|
|
166
|
+
publish(input, snapshot);
|
|
167
|
+
};
|
|
168
|
+
const commitRejected = (error) => commit({
|
|
169
|
+
status: "rejected",
|
|
170
|
+
version,
|
|
171
|
+
key: input.key,
|
|
172
|
+
error
|
|
173
|
+
});
|
|
174
|
+
try {
|
|
175
|
+
Promise.resolve(input.node.run({
|
|
176
|
+
key: input.key,
|
|
177
|
+
signal: controller.signal,
|
|
178
|
+
read: input.readGraph
|
|
179
|
+
})).then((value) => commit({
|
|
180
|
+
status: "fulfilled",
|
|
181
|
+
version,
|
|
182
|
+
key: input.key,
|
|
183
|
+
value
|
|
184
|
+
}), commitRejected);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
commitRejected(error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function publish(input, snapshot) {
|
|
190
|
+
const previous = input.node.snapshot;
|
|
191
|
+
input.node.snapshot = snapshot;
|
|
192
|
+
if (input.reconcile) input.reconcile.publishAsync(input.node, previous);
|
|
193
|
+
else input.markDirtyPath(input.node.graphNodeId, []);
|
|
194
|
+
input.scheduleFlush();
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
197
|
+
export { readAsyncComputedNode as a, findLastSharedReturnProperty as c, writePath as d, invalidateAsyncComputed as i, pathsIntersect as l, createRuntimeAsyncComputedNodes as n, deletePath as o, demandAsyncComputed as r, dirtyPathForGraphWrite as s, ASYNC_SNAPSHOT_META_KEYS as t, readPath as u };
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { ProtocolStatePayload } from "@markless/serializer";
|
|
2
|
+
|
|
3
|
+
//#region src/graph.d.ts
|
|
4
|
+
type RuntimeGraphCell = {
|
|
5
|
+
readonly graphNodeId: string;
|
|
6
|
+
readonly value: unknown;
|
|
7
|
+
readonly readInitializer?: () => unknown;
|
|
8
|
+
};
|
|
9
|
+
type RuntimeGraphRead = (graphNodeId: string, path?: ReadonlyArray<string>) => unknown;
|
|
10
|
+
type RuntimeGraphComputedDependency = {
|
|
11
|
+
readonly graphNodeId: string;
|
|
12
|
+
readonly path?: ReadonlyArray<string>;
|
|
13
|
+
};
|
|
14
|
+
type RuntimeGraphComputed = {
|
|
15
|
+
readonly graphNodeId: string;
|
|
16
|
+
readonly dependencies: ReadonlyArray<RuntimeGraphComputedDependency>;
|
|
17
|
+
readonly compute: (read: RuntimeGraphRead) => unknown; /** How re-derived values reconcile against the node's previous value. */
|
|
18
|
+
readonly reconcile?: RuntimeGraphReconcileOptions;
|
|
19
|
+
};
|
|
20
|
+
type RuntimeGraphComputedDependencyNode = Omit<RuntimeGraphComputed, 'compute'> & {
|
|
21
|
+
readonly compute?: never;
|
|
22
|
+
};
|
|
23
|
+
type RuntimeGraphAsyncSnapshot = {
|
|
24
|
+
readonly status: 'idle';
|
|
25
|
+
readonly version: 0;
|
|
26
|
+
} | {
|
|
27
|
+
readonly status: 'pending';
|
|
28
|
+
readonly version: number;
|
|
29
|
+
readonly key: unknown;
|
|
30
|
+
readonly value?: unknown;
|
|
31
|
+
} | {
|
|
32
|
+
readonly status: 'fulfilled';
|
|
33
|
+
readonly version: number;
|
|
34
|
+
readonly key: unknown;
|
|
35
|
+
readonly value: unknown;
|
|
36
|
+
} | {
|
|
37
|
+
readonly status: 'rejected';
|
|
38
|
+
readonly version: number;
|
|
39
|
+
readonly key: unknown;
|
|
40
|
+
readonly error: unknown;
|
|
41
|
+
};
|
|
42
|
+
type RuntimeGraphAsyncComputed = {
|
|
43
|
+
readonly graphNodeId: string;
|
|
44
|
+
readonly dependencies: ReadonlyArray<RuntimeGraphComputedDependency>;
|
|
45
|
+
readonly initialSnapshot?: RuntimeGraphAsyncSnapshot;
|
|
46
|
+
readonly key: (read: RuntimeGraphRead) => unknown; /** How a fulfilled value reconciles against the previously published one. */
|
|
47
|
+
readonly reconcile?: RuntimeGraphReconcileOptions;
|
|
48
|
+
readonly run: (input: {
|
|
49
|
+
readonly key: unknown;
|
|
50
|
+
readonly signal: AbortSignal;
|
|
51
|
+
readonly read: RuntimeGraphRead;
|
|
52
|
+
}) => unknown | Promise<unknown>;
|
|
53
|
+
};
|
|
54
|
+
type DomJournalEntry = {
|
|
55
|
+
readonly type: 'setText';
|
|
56
|
+
readonly locator: string;
|
|
57
|
+
readonly value: unknown;
|
|
58
|
+
} | {
|
|
59
|
+
readonly type: 'setAttr' | 'setProp';
|
|
60
|
+
readonly locator: string;
|
|
61
|
+
readonly name: string;
|
|
62
|
+
readonly value: unknown;
|
|
63
|
+
} | {
|
|
64
|
+
readonly type: 'insertRange';
|
|
65
|
+
readonly locator: string;
|
|
66
|
+
readonly fragment: unknown;
|
|
67
|
+
} | {
|
|
68
|
+
readonly type: 'removeRange';
|
|
69
|
+
readonly locator: string;
|
|
70
|
+
} | {
|
|
71
|
+
readonly type: 'moveRange';
|
|
72
|
+
readonly locator: string;
|
|
73
|
+
readonly before: string;
|
|
74
|
+
} | {
|
|
75
|
+
readonly type: 'runCleanup';
|
|
76
|
+
readonly locator: string;
|
|
77
|
+
};
|
|
78
|
+
type DomJournalResult = DomJournalEntry | ReadonlyArray<DomJournalEntry>;
|
|
79
|
+
type DomJournalListener = (entries: ReadonlyArray<DomJournalEntry>) => void | Promise<void>;
|
|
80
|
+
type RuntimeGraphInput = {
|
|
81
|
+
readonly cells: ReadonlyArray<RuntimeGraphCell>;
|
|
82
|
+
readonly computed?: ReadonlyArray<RuntimeGraphComputed | RuntimeGraphComputedDependencyNode>;
|
|
83
|
+
readonly asyncComputed?: ReadonlyArray<RuntimeGraphAsyncComputed>;
|
|
84
|
+
readonly sharedDefinitions?: ProtocolStatePayload['sharedDefinitions'];
|
|
85
|
+
/**
|
|
86
|
+
* Installs derived reconciliation, from
|
|
87
|
+
* `@markless/runtime/graph-reconcile`. Without it a computed invalidation
|
|
88
|
+
* dirties the whole node and an async commit dirties the whole snapshot.
|
|
89
|
+
*/
|
|
90
|
+
readonly reconcile?: DerivedReconcilePlaneFactory;
|
|
91
|
+
};
|
|
92
|
+
type RuntimeGraphWrite = {
|
|
93
|
+
readonly graphNodeId: string;
|
|
94
|
+
readonly path?: ReadonlyArray<string>;
|
|
95
|
+
readonly value: unknown;
|
|
96
|
+
};
|
|
97
|
+
type RuntimeGraphSharedWrite = {
|
|
98
|
+
readonly definitionId: string;
|
|
99
|
+
readonly propertyName: string;
|
|
100
|
+
readonly path?: ReadonlyArray<string>;
|
|
101
|
+
readonly value: unknown;
|
|
102
|
+
};
|
|
103
|
+
type RuntimeGraphSharedPatchOperation = readonly [operation: 'set', path: ReadonlyArray<string>, value: unknown];
|
|
104
|
+
type RuntimeGraphSharedPatch = {
|
|
105
|
+
readonly id: string;
|
|
106
|
+
readonly scope?: RuntimeSharedDefinition['scope'];
|
|
107
|
+
readonly version: number;
|
|
108
|
+
readonly patch: ReadonlyArray<RuntimeGraphSharedPatchOperation>;
|
|
109
|
+
};
|
|
110
|
+
type RuntimeGraphUpdate = {
|
|
111
|
+
readonly graphNodeId: string;
|
|
112
|
+
readonly path?: ReadonlyArray<string>;
|
|
113
|
+
readonly update: (value: unknown) => unknown;
|
|
114
|
+
readonly returnValue?: 'previous' | 'next';
|
|
115
|
+
};
|
|
116
|
+
type RuntimeGraphCall = {
|
|
117
|
+
readonly graphNodeId: string;
|
|
118
|
+
readonly path?: ReadonlyArray<string>;
|
|
119
|
+
readonly method: string;
|
|
120
|
+
readonly args?: ReadonlyArray<unknown>;
|
|
121
|
+
};
|
|
122
|
+
type RuntimeGraphDelete = {
|
|
123
|
+
readonly graphNodeId: string;
|
|
124
|
+
readonly path: ReadonlyArray<string>;
|
|
125
|
+
};
|
|
126
|
+
type RuntimeGraphSubscription = {
|
|
127
|
+
readonly id: string;
|
|
128
|
+
readonly graphNodeId: string;
|
|
129
|
+
readonly path?: ReadonlyArray<string>;
|
|
130
|
+
readonly run: (value: unknown) => DomJournalResult | void | Promise<DomJournalResult | void>;
|
|
131
|
+
};
|
|
132
|
+
type RuntimeGraph = {
|
|
133
|
+
readonly read: (graphNodeId: string, path?: ReadonlyArray<string>) => unknown; /** Available only in debug-enabled builds (`__MARKLESS_DEBUG_ENABLED__`). */
|
|
134
|
+
readonly peekAsyncSnapshot?: (graphNodeId: string) => RuntimeGraphAsyncSnapshot | undefined;
|
|
135
|
+
readonly readShared: (definitionId: string, propertyName: string, path?: ReadonlyArray<string>) => unknown;
|
|
136
|
+
readonly writeShared: (write: RuntimeGraphSharedWrite) => boolean;
|
|
137
|
+
readonly getSharedDefinition: (definitionId: string) => NonNullable<ProtocolStatePayload['sharedDefinitions']>[number] | undefined;
|
|
138
|
+
readonly listSharedDefinitions: () => NonNullable<ProtocolStatePayload['sharedDefinitions']>;
|
|
139
|
+
readonly takeSharedPatches: () => RuntimeGraphSharedPatch[];
|
|
140
|
+
readonly applySharedPatch: (patch: RuntimeGraphSharedPatch) => boolean;
|
|
141
|
+
readonly write: (write: RuntimeGraphWrite) => void;
|
|
142
|
+
readonly update: (update: RuntimeGraphUpdate) => unknown;
|
|
143
|
+
readonly call: (call: RuntimeGraphCall) => unknown;
|
|
144
|
+
readonly delete: (deletion: RuntimeGraphDelete) => boolean;
|
|
145
|
+
readonly subscribe: (subscription: RuntimeGraphSubscription) => () => void;
|
|
146
|
+
readonly subscribeJournal: (listener: DomJournalListener) => () => void;
|
|
147
|
+
readonly flush: () => Promise<void>;
|
|
148
|
+
readonly takeJournal: () => DomJournalEntry[];
|
|
149
|
+
};
|
|
150
|
+
type RuntimeSharedDefinition = NonNullable<ProtocolStatePayload['sharedDefinitions']>[number];
|
|
151
|
+
declare function createRuntimeGraph(input: RuntimeGraphInput): RuntimeGraph;
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/graph-scheduler.d.ts
|
|
154
|
+
type DirtyPath = {
|
|
155
|
+
readonly graphNodeId: string;
|
|
156
|
+
readonly path: ReadonlyArray<string>;
|
|
157
|
+
};
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/graph-computed.d.ts
|
|
160
|
+
type RuntimeComputedNode = Omit<RuntimeGraphComputed, 'compute'> & {
|
|
161
|
+
readonly compute?: RuntimeGraphComputed['compute'];
|
|
162
|
+
dirty: boolean;
|
|
163
|
+
value: unknown;
|
|
164
|
+
};
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/graph-async.d.ts
|
|
167
|
+
type RuntimeAsyncComputedNode = RuntimeGraphAsyncComputed & {
|
|
168
|
+
controller?: AbortController;
|
|
169
|
+
gate: number;
|
|
170
|
+
snapshot: RuntimeGraphAsyncSnapshot;
|
|
171
|
+
version: number;
|
|
172
|
+
};
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/graph-reconcile.d.ts
|
|
175
|
+
/**
|
|
176
|
+
* One array inside a derived value that reconciles by key instead of by
|
|
177
|
+
* element identity. `path` names the array (`[]` is the derived value itself)
|
|
178
|
+
* and `keyPath` is read on each element to get its identity.
|
|
179
|
+
*/
|
|
180
|
+
type RuntimeGraphReconcileKey = {
|
|
181
|
+
readonly path: ReadonlyArray<string>;
|
|
182
|
+
readonly keyPath: ReadonlyArray<string>;
|
|
183
|
+
};
|
|
184
|
+
/** Reconcile options carried by a computed or async computed graph node. */
|
|
185
|
+
type RuntimeGraphReconcileOptions = {
|
|
186
|
+
readonly keyed?: ReadonlyArray<RuntimeGraphReconcileKey>;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Objects a state write touched during the current flush, mapped to the paths
|
|
190
|
+
* written beneath them. State writes mutate objects in place, so a derived
|
|
191
|
+
* value that still holds the same object reference is not unchanged when that
|
|
192
|
+
* object was written through. See `diffDerivedValue`.
|
|
193
|
+
*/
|
|
194
|
+
type WriteTouchedRecord = ReadonlyMap<object, ReadonlyArray<ReadonlyArray<string>>>;
|
|
195
|
+
type DiffDerivedValueInput = {
|
|
196
|
+
readonly previous: unknown;
|
|
197
|
+
readonly next: unknown;
|
|
198
|
+
readonly keyed?: ReadonlyArray<RuntimeGraphReconcileKey>;
|
|
199
|
+
readonly touched?: WriteTouchedRecord;
|
|
200
|
+
readonly baselineStale?: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* What an `Object.is`-identical plain object or array below the root means.
|
|
203
|
+
* `'unchanged'` (the default) trusts identity, which is sound for a caller
|
|
204
|
+
* that also supplies the write-touched record of the same flush.
|
|
205
|
+
* `'unknown'` is for a caller with no such record — the async commit — and
|
|
206
|
+
* reports every identical container at its own path instead.
|
|
207
|
+
*/
|
|
208
|
+
readonly identicalContainers?: 'unchanged' | 'unknown';
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Compares a derived value with the previous value of the same graph node and
|
|
212
|
+
* reports the graph paths that changed. It never mutates either value: the
|
|
213
|
+
* caller replaces the node's value pointer and invalidates the reported paths.
|
|
214
|
+
*
|
|
215
|
+
* Rules (specs/framework/03-state-graph.md "Derived reconciliation"):
|
|
216
|
+
* - identical references are unchanged, unless a write touched that object in
|
|
217
|
+
* this flush, in which case the touched remainders are reported; the same
|
|
218
|
+
* holds when only the previous value held the written object, because an
|
|
219
|
+
* in-place write left that baseline already carrying the new field;
|
|
220
|
+
* - primitives, functions, `Map`, `Set`, `Date` and class instances are leaves;
|
|
221
|
+
* - plain objects reconcile field by field;
|
|
222
|
+
* - arrays reconcile by declared key, otherwise by element identity, and a
|
|
223
|
+
* length or structural mismatch reports the array's own path;
|
|
224
|
+
* - a declared key that is missing or duplicated on either side falls back to
|
|
225
|
+
* structural reconciliation for that array, which reports the array's own
|
|
226
|
+
* path: keys are never matched partially and an index is never identity;
|
|
227
|
+
* - a missing baseline (first value, or a value whose write-touched record was
|
|
228
|
+
* already cleared) reports the whole node.
|
|
229
|
+
*
|
|
230
|
+
* `identicalContainers: 'unknown'` is for a caller that has no write-touched
|
|
231
|
+
* record for the flush that produced the new value, so identity below the root
|
|
232
|
+
* proves nothing: every `Object.is`-identical plain object or array met below
|
|
233
|
+
* the root is reported at its own path and is not walked into. An identical
|
|
234
|
+
* root still reports nothing — nothing narrower is known about it, and the
|
|
235
|
+
* caller decides what a wholly identical value means — and identical
|
|
236
|
+
* primitives and other leaves stay unchanged. The default, `'unchanged'`,
|
|
237
|
+
* trusts identity as above.
|
|
238
|
+
*/
|
|
239
|
+
declare function diffDerivedValue(input: DiffDerivedValueInput): ReadonlyArray<ReadonlyArray<string>>;
|
|
240
|
+
/** Plain objects and arrays are the only values reconciliation walks into. */
|
|
241
|
+
declare function isDiffableContainer(value: unknown): value is object;
|
|
242
|
+
declare function samePath(a: ReadonlyArray<string>, b: ReadonlyArray<string>): boolean;
|
|
243
|
+
/**
|
|
244
|
+
* What the plane needs from the graph it is installed in. The graph passes its
|
|
245
|
+
* own internals; nothing here is part of the public runtime surface.
|
|
246
|
+
*/
|
|
247
|
+
type DerivedReconcileInternals = {
|
|
248
|
+
readonly cells: Map<string, unknown>;
|
|
249
|
+
readonly computedNodes: ReadonlyMap<string, RuntimeComputedNode>;
|
|
250
|
+
readonly subscriptions: ReadonlyArray<RuntimeGraphSubscription>;
|
|
251
|
+
readonly dirtyPaths: DirtyPath[];
|
|
252
|
+
readonly readGraph: RuntimeGraphRead;
|
|
253
|
+
readonly markDirtyPath: (graphNodeId: string, path: ReadonlyArray<string>) => void;
|
|
254
|
+
readonly scheduleFlush: () => void;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Derived reconciliation as a plug-in plane. The core graph carries the hook
|
|
258
|
+
* points and nothing else, so an app that never installs the plane does not
|
|
259
|
+
* load this module and its computed invalidations dirty the whole node the way
|
|
260
|
+
* they did before reconciliation existed.
|
|
261
|
+
*/
|
|
262
|
+
type DerivedReconcilePlane = {
|
|
263
|
+
/** Records the objects a state write mutated in place during this flush. */readonly recordWrite: (graphNodeId: string, path: ReadonlyArray<string>) => void; /** A dependency write invalidated this computed; its paths are not known yet. */
|
|
264
|
+
readonly invalidateComputed: (computed: RuntimeComputedNode) => void; /** Reports the paths a just-recomputed value moved, and wakes their subscribers. */
|
|
265
|
+
readonly reconcileComputed: (computed: RuntimeComputedNode, previous: unknown) => void; /** Commits a whole derived value onto a cell-backed computed node. */
|
|
266
|
+
readonly commitDerived: (write: RuntimeGraphWrite, path: ReadonlyArray<string>) => boolean; /** Dirties the paths an async snapshot commit changed. */
|
|
267
|
+
readonly publishAsync: (node: RuntimeAsyncComputedNode, previous: RuntimeGraphAsyncSnapshot) => void; /** Flush-pass start: recompute the dirty computeds something is subscribed to. */
|
|
268
|
+
readonly recomputeSubscribed: () => void; /** Flush end: drop the per-flush record and mark the baselines it leaves stale. */
|
|
269
|
+
readonly settleBaselines: () => void;
|
|
270
|
+
};
|
|
271
|
+
type DerivedReconcilePlaneFactory = (internals: DerivedReconcileInternals) => DerivedReconcilePlane;
|
|
272
|
+
declare const createDerivedReconcilePlane: DerivedReconcilePlaneFactory;
|
|
273
|
+
//#endregion
|
|
274
|
+
export { RuntimeGraphUpdate as A, RuntimeGraphDelete as C, RuntimeGraphSharedPatchOperation as D, RuntimeGraphSharedPatch as E, createRuntimeGraph as M, RuntimeGraphSharedWrite as O, RuntimeGraphComputedDependencyNode as S, RuntimeGraphRead as T, RuntimeGraphAsyncSnapshot as _, RuntimeGraphReconcileKey as a, RuntimeGraphComputed as b, createDerivedReconcilePlane as c, samePath as d, DomJournalEntry as f, RuntimeGraphAsyncComputed as g, RuntimeGraph as h, DiffDerivedValueInput as i, RuntimeGraphWrite as j, RuntimeGraphSubscription as k, diffDerivedValue as l, DomJournalResult as m, DerivedReconcilePlane as n, RuntimeGraphReconcileOptions as o, DomJournalListener as p, DerivedReconcilePlaneFactory as r, WriteTouchedRecord as s, DerivedReconcileInternals as t, isDiffableContainer as u, RuntimeGraphCall as v, RuntimeGraphInput as w, RuntimeGraphComputedDependency as x, RuntimeGraphCell as y };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as RuntimeGraphReconcileKey, c as createDerivedReconcilePlane, d as samePath, i as DiffDerivedValueInput, l as diffDerivedValue, n as DerivedReconcilePlane, o as RuntimeGraphReconcileOptions, r as DerivedReconcilePlaneFactory, s as WriteTouchedRecord, t as DerivedReconcileInternals, u as isDiffableContainer } from "./graph-reconcile-DoBBfYsT.js";
|
|
2
|
+
export { DerivedReconcileInternals, DerivedReconcilePlane, DerivedReconcilePlaneFactory, DiffDerivedValueInput, RuntimeGraphReconcileKey, RuntimeGraphReconcileOptions, WriteTouchedRecord, createDerivedReconcilePlane, diffDerivedValue, isDiffableContainer, samePath };
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { t as ASYNC_SNAPSHOT_META_KEYS, u as readPath } from "./graph-async-Db6SqB5x.js";
|
|
2
|
+
//#region src/graph-reconcile.ts
|
|
3
|
+
const WHOLE_NODE = Object.freeze([Object.freeze([])]);
|
|
4
|
+
/**
|
|
5
|
+
* Compares a derived value with the previous value of the same graph node and
|
|
6
|
+
* reports the graph paths that changed. It never mutates either value: the
|
|
7
|
+
* caller replaces the node's value pointer and invalidates the reported paths.
|
|
8
|
+
*
|
|
9
|
+
* Rules (specs/framework/03-state-graph.md "Derived reconciliation"):
|
|
10
|
+
* - identical references are unchanged, unless a write touched that object in
|
|
11
|
+
* this flush, in which case the touched remainders are reported; the same
|
|
12
|
+
* holds when only the previous value held the written object, because an
|
|
13
|
+
* in-place write left that baseline already carrying the new field;
|
|
14
|
+
* - primitives, functions, `Map`, `Set`, `Date` and class instances are leaves;
|
|
15
|
+
* - plain objects reconcile field by field;
|
|
16
|
+
* - arrays reconcile by declared key, otherwise by element identity, and a
|
|
17
|
+
* length or structural mismatch reports the array's own path;
|
|
18
|
+
* - a declared key that is missing or duplicated on either side falls back to
|
|
19
|
+
* structural reconciliation for that array, which reports the array's own
|
|
20
|
+
* path: keys are never matched partially and an index is never identity;
|
|
21
|
+
* - a missing baseline (first value, or a value whose write-touched record was
|
|
22
|
+
* already cleared) reports the whole node.
|
|
23
|
+
*
|
|
24
|
+
* `identicalContainers: 'unknown'` is for a caller that has no write-touched
|
|
25
|
+
* record for the flush that produced the new value, so identity below the root
|
|
26
|
+
* proves nothing: every `Object.is`-identical plain object or array met below
|
|
27
|
+
* the root is reported at its own path and is not walked into. An identical
|
|
28
|
+
* root still reports nothing — nothing narrower is known about it, and the
|
|
29
|
+
* caller decides what a wholly identical value means — and identical
|
|
30
|
+
* primitives and other leaves stay unchanged. The default, `'unchanged'`,
|
|
31
|
+
* trusts identity as above.
|
|
32
|
+
*/
|
|
33
|
+
function diffDerivedValue(input) {
|
|
34
|
+
if (input.baselineStale) return WHOLE_NODE;
|
|
35
|
+
if (input.previous === void 0 && input.next !== void 0) return WHOLE_NODE;
|
|
36
|
+
const changed = [];
|
|
37
|
+
const active = /* @__PURE__ */ new Map();
|
|
38
|
+
function emitTouched(value, path) {
|
|
39
|
+
if (!input.touched || typeof value !== "object" || value === null) return;
|
|
40
|
+
const remainders = input.touched.get(value);
|
|
41
|
+
if (!remainders) return;
|
|
42
|
+
for (const remaining of remainders) {
|
|
43
|
+
const touchedPath = [...path, ...remaining];
|
|
44
|
+
if (changed.some((entry) => samePath(entry, touchedPath))) continue;
|
|
45
|
+
changed.push(touchedPath);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* What an identical reference met at `path` reports. With a write-touched
|
|
50
|
+
* record identity means unchanged apart from the recorded remainders; in
|
|
51
|
+
* `'unknown'` mode there is no such record, so an identical container below
|
|
52
|
+
* the root is reported whole rather than trusted or walked into.
|
|
53
|
+
*/
|
|
54
|
+
function reportIdentical(value, path) {
|
|
55
|
+
if (input.identicalContainers === "unknown" && path.length > 0 && isDiffableContainer(value)) {
|
|
56
|
+
changed.push(path);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
emitTouched(value, path);
|
|
60
|
+
}
|
|
61
|
+
function keyedFor(path) {
|
|
62
|
+
return input.keyed?.find((entry) => samePath(entry.path, path));
|
|
63
|
+
}
|
|
64
|
+
function walkArray(previous, next, path) {
|
|
65
|
+
const mark = changed.length;
|
|
66
|
+
const fallBackToStructural = () => {
|
|
67
|
+
changed.length = mark;
|
|
68
|
+
changed.push(path);
|
|
69
|
+
};
|
|
70
|
+
if (previous.length !== next.length) {
|
|
71
|
+
fallBackToStructural();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const keyed = keyedFor(path);
|
|
75
|
+
if (keyed) {
|
|
76
|
+
const fault = keyFault(previous, keyed) ?? keyFault(next, keyed);
|
|
77
|
+
if (fault) {
|
|
78
|
+
reportKeyFault(path, keyed, fault);
|
|
79
|
+
fallBackToStructural();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (let index = 0; index < next.length; index++) {
|
|
84
|
+
const previousItem = previous[index];
|
|
85
|
+
const nextItem = next[index];
|
|
86
|
+
const itemPath = [...path, String(index)];
|
|
87
|
+
if (Object.is(previousItem, nextItem)) {
|
|
88
|
+
reportIdentical(nextItem, itemPath);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (keyed && isDiffableContainer(previousItem) && isDiffableContainer(nextItem)) {
|
|
92
|
+
const previousKey = readPath(previousItem, keyed.keyPath);
|
|
93
|
+
const nextKey = readPath(nextItem, keyed.keyPath);
|
|
94
|
+
if (!Object.is(previousKey, nextKey)) {
|
|
95
|
+
fallBackToStructural();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
walk(previousItem, nextItem, itemPath);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
changed.push(itemPath);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function walkObject(previous, next, path) {
|
|
105
|
+
for (const key of new Set([...Object.keys(previous), ...Object.keys(next)])) {
|
|
106
|
+
const keyPath = [...path, key];
|
|
107
|
+
const inPrevious = Object.prototype.hasOwnProperty.call(previous, key);
|
|
108
|
+
const inNext = Object.prototype.hasOwnProperty.call(next, key);
|
|
109
|
+
if (!inPrevious || !inNext) {
|
|
110
|
+
changed.push(keyPath);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
walk(previous[key], next[key], keyPath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function walk(previous, next, path) {
|
|
117
|
+
if (Object.is(previous, next)) {
|
|
118
|
+
reportIdentical(next, path);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (!isDiffableContainer(previous) || !isDiffableContainer(next)) {
|
|
122
|
+
changed.push(path);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const previousIsArray = Array.isArray(previous);
|
|
126
|
+
if (previousIsArray !== Array.isArray(next)) {
|
|
127
|
+
changed.push(path);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let pairedWithPrevious = active.get(previous);
|
|
131
|
+
if (pairedWithPrevious?.has(next)) return;
|
|
132
|
+
if (!pairedWithPrevious) {
|
|
133
|
+
pairedWithPrevious = /* @__PURE__ */ new Set();
|
|
134
|
+
active.set(previous, pairedWithPrevious);
|
|
135
|
+
}
|
|
136
|
+
pairedWithPrevious.add(next);
|
|
137
|
+
try {
|
|
138
|
+
if (previousIsArray) walkArray(previous, next, path);
|
|
139
|
+
else walkObject(previous, next, path);
|
|
140
|
+
} finally {
|
|
141
|
+
pairedWithPrevious.delete(next);
|
|
142
|
+
if (pairedWithPrevious.size === 0) active.delete(previous);
|
|
143
|
+
}
|
|
144
|
+
emitTouched(previous, path);
|
|
145
|
+
}
|
|
146
|
+
walk(input.previous, input.next, []);
|
|
147
|
+
return changed;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Reads the declared key of every element and reports the first element that
|
|
151
|
+
* has no key or repeats one already used in the same array. Reads only; it
|
|
152
|
+
* never touches the elements it inspects.
|
|
153
|
+
*/
|
|
154
|
+
function keyFault(items, keyed) {
|
|
155
|
+
const seen = /* @__PURE__ */ new Set();
|
|
156
|
+
for (let index = 0; index < items.length; index++) {
|
|
157
|
+
const key = readPath(items[index], keyed.keyPath);
|
|
158
|
+
if (key === void 0) return {
|
|
159
|
+
reason: "missing",
|
|
160
|
+
index,
|
|
161
|
+
key
|
|
162
|
+
};
|
|
163
|
+
if (seen.has(key)) return {
|
|
164
|
+
reason: "duplicate",
|
|
165
|
+
index,
|
|
166
|
+
key
|
|
167
|
+
};
|
|
168
|
+
seen.add(key);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Names the array and the offending key in debug-enabled builds. Keyed
|
|
173
|
+
* reconciliation degrading to structural reconciliation is silent in
|
|
174
|
+
* production — the result is still correct, only coarser.
|
|
175
|
+
*/
|
|
176
|
+
function reportKeyFault(path, keyed, fault) {
|
|
177
|
+
if (typeof __MARKLESS_DEBUG_ENABLED__ === "undefined" || !__MARKLESS_DEBUG_ENABLED__) return;
|
|
178
|
+
const array = path.length === 0 ? "the derived value" : `derived path ${path.join(".")}`;
|
|
179
|
+
const keyPath = keyed.keyPath.length === 0 ? "the element itself" : keyed.keyPath.join(".");
|
|
180
|
+
const cause = fault.reason === "missing" ? `element ${fault.index} has no key at ${keyPath}` : `key ${describeKey(fault.key)} at ${keyPath} is used by more than one element (element ${fault.index})`;
|
|
181
|
+
console.warn(`markless: reconciling ${array} by key is not possible because ${cause}; falling back to structural reconciliation for that array.`);
|
|
182
|
+
}
|
|
183
|
+
function describeKey(key) {
|
|
184
|
+
return typeof key === "string" ? JSON.stringify(key) : String(key);
|
|
185
|
+
}
|
|
186
|
+
/** Plain objects and arrays are the only values reconciliation walks into. */
|
|
187
|
+
function isDiffableContainer(value) {
|
|
188
|
+
if (typeof value !== "object" || value === null) return false;
|
|
189
|
+
if (Array.isArray(value)) return true;
|
|
190
|
+
const prototype = Object.getPrototypeOf(value);
|
|
191
|
+
return prototype === Object.prototype || prototype === null;
|
|
192
|
+
}
|
|
193
|
+
function samePath(a, b) {
|
|
194
|
+
return a.length === b.length && a.every((segment, index) => b[index] === segment);
|
|
195
|
+
}
|
|
196
|
+
const createDerivedReconcilePlane = (internals) => {
|
|
197
|
+
const { cells, computedNodes, subscriptions, dirtyPaths } = internals;
|
|
198
|
+
const { readGraph, markDirtyPath, scheduleFlush } = internals;
|
|
199
|
+
const writeTouched = /* @__PURE__ */ new Map();
|
|
200
|
+
const dirtyComputed = /* @__PURE__ */ new Set();
|
|
201
|
+
const invalidated = /* @__PURE__ */ new Set();
|
|
202
|
+
const baselineStale = /* @__PURE__ */ new Set();
|
|
203
|
+
const changedPaths = (computed, previous, next) => diffDerivedValue({
|
|
204
|
+
previous,
|
|
205
|
+
next,
|
|
206
|
+
keyed: computed.reconcile?.keyed,
|
|
207
|
+
touched: writeTouched,
|
|
208
|
+
baselineStale: baselineStale.has(computed)
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
recordWrite(graphNodeId, path) {
|
|
212
|
+
if (computedNodes.size === 0 || computedNodes.has(graphNodeId)) return;
|
|
213
|
+
let container = cells.get(graphNodeId);
|
|
214
|
+
for (let depth = 0; depth <= path.length; depth++) {
|
|
215
|
+
if (typeof container === "object" && container !== null) {
|
|
216
|
+
const remainders = writeTouched.get(container) ?? [];
|
|
217
|
+
const remaining = path.slice(depth);
|
|
218
|
+
if (!remainders.some((entry) => samePath(entry, remaining))) {
|
|
219
|
+
remainders.push(remaining);
|
|
220
|
+
writeTouched.set(container, remainders);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (depth === path.length) break;
|
|
224
|
+
container = readPath(container, [path[depth]]);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
invalidateComputed(computed) {
|
|
228
|
+
invalidated.add(computed);
|
|
229
|
+
dirtyComputed.add(computed);
|
|
230
|
+
},
|
|
231
|
+
reconcileComputed(computed, previous) {
|
|
232
|
+
const changed = invalidated.has(computed) ? changedPaths(computed, previous, computed.value) : [];
|
|
233
|
+
invalidated.delete(computed);
|
|
234
|
+
baselineStale.delete(computed);
|
|
235
|
+
if (changed.length === 0) return;
|
|
236
|
+
for (const path of changed) dirtyPaths.push({
|
|
237
|
+
graphNodeId: computed.graphNodeId,
|
|
238
|
+
path
|
|
239
|
+
});
|
|
240
|
+
scheduleFlush();
|
|
241
|
+
},
|
|
242
|
+
commitDerived(write, path) {
|
|
243
|
+
const graphNodeId = write.graphNodeId;
|
|
244
|
+
const computed = computedNodes.get(graphNodeId);
|
|
245
|
+
if (!computed || computed.compute || path.length > 0) return false;
|
|
246
|
+
const changed = changedPaths(computed, cells.get(graphNodeId), write.value);
|
|
247
|
+
cells.set(graphNodeId, write.value);
|
|
248
|
+
baselineStale.delete(computed);
|
|
249
|
+
if (changed.length === 0) return true;
|
|
250
|
+
for (const changedPath of changed) markDirtyPath(graphNodeId, changedPath);
|
|
251
|
+
scheduleFlush();
|
|
252
|
+
return true;
|
|
253
|
+
},
|
|
254
|
+
publishAsync(node, previous) {
|
|
255
|
+
for (const path of asyncCommitPaths(node, previous, node.snapshot)) markDirtyPath(node.graphNodeId, path);
|
|
256
|
+
},
|
|
257
|
+
recomputeSubscribed() {
|
|
258
|
+
for (const computed of Array.from(dirtyComputed)) {
|
|
259
|
+
if (!computed.dirty) {
|
|
260
|
+
dirtyComputed.delete(computed);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (!subscriptions.some((subscription) => subscription.graphNodeId === computed.graphNodeId)) continue;
|
|
264
|
+
dirtyComputed.delete(computed);
|
|
265
|
+
readGraph(computed.graphNodeId);
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
settleBaselines() {
|
|
269
|
+
for (const computed of dirtyComputed) if (computed.dirty) baselineStale.add(computed);
|
|
270
|
+
dirtyComputed.clear();
|
|
271
|
+
writeTouched.clear();
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* The graph paths a snapshot commit changed. Snapshot metadata is addressed as
|
|
277
|
+
* `status`/`version`/`key`/`error`; the fulfilled value is addressed both as
|
|
278
|
+
* `value.<path>` and, for compiled reads, as the bare `<path>` (see
|
|
279
|
+
* `readAsyncComputedNode`), so a changed value path is reported in both
|
|
280
|
+
* coordinate systems.
|
|
281
|
+
*
|
|
282
|
+
* A value commit compares structurally only. Unlike a sync computed, an async
|
|
283
|
+
* snapshot can land long after the flush that wrote the state it embeds, so the
|
|
284
|
+
* per-flush write-touched record is already cleared by the time it publishes.
|
|
285
|
+
* Identity below the root therefore proves nothing here: a runner that returns
|
|
286
|
+
* live state objects publishes the very rows a later write mutated in place,
|
|
287
|
+
* and treating them as unchanged would silently withhold that write from the
|
|
288
|
+
* subscribers of those paths. The diff runs in `identicalContainers: 'unknown'`
|
|
289
|
+
* mode, which reports each identical nested container at its path; a value
|
|
290
|
+
* rebuilt from fresh objects still reconciles field by field, and an identical
|
|
291
|
+
* root is handled by the whole-node rule below.
|
|
292
|
+
*/
|
|
293
|
+
function asyncCommitPaths(node, previous, next) {
|
|
294
|
+
const paths = [];
|
|
295
|
+
if (previous.status !== next.status) paths.push(["status"]);
|
|
296
|
+
if (previous.version !== next.version) paths.push(["version"]);
|
|
297
|
+
if (!Object.is(snapshotField(previous, "key"), snapshotField(next, "key"))) paths.push(["key"]);
|
|
298
|
+
if (!Object.is(snapshotField(previous, "error"), snapshotField(next, "error"))) paths.push(["error"]);
|
|
299
|
+
if (next.status === "pending") return paths;
|
|
300
|
+
const previousValue = snapshotField(previous, "value");
|
|
301
|
+
const nextValue = snapshotField(next, "value");
|
|
302
|
+
if (!Object.is(previousValue, nextValue) && isDiffableContainer(previousValue) && isDiffableContainer(nextValue)) {
|
|
303
|
+
const changed = diffDerivedValue({
|
|
304
|
+
previous: previousValue,
|
|
305
|
+
next: nextValue,
|
|
306
|
+
keyed: node.reconcile?.keyed,
|
|
307
|
+
identicalContainers: "unknown"
|
|
308
|
+
});
|
|
309
|
+
if (changed.every((path) => path.length > 0)) {
|
|
310
|
+
for (const path of changed) {
|
|
311
|
+
paths.push(["value", ...path]);
|
|
312
|
+
if (!ASYNC_SNAPSHOT_META_KEYS.has(path[0])) paths.push([...path]);
|
|
313
|
+
}
|
|
314
|
+
return paths;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
paths.push([]);
|
|
318
|
+
return paths;
|
|
319
|
+
}
|
|
320
|
+
function snapshotField(snapshot, field) {
|
|
321
|
+
return snapshot[field];
|
|
322
|
+
}
|
|
323
|
+
//#endregion
|
|
324
|
+
export { createDerivedReconcilePlane, diffDerivedValue, isDiffableContainer, samePath };
|
package/dist/graph.d.ts
CHANGED
|
@@ -1,145 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
//#region src/graph.d.ts
|
|
4
|
-
type RuntimeGraphCell = {
|
|
5
|
-
readonly graphNodeId: string;
|
|
6
|
-
readonly value: unknown;
|
|
7
|
-
readonly readInitializer?: () => unknown;
|
|
8
|
-
};
|
|
9
|
-
type RuntimeGraphRead = (graphNodeId: string, path?: ReadonlyArray<string>) => unknown;
|
|
10
|
-
type RuntimeGraphComputedDependency = {
|
|
11
|
-
readonly graphNodeId: string;
|
|
12
|
-
readonly path?: ReadonlyArray<string>;
|
|
13
|
-
};
|
|
14
|
-
type RuntimeGraphComputed = {
|
|
15
|
-
readonly graphNodeId: string;
|
|
16
|
-
readonly dependencies: ReadonlyArray<RuntimeGraphComputedDependency>;
|
|
17
|
-
readonly compute: (read: RuntimeGraphRead) => unknown;
|
|
18
|
-
};
|
|
19
|
-
type RuntimeGraphComputedDependencyNode = Omit<RuntimeGraphComputed, 'compute'> & {
|
|
20
|
-
readonly compute?: never;
|
|
21
|
-
};
|
|
22
|
-
type RuntimeGraphAsyncSnapshot = {
|
|
23
|
-
readonly status: 'idle';
|
|
24
|
-
readonly version: 0;
|
|
25
|
-
} | {
|
|
26
|
-
readonly status: 'pending';
|
|
27
|
-
readonly version: number;
|
|
28
|
-
readonly key: unknown;
|
|
29
|
-
readonly value?: unknown;
|
|
30
|
-
} | {
|
|
31
|
-
readonly status: 'fulfilled';
|
|
32
|
-
readonly version: number;
|
|
33
|
-
readonly key: unknown;
|
|
34
|
-
readonly value: unknown;
|
|
35
|
-
} | {
|
|
36
|
-
readonly status: 'rejected';
|
|
37
|
-
readonly version: number;
|
|
38
|
-
readonly key: unknown;
|
|
39
|
-
readonly error: unknown;
|
|
40
|
-
};
|
|
41
|
-
type RuntimeGraphAsyncComputed = {
|
|
42
|
-
readonly graphNodeId: string;
|
|
43
|
-
readonly dependencies: ReadonlyArray<RuntimeGraphComputedDependency>;
|
|
44
|
-
readonly initialSnapshot?: RuntimeGraphAsyncSnapshot;
|
|
45
|
-
readonly key: (read: RuntimeGraphRead) => unknown;
|
|
46
|
-
readonly run: (input: {
|
|
47
|
-
readonly key: unknown;
|
|
48
|
-
readonly signal: AbortSignal;
|
|
49
|
-
readonly read: RuntimeGraphRead;
|
|
50
|
-
}) => unknown | Promise<unknown>;
|
|
51
|
-
};
|
|
52
|
-
type DomJournalEntry = {
|
|
53
|
-
readonly type: 'setText';
|
|
54
|
-
readonly locator: string;
|
|
55
|
-
readonly value: unknown;
|
|
56
|
-
} | {
|
|
57
|
-
readonly type: 'setAttr' | 'setProp';
|
|
58
|
-
readonly locator: string;
|
|
59
|
-
readonly name: string;
|
|
60
|
-
readonly value: unknown;
|
|
61
|
-
} | {
|
|
62
|
-
readonly type: 'insertRange';
|
|
63
|
-
readonly locator: string;
|
|
64
|
-
readonly fragment: unknown;
|
|
65
|
-
} | {
|
|
66
|
-
readonly type: 'removeRange';
|
|
67
|
-
readonly locator: string;
|
|
68
|
-
} | {
|
|
69
|
-
readonly type: 'moveRange';
|
|
70
|
-
readonly locator: string;
|
|
71
|
-
readonly before: string;
|
|
72
|
-
} | {
|
|
73
|
-
readonly type: 'runCleanup';
|
|
74
|
-
readonly locator: string;
|
|
75
|
-
};
|
|
76
|
-
type DomJournalResult = DomJournalEntry | ReadonlyArray<DomJournalEntry>;
|
|
77
|
-
type DomJournalListener = (entries: ReadonlyArray<DomJournalEntry>) => void | Promise<void>;
|
|
78
|
-
type RuntimeGraphInput = {
|
|
79
|
-
readonly cells: ReadonlyArray<RuntimeGraphCell>;
|
|
80
|
-
readonly computed?: ReadonlyArray<RuntimeGraphComputed | RuntimeGraphComputedDependencyNode>;
|
|
81
|
-
readonly asyncComputed?: ReadonlyArray<RuntimeGraphAsyncComputed>;
|
|
82
|
-
readonly sharedDefinitions?: ProtocolStatePayload['sharedDefinitions'];
|
|
83
|
-
};
|
|
84
|
-
type RuntimeGraphWrite = {
|
|
85
|
-
readonly graphNodeId: string;
|
|
86
|
-
readonly path?: ReadonlyArray<string>;
|
|
87
|
-
readonly value: unknown;
|
|
88
|
-
};
|
|
89
|
-
type RuntimeGraphSharedWrite = {
|
|
90
|
-
readonly definitionId: string;
|
|
91
|
-
readonly propertyName: string;
|
|
92
|
-
readonly path?: ReadonlyArray<string>;
|
|
93
|
-
readonly value: unknown;
|
|
94
|
-
};
|
|
95
|
-
type RuntimeGraphSharedPatchOperation = readonly [operation: 'set', path: ReadonlyArray<string>, value: unknown];
|
|
96
|
-
type RuntimeGraphSharedPatch = {
|
|
97
|
-
readonly id: string;
|
|
98
|
-
readonly scope?: RuntimeSharedDefinition['scope'];
|
|
99
|
-
readonly version: number;
|
|
100
|
-
readonly patch: ReadonlyArray<RuntimeGraphSharedPatchOperation>;
|
|
101
|
-
};
|
|
102
|
-
type RuntimeGraphUpdate = {
|
|
103
|
-
readonly graphNodeId: string;
|
|
104
|
-
readonly path?: ReadonlyArray<string>;
|
|
105
|
-
readonly update: (value: unknown) => unknown;
|
|
106
|
-
readonly returnValue?: 'previous' | 'next';
|
|
107
|
-
};
|
|
108
|
-
type RuntimeGraphCall = {
|
|
109
|
-
readonly graphNodeId: string;
|
|
110
|
-
readonly path?: ReadonlyArray<string>;
|
|
111
|
-
readonly method: string;
|
|
112
|
-
readonly args?: ReadonlyArray<unknown>;
|
|
113
|
-
};
|
|
114
|
-
type RuntimeGraphDelete = {
|
|
115
|
-
readonly graphNodeId: string;
|
|
116
|
-
readonly path: ReadonlyArray<string>;
|
|
117
|
-
};
|
|
118
|
-
type RuntimeGraphSubscription = {
|
|
119
|
-
readonly id: string;
|
|
120
|
-
readonly graphNodeId: string;
|
|
121
|
-
readonly path?: ReadonlyArray<string>;
|
|
122
|
-
readonly run: (value: unknown) => DomJournalResult | void | Promise<DomJournalResult | void>;
|
|
123
|
-
};
|
|
124
|
-
type RuntimeGraph = {
|
|
125
|
-
readonly read: (graphNodeId: string, path?: ReadonlyArray<string>) => unknown; /** Available only in debug-enabled builds (`__MARKLESS_DEBUG_ENABLED__`). */
|
|
126
|
-
readonly peekAsyncSnapshot?: (graphNodeId: string) => RuntimeGraphAsyncSnapshot | undefined;
|
|
127
|
-
readonly readShared: (definitionId: string, propertyName: string, path?: ReadonlyArray<string>) => unknown;
|
|
128
|
-
readonly writeShared: (write: RuntimeGraphSharedWrite) => boolean;
|
|
129
|
-
readonly getSharedDefinition: (definitionId: string) => NonNullable<ProtocolStatePayload['sharedDefinitions']>[number] | undefined;
|
|
130
|
-
readonly listSharedDefinitions: () => NonNullable<ProtocolStatePayload['sharedDefinitions']>;
|
|
131
|
-
readonly takeSharedPatches: () => RuntimeGraphSharedPatch[];
|
|
132
|
-
readonly applySharedPatch: (patch: RuntimeGraphSharedPatch) => boolean;
|
|
133
|
-
readonly write: (write: RuntimeGraphWrite) => void;
|
|
134
|
-
readonly update: (update: RuntimeGraphUpdate) => unknown;
|
|
135
|
-
readonly call: (call: RuntimeGraphCall) => unknown;
|
|
136
|
-
readonly delete: (deletion: RuntimeGraphDelete) => boolean;
|
|
137
|
-
readonly subscribe: (subscription: RuntimeGraphSubscription) => () => void;
|
|
138
|
-
readonly subscribeJournal: (listener: DomJournalListener) => () => void;
|
|
139
|
-
readonly flush: () => Promise<void>;
|
|
140
|
-
readonly takeJournal: () => DomJournalEntry[];
|
|
141
|
-
};
|
|
142
|
-
type RuntimeSharedDefinition = NonNullable<ProtocolStatePayload['sharedDefinitions']>[number];
|
|
143
|
-
declare function createRuntimeGraph(input: RuntimeGraphInput): RuntimeGraph;
|
|
144
|
-
//#endregion
|
|
145
|
-
export { DomJournalEntry, DomJournalListener, DomJournalResult, RuntimeGraph, RuntimeGraphAsyncComputed, RuntimeGraphAsyncSnapshot, RuntimeGraphCall, RuntimeGraphCell, RuntimeGraphComputed, RuntimeGraphComputedDependency, RuntimeGraphComputedDependencyNode, RuntimeGraphDelete, RuntimeGraphInput, RuntimeGraphRead, RuntimeGraphSharedPatch, RuntimeGraphSharedPatchOperation, RuntimeGraphSharedWrite, RuntimeGraphSubscription, RuntimeGraphUpdate, RuntimeGraphWrite, createRuntimeGraph };
|
|
1
|
+
import { A as RuntimeGraphUpdate, C as RuntimeGraphDelete, D as RuntimeGraphSharedPatchOperation, E as RuntimeGraphSharedPatch, M as createRuntimeGraph, O as RuntimeGraphSharedWrite, S as RuntimeGraphComputedDependencyNode, T as RuntimeGraphRead, _ as RuntimeGraphAsyncSnapshot, a as RuntimeGraphReconcileKey, b as RuntimeGraphComputed, f as DomJournalEntry, g as RuntimeGraphAsyncComputed, h as RuntimeGraph, i as DiffDerivedValueInput, j as RuntimeGraphWrite, k as RuntimeGraphSubscription, m as DomJournalResult, n as DerivedReconcilePlane, o as RuntimeGraphReconcileOptions, p as DomJournalListener, r as DerivedReconcilePlaneFactory, s as WriteTouchedRecord, t as DerivedReconcileInternals, v as RuntimeGraphCall, w as RuntimeGraphInput, x as RuntimeGraphComputedDependency, y as RuntimeGraphCell } from "./graph-reconcile-DoBBfYsT.js";
|
|
2
|
+
export { DerivedReconcileInternals, DerivedReconcilePlane, DerivedReconcilePlaneFactory, DiffDerivedValueInput, DomJournalEntry, DomJournalListener, DomJournalResult, RuntimeGraph, RuntimeGraphAsyncComputed, RuntimeGraphAsyncSnapshot, RuntimeGraphCall, RuntimeGraphCell, RuntimeGraphComputed, RuntimeGraphComputedDependency, RuntimeGraphComputedDependencyNode, RuntimeGraphDelete, RuntimeGraphInput, RuntimeGraphRead, RuntimeGraphReconcileKey, RuntimeGraphReconcileOptions, RuntimeGraphSharedPatch, RuntimeGraphSharedPatchOperation, RuntimeGraphSharedWrite, RuntimeGraphSubscription, RuntimeGraphUpdate, RuntimeGraphWrite, WriteTouchedRecord, createRuntimeGraph };
|
package/dist/graph.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createRuntimeGraph } from "./graph-
|
|
1
|
+
import { t as createRuntimeGraph } from "./graph-B77v_FUC.js";
|
|
2
2
|
export { createRuntimeGraph };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { DomJournalEntry, DomJournalListener, DomJournalResult, RuntimeGraph, RuntimeGraphAsyncComputed, RuntimeGraphAsyncSnapshot, RuntimeGraphCall, RuntimeGraphCell, RuntimeGraphComputed, RuntimeGraphComputedDependency, RuntimeGraphComputedDependencyNode, RuntimeGraphDelete, RuntimeGraphInput, RuntimeGraphRead, RuntimeGraphSharedPatch, RuntimeGraphSharedPatchOperation, RuntimeGraphSharedWrite, RuntimeGraphSubscription, RuntimeGraphUpdate, RuntimeGraphWrite, createRuntimeGraph };
|
|
1
|
+
import { A as RuntimeGraphUpdate, C as RuntimeGraphDelete, D as RuntimeGraphSharedPatchOperation, E as RuntimeGraphSharedPatch, M as createRuntimeGraph, O as RuntimeGraphSharedWrite, S as RuntimeGraphComputedDependencyNode, T as RuntimeGraphRead, _ as RuntimeGraphAsyncSnapshot, a as RuntimeGraphReconcileKey, b as RuntimeGraphComputed, f as DomJournalEntry, g as RuntimeGraphAsyncComputed, h as RuntimeGraph, i as DiffDerivedValueInput, j as RuntimeGraphWrite, k as RuntimeGraphSubscription, m as DomJournalResult, n as DerivedReconcilePlane, o as RuntimeGraphReconcileOptions, p as DomJournalListener, r as DerivedReconcilePlaneFactory, s as WriteTouchedRecord, t as DerivedReconcileInternals, v as RuntimeGraphCall, w as RuntimeGraphInput, x as RuntimeGraphComputedDependency, y as RuntimeGraphCell } from "./graph-reconcile-DoBBfYsT.js";
|
|
2
|
+
export { DerivedReconcileInternals, DerivedReconcilePlane, DerivedReconcilePlaneFactory, DiffDerivedValueInput, DomJournalEntry, DomJournalListener, DomJournalResult, RuntimeGraph, RuntimeGraphAsyncComputed, RuntimeGraphAsyncSnapshot, RuntimeGraphCall, RuntimeGraphCell, RuntimeGraphComputed, RuntimeGraphComputedDependency, RuntimeGraphComputedDependencyNode, RuntimeGraphDelete, RuntimeGraphInput, RuntimeGraphRead, RuntimeGraphReconcileKey, RuntimeGraphReconcileOptions, RuntimeGraphSharedPatch, RuntimeGraphSharedPatchOperation, RuntimeGraphSharedWrite, RuntimeGraphSubscription, RuntimeGraphUpdate, RuntimeGraphWrite, WriteTouchedRecord, createRuntimeGraph };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createRuntimeGraph } from "./graph-
|
|
1
|
+
import { t as createRuntimeGraph } from "./graph-B77v_FUC.js";
|
|
2
2
|
export { createRuntimeGraph };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markless/runtime",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Markless state graph runtime: reads, writes, computed invalidation, flush journal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,10 +22,14 @@
|
|
|
22
22
|
"./graph": {
|
|
23
23
|
"types": "./dist/graph.d.ts",
|
|
24
24
|
"default": "./dist/graph.js"
|
|
25
|
+
},
|
|
26
|
+
"./graph-reconcile": {
|
|
27
|
+
"types": "./dist/graph-reconcile.d.ts",
|
|
28
|
+
"default": "./dist/graph-reconcile.js"
|
|
25
29
|
}
|
|
26
30
|
},
|
|
27
31
|
"dependencies": {
|
|
28
|
-
"@markless/serializer": "0.3.
|
|
32
|
+
"@markless/serializer": "0.3.3"
|
|
29
33
|
},
|
|
30
34
|
"files": [
|
|
31
35
|
"dist"
|