@dxos/app-graph 0.8.4-main.f9ba587 → 0.8.4-main.fffef41
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/lib/browser/index.mjs +249 -191
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +249 -191
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/graph-builder.d.ts +29 -18
- package/dist/types/src/graph-builder.d.ts.map +1 -1
- package/dist/types/src/graph.d.ts +25 -21
- package/dist/types/src/graph.d.ts.map +1 -1
- package/dist/types/src/node.d.ts +1 -1
- package/dist/types/src/node.d.ts.map +1 -1
- package/dist/types/src/stories/EchoGraph.stories.d.ts +8 -10
- package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
- package/dist/types/src/testing.d.ts +3 -3
- package/dist/types/src/testing.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +32 -34
- package/src/graph-builder.test.ts +90 -32
- package/src/graph-builder.ts +109 -60
- package/src/graph.test.ts +4 -4
- package/src/graph.ts +130 -89
- package/src/node.ts +5 -3
- package/src/signals-integration.test.ts +29 -28
- package/src/stories/EchoGraph.stories.tsx +49 -39
- package/src/stories/Tree.tsx +1 -1
- package/src/testing.ts +4 -4
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
2
|
|
|
3
3
|
// src/graph.ts
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
4
|
+
import { Atom, Registry } from "@effect-atom/atom-react";
|
|
5
|
+
import * as Function from "effect/Function";
|
|
6
|
+
import * as Option from "effect/Option";
|
|
7
|
+
import * as Record from "effect/Record";
|
|
6
8
|
import { Event, Trigger } from "@dxos/async";
|
|
7
9
|
import { todo } from "@dxos/debug";
|
|
8
10
|
import { invariant } from "@dxos/invariant";
|
|
@@ -14,7 +16,7 @@ var getGraph = (node) => {
|
|
|
14
16
|
const graph = node[graphSymbol];
|
|
15
17
|
invariant(graph, "Node is not associated with a graph.", {
|
|
16
18
|
F: __dxlog_file,
|
|
17
|
-
L:
|
|
19
|
+
L: 29,
|
|
18
20
|
S: void 0,
|
|
19
21
|
A: [
|
|
20
22
|
"graph",
|
|
@@ -28,90 +30,95 @@ var ROOT_TYPE = "dxos.org/type/GraphRoot";
|
|
|
28
30
|
var ACTION_TYPE = "dxos.org/type/GraphAction";
|
|
29
31
|
var ACTION_GROUP_TYPE = "dxos.org/type/GraphActionGroup";
|
|
30
32
|
var Graph = class {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
33
|
+
onNodeChanged = new Event();
|
|
34
|
+
_onExpand;
|
|
35
|
+
_onInitialize;
|
|
36
|
+
_onRemoveNode;
|
|
37
|
+
_registry;
|
|
38
|
+
_expanded = Record.empty();
|
|
39
|
+
_initialized = Record.empty();
|
|
40
|
+
_initialEdges = Record.empty();
|
|
41
|
+
_initialNodes = Record.fromEntries([
|
|
42
|
+
[
|
|
43
|
+
ROOT_ID,
|
|
44
|
+
this._constructNode({
|
|
45
|
+
id: ROOT_ID,
|
|
46
|
+
type: ROOT_TYPE,
|
|
47
|
+
data: null,
|
|
48
|
+
properties: {}
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
]);
|
|
52
|
+
/** @internal */
|
|
53
|
+
_node = Atom.family((id) => {
|
|
54
|
+
const initial = Option.flatten(Record.get(this._initialNodes, id));
|
|
55
|
+
return Atom.make(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:node:${id}`));
|
|
56
|
+
});
|
|
57
|
+
_nodeOrThrow = Atom.family((id) => {
|
|
58
|
+
return Atom.make((get2) => {
|
|
59
|
+
const node = get2(this._node(id));
|
|
60
|
+
invariant(Option.isSome(node), `Node not available: ${id}`, {
|
|
61
|
+
F: __dxlog_file,
|
|
62
|
+
L: 267,
|
|
63
|
+
S: this,
|
|
64
|
+
A: [
|
|
65
|
+
"Option.isSome(node)",
|
|
66
|
+
"`Node not available: ${id}`"
|
|
67
|
+
]
|
|
65
68
|
});
|
|
69
|
+
return node.value;
|
|
66
70
|
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (node.properties.label) {
|
|
97
|
-
obj.label = node.properties.label;
|
|
98
|
-
}
|
|
99
|
-
if (nodes.length) {
|
|
100
|
-
obj.nodes = nodes.map((n) => {
|
|
101
|
-
const nextSeen = [
|
|
102
|
-
...seen,
|
|
103
|
-
node.id
|
|
104
|
-
];
|
|
105
|
-
return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
|
|
106
|
-
}).filter(isNonNullable);
|
|
107
|
-
}
|
|
108
|
-
return obj;
|
|
71
|
+
});
|
|
72
|
+
_edges = Atom.family((id) => {
|
|
73
|
+
const initial = Record.get(this._initialEdges, id).pipe(Option.getOrElse(() => ({
|
|
74
|
+
inbound: [],
|
|
75
|
+
outbound: []
|
|
76
|
+
})));
|
|
77
|
+
return Atom.make(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:edges:${id}`));
|
|
78
|
+
});
|
|
79
|
+
// NOTE: Currently the argument to the family needs to be referentially stable for the atom to be referentially stable.
|
|
80
|
+
// TODO(wittjosiah): Atom feature request, support for something akin to `ComplexMap` to allow for complex arguments.
|
|
81
|
+
_connections = Atom.family((key) => {
|
|
82
|
+
return Atom.make((get2) => {
|
|
83
|
+
const [id, relation] = key.split("$");
|
|
84
|
+
const edges = get2(this._edges(id));
|
|
85
|
+
return edges[relation].map((id2) => get2(this._node(id2))).filter(Option.isSome).map((o) => o.value);
|
|
86
|
+
}).pipe(Atom.withLabel(`graph:connections:${key}`));
|
|
87
|
+
});
|
|
88
|
+
_actions = Atom.family((id) => {
|
|
89
|
+
return Atom.make((get2) => {
|
|
90
|
+
return get2(this._connections(`${id}$outbound`)).filter((node) => node.type === ACTION_TYPE || node.type === ACTION_GROUP_TYPE);
|
|
91
|
+
}).pipe(Atom.withLabel(`graph:actions:${id}`));
|
|
92
|
+
});
|
|
93
|
+
_json = Atom.family((id) => {
|
|
94
|
+
return Atom.make((get2) => {
|
|
95
|
+
const toJSON = (node, seen = []) => {
|
|
96
|
+
const nodes = get2(this.connections(node.id));
|
|
97
|
+
const obj = {
|
|
98
|
+
id: node.id,
|
|
99
|
+
type: node.type
|
|
109
100
|
};
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
101
|
+
if (node.properties.label) {
|
|
102
|
+
obj.label = node.properties.label;
|
|
103
|
+
}
|
|
104
|
+
if (nodes.length) {
|
|
105
|
+
obj.nodes = nodes.map((n) => {
|
|
106
|
+
const nextSeen = [
|
|
107
|
+
...seen,
|
|
108
|
+
node.id
|
|
109
|
+
];
|
|
110
|
+
return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
|
|
111
|
+
}).filter(isNonNullable);
|
|
112
|
+
}
|
|
113
|
+
return obj;
|
|
114
|
+
};
|
|
115
|
+
const root = get2(this.nodeOrThrow(id));
|
|
116
|
+
return toJSON(root);
|
|
117
|
+
}).pipe(Atom.withLabel(`graph:json:${id}`));
|
|
118
|
+
});
|
|
119
|
+
constructor({ registry, nodes, edges, onInitialize, onExpand, onRemoveNode } = {}) {
|
|
114
120
|
this._registry = registry ?? Registry.make();
|
|
121
|
+
this._onInitialize = onInitialize;
|
|
115
122
|
this._onExpand = onExpand;
|
|
116
123
|
this._onRemoveNode = onRemoveNode;
|
|
117
124
|
if (nodes) {
|
|
@@ -164,15 +171,22 @@ var Graph = class {
|
|
|
164
171
|
getEdges(id) {
|
|
165
172
|
return this._registry.get(this.edges(id));
|
|
166
173
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
async initialize(id) {
|
|
175
|
+
const initialized = Record.get(this._initialized, id).pipe(Option.getOrElse(() => false));
|
|
176
|
+
log("initialize", {
|
|
177
|
+
id,
|
|
178
|
+
initialized
|
|
179
|
+
}, {
|
|
180
|
+
F: __dxlog_file,
|
|
181
|
+
L: 399,
|
|
182
|
+
S: this,
|
|
183
|
+
C: (f, a) => f(...a)
|
|
184
|
+
});
|
|
185
|
+
if (!initialized) {
|
|
186
|
+
await this._onInitialize?.(id);
|
|
187
|
+
Record.set(this._initialized, id, true);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
176
190
|
expand(id, relation = "outbound") {
|
|
177
191
|
const key = `${id}$${relation}`;
|
|
178
192
|
const expanded = Record.get(this._expanded, key).pipe(Option.getOrElse(() => false));
|
|
@@ -181,7 +195,7 @@ var Graph = class {
|
|
|
181
195
|
expanded
|
|
182
196
|
}, {
|
|
183
197
|
F: __dxlog_file,
|
|
184
|
-
L:
|
|
198
|
+
L: 409,
|
|
185
199
|
S: this,
|
|
186
200
|
C: (f, a) => f(...a)
|
|
187
201
|
});
|
|
@@ -191,14 +205,14 @@ var Graph = class {
|
|
|
191
205
|
}
|
|
192
206
|
}
|
|
193
207
|
addNodes(nodes) {
|
|
194
|
-
|
|
208
|
+
Atom.batch(() => {
|
|
195
209
|
nodes.map((node) => this.addNode(node));
|
|
196
210
|
});
|
|
197
211
|
}
|
|
198
212
|
addNode({ nodes, edges, ...nodeArg }) {
|
|
199
213
|
const { id, type, data = null, properties = {} } = nodeArg;
|
|
200
|
-
const
|
|
201
|
-
const node = this._registry.get(
|
|
214
|
+
const nodeAtom = this._node(id);
|
|
215
|
+
const node = this._registry.get(nodeAtom);
|
|
202
216
|
Option.match(node, {
|
|
203
217
|
onSome: (node2) => {
|
|
204
218
|
const typeChanged = node2.type !== type;
|
|
@@ -211,7 +225,7 @@ var Graph = class {
|
|
|
211
225
|
propertiesChanged
|
|
212
226
|
}, {
|
|
213
227
|
F: __dxlog_file,
|
|
214
|
-
L:
|
|
228
|
+
L: 431,
|
|
215
229
|
S: this,
|
|
216
230
|
C: (f, a) => f(...a)
|
|
217
231
|
});
|
|
@@ -223,7 +237,7 @@ var Graph = class {
|
|
|
223
237
|
properties
|
|
224
238
|
}, {
|
|
225
239
|
F: __dxlog_file,
|
|
226
|
-
L:
|
|
240
|
+
L: 438,
|
|
227
241
|
S: this,
|
|
228
242
|
C: (f, a) => f(...a)
|
|
229
243
|
});
|
|
@@ -236,7 +250,7 @@ var Graph = class {
|
|
|
236
250
|
...properties
|
|
237
251
|
}
|
|
238
252
|
});
|
|
239
|
-
this._registry.set(
|
|
253
|
+
this._registry.set(nodeAtom, newNode);
|
|
240
254
|
this.onNodeChanged.emit({
|
|
241
255
|
id,
|
|
242
256
|
node: newNode
|
|
@@ -251,7 +265,7 @@ var Graph = class {
|
|
|
251
265
|
properties
|
|
252
266
|
}, {
|
|
253
267
|
F: __dxlog_file,
|
|
254
|
-
L:
|
|
268
|
+
L: 450,
|
|
255
269
|
S: this,
|
|
256
270
|
C: (f, a) => f(...a)
|
|
257
271
|
});
|
|
@@ -261,7 +275,7 @@ var Graph = class {
|
|
|
261
275
|
data,
|
|
262
276
|
properties
|
|
263
277
|
});
|
|
264
|
-
this._registry.set(
|
|
278
|
+
this._registry.set(nodeAtom, newNode);
|
|
265
279
|
this.onNodeChanged.emit({
|
|
266
280
|
id,
|
|
267
281
|
node: newNode
|
|
@@ -281,13 +295,13 @@ var Graph = class {
|
|
|
281
295
|
}
|
|
282
296
|
}
|
|
283
297
|
removeNodes(ids, edges = false) {
|
|
284
|
-
|
|
298
|
+
Atom.batch(() => {
|
|
285
299
|
ids.map((id) => this.removeNode(id, edges));
|
|
286
300
|
});
|
|
287
301
|
}
|
|
288
302
|
removeNode(id, edges = false) {
|
|
289
|
-
const
|
|
290
|
-
this._registry.set(
|
|
303
|
+
const nodeAtom = this._node(id);
|
|
304
|
+
this._registry.set(nodeAtom, Option.none());
|
|
291
305
|
this.onNodeChanged.emit({
|
|
292
306
|
id,
|
|
293
307
|
node: Option.none()
|
|
@@ -309,24 +323,24 @@ var Graph = class {
|
|
|
309
323
|
this._onRemoveNode?.(id);
|
|
310
324
|
}
|
|
311
325
|
addEdges(edges) {
|
|
312
|
-
|
|
326
|
+
Atom.batch(() => {
|
|
313
327
|
edges.map((edge) => this.addEdge(edge));
|
|
314
328
|
});
|
|
315
329
|
}
|
|
316
330
|
addEdge(edgeArg) {
|
|
317
|
-
const
|
|
318
|
-
const source = this._registry.get(
|
|
331
|
+
const sourceAtom = this._edges(edgeArg.source);
|
|
332
|
+
const source = this._registry.get(sourceAtom);
|
|
319
333
|
if (!source.outbound.includes(edgeArg.target)) {
|
|
320
334
|
log("add outbound edge", {
|
|
321
335
|
source: edgeArg.source,
|
|
322
336
|
target: edgeArg.target
|
|
323
337
|
}, {
|
|
324
338
|
F: __dxlog_file,
|
|
325
|
-
L:
|
|
339
|
+
L: 505,
|
|
326
340
|
S: this,
|
|
327
341
|
C: (f, a) => f(...a)
|
|
328
342
|
});
|
|
329
|
-
this._registry.set(
|
|
343
|
+
this._registry.set(sourceAtom, {
|
|
330
344
|
inbound: source.inbound,
|
|
331
345
|
outbound: [
|
|
332
346
|
...source.outbound,
|
|
@@ -334,19 +348,19 @@ var Graph = class {
|
|
|
334
348
|
]
|
|
335
349
|
});
|
|
336
350
|
}
|
|
337
|
-
const
|
|
338
|
-
const target = this._registry.get(
|
|
351
|
+
const targetAtom = this._edges(edgeArg.target);
|
|
352
|
+
const target = this._registry.get(targetAtom);
|
|
339
353
|
if (!target.inbound.includes(edgeArg.source)) {
|
|
340
354
|
log("add inbound edge", {
|
|
341
355
|
source: edgeArg.source,
|
|
342
356
|
target: edgeArg.target
|
|
343
357
|
}, {
|
|
344
358
|
F: __dxlog_file,
|
|
345
|
-
L:
|
|
359
|
+
L: 518,
|
|
346
360
|
S: this,
|
|
347
361
|
C: (f, a) => f(...a)
|
|
348
362
|
});
|
|
349
|
-
this._registry.set(
|
|
363
|
+
this._registry.set(targetAtom, {
|
|
350
364
|
inbound: [
|
|
351
365
|
...target.inbound,
|
|
352
366
|
edgeArg.source
|
|
@@ -356,30 +370,30 @@ var Graph = class {
|
|
|
356
370
|
}
|
|
357
371
|
}
|
|
358
372
|
removeEdges(edges, removeOrphans = false) {
|
|
359
|
-
|
|
373
|
+
Atom.batch(() => {
|
|
360
374
|
edges.map((edge) => this.removeEdge(edge, removeOrphans));
|
|
361
375
|
});
|
|
362
376
|
}
|
|
363
377
|
removeEdge(edgeArg, removeOrphans = false) {
|
|
364
|
-
const
|
|
365
|
-
const source = this._registry.get(
|
|
378
|
+
const sourceAtom = this._edges(edgeArg.source);
|
|
379
|
+
const source = this._registry.get(sourceAtom);
|
|
366
380
|
if (source.outbound.includes(edgeArg.target)) {
|
|
367
|
-
this._registry.set(
|
|
381
|
+
this._registry.set(sourceAtom, {
|
|
368
382
|
inbound: source.inbound,
|
|
369
383
|
outbound: source.outbound.filter((id) => id !== edgeArg.target)
|
|
370
384
|
});
|
|
371
385
|
}
|
|
372
|
-
const
|
|
373
|
-
const target = this._registry.get(
|
|
386
|
+
const targetAtom = this._edges(edgeArg.target);
|
|
387
|
+
const target = this._registry.get(targetAtom);
|
|
374
388
|
if (target.inbound.includes(edgeArg.source)) {
|
|
375
|
-
this._registry.set(
|
|
389
|
+
this._registry.set(targetAtom, {
|
|
376
390
|
inbound: target.inbound.filter((id) => id !== edgeArg.source),
|
|
377
391
|
outbound: target.outbound
|
|
378
392
|
});
|
|
379
393
|
}
|
|
380
394
|
if (removeOrphans) {
|
|
381
|
-
const source2 = this._registry.get(
|
|
382
|
-
const target2 = this._registry.get(
|
|
395
|
+
const source2 = this._registry.get(sourceAtom);
|
|
396
|
+
const target2 = this._registry.get(targetAtom);
|
|
383
397
|
if (source2.outbound.length === 0 && source2.inbound.length === 0 && edgeArg.source !== ROOT_ID) {
|
|
384
398
|
this.removeNodes([
|
|
385
399
|
edgeArg.source
|
|
@@ -393,15 +407,15 @@ var Graph = class {
|
|
|
393
407
|
}
|
|
394
408
|
}
|
|
395
409
|
sortEdges(id, relation, order) {
|
|
396
|
-
const
|
|
397
|
-
const edges = this._registry.get(
|
|
410
|
+
const edgesAtom = this._edges(id);
|
|
411
|
+
const edges = this._registry.get(edgesAtom);
|
|
398
412
|
const unsorted = edges[relation].filter((id2) => !order.includes(id2)) ?? [];
|
|
399
413
|
const sorted = order.filter((id2) => edges[relation].includes(id2)) ?? [];
|
|
400
414
|
edges[relation].splice(0, edges[relation].length, ...[
|
|
401
415
|
...sorted,
|
|
402
416
|
...unsorted
|
|
403
417
|
]);
|
|
404
|
-
this._registry.set(
|
|
418
|
+
this._registry.set(edgesAtom, edges);
|
|
405
419
|
}
|
|
406
420
|
traverse({ visitor, source = ROOT_ID, relation = "outbound" }, path = []) {
|
|
407
421
|
if (path.includes(source)) {
|
|
@@ -425,7 +439,7 @@ var Graph = class {
|
|
|
425
439
|
]));
|
|
426
440
|
}
|
|
427
441
|
getPath({ source = "root", target }) {
|
|
428
|
-
return pipe(this.getNode(source), Option.flatMap((node) => {
|
|
442
|
+
return Function.pipe(this.getNode(source), Option.flatMap((node) => {
|
|
429
443
|
let found = Option.none();
|
|
430
444
|
this.traverse({
|
|
431
445
|
source: node.id,
|
|
@@ -469,57 +483,65 @@ var Graph = class {
|
|
|
469
483
|
};
|
|
470
484
|
|
|
471
485
|
// src/graph-builder.ts
|
|
472
|
-
import {
|
|
486
|
+
import { Atom as Atom2, Registry as Registry2 } from "@effect-atom/atom-react";
|
|
473
487
|
import { effect } from "@preact/signals-core";
|
|
474
|
-
import
|
|
488
|
+
import * as Array from "effect/Array";
|
|
489
|
+
import * as Function2 from "effect/Function";
|
|
490
|
+
import * as Option2 from "effect/Option";
|
|
491
|
+
import * as Record2 from "effect/Record";
|
|
475
492
|
import { log as log2 } from "@dxos/log";
|
|
476
493
|
import { byPosition, getDebugName, isNode, isNonNullable as isNonNullable2 } from "@dxos/util";
|
|
477
494
|
|
|
478
495
|
// src/node.ts
|
|
479
496
|
var isGraphNode = (data) => data && typeof data === "object" && "id" in data && "properties" in data && data.properties ? typeof data.properties === "object" && "data" in data : false;
|
|
480
|
-
var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" : false;
|
|
497
|
+
var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" && data.type === ACTION_TYPE : false;
|
|
481
498
|
var actionGroupSymbol = Symbol("ActionGroup");
|
|
482
|
-
var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol : false;
|
|
499
|
+
var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol && data.type === ACTION_GROUP_TYPE : false;
|
|
483
500
|
var isActionLike = (data) => isAction(data) || isActionGroup(data);
|
|
484
501
|
|
|
485
502
|
// src/graph-builder.ts
|
|
486
503
|
var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
|
|
487
504
|
var createExtension = (extension) => {
|
|
488
|
-
const { id, position = "static", relation = "outbound", connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
|
|
505
|
+
const { id, position = "static", relation = "outbound", resolver: _resolver, connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
|
|
489
506
|
const getId = (key) => `${id}/${key}`;
|
|
490
|
-
const
|
|
491
|
-
const
|
|
492
|
-
const
|
|
507
|
+
const resolver = _resolver && Atom2.family((id2) => _resolver(id2).pipe(Atom2.withLabel(`graph-builder:_resolver:${id2}`)));
|
|
508
|
+
const connector = _connector && Atom2.family((node) => _connector(node).pipe(Atom2.withLabel(`graph-builder:_connector:${id}`)));
|
|
509
|
+
const actionGroups = _actionGroups && Atom2.family((node) => _actionGroups(node).pipe(Atom2.withLabel(`graph-builder:_actionGroups:${id}`)));
|
|
510
|
+
const actions = _actions && Atom2.family((node) => _actions(node).pipe(Atom2.withLabel(`graph-builder:_actions:${id}`)));
|
|
493
511
|
return [
|
|
494
|
-
|
|
512
|
+
resolver ? {
|
|
513
|
+
id: getId("resolver"),
|
|
514
|
+
position,
|
|
515
|
+
resolver
|
|
516
|
+
} : void 0,
|
|
495
517
|
connector ? {
|
|
496
518
|
id: getId("connector"),
|
|
497
519
|
position,
|
|
498
520
|
relation,
|
|
499
|
-
connector:
|
|
521
|
+
connector: Atom2.family((node) => Atom2.make((get2) => {
|
|
500
522
|
try {
|
|
501
|
-
return
|
|
523
|
+
return get2(connector(node));
|
|
502
524
|
} catch {
|
|
503
525
|
log2.warn("Error in connector", {
|
|
504
526
|
id: getId("connector"),
|
|
505
527
|
node
|
|
506
528
|
}, {
|
|
507
529
|
F: __dxlog_file2,
|
|
508
|
-
L:
|
|
530
|
+
L: 114,
|
|
509
531
|
S: void 0,
|
|
510
532
|
C: (f, a) => f(...a)
|
|
511
533
|
});
|
|
512
534
|
return [];
|
|
513
535
|
}
|
|
514
|
-
}).pipe(
|
|
536
|
+
}).pipe(Atom2.withLabel(`graph-builder:connector:${id}`)))
|
|
515
537
|
} : void 0,
|
|
516
538
|
actionGroups ? {
|
|
517
539
|
id: getId("actionGroups"),
|
|
518
540
|
position,
|
|
519
541
|
relation: "outbound",
|
|
520
|
-
connector:
|
|
542
|
+
connector: Atom2.family((node) => Atom2.make((get2) => {
|
|
521
543
|
try {
|
|
522
|
-
return
|
|
544
|
+
return get2(actionGroups(node)).map((arg) => ({
|
|
523
545
|
...arg,
|
|
524
546
|
data: actionGroupSymbol,
|
|
525
547
|
type: ACTION_GROUP_TYPE
|
|
@@ -530,21 +552,21 @@ var createExtension = (extension) => {
|
|
|
530
552
|
node
|
|
531
553
|
}, {
|
|
532
554
|
F: __dxlog_file2,
|
|
533
|
-
L:
|
|
555
|
+
L: 135,
|
|
534
556
|
S: void 0,
|
|
535
557
|
C: (f, a) => f(...a)
|
|
536
558
|
});
|
|
537
559
|
return [];
|
|
538
560
|
}
|
|
539
|
-
}).pipe(
|
|
561
|
+
}).pipe(Atom2.withLabel(`graph-builder:connector:actionGroups:${id}`)))
|
|
540
562
|
} : void 0,
|
|
541
563
|
actions ? {
|
|
542
564
|
id: getId("actions"),
|
|
543
565
|
position,
|
|
544
566
|
relation: "outbound",
|
|
545
|
-
connector:
|
|
567
|
+
connector: Atom2.family((node) => Atom2.make((get2) => {
|
|
546
568
|
try {
|
|
547
|
-
return
|
|
569
|
+
return get2(actions(node)).map((arg) => ({
|
|
548
570
|
...arg,
|
|
549
571
|
type: ACTION_TYPE
|
|
550
572
|
}));
|
|
@@ -554,13 +576,13 @@ var createExtension = (extension) => {
|
|
|
554
576
|
node
|
|
555
577
|
}, {
|
|
556
578
|
F: __dxlog_file2,
|
|
557
|
-
L:
|
|
579
|
+
L: 152,
|
|
558
580
|
S: void 0,
|
|
559
581
|
C: (f, a) => f(...a)
|
|
560
582
|
});
|
|
561
583
|
return [];
|
|
562
584
|
}
|
|
563
|
-
}).pipe(
|
|
585
|
+
}).pipe(Atom2.withLabel(`graph-builder:connector:actions:${id}`)))
|
|
564
586
|
} : void 0
|
|
565
587
|
].filter(isNonNullable2);
|
|
566
588
|
};
|
|
@@ -578,32 +600,19 @@ var flattenExtensions = (extension, acc = []) => {
|
|
|
578
600
|
}
|
|
579
601
|
};
|
|
580
602
|
var GraphBuilder = class _GraphBuilder {
|
|
603
|
+
// TODO(wittjosiah): Use Context.
|
|
604
|
+
_subscriptions = /* @__PURE__ */ new Map();
|
|
605
|
+
_extensions = Atom2.make(Record2.empty()).pipe(Atom2.keepAlive, Atom2.withLabel("graph-builder:extensions"));
|
|
606
|
+
_initialized = {};
|
|
607
|
+
_registry;
|
|
608
|
+
_graph;
|
|
581
609
|
constructor({ registry, ...params } = {}) {
|
|
582
|
-
// TODO(wittjosiah): Use Context.
|
|
583
|
-
this._connectorSubscriptions = /* @__PURE__ */ new Map();
|
|
584
|
-
this._extensions = Rx2.make(Record2.empty()).pipe(Rx2.keepAlive, Rx2.withLabel("graph-builder:extensions"));
|
|
585
|
-
this._connectors = Rx2.family((key) => {
|
|
586
|
-
return Rx2.make((get) => {
|
|
587
|
-
const [id, relation] = key.split("+");
|
|
588
|
-
const node = this._graph.node(id);
|
|
589
|
-
return pipe2(
|
|
590
|
-
get(this._extensions),
|
|
591
|
-
Record2.values,
|
|
592
|
-
// TODO(wittjosiah): Sort on write rather than read.
|
|
593
|
-
Array.sortBy(byPosition),
|
|
594
|
-
Array.filter(({ relation: _relation = "outbound" }) => _relation === relation),
|
|
595
|
-
Array.map(({ connector }) => connector?.(node)),
|
|
596
|
-
Array.filter(isNonNullable2),
|
|
597
|
-
Array.flatMap((result) => get(result))
|
|
598
|
-
);
|
|
599
|
-
}).pipe(Rx2.withLabel(`graph-builder:connectors:${key}`));
|
|
600
|
-
});
|
|
601
610
|
this._registry = registry ?? Registry2.make();
|
|
602
611
|
this._graph = new Graph({
|
|
603
612
|
...params,
|
|
604
613
|
registry: this._registry,
|
|
605
614
|
onExpand: (id, relation) => this._onExpand(id, relation),
|
|
606
|
-
|
|
615
|
+
onInitialize: (id) => this._onInitialize(id),
|
|
607
616
|
onRemoveNode: (id) => this._onRemoveNode(id)
|
|
608
617
|
});
|
|
609
618
|
}
|
|
@@ -673,9 +682,30 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
673
682
|
}
|
|
674
683
|
}
|
|
675
684
|
destroy() {
|
|
676
|
-
this.
|
|
677
|
-
this.
|
|
685
|
+
this._subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
686
|
+
this._subscriptions.clear();
|
|
678
687
|
}
|
|
688
|
+
_resolvers = Atom2.family((id) => {
|
|
689
|
+
return Atom2.make((get2) => {
|
|
690
|
+
return Function2.pipe(get2(this._extensions), Record2.values, Array.sortBy(byPosition), Array.map(({ resolver }) => resolver), Array.filter(isNonNullable2), Array.map((resolver) => get2(resolver(id))), Array.filter(isNonNullable2), Array.head);
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
_connectors = Atom2.family((key) => {
|
|
694
|
+
return Atom2.make((get2) => {
|
|
695
|
+
const [id, relation] = key.split("+");
|
|
696
|
+
const node = this._graph.node(id);
|
|
697
|
+
return Function2.pipe(
|
|
698
|
+
get2(this._extensions),
|
|
699
|
+
Record2.values,
|
|
700
|
+
// TODO(wittjosiah): Sort on write rather than read.
|
|
701
|
+
Array.sortBy(byPosition),
|
|
702
|
+
Array.filter(({ relation: _relation = "outbound" }) => _relation === relation),
|
|
703
|
+
Array.map(({ connector }) => connector?.(node)),
|
|
704
|
+
Array.filter(isNonNullable2),
|
|
705
|
+
Array.flatMap((result) => get2(result))
|
|
706
|
+
);
|
|
707
|
+
}).pipe(Atom2.withLabel(`graph-builder:connectors:${key}`));
|
|
708
|
+
});
|
|
679
709
|
_onExpand(id, relation) {
|
|
680
710
|
log2("onExpand", {
|
|
681
711
|
id,
|
|
@@ -683,7 +713,7 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
683
713
|
registry: getDebugName(this._registry)
|
|
684
714
|
}, {
|
|
685
715
|
F: __dxlog_file2,
|
|
686
|
-
L:
|
|
716
|
+
L: 329,
|
|
687
717
|
S: this,
|
|
688
718
|
C: (f, a) => f(...a)
|
|
689
719
|
});
|
|
@@ -700,12 +730,12 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
700
730
|
removed
|
|
701
731
|
}, {
|
|
702
732
|
F: __dxlog_file2,
|
|
703
|
-
L:
|
|
733
|
+
L: 340,
|
|
704
734
|
S: this,
|
|
705
735
|
C: (f, a) => f(...a)
|
|
706
736
|
});
|
|
707
737
|
const update = () => {
|
|
708
|
-
|
|
738
|
+
Atom2.batch(() => {
|
|
709
739
|
this._graph.removeEdges(removed.map((target) => ({
|
|
710
740
|
source: id,
|
|
711
741
|
target
|
|
@@ -729,34 +759,62 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
729
759
|
}, {
|
|
730
760
|
immediate: true
|
|
731
761
|
});
|
|
732
|
-
this.
|
|
762
|
+
this._subscriptions.set(id, cancel);
|
|
763
|
+
}
|
|
764
|
+
// TODO(wittjosiah): If the same node is added by a connector, the resolver should probably cancel itself?
|
|
765
|
+
async _onInitialize(id) {
|
|
766
|
+
log2("onInitialize", {
|
|
767
|
+
id
|
|
768
|
+
}, {
|
|
769
|
+
F: __dxlog_file2,
|
|
770
|
+
L: 377,
|
|
771
|
+
S: this,
|
|
772
|
+
C: (f, a) => f(...a)
|
|
773
|
+
});
|
|
774
|
+
const resolver = this._resolvers(id);
|
|
775
|
+
const cancel = this._registry.subscribe(resolver, (node) => {
|
|
776
|
+
const trigger = this._initialized[id];
|
|
777
|
+
Option2.match(node, {
|
|
778
|
+
onSome: (node2) => {
|
|
779
|
+
this._graph.addNodes([
|
|
780
|
+
node2
|
|
781
|
+
]);
|
|
782
|
+
trigger?.wake();
|
|
783
|
+
},
|
|
784
|
+
onNone: () => {
|
|
785
|
+
trigger?.wake();
|
|
786
|
+
this._graph.removeNodes([
|
|
787
|
+
id
|
|
788
|
+
]);
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
}, {
|
|
792
|
+
immediate: true
|
|
793
|
+
});
|
|
794
|
+
this._subscriptions.set(id, cancel);
|
|
733
795
|
}
|
|
734
|
-
// TODO(wittjosiah): On initialize to restore state from cache.
|
|
735
|
-
// private async _onInitialize(id: string) {
|
|
736
|
-
// log('onInitialize', { id });
|
|
737
|
-
// }
|
|
738
796
|
_onRemoveNode(id) {
|
|
739
|
-
this.
|
|
740
|
-
this.
|
|
797
|
+
this._subscriptions.get(id)?.();
|
|
798
|
+
this._subscriptions.delete(id);
|
|
741
799
|
}
|
|
742
800
|
};
|
|
743
|
-
var
|
|
744
|
-
return
|
|
801
|
+
var atomFromSignal = (cb) => {
|
|
802
|
+
return Atom2.make((get2) => {
|
|
745
803
|
const dispose = effect(() => {
|
|
746
|
-
|
|
804
|
+
get2.setSelf(cb());
|
|
747
805
|
});
|
|
748
|
-
|
|
806
|
+
get2.addFinalizer(() => dispose());
|
|
749
807
|
return cb();
|
|
750
808
|
});
|
|
751
809
|
};
|
|
752
|
-
var observableFamily =
|
|
753
|
-
return
|
|
754
|
-
const subscription = observable.subscribe((value) =>
|
|
755
|
-
|
|
810
|
+
var observableFamily = Atom2.family((observable) => {
|
|
811
|
+
return Atom2.make((get2) => {
|
|
812
|
+
const subscription = observable.subscribe((value) => get2.setSelf(value));
|
|
813
|
+
get2.addFinalizer(() => subscription.unsubscribe());
|
|
756
814
|
return observable.get();
|
|
757
815
|
});
|
|
758
816
|
});
|
|
759
|
-
var
|
|
817
|
+
var atomFromObservable = (observable) => {
|
|
760
818
|
return observableFamily(observable);
|
|
761
819
|
};
|
|
762
820
|
export {
|
|
@@ -767,14 +825,14 @@ export {
|
|
|
767
825
|
ROOT_ID,
|
|
768
826
|
ROOT_TYPE,
|
|
769
827
|
actionGroupSymbol,
|
|
828
|
+
atomFromObservable,
|
|
829
|
+
atomFromSignal,
|
|
770
830
|
createExtension,
|
|
771
831
|
flattenExtensions,
|
|
772
832
|
getGraph,
|
|
773
833
|
isAction,
|
|
774
834
|
isActionGroup,
|
|
775
835
|
isActionLike,
|
|
776
|
-
isGraphNode
|
|
777
|
-
rxFromObservable,
|
|
778
|
-
rxFromSignal
|
|
836
|
+
isGraphNode
|
|
779
837
|
};
|
|
780
838
|
//# sourceMappingURL=index.mjs.map
|