@dxos/app-graph 0.8.4-main.72ec0f3 → 0.8.4-main.74a063c4e0
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/chunk-AKBGYELG.mjs +1603 -0
- package/dist/lib/browser/chunk-AKBGYELG.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +29 -827
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +39 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HR5S4XYH.mjs +1604 -0
- package/dist/lib/node-esm/chunk-HR5S4XYH.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +29 -828
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +40 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/atoms.d.ts +8 -0
- package/dist/types/src/atoms.d.ts.map +1 -0
- package/dist/types/src/graph-builder.d.ts +111 -65
- package/dist/types/src/graph-builder.d.ts.map +1 -1
- package/dist/types/src/graph.d.ts +179 -213
- package/dist/types/src/graph.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +7 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/node-matcher.d.ts +244 -0
- package/dist/types/src/node-matcher.d.ts.map +1 -0
- package/dist/types/src/node-matcher.test.d.ts +2 -0
- package/dist/types/src/node-matcher.test.d.ts.map +1 -0
- package/dist/types/src/node.d.ts +50 -5
- package/dist/types/src/node.d.ts.map +1 -1
- package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/setup-graph-builder.d.ts +31 -0
- package/dist/types/src/testing/setup-graph-builder.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +39 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +47 -35
- package/src/atoms.ts +25 -0
- package/src/graph-builder.test.ts +995 -112
- package/src/graph-builder.ts +705 -284
- package/src/graph.test.ts +448 -120
- package/src/graph.ts +1019 -413
- package/src/index.ts +10 -3
- package/src/node-matcher.test.ts +301 -0
- package/src/node-matcher.ts +314 -0
- package/src/node.ts +82 -8
- package/src/stories/EchoGraph.stories.tsx +151 -121
- package/src/stories/Tree.tsx +2 -2
- package/src/testing/index.ts +5 -0
- package/src/testing/setup-graph-builder.ts +41 -0
- package/src/util.ts +95 -0
- package/dist/types/src/experimental/graph-projections.test.d.ts +0 -25
- package/dist/types/src/experimental/graph-projections.test.d.ts.map +0 -1
- package/dist/types/src/signals-integration.test.d.ts +0 -2
- package/dist/types/src/signals-integration.test.d.ts.map +0 -1
- package/dist/types/src/testing.d.ts +0 -5
- package/dist/types/src/testing.d.ts.map +0 -1
- package/src/experimental/graph-projections.test.ts +0 -56
- package/src/signals-integration.test.ts +0 -219
- package/src/testing.ts +0 -20
|
@@ -1,836 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
|
|
12
|
-
var graphSymbol = Symbol("graph");
|
|
13
|
-
var getGraph = (node) => {
|
|
14
|
-
const graph = node[graphSymbol];
|
|
15
|
-
invariant(graph, "Node is not associated with a graph.", {
|
|
16
|
-
F: __dxlog_file,
|
|
17
|
-
L: 29,
|
|
18
|
-
S: void 0,
|
|
19
|
-
A: [
|
|
20
|
-
"graph",
|
|
21
|
-
"'Node is not associated with a graph.'"
|
|
22
|
-
]
|
|
23
|
-
});
|
|
24
|
-
return graph;
|
|
25
|
-
};
|
|
26
|
-
var ROOT_ID = "root";
|
|
27
|
-
var ROOT_TYPE = "dxos.org/type/GraphRoot";
|
|
28
|
-
var ACTION_TYPE = "dxos.org/type/GraphAction";
|
|
29
|
-
var ACTION_GROUP_TYPE = "dxos.org/type/GraphActionGroup";
|
|
30
|
-
var Graph = class {
|
|
31
|
-
onNodeChanged = new Event();
|
|
32
|
-
_onExpand;
|
|
33
|
-
_onInitialize;
|
|
34
|
-
_onRemoveNode;
|
|
35
|
-
_registry;
|
|
36
|
-
_expanded = Record.empty();
|
|
37
|
-
_initialized = Record.empty();
|
|
38
|
-
_initialEdges = Record.empty();
|
|
39
|
-
_initialNodes = Record.fromEntries([
|
|
40
|
-
[
|
|
41
|
-
ROOT_ID,
|
|
42
|
-
this._constructNode({
|
|
43
|
-
id: ROOT_ID,
|
|
44
|
-
type: ROOT_TYPE,
|
|
45
|
-
data: null,
|
|
46
|
-
properties: {}
|
|
47
|
-
})
|
|
48
|
-
]
|
|
49
|
-
]);
|
|
50
|
-
/** @internal */
|
|
51
|
-
_node = Atom.family((id) => {
|
|
52
|
-
const initial = Option.flatten(Record.get(this._initialNodes, id));
|
|
53
|
-
return Atom.make(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:node:${id}`));
|
|
54
|
-
});
|
|
55
|
-
_nodeOrThrow = Atom.family((id) => {
|
|
56
|
-
return Atom.make((get2) => {
|
|
57
|
-
const node = get2(this._node(id));
|
|
58
|
-
invariant(Option.isSome(node), `Node not available: ${id}`, {
|
|
59
|
-
F: __dxlog_file,
|
|
60
|
-
L: 267,
|
|
61
|
-
S: this,
|
|
62
|
-
A: [
|
|
63
|
-
"Option.isSome(node)",
|
|
64
|
-
"`Node not available: ${id}`"
|
|
65
|
-
]
|
|
66
|
-
});
|
|
67
|
-
return node.value;
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
_edges = Atom.family((id) => {
|
|
71
|
-
const initial = Record.get(this._initialEdges, id).pipe(Option.getOrElse(() => ({
|
|
72
|
-
inbound: [],
|
|
73
|
-
outbound: []
|
|
74
|
-
})));
|
|
75
|
-
return Atom.make(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:edges:${id}`));
|
|
76
|
-
});
|
|
77
|
-
// NOTE: Currently the argument to the family needs to be referentially stable for the atom to be referentially stable.
|
|
78
|
-
// TODO(wittjosiah): Atom feature request, support for something akin to `ComplexMap` to allow for complex arguments.
|
|
79
|
-
_connections = Atom.family((key) => {
|
|
80
|
-
return Atom.make((get2) => {
|
|
81
|
-
const [id, relation] = key.split("$");
|
|
82
|
-
const edges = get2(this._edges(id));
|
|
83
|
-
return edges[relation].map((id2) => get2(this._node(id2))).filter(Option.isSome).map((o) => o.value);
|
|
84
|
-
}).pipe(Atom.withLabel(`graph:connections:${key}`));
|
|
85
|
-
});
|
|
86
|
-
_actions = Atom.family((id) => {
|
|
87
|
-
return Atom.make((get2) => {
|
|
88
|
-
return get2(this._connections(`${id}$outbound`)).filter((node) => node.type === ACTION_TYPE || node.type === ACTION_GROUP_TYPE);
|
|
89
|
-
}).pipe(Atom.withLabel(`graph:actions:${id}`));
|
|
90
|
-
});
|
|
91
|
-
_json = Atom.family((id) => {
|
|
92
|
-
return Atom.make((get2) => {
|
|
93
|
-
const toJSON = (node, seen = []) => {
|
|
94
|
-
const nodes = get2(this.connections(node.id));
|
|
95
|
-
const obj = {
|
|
96
|
-
id: node.id,
|
|
97
|
-
type: node.type
|
|
98
|
-
};
|
|
99
|
-
if (node.properties.label) {
|
|
100
|
-
obj.label = node.properties.label;
|
|
101
|
-
}
|
|
102
|
-
if (nodes.length) {
|
|
103
|
-
obj.nodes = nodes.map((n) => {
|
|
104
|
-
const nextSeen = [
|
|
105
|
-
...seen,
|
|
106
|
-
node.id
|
|
107
|
-
];
|
|
108
|
-
return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
|
|
109
|
-
}).filter(isNonNullable);
|
|
110
|
-
}
|
|
111
|
-
return obj;
|
|
112
|
-
};
|
|
113
|
-
const root = get2(this.nodeOrThrow(id));
|
|
114
|
-
return toJSON(root);
|
|
115
|
-
}).pipe(Atom.withLabel(`graph:json:${id}`));
|
|
116
|
-
});
|
|
117
|
-
constructor({ registry, nodes, edges, onInitialize, onExpand, onRemoveNode } = {}) {
|
|
118
|
-
this._registry = registry ?? Registry.make();
|
|
119
|
-
this._onInitialize = onInitialize;
|
|
120
|
-
this._onExpand = onExpand;
|
|
121
|
-
this._onRemoveNode = onRemoveNode;
|
|
122
|
-
if (nodes) {
|
|
123
|
-
nodes.forEach((node) => {
|
|
124
|
-
Record.set(this._initialNodes, node.id, this._constructNode(node));
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
if (edges) {
|
|
128
|
-
Object.entries(edges).forEach(([source, edges2]) => {
|
|
129
|
-
Record.set(this._initialEdges, source, edges2);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
toJSON(id = ROOT_ID) {
|
|
134
|
-
return this._registry.get(this._json(id));
|
|
135
|
-
}
|
|
136
|
-
json(id = ROOT_ID) {
|
|
137
|
-
return this._json(id);
|
|
138
|
-
}
|
|
139
|
-
node(id) {
|
|
140
|
-
return this._node(id);
|
|
141
|
-
}
|
|
142
|
-
nodeOrThrow(id) {
|
|
143
|
-
return this._nodeOrThrow(id);
|
|
144
|
-
}
|
|
145
|
-
connections(id, relation = "outbound") {
|
|
146
|
-
return this._connections(`${id}$${relation}`);
|
|
147
|
-
}
|
|
148
|
-
actions(id) {
|
|
149
|
-
return this._actions(id);
|
|
150
|
-
}
|
|
151
|
-
edges(id) {
|
|
152
|
-
return this._edges(id);
|
|
153
|
-
}
|
|
154
|
-
get root() {
|
|
155
|
-
return this.getNodeOrThrow(ROOT_ID);
|
|
156
|
-
}
|
|
157
|
-
getNode(id) {
|
|
158
|
-
return this._registry.get(this.node(id));
|
|
159
|
-
}
|
|
160
|
-
getNodeOrThrow(id) {
|
|
161
|
-
return this._registry.get(this.nodeOrThrow(id));
|
|
162
|
-
}
|
|
163
|
-
getConnections(id, relation = "outbound") {
|
|
164
|
-
return this._registry.get(this.connections(id, relation));
|
|
165
|
-
}
|
|
166
|
-
getActions(id) {
|
|
167
|
-
return this._registry.get(this.actions(id));
|
|
168
|
-
}
|
|
169
|
-
getEdges(id) {
|
|
170
|
-
return this._registry.get(this.edges(id));
|
|
171
|
-
}
|
|
172
|
-
async initialize(id) {
|
|
173
|
-
const initialized = Record.get(this._initialized, id).pipe(Option.getOrElse(() => false));
|
|
174
|
-
log("initialize", {
|
|
175
|
-
id,
|
|
176
|
-
initialized
|
|
177
|
-
}, {
|
|
178
|
-
F: __dxlog_file,
|
|
179
|
-
L: 399,
|
|
180
|
-
S: this,
|
|
181
|
-
C: (f, a) => f(...a)
|
|
182
|
-
});
|
|
183
|
-
if (!initialized) {
|
|
184
|
-
await this._onInitialize?.(id);
|
|
185
|
-
Record.set(this._initialized, id, true);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
expand(id, relation = "outbound") {
|
|
189
|
-
const key = `${id}$${relation}`;
|
|
190
|
-
const expanded = Record.get(this._expanded, key).pipe(Option.getOrElse(() => false));
|
|
191
|
-
log("expand", {
|
|
192
|
-
key,
|
|
193
|
-
expanded
|
|
194
|
-
}, {
|
|
195
|
-
F: __dxlog_file,
|
|
196
|
-
L: 409,
|
|
197
|
-
S: this,
|
|
198
|
-
C: (f, a) => f(...a)
|
|
199
|
-
});
|
|
200
|
-
if (!expanded) {
|
|
201
|
-
this._onExpand?.(id, relation);
|
|
202
|
-
Record.set(this._expanded, key, true);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
addNodes(nodes) {
|
|
206
|
-
Atom.batch(() => {
|
|
207
|
-
nodes.map((node) => this.addNode(node));
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
addNode({ nodes, edges, ...nodeArg }) {
|
|
211
|
-
const { id, type, data = null, properties = {} } = nodeArg;
|
|
212
|
-
const nodeAtom = this._node(id);
|
|
213
|
-
const node = this._registry.get(nodeAtom);
|
|
214
|
-
Option.match(node, {
|
|
215
|
-
onSome: (node2) => {
|
|
216
|
-
const typeChanged = node2.type !== type;
|
|
217
|
-
const dataChanged = node2.data !== data;
|
|
218
|
-
const propertiesChanged = Object.keys(properties).some((key) => node2.properties[key] !== properties[key]);
|
|
219
|
-
log("existing node", {
|
|
220
|
-
id,
|
|
221
|
-
typeChanged,
|
|
222
|
-
dataChanged,
|
|
223
|
-
propertiesChanged
|
|
224
|
-
}, {
|
|
225
|
-
F: __dxlog_file,
|
|
226
|
-
L: 431,
|
|
227
|
-
S: this,
|
|
228
|
-
C: (f, a) => f(...a)
|
|
229
|
-
});
|
|
230
|
-
if (typeChanged || dataChanged || propertiesChanged) {
|
|
231
|
-
log("updating node", {
|
|
232
|
-
id,
|
|
233
|
-
type,
|
|
234
|
-
data,
|
|
235
|
-
properties
|
|
236
|
-
}, {
|
|
237
|
-
F: __dxlog_file,
|
|
238
|
-
L: 438,
|
|
239
|
-
S: this,
|
|
240
|
-
C: (f, a) => f(...a)
|
|
241
|
-
});
|
|
242
|
-
const newNode = Option.some({
|
|
243
|
-
...node2,
|
|
244
|
-
type,
|
|
245
|
-
data,
|
|
246
|
-
properties: {
|
|
247
|
-
...node2.properties,
|
|
248
|
-
...properties
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
this._registry.set(nodeAtom, newNode);
|
|
252
|
-
this.onNodeChanged.emit({
|
|
253
|
-
id,
|
|
254
|
-
node: newNode
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
onNone: () => {
|
|
259
|
-
log("new node", {
|
|
260
|
-
id,
|
|
261
|
-
type,
|
|
262
|
-
data,
|
|
263
|
-
properties
|
|
264
|
-
}, {
|
|
265
|
-
F: __dxlog_file,
|
|
266
|
-
L: 450,
|
|
267
|
-
S: this,
|
|
268
|
-
C: (f, a) => f(...a)
|
|
269
|
-
});
|
|
270
|
-
const newNode = this._constructNode({
|
|
271
|
-
id,
|
|
272
|
-
type,
|
|
273
|
-
data,
|
|
274
|
-
properties
|
|
275
|
-
});
|
|
276
|
-
this._registry.set(nodeAtom, newNode);
|
|
277
|
-
this.onNodeChanged.emit({
|
|
278
|
-
id,
|
|
279
|
-
node: newNode
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
if (nodes) {
|
|
284
|
-
this.addNodes(nodes);
|
|
285
|
-
const _edges = nodes.map((node2) => ({
|
|
286
|
-
source: id,
|
|
287
|
-
target: node2.id
|
|
288
|
-
}));
|
|
289
|
-
this.addEdges(_edges);
|
|
290
|
-
}
|
|
291
|
-
if (edges) {
|
|
292
|
-
todo();
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
removeNodes(ids, edges = false) {
|
|
296
|
-
Atom.batch(() => {
|
|
297
|
-
ids.map((id) => this.removeNode(id, edges));
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
removeNode(id, edges = false) {
|
|
301
|
-
const nodeAtom = this._node(id);
|
|
302
|
-
this._registry.set(nodeAtom, Option.none());
|
|
303
|
-
this.onNodeChanged.emit({
|
|
304
|
-
id,
|
|
305
|
-
node: Option.none()
|
|
306
|
-
});
|
|
307
|
-
if (edges) {
|
|
308
|
-
const { inbound, outbound } = this._registry.get(this._edges(id));
|
|
309
|
-
const edges2 = [
|
|
310
|
-
...inbound.map((source) => ({
|
|
311
|
-
source,
|
|
312
|
-
target: id
|
|
313
|
-
})),
|
|
314
|
-
...outbound.map((target) => ({
|
|
315
|
-
source: id,
|
|
316
|
-
target
|
|
317
|
-
}))
|
|
318
|
-
];
|
|
319
|
-
this.removeEdges(edges2);
|
|
320
|
-
}
|
|
321
|
-
this._onRemoveNode?.(id);
|
|
322
|
-
}
|
|
323
|
-
addEdges(edges) {
|
|
324
|
-
Atom.batch(() => {
|
|
325
|
-
edges.map((edge) => this.addEdge(edge));
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
addEdge(edgeArg) {
|
|
329
|
-
const sourceAtom = this._edges(edgeArg.source);
|
|
330
|
-
const source = this._registry.get(sourceAtom);
|
|
331
|
-
if (!source.outbound.includes(edgeArg.target)) {
|
|
332
|
-
log("add outbound edge", {
|
|
333
|
-
source: edgeArg.source,
|
|
334
|
-
target: edgeArg.target
|
|
335
|
-
}, {
|
|
336
|
-
F: __dxlog_file,
|
|
337
|
-
L: 505,
|
|
338
|
-
S: this,
|
|
339
|
-
C: (f, a) => f(...a)
|
|
340
|
-
});
|
|
341
|
-
this._registry.set(sourceAtom, {
|
|
342
|
-
inbound: source.inbound,
|
|
343
|
-
outbound: [
|
|
344
|
-
...source.outbound,
|
|
345
|
-
edgeArg.target
|
|
346
|
-
]
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
const targetAtom = this._edges(edgeArg.target);
|
|
350
|
-
const target = this._registry.get(targetAtom);
|
|
351
|
-
if (!target.inbound.includes(edgeArg.source)) {
|
|
352
|
-
log("add inbound edge", {
|
|
353
|
-
source: edgeArg.source,
|
|
354
|
-
target: edgeArg.target
|
|
355
|
-
}, {
|
|
356
|
-
F: __dxlog_file,
|
|
357
|
-
L: 518,
|
|
358
|
-
S: this,
|
|
359
|
-
C: (f, a) => f(...a)
|
|
360
|
-
});
|
|
361
|
-
this._registry.set(targetAtom, {
|
|
362
|
-
inbound: [
|
|
363
|
-
...target.inbound,
|
|
364
|
-
edgeArg.source
|
|
365
|
-
],
|
|
366
|
-
outbound: target.outbound
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
removeEdges(edges, removeOrphans = false) {
|
|
371
|
-
Atom.batch(() => {
|
|
372
|
-
edges.map((edge) => this.removeEdge(edge, removeOrphans));
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
removeEdge(edgeArg, removeOrphans = false) {
|
|
376
|
-
const sourceAtom = this._edges(edgeArg.source);
|
|
377
|
-
const source = this._registry.get(sourceAtom);
|
|
378
|
-
if (source.outbound.includes(edgeArg.target)) {
|
|
379
|
-
this._registry.set(sourceAtom, {
|
|
380
|
-
inbound: source.inbound,
|
|
381
|
-
outbound: source.outbound.filter((id) => id !== edgeArg.target)
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
const targetAtom = this._edges(edgeArg.target);
|
|
385
|
-
const target = this._registry.get(targetAtom);
|
|
386
|
-
if (target.inbound.includes(edgeArg.source)) {
|
|
387
|
-
this._registry.set(targetAtom, {
|
|
388
|
-
inbound: target.inbound.filter((id) => id !== edgeArg.source),
|
|
389
|
-
outbound: target.outbound
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
if (removeOrphans) {
|
|
393
|
-
const source2 = this._registry.get(sourceAtom);
|
|
394
|
-
const target2 = this._registry.get(targetAtom);
|
|
395
|
-
if (source2.outbound.length === 0 && source2.inbound.length === 0 && edgeArg.source !== ROOT_ID) {
|
|
396
|
-
this.removeNodes([
|
|
397
|
-
edgeArg.source
|
|
398
|
-
]);
|
|
399
|
-
}
|
|
400
|
-
if (target2.outbound.length === 0 && target2.inbound.length === 0 && edgeArg.target !== ROOT_ID) {
|
|
401
|
-
this.removeNodes([
|
|
402
|
-
edgeArg.target
|
|
403
|
-
]);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
sortEdges(id, relation, order) {
|
|
408
|
-
const edgesAtom = this._edges(id);
|
|
409
|
-
const edges = this._registry.get(edgesAtom);
|
|
410
|
-
const unsorted = edges[relation].filter((id2) => !order.includes(id2)) ?? [];
|
|
411
|
-
const sorted = order.filter((id2) => edges[relation].includes(id2)) ?? [];
|
|
412
|
-
edges[relation].splice(0, edges[relation].length, ...[
|
|
413
|
-
...sorted,
|
|
414
|
-
...unsorted
|
|
415
|
-
]);
|
|
416
|
-
this._registry.set(edgesAtom, edges);
|
|
417
|
-
}
|
|
418
|
-
traverse({ visitor, source = ROOT_ID, relation = "outbound" }, path = []) {
|
|
419
|
-
if (path.includes(source)) {
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
const node = this.getNodeOrThrow(source);
|
|
423
|
-
const shouldContinue = visitor(node, [
|
|
424
|
-
...path,
|
|
425
|
-
source
|
|
426
|
-
]);
|
|
427
|
-
if (shouldContinue === false) {
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
Object.values(this.getConnections(source, relation)).forEach((child) => this.traverse({
|
|
431
|
-
source: child.id,
|
|
432
|
-
relation,
|
|
433
|
-
visitor
|
|
434
|
-
}, [
|
|
435
|
-
...path,
|
|
436
|
-
source
|
|
437
|
-
]));
|
|
438
|
-
}
|
|
439
|
-
getPath({ source = "root", target }) {
|
|
440
|
-
return Function.pipe(this.getNode(source), Option.flatMap((node) => {
|
|
441
|
-
let found = Option.none();
|
|
442
|
-
this.traverse({
|
|
443
|
-
source: node.id,
|
|
444
|
-
visitor: (node2, path) => {
|
|
445
|
-
if (Option.isSome(found)) {
|
|
446
|
-
return false;
|
|
447
|
-
}
|
|
448
|
-
if (node2.id === target) {
|
|
449
|
-
found = Option.some(path);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
return found;
|
|
454
|
-
}));
|
|
455
|
-
}
|
|
456
|
-
async waitForPath(params, { timeout = 5e3, interval = 500 } = {}) {
|
|
457
|
-
const path = this.getPath(params);
|
|
458
|
-
if (Option.isSome(path)) {
|
|
459
|
-
return path.value;
|
|
460
|
-
}
|
|
461
|
-
const trigger = new Trigger();
|
|
462
|
-
const i = setInterval(() => {
|
|
463
|
-
const path2 = this.getPath(params);
|
|
464
|
-
if (Option.isSome(path2)) {
|
|
465
|
-
trigger.wake(path2.value);
|
|
466
|
-
}
|
|
467
|
-
}, interval);
|
|
468
|
-
return trigger.wait({
|
|
469
|
-
timeout
|
|
470
|
-
}).finally(() => clearInterval(i));
|
|
471
|
-
}
|
|
472
|
-
/** @internal */
|
|
473
|
-
_constructNode(node) {
|
|
474
|
-
return Option.some({
|
|
475
|
-
[graphSymbol]: this,
|
|
476
|
-
data: null,
|
|
477
|
-
properties: {},
|
|
478
|
-
...node
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
// src/graph-builder.ts
|
|
484
|
-
import { Atom as Atom2, Registry as Registry2 } from "@effect-atom/atom-react";
|
|
485
|
-
import { effect } from "@preact/signals-core";
|
|
486
|
-
import * as Array from "effect/Array";
|
|
487
|
-
import * as Function2 from "effect/Function";
|
|
488
|
-
import * as Option2 from "effect/Option";
|
|
489
|
-
import * as Record2 from "effect/Record";
|
|
490
|
-
import { log as log2 } from "@dxos/log";
|
|
491
|
-
import { byPosition, getDebugName, isNode, isNonNullable as isNonNullable2 } from "@dxos/util";
|
|
1
|
+
import {
|
|
2
|
+
__export,
|
|
3
|
+
getParentId,
|
|
4
|
+
getSegmentId,
|
|
5
|
+
graph_builder_exports,
|
|
6
|
+
graph_exports,
|
|
7
|
+
node_exports,
|
|
8
|
+
node_matcher_exports,
|
|
9
|
+
qualifyId
|
|
10
|
+
} from "./chunk-AKBGYELG.mjs";
|
|
492
11
|
|
|
493
|
-
// src/
|
|
494
|
-
var
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const { id, position = "static", relation = "outbound", resolver: _resolver, connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
|
|
504
|
-
const getId = (key) => `${id}/${key}`;
|
|
505
|
-
const resolver = _resolver && Atom2.family((id2) => _resolver(id2).pipe(Atom2.withLabel(`graph-builder:_resolver:${id2}`)));
|
|
506
|
-
const connector = _connector && Atom2.family((node) => _connector(node).pipe(Atom2.withLabel(`graph-builder:_connector:${id}`)));
|
|
507
|
-
const actionGroups = _actionGroups && Atom2.family((node) => _actionGroups(node).pipe(Atom2.withLabel(`graph-builder:_actionGroups:${id}`)));
|
|
508
|
-
const actions = _actions && Atom2.family((node) => _actions(node).pipe(Atom2.withLabel(`graph-builder:_actions:${id}`)));
|
|
509
|
-
return [
|
|
510
|
-
resolver ? {
|
|
511
|
-
id: getId("resolver"),
|
|
512
|
-
position,
|
|
513
|
-
resolver
|
|
514
|
-
} : void 0,
|
|
515
|
-
connector ? {
|
|
516
|
-
id: getId("connector"),
|
|
517
|
-
position,
|
|
518
|
-
relation,
|
|
519
|
-
connector: Atom2.family((node) => Atom2.make((get2) => {
|
|
520
|
-
try {
|
|
521
|
-
return get2(connector(node));
|
|
522
|
-
} catch {
|
|
523
|
-
log2.warn("Error in connector", {
|
|
524
|
-
id: getId("connector"),
|
|
525
|
-
node
|
|
526
|
-
}, {
|
|
527
|
-
F: __dxlog_file2,
|
|
528
|
-
L: 114,
|
|
529
|
-
S: void 0,
|
|
530
|
-
C: (f, a) => f(...a)
|
|
531
|
-
});
|
|
532
|
-
return [];
|
|
533
|
-
}
|
|
534
|
-
}).pipe(Atom2.withLabel(`graph-builder:connector:${id}`)))
|
|
535
|
-
} : void 0,
|
|
536
|
-
actionGroups ? {
|
|
537
|
-
id: getId("actionGroups"),
|
|
538
|
-
position,
|
|
539
|
-
relation: "outbound",
|
|
540
|
-
connector: Atom2.family((node) => Atom2.make((get2) => {
|
|
541
|
-
try {
|
|
542
|
-
return get2(actionGroups(node)).map((arg) => ({
|
|
543
|
-
...arg,
|
|
544
|
-
data: actionGroupSymbol,
|
|
545
|
-
type: ACTION_GROUP_TYPE
|
|
546
|
-
}));
|
|
547
|
-
} catch {
|
|
548
|
-
log2.warn("Error in actionGroups", {
|
|
549
|
-
id: getId("actionGroups"),
|
|
550
|
-
node
|
|
551
|
-
}, {
|
|
552
|
-
F: __dxlog_file2,
|
|
553
|
-
L: 135,
|
|
554
|
-
S: void 0,
|
|
555
|
-
C: (f, a) => f(...a)
|
|
556
|
-
});
|
|
557
|
-
return [];
|
|
558
|
-
}
|
|
559
|
-
}).pipe(Atom2.withLabel(`graph-builder:connector:actionGroups:${id}`)))
|
|
560
|
-
} : void 0,
|
|
561
|
-
actions ? {
|
|
562
|
-
id: getId("actions"),
|
|
563
|
-
position,
|
|
564
|
-
relation: "outbound",
|
|
565
|
-
connector: Atom2.family((node) => Atom2.make((get2) => {
|
|
566
|
-
try {
|
|
567
|
-
return get2(actions(node)).map((arg) => ({
|
|
568
|
-
...arg,
|
|
569
|
-
type: ACTION_TYPE
|
|
570
|
-
}));
|
|
571
|
-
} catch {
|
|
572
|
-
log2.warn("Error in actions", {
|
|
573
|
-
id: getId("actions"),
|
|
574
|
-
node
|
|
575
|
-
}, {
|
|
576
|
-
F: __dxlog_file2,
|
|
577
|
-
L: 152,
|
|
578
|
-
S: void 0,
|
|
579
|
-
C: (f, a) => f(...a)
|
|
580
|
-
});
|
|
581
|
-
return [];
|
|
582
|
-
}
|
|
583
|
-
}).pipe(Atom2.withLabel(`graph-builder:connector:actions:${id}`)))
|
|
584
|
-
} : void 0
|
|
585
|
-
].filter(isNonNullable2);
|
|
586
|
-
};
|
|
587
|
-
var flattenExtensions = (extension, acc = []) => {
|
|
588
|
-
if (Array.isArray(extension)) {
|
|
589
|
-
return [
|
|
590
|
-
...acc,
|
|
591
|
-
...extension.flatMap((ext) => flattenExtensions(ext, acc))
|
|
592
|
-
];
|
|
593
|
-
} else {
|
|
594
|
-
return [
|
|
595
|
-
...acc,
|
|
596
|
-
extension
|
|
597
|
-
];
|
|
598
|
-
}
|
|
599
|
-
};
|
|
600
|
-
var GraphBuilder = class _GraphBuilder {
|
|
601
|
-
// TODO(wittjosiah): Use Context.
|
|
602
|
-
_subscriptions = /* @__PURE__ */ new Map();
|
|
603
|
-
_extensions = Atom2.make(Record2.empty()).pipe(Atom2.keepAlive, Atom2.withLabel("graph-builder:extensions"));
|
|
604
|
-
_initialized = {};
|
|
605
|
-
_registry;
|
|
606
|
-
_graph;
|
|
607
|
-
constructor({ registry, ...params } = {}) {
|
|
608
|
-
this._registry = registry ?? Registry2.make();
|
|
609
|
-
this._graph = new Graph({
|
|
610
|
-
...params,
|
|
611
|
-
registry: this._registry,
|
|
612
|
-
onExpand: (id, relation) => this._onExpand(id, relation),
|
|
613
|
-
onInitialize: (id) => this._onInitialize(id),
|
|
614
|
-
onRemoveNode: (id) => this._onRemoveNode(id)
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
static from(pickle, registry) {
|
|
618
|
-
if (!pickle) {
|
|
619
|
-
return new _GraphBuilder({
|
|
620
|
-
registry
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
const { nodes, edges } = JSON.parse(pickle);
|
|
624
|
-
return new _GraphBuilder({
|
|
625
|
-
nodes,
|
|
626
|
-
edges,
|
|
627
|
-
registry
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
get graph() {
|
|
631
|
-
return this._graph;
|
|
632
|
-
}
|
|
633
|
-
get extensions() {
|
|
634
|
-
return this._extensions;
|
|
635
|
-
}
|
|
636
|
-
addExtension(extensions) {
|
|
637
|
-
flattenExtensions(extensions).forEach((extension) => {
|
|
638
|
-
const extensions2 = this._registry.get(this._extensions);
|
|
639
|
-
this._registry.set(this._extensions, Record2.set(extensions2, extension.id, extension));
|
|
640
|
-
});
|
|
641
|
-
return this;
|
|
642
|
-
}
|
|
643
|
-
removeExtension(id) {
|
|
644
|
-
const extensions = this._registry.get(this._extensions);
|
|
645
|
-
this._registry.set(this._extensions, Record2.remove(extensions, id));
|
|
646
|
-
return this;
|
|
647
|
-
}
|
|
648
|
-
async explore({ registry = Registry2.make(), source = ROOT_ID, relation = "outbound", visitor }, path = []) {
|
|
649
|
-
if (path.includes(source)) {
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
if (!isNode()) {
|
|
653
|
-
const { yieldOrContinue } = await import("main-thread-scheduling");
|
|
654
|
-
await yieldOrContinue("idle");
|
|
655
|
-
}
|
|
656
|
-
const node = registry.get(this._graph.nodeOrThrow(source));
|
|
657
|
-
const shouldContinue = await visitor(node, [
|
|
658
|
-
...path,
|
|
659
|
-
node.id
|
|
660
|
-
]);
|
|
661
|
-
if (shouldContinue === false) {
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
const nodes = Object.values(this._registry.get(this._extensions)).filter((extension) => relation === (extension.relation ?? "outbound")).map((extension) => extension.connector).filter(isNonNullable2).flatMap((connector) => registry.get(connector(this._graph.node(source))));
|
|
665
|
-
await Promise.all(nodes.map((nodeArg) => {
|
|
666
|
-
registry.set(this._graph._node(nodeArg.id), this._graph._constructNode(nodeArg));
|
|
667
|
-
return this.explore({
|
|
668
|
-
registry,
|
|
669
|
-
source: nodeArg.id,
|
|
670
|
-
relation,
|
|
671
|
-
visitor
|
|
672
|
-
}, [
|
|
673
|
-
...path,
|
|
674
|
-
node.id
|
|
675
|
-
]);
|
|
676
|
-
}));
|
|
677
|
-
if (registry !== this._registry) {
|
|
678
|
-
registry.reset();
|
|
679
|
-
registry.dispose();
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
destroy() {
|
|
683
|
-
this._subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
684
|
-
this._subscriptions.clear();
|
|
685
|
-
}
|
|
686
|
-
_resolvers = Atom2.family((id) => {
|
|
687
|
-
return Atom2.make((get2) => {
|
|
688
|
-
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);
|
|
689
|
-
});
|
|
690
|
-
});
|
|
691
|
-
_connectors = Atom2.family((key) => {
|
|
692
|
-
return Atom2.make((get2) => {
|
|
693
|
-
const [id, relation] = key.split("+");
|
|
694
|
-
const node = this._graph.node(id);
|
|
695
|
-
return Function2.pipe(
|
|
696
|
-
get2(this._extensions),
|
|
697
|
-
Record2.values,
|
|
698
|
-
// TODO(wittjosiah): Sort on write rather than read.
|
|
699
|
-
Array.sortBy(byPosition),
|
|
700
|
-
Array.filter(({ relation: _relation = "outbound" }) => _relation === relation),
|
|
701
|
-
Array.map(({ connector }) => connector?.(node)),
|
|
702
|
-
Array.filter(isNonNullable2),
|
|
703
|
-
Array.flatMap((result) => get2(result))
|
|
704
|
-
);
|
|
705
|
-
}).pipe(Atom2.withLabel(`graph-builder:connectors:${key}`));
|
|
706
|
-
});
|
|
707
|
-
_onExpand(id, relation) {
|
|
708
|
-
log2("onExpand", {
|
|
709
|
-
id,
|
|
710
|
-
relation,
|
|
711
|
-
registry: getDebugName(this._registry)
|
|
712
|
-
}, {
|
|
713
|
-
F: __dxlog_file2,
|
|
714
|
-
L: 329,
|
|
715
|
-
S: this,
|
|
716
|
-
C: (f, a) => f(...a)
|
|
717
|
-
});
|
|
718
|
-
const connectors = this._connectors(`${id}+${relation}`);
|
|
719
|
-
let previous = [];
|
|
720
|
-
const cancel = this._registry.subscribe(connectors, (nodes) => {
|
|
721
|
-
const ids = nodes.map((n) => n.id);
|
|
722
|
-
const removed = previous.filter((id2) => !ids.includes(id2));
|
|
723
|
-
previous = ids;
|
|
724
|
-
log2("update", {
|
|
725
|
-
id,
|
|
726
|
-
relation,
|
|
727
|
-
ids,
|
|
728
|
-
removed
|
|
729
|
-
}, {
|
|
730
|
-
F: __dxlog_file2,
|
|
731
|
-
L: 340,
|
|
732
|
-
S: this,
|
|
733
|
-
C: (f, a) => f(...a)
|
|
734
|
-
});
|
|
735
|
-
const update = () => {
|
|
736
|
-
Atom2.batch(() => {
|
|
737
|
-
this._graph.removeEdges(removed.map((target) => ({
|
|
738
|
-
source: id,
|
|
739
|
-
target
|
|
740
|
-
})), true);
|
|
741
|
-
this._graph.addNodes(nodes);
|
|
742
|
-
this._graph.addEdges(nodes.map((node) => relation === "outbound" ? {
|
|
743
|
-
source: id,
|
|
744
|
-
target: node.id
|
|
745
|
-
} : {
|
|
746
|
-
source: node.id,
|
|
747
|
-
target: id
|
|
748
|
-
}));
|
|
749
|
-
this._graph.sortEdges(id, relation, nodes.map(({ id: id2 }) => id2));
|
|
750
|
-
});
|
|
751
|
-
};
|
|
752
|
-
if (typeof requestAnimationFrame === "function") {
|
|
753
|
-
requestAnimationFrame(update);
|
|
754
|
-
} else {
|
|
755
|
-
update();
|
|
756
|
-
}
|
|
757
|
-
}, {
|
|
758
|
-
immediate: true
|
|
759
|
-
});
|
|
760
|
-
this._subscriptions.set(id, cancel);
|
|
761
|
-
}
|
|
762
|
-
// TODO(wittjosiah): If the same node is added by a connector, the resolver should probably cancel itself?
|
|
763
|
-
async _onInitialize(id) {
|
|
764
|
-
log2("onInitialize", {
|
|
765
|
-
id
|
|
766
|
-
}, {
|
|
767
|
-
F: __dxlog_file2,
|
|
768
|
-
L: 377,
|
|
769
|
-
S: this,
|
|
770
|
-
C: (f, a) => f(...a)
|
|
771
|
-
});
|
|
772
|
-
const resolver = this._resolvers(id);
|
|
773
|
-
const cancel = this._registry.subscribe(resolver, (node) => {
|
|
774
|
-
const trigger = this._initialized[id];
|
|
775
|
-
Option2.match(node, {
|
|
776
|
-
onSome: (node2) => {
|
|
777
|
-
this._graph.addNodes([
|
|
778
|
-
node2
|
|
779
|
-
]);
|
|
780
|
-
trigger?.wake();
|
|
781
|
-
},
|
|
782
|
-
onNone: () => {
|
|
783
|
-
trigger?.wake();
|
|
784
|
-
this._graph.removeNodes([
|
|
785
|
-
id
|
|
786
|
-
]);
|
|
787
|
-
}
|
|
788
|
-
});
|
|
789
|
-
}, {
|
|
790
|
-
immediate: true
|
|
791
|
-
});
|
|
792
|
-
this._subscriptions.set(id, cancel);
|
|
793
|
-
}
|
|
794
|
-
_onRemoveNode(id) {
|
|
795
|
-
this._subscriptions.get(id)?.();
|
|
796
|
-
this._subscriptions.delete(id);
|
|
797
|
-
}
|
|
798
|
-
};
|
|
799
|
-
var atomFromSignal = (cb) => {
|
|
800
|
-
return Atom2.make((get2) => {
|
|
801
|
-
const dispose = effect(() => {
|
|
802
|
-
get2.setSelf(cb());
|
|
803
|
-
});
|
|
804
|
-
get2.addFinalizer(() => dispose());
|
|
805
|
-
return cb();
|
|
806
|
-
});
|
|
807
|
-
};
|
|
808
|
-
var observableFamily = Atom2.family((observable) => {
|
|
809
|
-
return Atom2.make((get2) => {
|
|
810
|
-
const subscription = observable.subscribe((value) => get2.setSelf(value));
|
|
811
|
-
get2.addFinalizer(() => subscription.unsubscribe());
|
|
12
|
+
// src/atoms.ts
|
|
13
|
+
var atoms_exports = {};
|
|
14
|
+
__export(atoms_exports, {
|
|
15
|
+
fromObservable: () => fromObservable
|
|
16
|
+
});
|
|
17
|
+
import { Atom } from "@effect-atom/atom-react";
|
|
18
|
+
var observableFamily = Atom.family((observable) => {
|
|
19
|
+
return Atom.make((get) => {
|
|
20
|
+
const subscription = observable.subscribe((value) => get.setSelf(value));
|
|
21
|
+
get.addFinalizer(() => subscription.unsubscribe());
|
|
812
22
|
return observable.get();
|
|
813
23
|
});
|
|
814
24
|
});
|
|
815
|
-
var
|
|
25
|
+
var fromObservable = (observable) => {
|
|
816
26
|
return observableFamily(observable);
|
|
817
27
|
};
|
|
818
28
|
export {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
atomFromSignal,
|
|
828
|
-
createExtension,
|
|
829
|
-
flattenExtensions,
|
|
830
|
-
getGraph,
|
|
831
|
-
isAction,
|
|
832
|
-
isActionGroup,
|
|
833
|
-
isActionLike,
|
|
834
|
-
isGraphNode
|
|
29
|
+
atoms_exports as CreateAtom,
|
|
30
|
+
graph_exports as Graph,
|
|
31
|
+
graph_builder_exports as GraphBuilder,
|
|
32
|
+
node_exports as Node,
|
|
33
|
+
node_matcher_exports as NodeMatcher,
|
|
34
|
+
getParentId,
|
|
35
|
+
getSegmentId,
|
|
36
|
+
qualifyId
|
|
835
37
|
};
|
|
836
38
|
//# sourceMappingURL=index.mjs.map
|