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