@dxos/app-graph 0.8.4-main.c4373fc → 0.8.4-main.d05673bc65
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 +1416 -695
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +1415 -695
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- 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 +112 -66
- package/dist/types/src/graph-builder.d.ts.map +1 -1
- package/dist/types/src/graph.d.ts +187 -221
- package/dist/types/src/graph.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +6 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/node-matcher.d.ts +218 -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 +44 -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/util.d.ts +34 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +36 -34
- package/src/atoms.ts +25 -0
- package/src/graph-builder.test.ts +895 -137
- package/src/graph-builder.ts +703 -289
- package/src/graph.test.ts +450 -122
- package/src/graph.ts +1053 -402
- package/src/index.ts +9 -3
- package/src/node-matcher.test.ts +301 -0
- package/src/node-matcher.ts +282 -0
- package/src/node.ts +56 -8
- package/src/stories/EchoGraph.stories.tsx +159 -120
- package/src/stories/Tree.tsx +1 -1
- package/src/util.ts +71 -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 -218
- package/src/testing.ts +0 -20
|
@@ -1,33 +1,149 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/atoms.ts
|
|
8
|
+
var atoms_exports = {};
|
|
9
|
+
__export(atoms_exports, {
|
|
10
|
+
fromObservable: () => fromObservable
|
|
11
|
+
});
|
|
12
|
+
import { Atom } from "@effect-atom/atom-react";
|
|
13
|
+
var observableFamily = Atom.family((observable) => {
|
|
14
|
+
return Atom.make((get2) => {
|
|
15
|
+
const subscription = observable.subscribe((value) => get2.setSelf(value));
|
|
16
|
+
get2.addFinalizer(() => subscription.unsubscribe());
|
|
17
|
+
return observable.get();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
var fromObservable = (observable) => {
|
|
21
|
+
return observableFamily(observable);
|
|
22
|
+
};
|
|
23
|
+
|
|
1
24
|
// src/graph.ts
|
|
2
|
-
|
|
25
|
+
var graph_exports = {};
|
|
26
|
+
__export(graph_exports, {
|
|
27
|
+
GraphKind: () => GraphKind,
|
|
28
|
+
GraphTypeId: () => GraphTypeId,
|
|
29
|
+
addEdge: () => addEdge,
|
|
30
|
+
addEdges: () => addEdges,
|
|
31
|
+
addNode: () => addNode,
|
|
32
|
+
addNodes: () => addNodes,
|
|
33
|
+
expand: () => expand,
|
|
34
|
+
getActions: () => getActions,
|
|
35
|
+
getConnections: () => getConnections,
|
|
36
|
+
getEdges: () => getEdges,
|
|
37
|
+
getGraph: () => getGraph,
|
|
38
|
+
getNode: () => getNode,
|
|
39
|
+
getNodeOrThrow: () => getNodeOrThrow,
|
|
40
|
+
getPath: () => getPath,
|
|
41
|
+
getRoot: () => getRoot,
|
|
42
|
+
initialize: () => initialize,
|
|
43
|
+
make: () => make,
|
|
44
|
+
relationFromKey: () => relationFromKey,
|
|
45
|
+
relationKey: () => relationKey,
|
|
46
|
+
removeEdge: () => removeEdge,
|
|
47
|
+
removeEdges: () => removeEdges,
|
|
48
|
+
removeNode: () => removeNode,
|
|
49
|
+
removeNodes: () => removeNodes,
|
|
50
|
+
sortEdges: () => sortEdges,
|
|
51
|
+
toJSON: () => toJSON,
|
|
52
|
+
traverse: () => traverse,
|
|
53
|
+
waitForPath: () => waitForPath
|
|
54
|
+
});
|
|
55
|
+
import { Atom as Atom2, Registry } from "@effect-atom/atom-react";
|
|
3
56
|
import * as Function from "effect/Function";
|
|
4
57
|
import * as Option from "effect/Option";
|
|
58
|
+
import * as Pipeable from "effect/Pipeable";
|
|
5
59
|
import * as Record from "effect/Record";
|
|
6
60
|
import { Event, Trigger } from "@dxos/async";
|
|
7
61
|
import { todo } from "@dxos/debug";
|
|
8
|
-
import { invariant } from "@dxos/invariant";
|
|
62
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
9
63
|
import { log } from "@dxos/log";
|
|
10
64
|
import { isNonNullable } from "@dxos/util";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
65
|
+
|
|
66
|
+
// src/node.ts
|
|
67
|
+
var node_exports = {};
|
|
68
|
+
__export(node_exports, {
|
|
69
|
+
ActionGroupType: () => ActionGroupType,
|
|
70
|
+
ActionType: () => ActionType,
|
|
71
|
+
RootId: () => RootId,
|
|
72
|
+
RootType: () => RootType,
|
|
73
|
+
actionGroupSymbol: () => actionGroupSymbol,
|
|
74
|
+
actionRelation: () => actionRelation,
|
|
75
|
+
childRelation: () => childRelation,
|
|
76
|
+
isAction: () => isAction,
|
|
77
|
+
isActionGroup: () => isActionGroup,
|
|
78
|
+
isActionLike: () => isActionLike,
|
|
79
|
+
isGraphNode: () => isGraphNode,
|
|
80
|
+
relation: () => relation
|
|
81
|
+
});
|
|
82
|
+
var RootId = "root";
|
|
83
|
+
var RootType = "org.dxos.type.graph-root";
|
|
84
|
+
var ActionType = "org.dxos.type.graph-action";
|
|
85
|
+
var ActionGroupType = "org.dxos.type.graph-action-group";
|
|
86
|
+
var relation = (kind, direction = "outbound") => ({
|
|
87
|
+
kind,
|
|
88
|
+
direction
|
|
89
|
+
});
|
|
90
|
+
var childRelation = (direction = "outbound") => relation("child", direction);
|
|
91
|
+
var actionRelation = (direction = "outbound") => relation("action", direction);
|
|
92
|
+
var isGraphNode = (data) => data && typeof data === "object" && "id" in data && "properties" in data && data.properties ? typeof data.properties === "object" && "data" in data : false;
|
|
93
|
+
var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" && data.type === ActionType : false;
|
|
94
|
+
var actionGroupSymbol = /* @__PURE__ */ Symbol("ActionGroup");
|
|
95
|
+
var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol && data.type === ActionGroupType : false;
|
|
96
|
+
var isActionLike = (data) => isAction(data) || isActionGroup(data);
|
|
97
|
+
|
|
98
|
+
// src/util.ts
|
|
99
|
+
import { invariant } from "@dxos/invariant";
|
|
100
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-graph/src/util.ts";
|
|
101
|
+
var Separators = {
|
|
102
|
+
primary: "",
|
|
103
|
+
secondary: "",
|
|
104
|
+
path: "/"
|
|
105
|
+
};
|
|
106
|
+
var normalizeRelation = (relation2) => relation2 == null ? childRelation() : typeof relation2 === "string" ? relation(relation2) : relation2;
|
|
107
|
+
var shallowEqual = (a, b) => {
|
|
108
|
+
if (a === b) return true;
|
|
109
|
+
if (a == null || b == null || typeof a !== "object" || typeof b !== "object") return false;
|
|
110
|
+
const keysA = Object.keys(a);
|
|
111
|
+
const keysB = Object.keys(b);
|
|
112
|
+
if (keysA.length !== keysB.length) {
|
|
113
|
+
return false;
|
|
21
114
|
}
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
var
|
|
25
|
-
|
|
115
|
+
return keysA.every((k) => a[k] === b[k]);
|
|
116
|
+
};
|
|
117
|
+
var nodeArgsUnchanged = (prev, next) => {
|
|
118
|
+
if (prev.length !== next.length) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return prev.every((prevNode, idx) => {
|
|
122
|
+
const nextNode = next[idx];
|
|
123
|
+
return prevNode.id === nextNode.id && prevNode.type === nextNode.type && shallowEqual(prevNode.data, nextNode.data) && shallowEqual(prevNode.properties, nextNode.properties);
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
var qualifyId = (parentId, segmentId) => `${parentId}${Separators.path}${segmentId}`;
|
|
127
|
+
var validateSegmentId = (id) => {
|
|
128
|
+
invariant(!id.includes(Separators.path), `Node segment ID must not contain '${Separators.path}': ${id}`, {
|
|
129
|
+
F: __dxlog_file,
|
|
130
|
+
L: 70,
|
|
131
|
+
S: void 0,
|
|
132
|
+
A: [
|
|
133
|
+
"!id.includes(Separators.path)",
|
|
134
|
+
"`Node segment ID must not contain '${Separators.path}': ${id}`"
|
|
135
|
+
]
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// src/graph.ts
|
|
140
|
+
var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
|
|
141
|
+
var graphSymbol = /* @__PURE__ */ Symbol("graph");
|
|
26
142
|
var getGraph = (node) => {
|
|
27
143
|
const graph = node[graphSymbol];
|
|
28
|
-
|
|
29
|
-
F:
|
|
30
|
-
L:
|
|
144
|
+
invariant2(graph, "Node is not associated with a graph.", {
|
|
145
|
+
F: __dxlog_file2,
|
|
146
|
+
L: 33,
|
|
31
147
|
S: void 0,
|
|
32
148
|
A: [
|
|
33
149
|
"graph",
|
|
@@ -36,499 +152,1261 @@ var getGraph = (node) => {
|
|
|
36
152
|
});
|
|
37
153
|
return graph;
|
|
38
154
|
};
|
|
39
|
-
var
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return
|
|
155
|
+
var GraphTypeId = /* @__PURE__ */ Symbol.for("@dxos/app-graph/Graph");
|
|
156
|
+
var GraphKind = /* @__PURE__ */ Symbol.for("@dxos/app-graph/GraphKind");
|
|
157
|
+
var GraphImpl = class {
|
|
158
|
+
[GraphTypeId] = GraphTypeId;
|
|
159
|
+
[GraphKind] = "writable";
|
|
160
|
+
pipe() {
|
|
161
|
+
return Pipeable.pipeArguments(this, arguments);
|
|
46
162
|
}
|
|
47
|
-
|
|
48
|
-
|
|
163
|
+
onNodeChanged = new Event();
|
|
164
|
+
_onExpand;
|
|
165
|
+
_onInitialize;
|
|
166
|
+
_onRemoveNode;
|
|
167
|
+
_registry;
|
|
168
|
+
_expanded = Record.empty();
|
|
169
|
+
_pendingExpands = /* @__PURE__ */ new Set();
|
|
170
|
+
_initialized = Record.empty();
|
|
171
|
+
_initialEdges = Record.empty();
|
|
172
|
+
_initialNodes = Record.fromEntries([
|
|
173
|
+
[
|
|
174
|
+
RootId,
|
|
175
|
+
this._constructNode({
|
|
176
|
+
id: RootId,
|
|
177
|
+
type: RootType,
|
|
178
|
+
data: null,
|
|
179
|
+
properties: {}
|
|
180
|
+
})
|
|
181
|
+
]
|
|
182
|
+
]);
|
|
183
|
+
/** @internal */
|
|
184
|
+
_node = Atom2.family((id) => {
|
|
185
|
+
const initial = Option.flatten(Record.get(this._initialNodes, id));
|
|
186
|
+
return Atom2.make(initial).pipe(Atom2.keepAlive, Atom2.withLabel(`graph:node:${id}`));
|
|
187
|
+
});
|
|
188
|
+
_nodeOrThrow = Atom2.family((id) => {
|
|
189
|
+
return Atom2.make((get2) => {
|
|
190
|
+
const node = get2(this._node(id));
|
|
191
|
+
invariant2(Option.isSome(node), `Node not available: ${id}`, {
|
|
192
|
+
F: __dxlog_file2,
|
|
193
|
+
L: 172,
|
|
194
|
+
S: this,
|
|
195
|
+
A: [
|
|
196
|
+
"Option.isSome(node)",
|
|
197
|
+
"`Node not available: ${id}`"
|
|
198
|
+
]
|
|
199
|
+
});
|
|
200
|
+
return node.value;
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
_edges = Atom2.family((id) => {
|
|
204
|
+
const initial = Record.get(this._initialEdges, id).pipe(Option.getOrElse(() => ({})));
|
|
205
|
+
return Atom2.make(initial).pipe(Atom2.keepAlive, Atom2.withLabel(`graph:edges:${id}`));
|
|
206
|
+
});
|
|
207
|
+
// NOTE: Currently the argument to the family needs to be referentially stable for the atom to be referentially stable.
|
|
208
|
+
// TODO(wittjosiah): Atom feature request, support for something akin to `ComplexMap` to allow for complex arguments.
|
|
209
|
+
_connections = Atom2.family((key) => {
|
|
210
|
+
return Atom2.make((get2) => {
|
|
211
|
+
if (!key || key.indexOf(Separators.primary) <= 0) {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
const { id, relation: relation2 } = relationFromConnectionKey(key);
|
|
215
|
+
const edges = get2(this._edges(id));
|
|
216
|
+
return (edges[relationKey(relation2)] ?? []).map((id2) => get2(this._node(id2))).filter(Option.isSome).map((o) => o.value);
|
|
217
|
+
}).pipe(Atom2.withLabel(`graph:connections:${key}`));
|
|
218
|
+
});
|
|
219
|
+
_actions = Atom2.family((id) => {
|
|
220
|
+
return Atom2.make((get2) => {
|
|
221
|
+
if (!id) {
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
return get2(this._connections(connectionKey(id, actionRelation())));
|
|
225
|
+
}).pipe(Atom2.withLabel(`graph:actions:${id}`));
|
|
226
|
+
});
|
|
227
|
+
_json = Atom2.family((id) => {
|
|
228
|
+
return Atom2.make((get2) => {
|
|
229
|
+
const toJSON2 = (node, seen = []) => {
|
|
230
|
+
const nodes = get2(this._connections(connectionKey(node.id, "child")));
|
|
231
|
+
const obj = {
|
|
232
|
+
id: node.id,
|
|
233
|
+
type: node.type
|
|
234
|
+
};
|
|
235
|
+
if (node.properties.label) {
|
|
236
|
+
obj.label = node.properties.label;
|
|
237
|
+
}
|
|
238
|
+
if (nodes.length) {
|
|
239
|
+
obj.nodes = nodes.map((n) => {
|
|
240
|
+
const nextSeen = [
|
|
241
|
+
...seen,
|
|
242
|
+
node.id
|
|
243
|
+
];
|
|
244
|
+
return nextSeen.includes(n.id) ? void 0 : toJSON2(n, nextSeen);
|
|
245
|
+
}).filter(isNonNullable);
|
|
246
|
+
}
|
|
247
|
+
return obj;
|
|
248
|
+
};
|
|
249
|
+
const root = get2(this._nodeOrThrow(id));
|
|
250
|
+
return toJSON2(root);
|
|
251
|
+
}).pipe(Atom2.withLabel(`graph:json:${id}`));
|
|
252
|
+
});
|
|
253
|
+
constructor({ registry, nodes, edges, onInitialize, onExpand, onRemoveNode } = {}) {
|
|
254
|
+
this._registry = registry ?? Registry.make();
|
|
255
|
+
this._onInitialize = onInitialize;
|
|
256
|
+
this._onExpand = onExpand;
|
|
257
|
+
this._onRemoveNode = onRemoveNode;
|
|
258
|
+
if (nodes) {
|
|
259
|
+
nodes.forEach((node) => {
|
|
260
|
+
Record.set(this._initialNodes, node.id, this._constructNode(node));
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
if (edges) {
|
|
264
|
+
Object.entries(edges).forEach(([source, edges2]) => {
|
|
265
|
+
Record.set(this._initialEdges, source, edges2);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
json(id = RootId) {
|
|
270
|
+
return jsonImpl(this, id);
|
|
49
271
|
}
|
|
50
272
|
node(id) {
|
|
51
|
-
return this
|
|
273
|
+
return nodeImpl(this, id);
|
|
52
274
|
}
|
|
53
275
|
nodeOrThrow(id) {
|
|
54
|
-
return this
|
|
276
|
+
return nodeOrThrowImpl(this, id);
|
|
55
277
|
}
|
|
56
|
-
connections(id,
|
|
57
|
-
return this
|
|
278
|
+
connections(id, relation2) {
|
|
279
|
+
return connectionsImpl(this, id, relation2);
|
|
58
280
|
}
|
|
59
281
|
actions(id) {
|
|
60
|
-
return this
|
|
282
|
+
return actionsImpl(this, id);
|
|
61
283
|
}
|
|
62
284
|
edges(id) {
|
|
63
|
-
return this
|
|
285
|
+
return edgesImpl(this, id);
|
|
64
286
|
}
|
|
65
|
-
|
|
66
|
-
|
|
287
|
+
/** @internal */
|
|
288
|
+
_constructNode(node) {
|
|
289
|
+
return Option.some({
|
|
290
|
+
[graphSymbol]: this,
|
|
291
|
+
data: null,
|
|
292
|
+
properties: {},
|
|
293
|
+
...node
|
|
294
|
+
});
|
|
67
295
|
}
|
|
68
|
-
|
|
69
|
-
|
|
296
|
+
};
|
|
297
|
+
var getInternal = (graph) => {
|
|
298
|
+
return graph;
|
|
299
|
+
};
|
|
300
|
+
var toJSON = (graph, id = RootId) => {
|
|
301
|
+
const internal = getInternal(graph);
|
|
302
|
+
return internal._registry.get(internal._json(id));
|
|
303
|
+
};
|
|
304
|
+
var jsonImpl = (graph, id = RootId) => {
|
|
305
|
+
const internal = getInternal(graph);
|
|
306
|
+
return internal._json(id);
|
|
307
|
+
};
|
|
308
|
+
var nodeImpl = (graph, id) => {
|
|
309
|
+
const internal = getInternal(graph);
|
|
310
|
+
return internal._node(id);
|
|
311
|
+
};
|
|
312
|
+
var nodeOrThrowImpl = (graph, id) => {
|
|
313
|
+
const internal = getInternal(graph);
|
|
314
|
+
return internal._nodeOrThrow(id);
|
|
315
|
+
};
|
|
316
|
+
var connectionsImpl = (graph, id, relation2) => {
|
|
317
|
+
const internal = getInternal(graph);
|
|
318
|
+
return internal._connections(connectionKey(id, relation2));
|
|
319
|
+
};
|
|
320
|
+
var actionsImpl = (graph, id) => {
|
|
321
|
+
const internal = getInternal(graph);
|
|
322
|
+
return internal._actions(id);
|
|
323
|
+
};
|
|
324
|
+
var edgesImpl = (graph, id) => {
|
|
325
|
+
const internal = getInternal(graph);
|
|
326
|
+
return internal._edges(id);
|
|
327
|
+
};
|
|
328
|
+
var getNodeImpl = (graph, id) => {
|
|
329
|
+
const internal = getInternal(graph);
|
|
330
|
+
return internal._registry.get(nodeImpl(graph, id));
|
|
331
|
+
};
|
|
332
|
+
function getNode(graphOrId, id) {
|
|
333
|
+
if (typeof graphOrId === "string") {
|
|
334
|
+
const id2 = graphOrId;
|
|
335
|
+
return (graph) => getNodeImpl(graph, id2);
|
|
336
|
+
} else {
|
|
337
|
+
const graph = graphOrId;
|
|
338
|
+
return getNodeImpl(graph, id);
|
|
70
339
|
}
|
|
71
|
-
|
|
72
|
-
|
|
340
|
+
}
|
|
341
|
+
var getNodeOrThrowImpl = (graph, id) => {
|
|
342
|
+
const internal = getInternal(graph);
|
|
343
|
+
return internal._registry.get(nodeOrThrowImpl(graph, id));
|
|
344
|
+
};
|
|
345
|
+
function getNodeOrThrow(graphOrId, id) {
|
|
346
|
+
if (typeof graphOrId === "string") {
|
|
347
|
+
const id2 = graphOrId;
|
|
348
|
+
return (graph) => getNodeOrThrowImpl(graph, id2);
|
|
349
|
+
} else {
|
|
350
|
+
const graph = graphOrId;
|
|
351
|
+
return getNodeOrThrowImpl(graph, id);
|
|
73
352
|
}
|
|
74
|
-
|
|
75
|
-
|
|
353
|
+
}
|
|
354
|
+
function getRoot(graph) {
|
|
355
|
+
return getNodeOrThrowImpl(graph, RootId);
|
|
356
|
+
}
|
|
357
|
+
var getConnectionsImpl = (graph, id, relation2) => {
|
|
358
|
+
const internal = getInternal(graph);
|
|
359
|
+
return internal._registry.get(connectionsImpl(graph, id, relation2));
|
|
360
|
+
};
|
|
361
|
+
function getConnections(graphOrId, idOrRelation, relation2) {
|
|
362
|
+
if (typeof graphOrId === "string") {
|
|
363
|
+
const id = graphOrId;
|
|
364
|
+
const rel = idOrRelation;
|
|
365
|
+
return (graph) => getConnectionsImpl(graph, id, rel);
|
|
366
|
+
} else {
|
|
367
|
+
const graph = graphOrId;
|
|
368
|
+
const id = idOrRelation;
|
|
369
|
+
invariant2(relation2 !== void 0, "Relation is required.", {
|
|
370
|
+
F: __dxlog_file2,
|
|
371
|
+
L: 446,
|
|
372
|
+
S: this,
|
|
373
|
+
A: [
|
|
374
|
+
"relation !== undefined",
|
|
375
|
+
"'Relation is required.'"
|
|
376
|
+
]
|
|
377
|
+
});
|
|
378
|
+
const rel = relation2;
|
|
379
|
+
return getConnectionsImpl(graph, id, rel);
|
|
76
380
|
}
|
|
77
|
-
|
|
78
|
-
|
|
381
|
+
}
|
|
382
|
+
var getActionsImpl = (graph, id) => {
|
|
383
|
+
const internal = getInternal(graph);
|
|
384
|
+
return internal._registry.get(actionsImpl(graph, id));
|
|
385
|
+
};
|
|
386
|
+
function getActions(graphOrId, id) {
|
|
387
|
+
if (typeof graphOrId === "string") {
|
|
388
|
+
const id2 = graphOrId;
|
|
389
|
+
return (graph) => getActionsImpl(graph, id2);
|
|
390
|
+
} else {
|
|
391
|
+
const graph = graphOrId;
|
|
392
|
+
return getActionsImpl(graph, id);
|
|
79
393
|
}
|
|
80
|
-
|
|
81
|
-
|
|
394
|
+
}
|
|
395
|
+
var getEdgesImpl = (graph, id) => {
|
|
396
|
+
const internal = getInternal(graph);
|
|
397
|
+
return internal._registry.get(edgesImpl(graph, id));
|
|
398
|
+
};
|
|
399
|
+
function getEdges(graphOrId, id) {
|
|
400
|
+
if (typeof graphOrId === "string") {
|
|
401
|
+
const id2 = graphOrId;
|
|
402
|
+
return (graph) => getEdgesImpl(graph, id2);
|
|
403
|
+
} else {
|
|
404
|
+
const graph = graphOrId;
|
|
405
|
+
return getEdgesImpl(graph, id);
|
|
82
406
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
407
|
+
}
|
|
408
|
+
var traverseImpl = (graph, options, path = []) => {
|
|
409
|
+
const { visitor, source = RootId, relation: relation2 } = options;
|
|
410
|
+
if (path.includes(source)) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const node = getNodeOrThrow(graph, source);
|
|
414
|
+
const shouldContinue = visitor(node, [
|
|
415
|
+
...path,
|
|
416
|
+
source
|
|
417
|
+
]);
|
|
418
|
+
if (shouldContinue === false) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const relations = Array.isArray(relation2) ? relation2 : [
|
|
422
|
+
relation2
|
|
423
|
+
];
|
|
424
|
+
const seen = /* @__PURE__ */ new Set();
|
|
425
|
+
for (const rel of relations) {
|
|
426
|
+
for (const connected of getConnections(graph, source, rel)) {
|
|
427
|
+
if (!seen.has(connected.id)) {
|
|
428
|
+
seen.add(connected.id);
|
|
429
|
+
traverseImpl(graph, {
|
|
430
|
+
source: connected.id,
|
|
431
|
+
relation: relation2,
|
|
432
|
+
visitor
|
|
433
|
+
}, [
|
|
434
|
+
...path,
|
|
435
|
+
source
|
|
436
|
+
]);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
function traverse(graphOrOptions, optionsOrPath, path) {
|
|
442
|
+
if (typeof graphOrOptions === "object" && "visitor" in graphOrOptions) {
|
|
443
|
+
const options = graphOrOptions;
|
|
444
|
+
const pathArg = Array.isArray(optionsOrPath) ? optionsOrPath : void 0;
|
|
445
|
+
return (graph) => traverseImpl(graph, options, pathArg);
|
|
446
|
+
} else {
|
|
447
|
+
const graph = graphOrOptions;
|
|
448
|
+
const options = optionsOrPath;
|
|
449
|
+
const pathArg = path ?? (Array.isArray(optionsOrPath) ? optionsOrPath : void 0);
|
|
450
|
+
return traverseImpl(graph, options, pathArg);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
var getPathImpl = (graph, params) => {
|
|
454
|
+
return Function.pipe(getNode(graph, params.source ?? "root"), Option.flatMap((node) => {
|
|
455
|
+
let found = Option.none();
|
|
456
|
+
traverseImpl(graph, {
|
|
457
|
+
source: node.id,
|
|
458
|
+
relation: "child",
|
|
459
|
+
visitor: (node2, path) => {
|
|
460
|
+
if (Option.isSome(found)) {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
if (node2.id === params.target) {
|
|
464
|
+
found = Option.some(path);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
93
467
|
});
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
468
|
+
return found;
|
|
469
|
+
}));
|
|
470
|
+
};
|
|
471
|
+
function getPath(graphOrParams, params) {
|
|
472
|
+
if (params === void 0 && typeof graphOrParams === "object" && "target" in graphOrParams) {
|
|
473
|
+
const params2 = graphOrParams;
|
|
474
|
+
return (graph) => getPathImpl(graph, params2);
|
|
475
|
+
} else {
|
|
476
|
+
const graph = graphOrParams;
|
|
477
|
+
return getPathImpl(graph, params);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
var waitForPathImpl = (graph, params, options) => {
|
|
481
|
+
const { timeout = 5e3, interval = 500 } = options ?? {};
|
|
482
|
+
const path = getPathImpl(graph, params);
|
|
483
|
+
if (Option.isSome(path)) {
|
|
484
|
+
return Promise.resolve(path.value);
|
|
485
|
+
}
|
|
486
|
+
const trigger = new Trigger();
|
|
487
|
+
const i = setInterval(() => {
|
|
488
|
+
const path2 = getPathImpl(graph, params);
|
|
489
|
+
if (Option.isSome(path2)) {
|
|
490
|
+
trigger.wake(path2.value);
|
|
97
491
|
}
|
|
492
|
+
}, interval);
|
|
493
|
+
return trigger.wait({
|
|
494
|
+
timeout
|
|
495
|
+
}).finally(() => clearInterval(i));
|
|
496
|
+
};
|
|
497
|
+
function waitForPath(graphOrParams, paramsOrOptions, options) {
|
|
498
|
+
if (typeof graphOrParams === "object" && "target" in graphOrParams) {
|
|
499
|
+
const params = graphOrParams;
|
|
500
|
+
const opts = typeof paramsOrOptions === "object" && !("target" in paramsOrOptions) ? paramsOrOptions : void 0;
|
|
501
|
+
return (graph) => waitForPathImpl(graph, params, opts);
|
|
502
|
+
} else {
|
|
503
|
+
const graph = graphOrParams;
|
|
504
|
+
const params = paramsOrOptions;
|
|
505
|
+
return waitForPathImpl(graph, params, options);
|
|
98
506
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
507
|
+
}
|
|
508
|
+
var initializeImpl = async (graph, id) => {
|
|
509
|
+
const internal = getInternal(graph);
|
|
510
|
+
const initialized = Record.get(internal._initialized, id).pipe(Option.getOrElse(() => false));
|
|
511
|
+
log("initialize", {
|
|
512
|
+
id,
|
|
513
|
+
initialized
|
|
514
|
+
}, {
|
|
515
|
+
F: __dxlog_file2,
|
|
516
|
+
L: 668,
|
|
517
|
+
S: void 0,
|
|
518
|
+
C: (f, a) => f(...a)
|
|
519
|
+
});
|
|
520
|
+
if (!initialized) {
|
|
521
|
+
Record.set(internal._initialized, id, true);
|
|
522
|
+
await internal._onInitialize?.(id);
|
|
523
|
+
}
|
|
524
|
+
return graph;
|
|
525
|
+
};
|
|
526
|
+
function initialize(graphOrId, id) {
|
|
527
|
+
if (typeof graphOrId === "string") {
|
|
528
|
+
const id2 = graphOrId;
|
|
529
|
+
return (graph) => initializeImpl(graph, id2);
|
|
530
|
+
} else {
|
|
531
|
+
const graph = graphOrId;
|
|
532
|
+
return initializeImpl(graph, id);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
var expandImpl = (graph, id, relation2) => {
|
|
536
|
+
const internal = getInternal(graph);
|
|
537
|
+
const normalizedRelation = normalizeRelation(relation2);
|
|
538
|
+
const key = `${id}${Separators.primary}${relationKey(normalizedRelation)}`;
|
|
539
|
+
const nodeOpt = internal._registry.get(internal._node(id));
|
|
540
|
+
if (Option.isNone(nodeOpt)) {
|
|
541
|
+
internal._pendingExpands.add(key);
|
|
102
542
|
log("expand", {
|
|
103
543
|
key,
|
|
104
|
-
|
|
544
|
+
deferred: true
|
|
105
545
|
}, {
|
|
106
|
-
F:
|
|
107
|
-
L:
|
|
108
|
-
S:
|
|
546
|
+
F: __dxlog_file2,
|
|
547
|
+
L: 714,
|
|
548
|
+
S: void 0,
|
|
109
549
|
C: (f, a) => f(...a)
|
|
110
550
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
551
|
+
return graph;
|
|
552
|
+
}
|
|
553
|
+
const expanded = Record.get(internal._expanded, key).pipe(Option.getOrElse(() => false));
|
|
554
|
+
log("expand", {
|
|
555
|
+
key,
|
|
556
|
+
expanded
|
|
557
|
+
}, {
|
|
558
|
+
F: __dxlog_file2,
|
|
559
|
+
L: 719,
|
|
560
|
+
S: void 0,
|
|
561
|
+
C: (f, a) => f(...a)
|
|
562
|
+
});
|
|
563
|
+
if (!expanded) {
|
|
564
|
+
Record.set(internal._expanded, key, true);
|
|
565
|
+
internal._onExpand?.(id, normalizedRelation);
|
|
115
566
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
567
|
+
return graph;
|
|
568
|
+
};
|
|
569
|
+
function expand(graphOrId, idOrRelation, relation2) {
|
|
570
|
+
if (typeof graphOrId === "string") {
|
|
571
|
+
const id = graphOrId;
|
|
572
|
+
const rel = idOrRelation;
|
|
573
|
+
return (graph) => expandImpl(graph, id, rel);
|
|
574
|
+
} else {
|
|
575
|
+
const graph = graphOrId;
|
|
576
|
+
const id = idOrRelation;
|
|
577
|
+
invariant2(relation2 !== void 0, "Relation is required.", {
|
|
578
|
+
F: __dxlog_file2,
|
|
579
|
+
L: 755,
|
|
580
|
+
S: this,
|
|
581
|
+
A: [
|
|
582
|
+
"relation !== undefined",
|
|
583
|
+
"'Relation is required.'"
|
|
584
|
+
]
|
|
119
585
|
});
|
|
586
|
+
const rel = relation2;
|
|
587
|
+
return expandImpl(graph, id, rel);
|
|
120
588
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
589
|
+
}
|
|
590
|
+
var sortEdgesImpl = (graph, id, relation2, order) => {
|
|
591
|
+
const internal = getInternal(graph);
|
|
592
|
+
const edgesAtom = internal._edges(id);
|
|
593
|
+
const edges = internal._registry.get(edgesAtom);
|
|
594
|
+
const relationId = relationKey(relation2);
|
|
595
|
+
const current = edges[relationId] ?? [];
|
|
596
|
+
const unsorted = current.filter((id2) => !order.includes(id2));
|
|
597
|
+
const sorted = order.filter((id2) => current.includes(id2));
|
|
598
|
+
const newOrder = [
|
|
599
|
+
...sorted,
|
|
600
|
+
...unsorted
|
|
601
|
+
];
|
|
602
|
+
if (newOrder.length === current.length && newOrder.every((id2, i) => id2 === current[i])) {
|
|
603
|
+
return graph;
|
|
604
|
+
}
|
|
605
|
+
internal._registry.set(edgesAtom, {
|
|
606
|
+
...edges,
|
|
607
|
+
[relationId]: newOrder
|
|
608
|
+
});
|
|
609
|
+
return graph;
|
|
610
|
+
};
|
|
611
|
+
function sortEdges(graphOrId, idOrRelation, relationOrOrder, order) {
|
|
612
|
+
if (typeof graphOrId === "string") {
|
|
613
|
+
const id = graphOrId;
|
|
614
|
+
const relation2 = idOrRelation;
|
|
615
|
+
const order2 = relationOrOrder;
|
|
616
|
+
return (graph) => sortEdgesImpl(graph, id, relation2, order2);
|
|
617
|
+
} else {
|
|
618
|
+
const graph = graphOrId;
|
|
619
|
+
const id = idOrRelation;
|
|
620
|
+
const relation2 = relationOrOrder;
|
|
621
|
+
return sortEdgesImpl(graph, id, relation2, order);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
var addNodesImpl = (graph, nodes) => {
|
|
625
|
+
Atom2.batch(() => {
|
|
626
|
+
nodes.map((node) => addNodeImpl(graph, node));
|
|
627
|
+
});
|
|
628
|
+
return graph;
|
|
629
|
+
};
|
|
630
|
+
function addNodes(graphOrNodes, nodes) {
|
|
631
|
+
if (nodes === void 0) {
|
|
632
|
+
const nodes2 = graphOrNodes;
|
|
633
|
+
return (graph) => addNodesImpl(graph, nodes2);
|
|
634
|
+
} else {
|
|
635
|
+
const graph = graphOrNodes;
|
|
636
|
+
return addNodesImpl(graph, nodes);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
var addNodeImpl = (graph, nodeArg) => {
|
|
640
|
+
const internal = getInternal(graph);
|
|
641
|
+
const { nodes, edges, id, type, data = null, properties = {}, ...rest } = nodeArg;
|
|
642
|
+
const nodeAtom = internal._node(id);
|
|
643
|
+
const existingNode = internal._registry.get(nodeAtom);
|
|
644
|
+
Option.match(existingNode, {
|
|
645
|
+
onSome: (existing) => {
|
|
646
|
+
const typeChanged = existing.type !== type;
|
|
647
|
+
const dataChanged = !shallowEqual(existing.data, data);
|
|
648
|
+
const propertiesChanged = Object.keys(properties).some((key) => existing.properties[key] !== properties[key]);
|
|
649
|
+
log("existing node", {
|
|
650
|
+
id,
|
|
651
|
+
typeChanged,
|
|
652
|
+
dataChanged,
|
|
653
|
+
propertiesChanged
|
|
654
|
+
}, {
|
|
655
|
+
F: __dxlog_file2,
|
|
656
|
+
L: 877,
|
|
657
|
+
S: void 0,
|
|
658
|
+
C: (f, a) => f(...a)
|
|
659
|
+
});
|
|
660
|
+
if (typeChanged || dataChanged || propertiesChanged) {
|
|
661
|
+
log("updating node", {
|
|
171
662
|
id,
|
|
172
663
|
type,
|
|
173
664
|
data,
|
|
174
665
|
properties
|
|
175
666
|
}, {
|
|
176
|
-
F:
|
|
177
|
-
L:
|
|
178
|
-
S:
|
|
667
|
+
F: __dxlog_file2,
|
|
668
|
+
L: 884,
|
|
669
|
+
S: void 0,
|
|
179
670
|
C: (f, a) => f(...a)
|
|
180
671
|
});
|
|
181
|
-
const newNode =
|
|
182
|
-
|
|
672
|
+
const newNode = Option.some({
|
|
673
|
+
...existing,
|
|
674
|
+
...rest,
|
|
183
675
|
type,
|
|
184
676
|
data,
|
|
185
|
-
properties
|
|
677
|
+
properties: {
|
|
678
|
+
...existing.properties,
|
|
679
|
+
...properties
|
|
680
|
+
}
|
|
186
681
|
});
|
|
187
|
-
|
|
188
|
-
|
|
682
|
+
internal._registry.set(nodeAtom, newNode);
|
|
683
|
+
graph.onNodeChanged.emit({
|
|
189
684
|
id,
|
|
190
685
|
node: newNode
|
|
191
686
|
});
|
|
192
687
|
}
|
|
688
|
+
},
|
|
689
|
+
onNone: () => {
|
|
690
|
+
log("new node", {
|
|
691
|
+
id,
|
|
692
|
+
type,
|
|
693
|
+
data,
|
|
694
|
+
properties
|
|
695
|
+
}, {
|
|
696
|
+
F: __dxlog_file2,
|
|
697
|
+
L: 897,
|
|
698
|
+
S: void 0,
|
|
699
|
+
C: (f, a) => f(...a)
|
|
700
|
+
});
|
|
701
|
+
const newNode = internal._constructNode({
|
|
702
|
+
id,
|
|
703
|
+
type,
|
|
704
|
+
data,
|
|
705
|
+
properties,
|
|
706
|
+
...rest
|
|
707
|
+
});
|
|
708
|
+
internal._registry.set(nodeAtom, newNode);
|
|
709
|
+
graph.onNodeChanged.emit({
|
|
710
|
+
id,
|
|
711
|
+
node: newNode
|
|
712
|
+
});
|
|
713
|
+
const prefix = `${id}${Separators.primary}`;
|
|
714
|
+
const toApply = [
|
|
715
|
+
...internal._pendingExpands
|
|
716
|
+
].filter((k) => k.startsWith(prefix));
|
|
717
|
+
for (const pendingKey of toApply) {
|
|
718
|
+
internal._pendingExpands.delete(pendingKey);
|
|
719
|
+
const relation2 = relationFromKey(pendingKey.slice(prefix.length));
|
|
720
|
+
Record.set(internal._expanded, pendingKey, true);
|
|
721
|
+
internal._onExpand?.(id, relation2);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
if (nodes) {
|
|
726
|
+
addNodesImpl(graph, nodes);
|
|
727
|
+
const _edges = nodes.map((node) => ({
|
|
728
|
+
source: id,
|
|
729
|
+
target: node.id,
|
|
730
|
+
relation: "child"
|
|
731
|
+
}));
|
|
732
|
+
addEdgesImpl(graph, _edges);
|
|
733
|
+
}
|
|
734
|
+
if (edges) {
|
|
735
|
+
todo();
|
|
736
|
+
}
|
|
737
|
+
return graph;
|
|
738
|
+
};
|
|
739
|
+
function addNode(graphOrNodeArg, nodeArg) {
|
|
740
|
+
if (nodeArg === void 0) {
|
|
741
|
+
const nodeArg2 = graphOrNodeArg;
|
|
742
|
+
return (graph) => addNodeImpl(graph, nodeArg2);
|
|
743
|
+
} else {
|
|
744
|
+
const graph = graphOrNodeArg;
|
|
745
|
+
return addNodeImpl(graph, nodeArg);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
var removeNodesImpl = (graph, ids, edges = false) => {
|
|
749
|
+
Atom2.batch(() => {
|
|
750
|
+
ids.map((id) => removeNodeImpl(graph, id, edges));
|
|
751
|
+
});
|
|
752
|
+
return graph;
|
|
753
|
+
};
|
|
754
|
+
function removeNodes(graphOrIds, idsOrEdges, edges) {
|
|
755
|
+
if (Array.isArray(graphOrIds)) {
|
|
756
|
+
const ids = graphOrIds;
|
|
757
|
+
const edgesArg = typeof idsOrEdges === "boolean" ? idsOrEdges : false;
|
|
758
|
+
return (graph) => removeNodesImpl(graph, ids, edgesArg);
|
|
759
|
+
} else {
|
|
760
|
+
const graph = graphOrIds;
|
|
761
|
+
const ids = idsOrEdges;
|
|
762
|
+
const edgesArg = edges ?? false;
|
|
763
|
+
return removeNodesImpl(graph, ids, edgesArg);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
var removeNodeImpl = (graph, id, edges = false) => {
|
|
767
|
+
const internal = getInternal(graph);
|
|
768
|
+
const nodeAtom = internal._node(id);
|
|
769
|
+
internal._registry.set(nodeAtom, Option.none());
|
|
770
|
+
graph.onNodeChanged.emit({
|
|
771
|
+
id,
|
|
772
|
+
node: Option.none()
|
|
773
|
+
});
|
|
774
|
+
if (edges) {
|
|
775
|
+
const nodeEdges = internal._registry.get(internal._edges(id));
|
|
776
|
+
const edgesToRemove = [];
|
|
777
|
+
for (const [relationKeyValue, relatedIds] of Object.entries(nodeEdges)) {
|
|
778
|
+
const relation2 = relationFromKey(relationKeyValue);
|
|
779
|
+
const isInboundRelation = relation2.direction === "inbound";
|
|
780
|
+
for (const relatedId of relatedIds) {
|
|
781
|
+
if (isInboundRelation) {
|
|
782
|
+
edgesToRemove.push({
|
|
783
|
+
source: relatedId,
|
|
784
|
+
target: id,
|
|
785
|
+
relation: inverseRelation(relation2)
|
|
786
|
+
});
|
|
787
|
+
} else {
|
|
788
|
+
edgesToRemove.push({
|
|
789
|
+
source: id,
|
|
790
|
+
target: relatedId,
|
|
791
|
+
relation: relation2
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
removeEdgesImpl(graph, edgesToRemove);
|
|
797
|
+
}
|
|
798
|
+
internal._onRemoveNode?.(id);
|
|
799
|
+
return graph;
|
|
800
|
+
};
|
|
801
|
+
function removeNode(graphOrId, idOrEdges, edges) {
|
|
802
|
+
if (typeof graphOrId === "string") {
|
|
803
|
+
const id = graphOrId;
|
|
804
|
+
const edgesArg = typeof idOrEdges === "boolean" ? idOrEdges : false;
|
|
805
|
+
return (graph) => removeNodeImpl(graph, id, edgesArg);
|
|
806
|
+
} else {
|
|
807
|
+
const graph = graphOrId;
|
|
808
|
+
const id = idOrEdges;
|
|
809
|
+
const edgesArg = edges ?? false;
|
|
810
|
+
return removeNodeImpl(graph, id, edgesArg);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
var addEdgesImpl = (graph, edges) => {
|
|
814
|
+
Atom2.batch(() => {
|
|
815
|
+
edges.map((edge) => addEdgeImpl(graph, edge));
|
|
816
|
+
});
|
|
817
|
+
return graph;
|
|
818
|
+
};
|
|
819
|
+
function addEdges(graphOrEdges, edges) {
|
|
820
|
+
if (edges === void 0) {
|
|
821
|
+
const edges2 = graphOrEdges;
|
|
822
|
+
return (graph) => addEdgesImpl(graph, edges2);
|
|
823
|
+
} else {
|
|
824
|
+
const graph = graphOrEdges;
|
|
825
|
+
return addEdgesImpl(graph, edges);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
var addEdgeImpl = (graph, edgeArg) => {
|
|
829
|
+
const relation2 = normalizeRelation(edgeArg.relation);
|
|
830
|
+
const relationId = relationKey(relation2);
|
|
831
|
+
const inverse = inverseRelation(relation2);
|
|
832
|
+
const inverseId = relationKey(inverse);
|
|
833
|
+
const internal = getInternal(graph);
|
|
834
|
+
const sourceAtom = internal._edges(edgeArg.source);
|
|
835
|
+
const source = internal._registry.get(sourceAtom);
|
|
836
|
+
const sourceList = source[relationId] ?? [];
|
|
837
|
+
if (!sourceList.includes(edgeArg.target)) {
|
|
838
|
+
log("add edge", {
|
|
839
|
+
source: edgeArg.source,
|
|
840
|
+
target: edgeArg.target,
|
|
841
|
+
relation: relationId
|
|
842
|
+
}, {
|
|
843
|
+
F: __dxlog_file2,
|
|
844
|
+
L: 1081,
|
|
845
|
+
S: void 0,
|
|
846
|
+
C: (f, a) => f(...a)
|
|
193
847
|
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
target
|
|
199
|
-
|
|
200
|
-
|
|
848
|
+
internal._registry.set(sourceAtom, {
|
|
849
|
+
...source,
|
|
850
|
+
[relationId]: [
|
|
851
|
+
...sourceList,
|
|
852
|
+
edgeArg.target
|
|
853
|
+
]
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
const targetAtom = internal._edges(edgeArg.target);
|
|
857
|
+
const target = internal._registry.get(targetAtom);
|
|
858
|
+
const targetList = target[inverseId] ?? [];
|
|
859
|
+
if (!targetList.includes(edgeArg.source)) {
|
|
860
|
+
log("add inverse edge", {
|
|
861
|
+
source: edgeArg.source,
|
|
862
|
+
target: edgeArg.target,
|
|
863
|
+
relation: inverseId
|
|
864
|
+
}, {
|
|
865
|
+
F: __dxlog_file2,
|
|
866
|
+
L: 1089,
|
|
867
|
+
S: void 0,
|
|
868
|
+
C: (f, a) => f(...a)
|
|
869
|
+
});
|
|
870
|
+
internal._registry.set(targetAtom, {
|
|
871
|
+
...target,
|
|
872
|
+
[inverseId]: [
|
|
873
|
+
...targetList,
|
|
874
|
+
edgeArg.source
|
|
875
|
+
]
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
return graph;
|
|
879
|
+
};
|
|
880
|
+
function addEdge(graphOrEdgeArg, edgeArg) {
|
|
881
|
+
if (edgeArg === void 0) {
|
|
882
|
+
const edgeArg2 = graphOrEdgeArg;
|
|
883
|
+
return (graph) => addEdgeImpl(graph, edgeArg2);
|
|
884
|
+
} else {
|
|
885
|
+
const graph = graphOrEdgeArg;
|
|
886
|
+
return addEdgeImpl(graph, edgeArg);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
var removeEdgesImpl = (graph, edges, removeOrphans = false) => {
|
|
890
|
+
Atom2.batch(() => {
|
|
891
|
+
edges.map((edge) => removeEdgeImpl(graph, edge, removeOrphans));
|
|
892
|
+
});
|
|
893
|
+
return graph;
|
|
894
|
+
};
|
|
895
|
+
function removeEdges(graphOrEdges, edgesOrRemoveOrphans, removeOrphans) {
|
|
896
|
+
if (Array.isArray(graphOrEdges)) {
|
|
897
|
+
const edges = graphOrEdges;
|
|
898
|
+
const removeOrphansArg = typeof edgesOrRemoveOrphans === "boolean" ? edgesOrRemoveOrphans : false;
|
|
899
|
+
return (graph) => removeEdgesImpl(graph, edges, removeOrphansArg);
|
|
900
|
+
} else {
|
|
901
|
+
const graph = graphOrEdges;
|
|
902
|
+
const edges = edgesOrRemoveOrphans;
|
|
903
|
+
const removeOrphansArg = removeOrphans ?? false;
|
|
904
|
+
return removeEdgesImpl(graph, edges, removeOrphansArg);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
var removeEdgeImpl = (graph, edgeArg, removeOrphans = false) => {
|
|
908
|
+
const relation2 = normalizeRelation(edgeArg.relation);
|
|
909
|
+
const relationId = relationKey(relation2);
|
|
910
|
+
const inverse = inverseRelation(relation2);
|
|
911
|
+
const inverseId = relationKey(inverse);
|
|
912
|
+
const internal = getInternal(graph);
|
|
913
|
+
const sourceAtom = internal._edges(edgeArg.source);
|
|
914
|
+
const source = internal._registry.get(sourceAtom);
|
|
915
|
+
const sourceList = source[relationId] ?? [];
|
|
916
|
+
if (sourceList.includes(edgeArg.target)) {
|
|
917
|
+
internal._registry.set(sourceAtom, {
|
|
918
|
+
...source,
|
|
919
|
+
[relationId]: sourceList.filter((id) => id !== edgeArg.target)
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
const targetAtom = internal._edges(edgeArg.target);
|
|
923
|
+
const target = internal._registry.get(targetAtom);
|
|
924
|
+
const targetList = target[inverseId] ?? [];
|
|
925
|
+
if (targetList.includes(edgeArg.source)) {
|
|
926
|
+
internal._registry.set(targetAtom, {
|
|
927
|
+
...target,
|
|
928
|
+
[inverseId]: targetList.filter((id) => id !== edgeArg.source)
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
if (removeOrphans) {
|
|
932
|
+
const sourceAfter = internal._registry.get(sourceAtom);
|
|
933
|
+
const targetAfter = internal._registry.get(targetAtom);
|
|
934
|
+
const isEmpty = (edges) => Object.values(edges).every((ids) => ids.length === 0);
|
|
935
|
+
if (isEmpty(sourceAfter) && edgeArg.source !== RootId) {
|
|
936
|
+
removeNodesImpl(graph, [
|
|
937
|
+
edgeArg.source
|
|
938
|
+
]);
|
|
201
939
|
}
|
|
202
|
-
if (
|
|
203
|
-
|
|
940
|
+
if (isEmpty(targetAfter) && edgeArg.target !== RootId) {
|
|
941
|
+
removeNodesImpl(graph, [
|
|
942
|
+
edgeArg.target
|
|
943
|
+
]);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return graph;
|
|
947
|
+
};
|
|
948
|
+
function removeEdge(graphOrEdgeArg, edgeArgOrRemoveOrphans, removeOrphans) {
|
|
949
|
+
if (edgeArgOrRemoveOrphans === void 0 || typeof edgeArgOrRemoveOrphans === "boolean" || "source" in graphOrEdgeArg) {
|
|
950
|
+
const edgeArg = graphOrEdgeArg;
|
|
951
|
+
const removeOrphansArg = typeof edgeArgOrRemoveOrphans === "boolean" ? edgeArgOrRemoveOrphans : false;
|
|
952
|
+
return (graph) => removeEdgeImpl(graph, edgeArg, removeOrphansArg);
|
|
953
|
+
} else {
|
|
954
|
+
const graph = graphOrEdgeArg;
|
|
955
|
+
const edgeArg = edgeArgOrRemoveOrphans;
|
|
956
|
+
const removeOrphansArg = removeOrphans ?? false;
|
|
957
|
+
return removeEdgeImpl(graph, edgeArg, removeOrphansArg);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
var make = (params) => {
|
|
961
|
+
return new GraphImpl(params);
|
|
962
|
+
};
|
|
963
|
+
var relationKey = (relation2) => {
|
|
964
|
+
const normalized = normalizeRelation(relation2);
|
|
965
|
+
return `${normalized.kind}${Separators.secondary}${normalized.direction}`;
|
|
966
|
+
};
|
|
967
|
+
var relationFromKey = (encoded) => {
|
|
968
|
+
const separatorIndex = encoded.lastIndexOf(Separators.secondary);
|
|
969
|
+
invariant2(separatorIndex > 0 && separatorIndex < encoded.length - 1, `Invalid relation key: ${encoded}`, {
|
|
970
|
+
F: __dxlog_file2,
|
|
971
|
+
L: 1234,
|
|
972
|
+
S: void 0,
|
|
973
|
+
A: [
|
|
974
|
+
"separatorIndex > 0 && separatorIndex < encoded.length - 1",
|
|
975
|
+
"`Invalid relation key: ${encoded}`"
|
|
976
|
+
]
|
|
977
|
+
});
|
|
978
|
+
const kind = encoded.slice(0, separatorIndex);
|
|
979
|
+
const directionRaw = encoded.slice(separatorIndex + 1);
|
|
980
|
+
invariant2(directionRaw === "outbound" || directionRaw === "inbound", `Invalid relation direction: ${directionRaw}`, {
|
|
981
|
+
F: __dxlog_file2,
|
|
982
|
+
L: 1237,
|
|
983
|
+
S: void 0,
|
|
984
|
+
A: [
|
|
985
|
+
"directionRaw === 'outbound' || directionRaw === 'inbound'",
|
|
986
|
+
"`Invalid relation direction: ${directionRaw}`"
|
|
987
|
+
]
|
|
988
|
+
});
|
|
989
|
+
return relation(kind, directionRaw);
|
|
990
|
+
};
|
|
991
|
+
var connectionKey = (id, relation2) => `${id}${Separators.primary}${relationKey(relation2)}`;
|
|
992
|
+
var relationFromConnectionKey = (key) => {
|
|
993
|
+
const separatorIndex = key.indexOf(Separators.primary);
|
|
994
|
+
invariant2(separatorIndex > 0 && separatorIndex < key.length - 1, `Invalid connection key: ${key}`, {
|
|
995
|
+
F: __dxlog_file2,
|
|
996
|
+
L: 1246,
|
|
997
|
+
S: void 0,
|
|
998
|
+
A: [
|
|
999
|
+
"separatorIndex > 0 && separatorIndex < key.length - 1",
|
|
1000
|
+
"`Invalid connection key: ${key}`"
|
|
1001
|
+
]
|
|
1002
|
+
});
|
|
1003
|
+
const id = key.slice(0, separatorIndex);
|
|
1004
|
+
const encodedRelation = key.slice(separatorIndex + 1);
|
|
1005
|
+
return {
|
|
1006
|
+
id,
|
|
1007
|
+
relation: relationFromKey(encodedRelation)
|
|
1008
|
+
};
|
|
1009
|
+
};
|
|
1010
|
+
var inverseRelation = (relation2) => {
|
|
1011
|
+
const normalized = normalizeRelation(relation2);
|
|
1012
|
+
return relation(normalized.kind, normalized.direction === "outbound" ? "inbound" : "outbound");
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1015
|
+
// src/graph-builder.ts
|
|
1016
|
+
var graph_builder_exports = {};
|
|
1017
|
+
__export(graph_builder_exports, {
|
|
1018
|
+
GraphBuilderTypeId: () => GraphBuilderTypeId,
|
|
1019
|
+
addExtension: () => addExtension,
|
|
1020
|
+
createConnector: () => createConnector,
|
|
1021
|
+
createExtension: () => createExtension,
|
|
1022
|
+
createExtensionRaw: () => createExtensionRaw,
|
|
1023
|
+
createTypeExtension: () => createTypeExtension,
|
|
1024
|
+
destroy: () => destroy,
|
|
1025
|
+
explore: () => explore,
|
|
1026
|
+
flattenExtensions: () => flattenExtensions,
|
|
1027
|
+
flush: () => flush,
|
|
1028
|
+
from: () => from,
|
|
1029
|
+
make: () => make2,
|
|
1030
|
+
removeExtension: () => removeExtension
|
|
1031
|
+
});
|
|
1032
|
+
import { Atom as Atom3, Registry as Registry2 } from "@effect-atom/atom-react";
|
|
1033
|
+
import * as Array2 from "effect/Array";
|
|
1034
|
+
import * as Effect from "effect/Effect";
|
|
1035
|
+
import * as Function2 from "effect/Function";
|
|
1036
|
+
import * as Option3 from "effect/Option";
|
|
1037
|
+
import * as Pipeable2 from "effect/Pipeable";
|
|
1038
|
+
import * as Record2 from "effect/Record";
|
|
1039
|
+
import { scheduleTask, yieldOrContinue } from "main-thread-scheduling";
|
|
1040
|
+
import { log as log2 } from "@dxos/log";
|
|
1041
|
+
import { byPosition, getDebugName, isNonNullable as isNonNullable2 } from "@dxos/util";
|
|
1042
|
+
|
|
1043
|
+
// src/node-matcher.ts
|
|
1044
|
+
var node_matcher_exports = {};
|
|
1045
|
+
__export(node_matcher_exports, {
|
|
1046
|
+
whenAll: () => whenAll,
|
|
1047
|
+
whenAny: () => whenAny,
|
|
1048
|
+
whenEchoObject: () => whenEchoObject,
|
|
1049
|
+
whenEchoObjectMatches: () => whenEchoObjectMatches,
|
|
1050
|
+
whenEchoType: () => whenEchoType,
|
|
1051
|
+
whenEchoTypeMatches: () => whenEchoTypeMatches,
|
|
1052
|
+
whenId: () => whenId,
|
|
1053
|
+
whenNodeType: () => whenNodeType,
|
|
1054
|
+
whenNot: () => whenNot,
|
|
1055
|
+
whenRoot: () => whenRoot
|
|
1056
|
+
});
|
|
1057
|
+
import * as Option2 from "effect/Option";
|
|
1058
|
+
import { Obj } from "@dxos/echo";
|
|
1059
|
+
var whenRoot = (node) => node.id === RootId ? Option2.some(node) : Option2.none();
|
|
1060
|
+
var whenId = (id) => (node) => node.id === id ? Option2.some(node) : Option2.none();
|
|
1061
|
+
var whenNodeType = (type) => (node) => node.type === type ? Option2.some(node) : Option2.none();
|
|
1062
|
+
var whenEchoType = (type) => (node) => Obj.instanceOf(type, node.data) ? Option2.some(node.data) : Option2.none();
|
|
1063
|
+
var whenEchoObject = (node) => Obj.isObject(node.data) ? Option2.some(node.data) : Option2.none();
|
|
1064
|
+
var whenAll = (...matchers) => (node) => {
|
|
1065
|
+
for (const candidate of matchers) {
|
|
1066
|
+
if (Option2.isNone(candidate(node))) {
|
|
1067
|
+
return Option2.none();
|
|
204
1068
|
}
|
|
205
1069
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
1070
|
+
return Option2.some(node);
|
|
1071
|
+
};
|
|
1072
|
+
var whenAny = (...matchers) => (node) => {
|
|
1073
|
+
for (const candidate of matchers) {
|
|
1074
|
+
if (Option2.isSome(candidate(node))) {
|
|
1075
|
+
return Option2.some(node);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
return Option2.none();
|
|
1079
|
+
};
|
|
1080
|
+
var whenEchoTypeMatches = (type) => (node) => Obj.instanceOf(type, node.data) ? Option2.some(node) : Option2.none();
|
|
1081
|
+
var whenEchoObjectMatches = (node) => Obj.isObject(node.data) ? Option2.some(node) : Option2.none();
|
|
1082
|
+
var whenNot = (matcher) => (node) => Option2.isNone(matcher(node)) ? Option2.some(node) : Option2.none();
|
|
1083
|
+
|
|
1084
|
+
// src/graph-builder.ts
|
|
1085
|
+
var __dxlog_file3 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
|
|
1086
|
+
var GraphBuilderTypeId = /* @__PURE__ */ Symbol.for("@dxos/app-graph/GraphBuilder");
|
|
1087
|
+
var GraphBuilderImpl = class {
|
|
1088
|
+
[GraphBuilderTypeId] = GraphBuilderTypeId;
|
|
1089
|
+
pipe() {
|
|
1090
|
+
return Pipeable2.pipeArguments(this, arguments);
|
|
1091
|
+
}
|
|
1092
|
+
// TODO(wittjosiah): Use Context.
|
|
1093
|
+
/** Active subscriptions keyed by composite ID, cleaned up on node removal. */
|
|
1094
|
+
_subscriptions = /* @__PURE__ */ new Map();
|
|
1095
|
+
/** Connector updates pending flush, keyed by connector key. */
|
|
1096
|
+
_dirtyConnectors = /* @__PURE__ */ new Map();
|
|
1097
|
+
/** Last-flushed node IDs per connector key, used for edge removal on update. */
|
|
1098
|
+
_connectorPrevious = /* @__PURE__ */ new Map();
|
|
1099
|
+
/** Last-flushed node args per connector key, used for change detection. */
|
|
1100
|
+
_connectorPreviousArgs = /* @__PURE__ */ new Map();
|
|
1101
|
+
/** Whether a dirty-flush task is already scheduled. */
|
|
1102
|
+
_flushScheduled = false;
|
|
1103
|
+
/** Resolves when the current flush completes. */
|
|
1104
|
+
_flushPromise = Promise.resolve();
|
|
1105
|
+
/** Registered builder extensions keyed by extension ID. */
|
|
1106
|
+
_extensions = Atom3.make(Record2.empty()).pipe(Atom3.keepAlive, Atom3.withLabel("graph-builder:extensions"));
|
|
1107
|
+
/** Triggers signalling that a node's resolver has fired at least once. */
|
|
1108
|
+
_initialized = {};
|
|
1109
|
+
/** Shared atom registry for reactive subscriptions. */
|
|
1110
|
+
_registry;
|
|
1111
|
+
/** Backing graph with internal accessors for node atoms and construction. */
|
|
1112
|
+
_graph;
|
|
1113
|
+
constructor({ registry, ...params } = {}) {
|
|
1114
|
+
this._registry = registry ?? Registry2.make();
|
|
1115
|
+
const graph = make({
|
|
1116
|
+
...params,
|
|
1117
|
+
registry: this._registry,
|
|
1118
|
+
onExpand: (id, relation2) => this._onExpand(id, relation2),
|
|
1119
|
+
onInitialize: (id) => this._onInitialize(id),
|
|
1120
|
+
onRemoveNode: (id) => this._onRemoveNode(id)
|
|
209
1121
|
});
|
|
1122
|
+
this._graph = graph;
|
|
1123
|
+
}
|
|
1124
|
+
get graph() {
|
|
1125
|
+
return this._graph;
|
|
1126
|
+
}
|
|
1127
|
+
get extensions() {
|
|
1128
|
+
return this._extensions;
|
|
1129
|
+
}
|
|
1130
|
+
/** Apply a set of node changes for a single connector key. */
|
|
1131
|
+
_applyConnectorUpdate(key, nodes, previous) {
|
|
1132
|
+
const { id, relation: relation2 } = relationFromConnectorKey(key);
|
|
1133
|
+
const ids = nodes.map((node) => node.id);
|
|
1134
|
+
const removed = previous.filter((pid) => !ids.includes(pid));
|
|
1135
|
+
this._connectorPrevious.set(key, ids);
|
|
1136
|
+
this._connectorPreviousArgs.set(key, nodes);
|
|
1137
|
+
removeEdges(this._graph, removed.map((target) => ({
|
|
1138
|
+
source: id,
|
|
1139
|
+
target,
|
|
1140
|
+
relation: relation2
|
|
1141
|
+
})), true);
|
|
1142
|
+
addNodes(this._graph, nodes);
|
|
1143
|
+
addEdges(this._graph, nodes.map((node) => ({
|
|
1144
|
+
source: id,
|
|
1145
|
+
target: node.id,
|
|
1146
|
+
relation: relation2
|
|
1147
|
+
})));
|
|
1148
|
+
if (ids.length > 0) {
|
|
1149
|
+
const sortedIds = [
|
|
1150
|
+
...nodes
|
|
1151
|
+
].sort((a, b) => byPosition(a.properties ?? {}, b.properties ?? {})).map((n) => n.id);
|
|
1152
|
+
sortEdges(this._graph, id, relation2, sortedIds);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
_scheduleDirtyFlush() {
|
|
1156
|
+
if (!this._flushScheduled) {
|
|
1157
|
+
this._flushScheduled = true;
|
|
1158
|
+
this._flushPromise = scheduleTask(() => {
|
|
1159
|
+
this._flushScheduled = false;
|
|
1160
|
+
while (this._dirtyConnectors.size > 0) {
|
|
1161
|
+
const entries = [
|
|
1162
|
+
...this._dirtyConnectors.entries()
|
|
1163
|
+
];
|
|
1164
|
+
this._dirtyConnectors.clear();
|
|
1165
|
+
Atom3.batch(() => {
|
|
1166
|
+
for (const [key, { nodes, previous }] of entries) {
|
|
1167
|
+
this._applyConnectorUpdate(key, nodes, previous);
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
}, {
|
|
1172
|
+
strategy: "smooth"
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
210
1175
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
1176
|
+
_resolvers = Atom3.family((id) => {
|
|
1177
|
+
return Atom3.make((get2) => {
|
|
1178
|
+
return Function2.pipe(get2(this._extensions), Record2.values, Array2.sortBy(byPosition), Array2.map(({ resolver }) => resolver), Array2.filter(isNonNullable2), Array2.map((resolver) => get2(resolver(id))), Array2.filter(isNonNullable2), Array2.head);
|
|
1179
|
+
});
|
|
1180
|
+
});
|
|
1181
|
+
_connectors = Atom3.family((key) => {
|
|
1182
|
+
return Atom3.make((get2) => {
|
|
1183
|
+
const { id, relation: relation2 } = relationFromConnectorKey(key);
|
|
1184
|
+
const node = this._graph.node(id);
|
|
1185
|
+
const sourceNode = Option3.getOrElse(get2(node), () => void 0);
|
|
1186
|
+
if (!sourceNode) {
|
|
1187
|
+
return [];
|
|
1188
|
+
}
|
|
1189
|
+
const extensions = Function2.pipe(get2(this._extensions), Record2.values, Array2.sortBy(byPosition), Array2.filter((ext) => relationKey(ext.relation ?? "child") === relationKey(relation2) && ext.connector != null));
|
|
1190
|
+
const nodes = [];
|
|
1191
|
+
for (const ext of extensions) {
|
|
1192
|
+
const result = get2(ext.connector(node));
|
|
1193
|
+
nodes.push(...result);
|
|
1194
|
+
}
|
|
1195
|
+
return nodes;
|
|
1196
|
+
}).pipe(Atom3.withLabel(`graph-builder:connectors:${key}`));
|
|
1197
|
+
});
|
|
1198
|
+
_onExpand(id, relation2) {
|
|
1199
|
+
log2("onExpand", {
|
|
215
1200
|
id,
|
|
216
|
-
|
|
1201
|
+
relation: relation2,
|
|
1202
|
+
registry: getDebugName(this._registry)
|
|
1203
|
+
}, {
|
|
1204
|
+
F: __dxlog_file3,
|
|
1205
|
+
L: 261,
|
|
1206
|
+
S: this,
|
|
1207
|
+
C: (f, a) => f(...a)
|
|
217
1208
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
...inbound.map((source) => ({
|
|
222
|
-
source,
|
|
223
|
-
target: id
|
|
224
|
-
})),
|
|
225
|
-
...outbound.map((target) => ({
|
|
226
|
-
source: id,
|
|
227
|
-
target
|
|
228
|
-
}))
|
|
229
|
-
];
|
|
230
|
-
this.removeEdges(edges2);
|
|
1209
|
+
this._expandRelation(id, relation2);
|
|
1210
|
+
if (relation2.kind === "child" && relation2.direction === "outbound") {
|
|
1211
|
+
expand(this._graph, id, "action");
|
|
231
1212
|
}
|
|
232
|
-
this._onRemoveNode?.(id);
|
|
233
|
-
}
|
|
234
|
-
addEdges(edges) {
|
|
235
|
-
Rx.batch(() => {
|
|
236
|
-
edges.map((edge) => this.addEdge(edge));
|
|
237
|
-
});
|
|
238
1213
|
}
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
edgeArg.target
|
|
257
|
-
]
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
const targetRx = this._edges(edgeArg.target);
|
|
261
|
-
const target = this._registry.get(targetRx);
|
|
262
|
-
if (!target.inbound.includes(edgeArg.source)) {
|
|
263
|
-
log("add inbound edge", {
|
|
264
|
-
source: edgeArg.source,
|
|
265
|
-
target: edgeArg.target
|
|
1214
|
+
_expandRelation(id, relation2) {
|
|
1215
|
+
const key = connectorKey(id, relation2);
|
|
1216
|
+
const connectors = this._connectors(key);
|
|
1217
|
+
const cancel = this._registry.subscribe(connectors, (rawNodes) => {
|
|
1218
|
+
const nodes = qualifyNodeArgs(id)(rawNodes);
|
|
1219
|
+
const previous = this._connectorPrevious.get(key) ?? [];
|
|
1220
|
+
const ids = nodes.map((n) => n.id);
|
|
1221
|
+
if (ids.length === previous.length && ids.every((nodeId, idx) => nodeId === previous[idx])) {
|
|
1222
|
+
const prevArgs = this._connectorPreviousArgs.get(key);
|
|
1223
|
+
if (prevArgs && nodeArgsUnchanged(prevArgs, nodes)) {
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
log2("update", {
|
|
1228
|
+
id,
|
|
1229
|
+
relation: relation2,
|
|
1230
|
+
ids
|
|
266
1231
|
}, {
|
|
267
|
-
F:
|
|
268
|
-
L:
|
|
1232
|
+
F: __dxlog_file3,
|
|
1233
|
+
L: 288,
|
|
269
1234
|
S: this,
|
|
270
1235
|
C: (f, a) => f(...a)
|
|
271
1236
|
});
|
|
272
|
-
this.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
edgeArg.source
|
|
276
|
-
],
|
|
277
|
-
outbound: target.outbound
|
|
1237
|
+
this._dirtyConnectors.set(key, {
|
|
1238
|
+
nodes,
|
|
1239
|
+
previous
|
|
278
1240
|
});
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
Rx.batch(() => {
|
|
283
|
-
edges.map((edge) => this.removeEdge(edge, removeOrphans));
|
|
1241
|
+
this._scheduleDirtyFlush();
|
|
1242
|
+
}, {
|
|
1243
|
+
immediate: true
|
|
284
1244
|
});
|
|
1245
|
+
this._subscriptions.set(subscriptionKey(id, "expand", key), cancel);
|
|
285
1246
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
this.
|
|
299
|
-
|
|
300
|
-
|
|
1247
|
+
// TODO(wittjosiah): If the same node is added by a connector, the resolver should probably cancel itself?
|
|
1248
|
+
async _onInitialize(id) {
|
|
1249
|
+
log2("onInitialize", {
|
|
1250
|
+
id
|
|
1251
|
+
}, {
|
|
1252
|
+
F: __dxlog_file3,
|
|
1253
|
+
L: 300,
|
|
1254
|
+
S: this,
|
|
1255
|
+
C: (f, a) => f(...a)
|
|
1256
|
+
});
|
|
1257
|
+
const resolver = this._resolvers(id);
|
|
1258
|
+
const cancel = this._registry.subscribe(resolver, (node) => {
|
|
1259
|
+
const trigger = this._initialized[id];
|
|
1260
|
+
Option3.match(node, {
|
|
1261
|
+
onSome: (node2) => {
|
|
1262
|
+
const connectorOwned = [
|
|
1263
|
+
...this._connectorPrevious.values()
|
|
1264
|
+
].some((ids) => ids.includes(id));
|
|
1265
|
+
if (!connectorOwned) {
|
|
1266
|
+
addNodes(this._graph, [
|
|
1267
|
+
node2
|
|
1268
|
+
]);
|
|
1269
|
+
}
|
|
1270
|
+
trigger?.wake();
|
|
1271
|
+
},
|
|
1272
|
+
onNone: () => {
|
|
1273
|
+
trigger?.wake();
|
|
1274
|
+
removeNodes(this._graph, [
|
|
1275
|
+
id
|
|
1276
|
+
]);
|
|
1277
|
+
}
|
|
301
1278
|
});
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
this.
|
|
313
|
-
edgeArg.target
|
|
314
|
-
]);
|
|
1279
|
+
}, {
|
|
1280
|
+
immediate: true
|
|
1281
|
+
});
|
|
1282
|
+
this._subscriptions.set(subscriptionKey(id, "init"), cancel);
|
|
1283
|
+
}
|
|
1284
|
+
_onRemoveNode(id) {
|
|
1285
|
+
const prefix = `${id}${Separators.primary}`;
|
|
1286
|
+
for (const [key, cleanup] of this._subscriptions) {
|
|
1287
|
+
if (key.startsWith(prefix)) {
|
|
1288
|
+
cleanup();
|
|
1289
|
+
this._subscriptions.delete(key);
|
|
315
1290
|
}
|
|
316
1291
|
}
|
|
317
1292
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
this._registry.set(edgesRx, edges);
|
|
1293
|
+
};
|
|
1294
|
+
var make2 = (params) => {
|
|
1295
|
+
return new GraphBuilderImpl(params);
|
|
1296
|
+
};
|
|
1297
|
+
var from = (pickle, registry) => {
|
|
1298
|
+
if (!pickle) {
|
|
1299
|
+
return make2({
|
|
1300
|
+
registry
|
|
1301
|
+
});
|
|
328
1302
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
1303
|
+
const { nodes, edges } = JSON.parse(pickle);
|
|
1304
|
+
return make2({
|
|
1305
|
+
nodes,
|
|
1306
|
+
edges,
|
|
1307
|
+
registry
|
|
1308
|
+
});
|
|
1309
|
+
};
|
|
1310
|
+
var addExtensionImpl = (builder, extensions) => {
|
|
1311
|
+
const internal = builder;
|
|
1312
|
+
flattenExtensions(extensions).forEach((extension) => {
|
|
1313
|
+
const extensions2 = internal._registry.get(internal._extensions);
|
|
1314
|
+
internal._registry.set(internal._extensions, Record2.set(extensions2, extension.id, extension));
|
|
1315
|
+
});
|
|
1316
|
+
return builder;
|
|
1317
|
+
};
|
|
1318
|
+
function addExtension(builderOrExtensions, extensions) {
|
|
1319
|
+
if (extensions === void 0) {
|
|
1320
|
+
const extensions2 = builderOrExtensions;
|
|
1321
|
+
return (builder) => addExtensionImpl(builder, extensions2);
|
|
1322
|
+
} else {
|
|
1323
|
+
const builder = builderOrExtensions;
|
|
1324
|
+
return addExtensionImpl(builder, extensions);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
var removeExtensionImpl = (builder, id) => {
|
|
1328
|
+
const internal = builder;
|
|
1329
|
+
const extensions = internal._registry.get(internal._extensions);
|
|
1330
|
+
internal._registry.set(internal._extensions, Record2.remove(extensions, id));
|
|
1331
|
+
return builder;
|
|
1332
|
+
};
|
|
1333
|
+
function removeExtension(builderOrId, id) {
|
|
1334
|
+
if (typeof builderOrId === "string") {
|
|
1335
|
+
const id2 = builderOrId;
|
|
1336
|
+
return (builder) => removeExtensionImpl(builder, id2);
|
|
1337
|
+
} else {
|
|
1338
|
+
const builder = builderOrId;
|
|
1339
|
+
return removeExtensionImpl(builder, id);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
var exploreImpl = async (builder, options, path = []) => {
|
|
1343
|
+
const internal = builder;
|
|
1344
|
+
const { registry = Registry2.make(), source = RootId, relation: relation2, visitor } = options;
|
|
1345
|
+
if (path.includes(source)) {
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
await yieldOrContinue("idle");
|
|
1349
|
+
const node = registry.get(internal._graph.nodeOrThrow(source));
|
|
1350
|
+
const shouldContinue = await visitor(node, [
|
|
1351
|
+
...path,
|
|
1352
|
+
node.id
|
|
1353
|
+
]);
|
|
1354
|
+
if (shouldContinue === false) {
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
const nodes = Function2.pipe(internal._registry.get(internal._extensions), Record2.values, Array2.map((extension) => extension.connector), Array2.filter(isNonNullable2), Array2.flatMap((connector) => registry.get(connector(internal._graph.node(source)))), qualifyNodeArgs(source));
|
|
1358
|
+
await Promise.all(nodes.map((nodeArg) => {
|
|
1359
|
+
registry.set(internal._graph._node(nodeArg.id), internal._graph._constructNode(nodeArg));
|
|
1360
|
+
return exploreImpl(builder, {
|
|
1361
|
+
registry,
|
|
1362
|
+
source: nodeArg.id,
|
|
1363
|
+
relation: relation2,
|
|
344
1364
|
visitor
|
|
345
1365
|
}, [
|
|
346
1366
|
...path,
|
|
347
|
-
|
|
348
|
-
])
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
this.traverse({
|
|
354
|
-
source: node.id,
|
|
355
|
-
visitor: (node2, path) => {
|
|
356
|
-
if (Option.isSome(found)) {
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
if (node2.id === target) {
|
|
360
|
-
found = Option.some(path);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
return found;
|
|
365
|
-
}));
|
|
366
|
-
}
|
|
367
|
-
async waitForPath(params, { timeout = 5e3, interval = 500 } = {}) {
|
|
368
|
-
const path = this.getPath(params);
|
|
369
|
-
if (Option.isSome(path)) {
|
|
370
|
-
return path.value;
|
|
371
|
-
}
|
|
372
|
-
const trigger = new Trigger();
|
|
373
|
-
const i = setInterval(() => {
|
|
374
|
-
const path2 = this.getPath(params);
|
|
375
|
-
if (Option.isSome(path2)) {
|
|
376
|
-
trigger.wake(path2.value);
|
|
377
|
-
}
|
|
378
|
-
}, interval);
|
|
379
|
-
return trigger.wait({
|
|
380
|
-
timeout
|
|
381
|
-
}).finally(() => clearInterval(i));
|
|
382
|
-
}
|
|
383
|
-
/** @internal */
|
|
384
|
-
_constructNode(node) {
|
|
385
|
-
return Option.some({
|
|
386
|
-
[graphSymbol]: this,
|
|
387
|
-
data: null,
|
|
388
|
-
properties: {},
|
|
389
|
-
...node
|
|
390
|
-
});
|
|
1367
|
+
node.id
|
|
1368
|
+
]);
|
|
1369
|
+
}));
|
|
1370
|
+
if (registry !== internal._registry) {
|
|
1371
|
+
registry.reset();
|
|
1372
|
+
registry.dispose();
|
|
391
1373
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
ROOT_ID,
|
|
404
|
-
this._constructNode({
|
|
405
|
-
id: ROOT_ID,
|
|
406
|
-
type: ROOT_TYPE,
|
|
407
|
-
data: null,
|
|
408
|
-
properties: {}
|
|
409
|
-
})
|
|
410
|
-
]
|
|
411
|
-
]));
|
|
412
|
-
_define_property(this, "_node", Rx.family((id) => {
|
|
413
|
-
const initial = Option.flatten(Record.get(this._initialNodes, id));
|
|
414
|
-
return Rx.make(initial).pipe(Rx.keepAlive, Rx.withLabel(`graph:node:${id}`));
|
|
415
|
-
}));
|
|
416
|
-
_define_property(this, "_nodeOrThrow", Rx.family((id) => {
|
|
417
|
-
return Rx.make((get2) => {
|
|
418
|
-
const node = get2(this._node(id));
|
|
419
|
-
invariant(Option.isSome(node), `Node not available: ${id}`, {
|
|
420
|
-
F: __dxlog_file,
|
|
421
|
-
L: 254,
|
|
422
|
-
S: this,
|
|
423
|
-
A: [
|
|
424
|
-
"Option.isSome(node)",
|
|
425
|
-
"`Node not available: ${id}`"
|
|
426
|
-
]
|
|
427
|
-
});
|
|
428
|
-
return node.value;
|
|
429
|
-
});
|
|
430
|
-
}));
|
|
431
|
-
_define_property(this, "_edges", Rx.family((id) => {
|
|
432
|
-
const initial = Record.get(this._initialEdges, id).pipe(Option.getOrElse(() => ({
|
|
433
|
-
inbound: [],
|
|
434
|
-
outbound: []
|
|
435
|
-
})));
|
|
436
|
-
return Rx.make(initial).pipe(Rx.keepAlive, Rx.withLabel(`graph:edges:${id}`));
|
|
437
|
-
}));
|
|
438
|
-
_define_property(this, "_connections", Rx.family((key) => {
|
|
439
|
-
return Rx.make((get2) => {
|
|
440
|
-
const [id, relation] = key.split("$");
|
|
441
|
-
const edges2 = get2(this._edges(id));
|
|
442
|
-
return edges2[relation].map((id2) => get2(this._node(id2))).filter(Option.isSome).map((o) => o.value);
|
|
443
|
-
}).pipe(Rx.withLabel(`graph:connections:${key}`));
|
|
444
|
-
}));
|
|
445
|
-
_define_property(this, "_actions", Rx.family((id) => {
|
|
446
|
-
return Rx.make((get2) => {
|
|
447
|
-
return get2(this._connections(`${id}$outbound`)).filter((node) => node.type === ACTION_TYPE || node.type === ACTION_GROUP_TYPE);
|
|
448
|
-
}).pipe(Rx.withLabel(`graph:actions:${id}`));
|
|
449
|
-
}));
|
|
450
|
-
_define_property(this, "_json", Rx.family((id) => {
|
|
451
|
-
return Rx.make((get2) => {
|
|
452
|
-
const toJSON = (node, seen = []) => {
|
|
453
|
-
const nodes2 = get2(this.connections(node.id));
|
|
454
|
-
const obj = {
|
|
455
|
-
id: node.id.length > 32 ? `${node.id.slice(0, 32)}...` : node.id,
|
|
456
|
-
type: node.type
|
|
457
|
-
};
|
|
458
|
-
if (node.properties.label) {
|
|
459
|
-
obj.label = node.properties.label;
|
|
460
|
-
}
|
|
461
|
-
if (nodes2.length) {
|
|
462
|
-
obj.nodes = nodes2.map((n) => {
|
|
463
|
-
const nextSeen = [
|
|
464
|
-
...seen,
|
|
465
|
-
node.id
|
|
466
|
-
];
|
|
467
|
-
return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
|
|
468
|
-
}).filter(isNonNullable);
|
|
469
|
-
}
|
|
470
|
-
return obj;
|
|
471
|
-
};
|
|
472
|
-
const root = get2(this.nodeOrThrow(id));
|
|
473
|
-
return toJSON(root);
|
|
474
|
-
}).pipe(Rx.withLabel(`graph:json:${id}`));
|
|
475
|
-
}));
|
|
476
|
-
this._registry = registry ?? Registry.make();
|
|
477
|
-
this._onInitialize = onInitialize;
|
|
478
|
-
this._onExpand = onExpand;
|
|
479
|
-
this._onRemoveNode = onRemoveNode;
|
|
480
|
-
if (nodes) {
|
|
481
|
-
nodes.forEach((node) => {
|
|
482
|
-
Record.set(this._initialNodes, node.id, this._constructNode(node));
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
if (edges) {
|
|
486
|
-
Object.entries(edges).forEach(([source, edges2]) => {
|
|
487
|
-
Record.set(this._initialEdges, source, edges2);
|
|
488
|
-
});
|
|
489
|
-
}
|
|
1374
|
+
};
|
|
1375
|
+
function explore(builderOrOptions, optionsOrPath, path) {
|
|
1376
|
+
if (typeof builderOrOptions === "object" && "visitor" in builderOrOptions) {
|
|
1377
|
+
const options = builderOrOptions;
|
|
1378
|
+
const path2 = Array2.isArray(optionsOrPath) ? optionsOrPath : void 0;
|
|
1379
|
+
return (builder) => exploreImpl(builder, options, path2);
|
|
1380
|
+
} else {
|
|
1381
|
+
const builder = builderOrOptions;
|
|
1382
|
+
const options = optionsOrPath;
|
|
1383
|
+
const pathArg = path ?? (Array2.isArray(optionsOrPath) ? optionsOrPath : void 0);
|
|
1384
|
+
return exploreImpl(builder, options, pathArg);
|
|
490
1385
|
}
|
|
1386
|
+
}
|
|
1387
|
+
var destroyImpl = (builder) => {
|
|
1388
|
+
const internal = builder;
|
|
1389
|
+
internal._subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
1390
|
+
internal._subscriptions.clear();
|
|
491
1391
|
};
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
import { effect } from "@preact/signals-core";
|
|
496
|
-
import * as Array from "effect/Array";
|
|
497
|
-
import * as Function2 from "effect/Function";
|
|
498
|
-
import * as Option2 from "effect/Option";
|
|
499
|
-
import * as Record2 from "effect/Record";
|
|
500
|
-
import { log as log2 } from "@dxos/log";
|
|
501
|
-
import { byPosition, getDebugName, isNode, isNonNullable as isNonNullable2 } from "@dxos/util";
|
|
502
|
-
|
|
503
|
-
// src/node.ts
|
|
504
|
-
var isGraphNode = (data) => data && typeof data === "object" && "id" in data && "properties" in data && data.properties ? typeof data.properties === "object" && "data" in data : false;
|
|
505
|
-
var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" && data.type === ACTION_TYPE : false;
|
|
506
|
-
var actionGroupSymbol = Symbol("ActionGroup");
|
|
507
|
-
var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol && data.type === ACTION_GROUP_TYPE : false;
|
|
508
|
-
var isActionLike = (data) => isAction(data) || isActionGroup(data);
|
|
509
|
-
|
|
510
|
-
// src/graph-builder.ts
|
|
511
|
-
function _define_property2(obj, key, value) {
|
|
512
|
-
if (key in obj) {
|
|
513
|
-
Object.defineProperty(obj, key, {
|
|
514
|
-
value,
|
|
515
|
-
enumerable: true,
|
|
516
|
-
configurable: true,
|
|
517
|
-
writable: true
|
|
518
|
-
});
|
|
1392
|
+
function destroy(builder) {
|
|
1393
|
+
if (builder === void 0) {
|
|
1394
|
+
return (builder2) => destroyImpl(builder2);
|
|
519
1395
|
} else {
|
|
520
|
-
|
|
1396
|
+
return destroyImpl(builder);
|
|
521
1397
|
}
|
|
522
|
-
return obj;
|
|
523
1398
|
}
|
|
524
|
-
var
|
|
525
|
-
|
|
526
|
-
|
|
1399
|
+
var flush = (builder) => {
|
|
1400
|
+
return builder._flushPromise;
|
|
1401
|
+
};
|
|
1402
|
+
var createExtensionRaw = (extension) => {
|
|
1403
|
+
const { id, position = "static", relation: relation2 = "child", resolver: _resolver, connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
|
|
1404
|
+
const normalizedRelation = normalizeRelation(relation2);
|
|
527
1405
|
const getId = (key) => `${id}/${key}`;
|
|
528
|
-
const resolver = _resolver &&
|
|
529
|
-
const connector = _connector &&
|
|
530
|
-
const actionGroups = _actionGroups &&
|
|
531
|
-
const actions = _actions &&
|
|
1406
|
+
const resolver = _resolver && Atom3.family((id2) => _resolver(id2).pipe(Atom3.withLabel(`graph-builder:_resolver:${id2}`)));
|
|
1407
|
+
const connector = _connector && Atom3.family((node) => _connector(node).pipe(Atom3.withLabel(`graph-builder:_connector:${id}`)));
|
|
1408
|
+
const actionGroups = _actionGroups && Atom3.family((node) => _actionGroups(node).pipe(Atom3.withLabel(`graph-builder:_actionGroups:${id}`)));
|
|
1409
|
+
const actions = _actions && Atom3.family((node) => _actions(node).pipe(Atom3.withLabel(`graph-builder:_actions:${id}`)));
|
|
532
1410
|
return [
|
|
533
1411
|
resolver ? {
|
|
534
1412
|
id: getId("resolver"),
|
|
@@ -538,77 +1416,148 @@ var createExtension = (extension) => {
|
|
|
538
1416
|
connector ? {
|
|
539
1417
|
id: getId("connector"),
|
|
540
1418
|
position,
|
|
541
|
-
relation,
|
|
542
|
-
connector:
|
|
1419
|
+
relation: normalizedRelation,
|
|
1420
|
+
connector: Atom3.family((node) => Atom3.make((get2) => {
|
|
543
1421
|
try {
|
|
544
1422
|
return get2(connector(node));
|
|
545
|
-
} catch {
|
|
1423
|
+
} catch (error) {
|
|
546
1424
|
log2.warn("Error in connector", {
|
|
547
1425
|
id: getId("connector"),
|
|
548
|
-
node
|
|
1426
|
+
node,
|
|
1427
|
+
error
|
|
549
1428
|
}, {
|
|
550
|
-
F:
|
|
551
|
-
L:
|
|
1429
|
+
F: __dxlog_file3,
|
|
1430
|
+
L: 596,
|
|
552
1431
|
S: void 0,
|
|
553
1432
|
C: (f, a) => f(...a)
|
|
554
1433
|
});
|
|
555
1434
|
return [];
|
|
556
1435
|
}
|
|
557
|
-
}).pipe(
|
|
1436
|
+
}).pipe(Atom3.withLabel(`graph-builder:connector:${id}`)))
|
|
558
1437
|
} : void 0,
|
|
559
1438
|
actionGroups ? {
|
|
560
1439
|
id: getId("actionGroups"),
|
|
561
1440
|
position,
|
|
562
|
-
relation:
|
|
563
|
-
connector:
|
|
1441
|
+
relation: actionRelation(),
|
|
1442
|
+
connector: Atom3.family((node) => Atom3.make((get2) => {
|
|
564
1443
|
try {
|
|
565
1444
|
return get2(actionGroups(node)).map((arg) => ({
|
|
566
1445
|
...arg,
|
|
567
1446
|
data: actionGroupSymbol,
|
|
568
|
-
type:
|
|
1447
|
+
type: ActionGroupType
|
|
569
1448
|
}));
|
|
570
|
-
} catch {
|
|
1449
|
+
} catch (error) {
|
|
571
1450
|
log2.warn("Error in actionGroups", {
|
|
572
1451
|
id: getId("actionGroups"),
|
|
573
|
-
node
|
|
1452
|
+
node,
|
|
1453
|
+
error
|
|
574
1454
|
}, {
|
|
575
|
-
F:
|
|
576
|
-
L:
|
|
1455
|
+
F: __dxlog_file3,
|
|
1456
|
+
L: 617,
|
|
577
1457
|
S: void 0,
|
|
578
1458
|
C: (f, a) => f(...a)
|
|
579
1459
|
});
|
|
580
1460
|
return [];
|
|
581
1461
|
}
|
|
582
|
-
}).pipe(
|
|
1462
|
+
}).pipe(Atom3.withLabel(`graph-builder:connector:actionGroups:${id}`)))
|
|
583
1463
|
} : void 0,
|
|
584
1464
|
actions ? {
|
|
585
1465
|
id: getId("actions"),
|
|
586
1466
|
position,
|
|
587
|
-
relation:
|
|
588
|
-
connector:
|
|
1467
|
+
relation: actionRelation(),
|
|
1468
|
+
connector: Atom3.family((node) => Atom3.make((get2) => {
|
|
589
1469
|
try {
|
|
590
1470
|
return get2(actions(node)).map((arg) => ({
|
|
591
1471
|
...arg,
|
|
592
|
-
type:
|
|
1472
|
+
type: ActionType
|
|
593
1473
|
}));
|
|
594
|
-
} catch {
|
|
1474
|
+
} catch (error) {
|
|
595
1475
|
log2.warn("Error in actions", {
|
|
596
1476
|
id: getId("actions"),
|
|
597
|
-
node
|
|
1477
|
+
node,
|
|
1478
|
+
error
|
|
598
1479
|
}, {
|
|
599
|
-
F:
|
|
600
|
-
L:
|
|
1480
|
+
F: __dxlog_file3,
|
|
1481
|
+
L: 634,
|
|
601
1482
|
S: void 0,
|
|
602
1483
|
C: (f, a) => f(...a)
|
|
603
1484
|
});
|
|
604
1485
|
return [];
|
|
605
1486
|
}
|
|
606
|
-
}).pipe(
|
|
1487
|
+
}).pipe(Atom3.withLabel(`graph-builder:connector:actions:${id}`)))
|
|
607
1488
|
} : void 0
|
|
608
1489
|
].filter(isNonNullable2);
|
|
609
1490
|
};
|
|
1491
|
+
var runEffectSyncWithFallback = (effect, context2, extensionId, fallback) => {
|
|
1492
|
+
return Effect.runSync(effect.pipe(Effect.provide(context2), Effect.catchAll((error) => {
|
|
1493
|
+
log2.warn("Extension failed", {
|
|
1494
|
+
extension: extensionId,
|
|
1495
|
+
error
|
|
1496
|
+
}, {
|
|
1497
|
+
F: __dxlog_file3,
|
|
1498
|
+
L: 677,
|
|
1499
|
+
S: void 0,
|
|
1500
|
+
C: (f, a) => f(...a)
|
|
1501
|
+
});
|
|
1502
|
+
return Effect.succeed(fallback);
|
|
1503
|
+
})));
|
|
1504
|
+
};
|
|
1505
|
+
var createExtension = (options) => Effect.map(Effect.context(), (context2) => {
|
|
1506
|
+
const { id, match: match3, actions, connector, resolver, relation: relation2, position } = options;
|
|
1507
|
+
const connectorExtension = connector ? createConnectorWithRuntime(id, match3, connector, context2) : void 0;
|
|
1508
|
+
const actionsExtension = actions ? (node) => Atom3.make((get2) => Function2.pipe(get2(node), Option3.flatMap(match3), Option3.map((matched) => runEffectSyncWithFallback(actions(matched, get2), context2, id, []).map((action) => ({
|
|
1509
|
+
...action,
|
|
1510
|
+
// Attach captured context for action execution.
|
|
1511
|
+
_actionContext: context2
|
|
1512
|
+
}))), Option3.getOrElse(() => []))) : void 0;
|
|
1513
|
+
const resolverExtension = resolver ? (nodeId) => Atom3.make((get2) => runEffectSyncWithFallback(resolver(nodeId, get2), context2, id, null) ?? null) : void 0;
|
|
1514
|
+
return createExtensionRaw({
|
|
1515
|
+
id,
|
|
1516
|
+
relation: relation2,
|
|
1517
|
+
position,
|
|
1518
|
+
connector: connectorExtension,
|
|
1519
|
+
actions: actionsExtension,
|
|
1520
|
+
resolver: resolverExtension
|
|
1521
|
+
});
|
|
1522
|
+
});
|
|
1523
|
+
var createConnector = (matcher, factory) => {
|
|
1524
|
+
return (node) => Atom3.make((get2) => Function2.pipe(get2(node), Option3.flatMap(matcher), Option3.map((data) => factory(data, get2)), Option3.getOrElse(() => [])));
|
|
1525
|
+
};
|
|
1526
|
+
var createConnectorWithRuntime = (extensionId, matcher, factory, context2) => {
|
|
1527
|
+
return (node) => Atom3.make((get2) => Function2.pipe(get2(node), Option3.flatMap(matcher), Option3.map((data) => runEffectSyncWithFallback(factory(data, get2), context2, extensionId, [])), Option3.getOrElse(() => [])));
|
|
1528
|
+
};
|
|
1529
|
+
var createTypeExtension = (options) => {
|
|
1530
|
+
const { id, type, actions, connector, relation: relation2, position } = options;
|
|
1531
|
+
return createExtension({
|
|
1532
|
+
id,
|
|
1533
|
+
match: whenEchoType(type),
|
|
1534
|
+
actions,
|
|
1535
|
+
connector,
|
|
1536
|
+
relation: relation2,
|
|
1537
|
+
position
|
|
1538
|
+
});
|
|
1539
|
+
};
|
|
1540
|
+
var qualifyNodeArgs = (parentId) => (nodes) => nodes.map((node) => {
|
|
1541
|
+
validateSegmentId(node.id);
|
|
1542
|
+
const qualified = qualifyId(parentId, node.id);
|
|
1543
|
+
return {
|
|
1544
|
+
...node,
|
|
1545
|
+
id: qualified,
|
|
1546
|
+
nodes: node.nodes ? qualifyNodeArgs(qualified)(node.nodes) : void 0
|
|
1547
|
+
};
|
|
1548
|
+
});
|
|
1549
|
+
var connectorKey = (id, relation2) => `${id}${Separators.primary}${relationKey(relation2)}`;
|
|
1550
|
+
var relationFromConnectorKey = (key) => {
|
|
1551
|
+
const separatorIndex = key.indexOf(Separators.primary);
|
|
1552
|
+
const id = key.slice(0, separatorIndex);
|
|
1553
|
+
return {
|
|
1554
|
+
id,
|
|
1555
|
+
relation: relationFromKey(key.slice(separatorIndex + 1))
|
|
1556
|
+
};
|
|
1557
|
+
};
|
|
1558
|
+
var subscriptionKey = (id, kind, detail) => detail != null ? `${id}${Separators.primary}${kind}${Separators.primary}${detail}` : `${id}${Separators.primary}${kind}`;
|
|
610
1559
|
var flattenExtensions = (extension, acc = []) => {
|
|
611
|
-
if (
|
|
1560
|
+
if (Array2.isArray(extension)) {
|
|
612
1561
|
return [
|
|
613
1562
|
...acc,
|
|
614
1563
|
...extension.flatMap((ext) => flattenExtensions(ext, acc))
|
|
@@ -620,239 +1569,11 @@ var flattenExtensions = (extension, acc = []) => {
|
|
|
620
1569
|
];
|
|
621
1570
|
}
|
|
622
1571
|
};
|
|
623
|
-
var GraphBuilder = class _GraphBuilder {
|
|
624
|
-
static from(pickle, registry) {
|
|
625
|
-
if (!pickle) {
|
|
626
|
-
return new _GraphBuilder({
|
|
627
|
-
registry
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
const { nodes, edges } = JSON.parse(pickle);
|
|
631
|
-
return new _GraphBuilder({
|
|
632
|
-
nodes,
|
|
633
|
-
edges,
|
|
634
|
-
registry
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
get graph() {
|
|
638
|
-
return this._graph;
|
|
639
|
-
}
|
|
640
|
-
get extensions() {
|
|
641
|
-
return this._extensions;
|
|
642
|
-
}
|
|
643
|
-
addExtension(extensions) {
|
|
644
|
-
flattenExtensions(extensions).forEach((extension) => {
|
|
645
|
-
const extensions2 = this._registry.get(this._extensions);
|
|
646
|
-
this._registry.set(this._extensions, Record2.set(extensions2, extension.id, extension));
|
|
647
|
-
});
|
|
648
|
-
return this;
|
|
649
|
-
}
|
|
650
|
-
removeExtension(id) {
|
|
651
|
-
const extensions = this._registry.get(this._extensions);
|
|
652
|
-
this._registry.set(this._extensions, Record2.remove(extensions, id));
|
|
653
|
-
return this;
|
|
654
|
-
}
|
|
655
|
-
async explore({ registry = Registry2.make(), source = ROOT_ID, relation = "outbound", visitor }, path = []) {
|
|
656
|
-
if (path.includes(source)) {
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
if (!isNode()) {
|
|
660
|
-
const { yieldOrContinue } = await import("main-thread-scheduling");
|
|
661
|
-
await yieldOrContinue("idle");
|
|
662
|
-
}
|
|
663
|
-
const node = registry.get(this._graph.nodeOrThrow(source));
|
|
664
|
-
const shouldContinue = await visitor(node, [
|
|
665
|
-
...path,
|
|
666
|
-
node.id
|
|
667
|
-
]);
|
|
668
|
-
if (shouldContinue === false) {
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
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))));
|
|
672
|
-
await Promise.all(nodes.map((nodeArg) => {
|
|
673
|
-
registry.set(this._graph._node(nodeArg.id), this._graph._constructNode(nodeArg));
|
|
674
|
-
return this.explore({
|
|
675
|
-
registry,
|
|
676
|
-
source: nodeArg.id,
|
|
677
|
-
relation,
|
|
678
|
-
visitor
|
|
679
|
-
}, [
|
|
680
|
-
...path,
|
|
681
|
-
node.id
|
|
682
|
-
]);
|
|
683
|
-
}));
|
|
684
|
-
if (registry !== this._registry) {
|
|
685
|
-
registry.reset();
|
|
686
|
-
registry.dispose();
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
destroy() {
|
|
690
|
-
this._subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
691
|
-
this._subscriptions.clear();
|
|
692
|
-
}
|
|
693
|
-
_onExpand(id, relation) {
|
|
694
|
-
log2("onExpand", {
|
|
695
|
-
id,
|
|
696
|
-
relation,
|
|
697
|
-
registry: getDebugName(this._registry)
|
|
698
|
-
}, {
|
|
699
|
-
F: __dxlog_file2,
|
|
700
|
-
L: 327,
|
|
701
|
-
S: this,
|
|
702
|
-
C: (f, a) => f(...a)
|
|
703
|
-
});
|
|
704
|
-
const connectors = this._connectors(`${id}+${relation}`);
|
|
705
|
-
let previous = [];
|
|
706
|
-
const cancel = this._registry.subscribe(connectors, (nodes) => {
|
|
707
|
-
const ids = nodes.map((n) => n.id);
|
|
708
|
-
const removed = previous.filter((id2) => !ids.includes(id2));
|
|
709
|
-
previous = ids;
|
|
710
|
-
log2("update", {
|
|
711
|
-
id,
|
|
712
|
-
relation,
|
|
713
|
-
ids,
|
|
714
|
-
removed
|
|
715
|
-
}, {
|
|
716
|
-
F: __dxlog_file2,
|
|
717
|
-
L: 338,
|
|
718
|
-
S: this,
|
|
719
|
-
C: (f, a) => f(...a)
|
|
720
|
-
});
|
|
721
|
-
const update = () => {
|
|
722
|
-
Rx2.batch(() => {
|
|
723
|
-
this._graph.removeEdges(removed.map((target) => ({
|
|
724
|
-
source: id,
|
|
725
|
-
target
|
|
726
|
-
})), true);
|
|
727
|
-
this._graph.addNodes(nodes);
|
|
728
|
-
this._graph.addEdges(nodes.map((node) => relation === "outbound" ? {
|
|
729
|
-
source: id,
|
|
730
|
-
target: node.id
|
|
731
|
-
} : {
|
|
732
|
-
source: node.id,
|
|
733
|
-
target: id
|
|
734
|
-
}));
|
|
735
|
-
this._graph.sortEdges(id, relation, nodes.map(({ id: id2 }) => id2));
|
|
736
|
-
});
|
|
737
|
-
};
|
|
738
|
-
if (typeof requestAnimationFrame === "function") {
|
|
739
|
-
requestAnimationFrame(update);
|
|
740
|
-
} else {
|
|
741
|
-
update();
|
|
742
|
-
}
|
|
743
|
-
}, {
|
|
744
|
-
immediate: true
|
|
745
|
-
});
|
|
746
|
-
this._subscriptions.set(id, cancel);
|
|
747
|
-
}
|
|
748
|
-
// TODO(wittjosiah): If the same node is added by a connector, the resolver should probably cancel itself?
|
|
749
|
-
async _onInitialize(id) {
|
|
750
|
-
log2("onInitialize", {
|
|
751
|
-
id
|
|
752
|
-
}, {
|
|
753
|
-
F: __dxlog_file2,
|
|
754
|
-
L: 375,
|
|
755
|
-
S: this,
|
|
756
|
-
C: (f, a) => f(...a)
|
|
757
|
-
});
|
|
758
|
-
const resolver = this._resolvers(id);
|
|
759
|
-
const cancel = this._registry.subscribe(resolver, (node) => {
|
|
760
|
-
const trigger = this._initialized[id];
|
|
761
|
-
Option2.match(node, {
|
|
762
|
-
onSome: (node2) => {
|
|
763
|
-
this._graph.addNodes([
|
|
764
|
-
node2
|
|
765
|
-
]);
|
|
766
|
-
trigger?.wake();
|
|
767
|
-
},
|
|
768
|
-
onNone: () => {
|
|
769
|
-
trigger?.wake();
|
|
770
|
-
this._graph.removeNodes([
|
|
771
|
-
id
|
|
772
|
-
]);
|
|
773
|
-
}
|
|
774
|
-
});
|
|
775
|
-
}, {
|
|
776
|
-
immediate: true
|
|
777
|
-
});
|
|
778
|
-
this._subscriptions.set(id, cancel);
|
|
779
|
-
}
|
|
780
|
-
_onRemoveNode(id) {
|
|
781
|
-
this._subscriptions.get(id)?.();
|
|
782
|
-
this._subscriptions.delete(id);
|
|
783
|
-
}
|
|
784
|
-
constructor({ registry, ...params } = {}) {
|
|
785
|
-
_define_property2(this, "_subscriptions", /* @__PURE__ */ new Map());
|
|
786
|
-
_define_property2(this, "_extensions", Rx2.make(Record2.empty()).pipe(Rx2.keepAlive, Rx2.withLabel("graph-builder:extensions")));
|
|
787
|
-
_define_property2(this, "_initialized", {});
|
|
788
|
-
_define_property2(this, "_registry", void 0);
|
|
789
|
-
_define_property2(this, "_graph", void 0);
|
|
790
|
-
_define_property2(this, "_resolvers", Rx2.family((id) => {
|
|
791
|
-
return Rx2.make((get2) => {
|
|
792
|
-
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);
|
|
793
|
-
});
|
|
794
|
-
}));
|
|
795
|
-
_define_property2(this, "_connectors", Rx2.family((key) => {
|
|
796
|
-
return Rx2.make((get2) => {
|
|
797
|
-
const [id, relation] = key.split("+");
|
|
798
|
-
const node = this._graph.node(id);
|
|
799
|
-
return Function2.pipe(
|
|
800
|
-
get2(this._extensions),
|
|
801
|
-
Record2.values,
|
|
802
|
-
// TODO(wittjosiah): Sort on write rather than read.
|
|
803
|
-
Array.sortBy(byPosition),
|
|
804
|
-
Array.filter(({ relation: _relation = "outbound" }) => _relation === relation),
|
|
805
|
-
Array.map(({ connector }) => connector?.(node)),
|
|
806
|
-
Array.filter(isNonNullable2),
|
|
807
|
-
Array.flatMap((result) => get2(result))
|
|
808
|
-
);
|
|
809
|
-
}).pipe(Rx2.withLabel(`graph-builder:connectors:${key}`));
|
|
810
|
-
}));
|
|
811
|
-
this._registry = registry ?? Registry2.make();
|
|
812
|
-
this._graph = new Graph({
|
|
813
|
-
...params,
|
|
814
|
-
registry: this._registry,
|
|
815
|
-
onExpand: (id, relation) => this._onExpand(id, relation),
|
|
816
|
-
onInitialize: (id) => this._onInitialize(id),
|
|
817
|
-
onRemoveNode: (id) => this._onRemoveNode(id)
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
};
|
|
821
|
-
var rxFromSignal = (cb) => {
|
|
822
|
-
return Rx2.make((get2) => {
|
|
823
|
-
const dispose = effect(() => {
|
|
824
|
-
get2.setSelf(cb());
|
|
825
|
-
});
|
|
826
|
-
get2.addFinalizer(() => dispose());
|
|
827
|
-
return cb();
|
|
828
|
-
});
|
|
829
|
-
};
|
|
830
|
-
var observableFamily = Rx2.family((observable) => {
|
|
831
|
-
return Rx2.make((get2) => {
|
|
832
|
-
const subscription = observable.subscribe((value) => get2.setSelf(value));
|
|
833
|
-
get2.addFinalizer(() => subscription.unsubscribe());
|
|
834
|
-
return observable.get();
|
|
835
|
-
});
|
|
836
|
-
});
|
|
837
|
-
var rxFromObservable = (observable) => {
|
|
838
|
-
return observableFamily(observable);
|
|
839
|
-
};
|
|
840
1572
|
export {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
ROOT_TYPE,
|
|
847
|
-
actionGroupSymbol,
|
|
848
|
-
createExtension,
|
|
849
|
-
flattenExtensions,
|
|
850
|
-
getGraph,
|
|
851
|
-
isAction,
|
|
852
|
-
isActionGroup,
|
|
853
|
-
isActionLike,
|
|
854
|
-
isGraphNode,
|
|
855
|
-
rxFromObservable,
|
|
856
|
-
rxFromSignal
|
|
1573
|
+
atoms_exports as CreateAtom,
|
|
1574
|
+
graph_exports as Graph,
|
|
1575
|
+
graph_builder_exports as GraphBuilder,
|
|
1576
|
+
node_exports as Node,
|
|
1577
|
+
node_matcher_exports as NodeMatcher
|
|
857
1578
|
};
|
|
858
1579
|
//# sourceMappingURL=index.mjs.map
|