@dxos/app-graph 0.8.2-staging.7ac8446 → 0.8.2
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 +593 -794
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +585 -785
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +593 -794
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/experimental/graph-projections.test.d.ts +25 -0
- package/dist/types/src/experimental/graph-projections.test.d.ts.map +1 -0
- package/dist/types/src/graph-builder.d.ts +48 -91
- package/dist/types/src/graph-builder.d.ts.map +1 -1
- package/dist/types/src/graph.d.ts +191 -98
- package/dist/types/src/graph.d.ts.map +1 -1
- package/dist/types/src/node.d.ts +3 -3
- package/dist/types/src/node.d.ts.map +1 -1
- package/dist/types/src/signals-integration.test.d.ts +2 -0
- package/dist/types/src/signals-integration.test.d.ts.map +1 -0
- package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
- package/dist/types/src/testing.d.ts +5 -0
- package/dist/types/src/testing.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -17
- package/src/experimental/graph-projections.test.ts +56 -0
- package/src/graph-builder.test.ts +293 -310
- package/src/graph-builder.ts +235 -318
- package/src/graph.test.ts +314 -463
- package/src/graph.ts +452 -455
- package/src/node.ts +4 -4
- package/src/signals-integration.test.ts +218 -0
- package/src/stories/EchoGraph.stories.tsx +67 -76
- package/src/testing.ts +20 -0
package/dist/lib/node/index.cjs
CHANGED
|
@@ -35,7 +35,6 @@ __export(node_exports, {
|
|
|
35
35
|
ROOT_ID: () => ROOT_ID,
|
|
36
36
|
ROOT_TYPE: () => ROOT_TYPE,
|
|
37
37
|
actionGroupSymbol: () => actionGroupSymbol,
|
|
38
|
-
cleanup: () => cleanup,
|
|
39
38
|
createExtension: () => createExtension,
|
|
40
39
|
flattenExtensions: () => flattenExtensions,
|
|
41
40
|
getGraph: () => getGraph,
|
|
@@ -43,34 +42,29 @@ __export(node_exports, {
|
|
|
43
42
|
isActionGroup: () => isActionGroup,
|
|
44
43
|
isActionLike: () => isActionLike,
|
|
45
44
|
isGraphNode: () => isGraphNode,
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
rxFromObservable: () => rxFromObservable,
|
|
46
|
+
rxFromSignal: () => rxFromSignal
|
|
48
47
|
});
|
|
49
48
|
module.exports = __toCommonJS(node_exports);
|
|
50
|
-
var
|
|
49
|
+
var import_rx_react = require("@effect-rx/rx-react");
|
|
50
|
+
var import_effect = require("effect");
|
|
51
51
|
var import_async = require("@dxos/async");
|
|
52
|
+
var import_debug = require("@dxos/debug");
|
|
52
53
|
var import_invariant = require("@dxos/invariant");
|
|
53
|
-
var import_live_object = require("@dxos/live-object");
|
|
54
54
|
var import_log = require("@dxos/log");
|
|
55
55
|
var import_util = require("@dxos/util");
|
|
56
|
-
var
|
|
57
|
-
var
|
|
58
|
-
var
|
|
59
|
-
var import_live_object2 = require("@dxos/live-object");
|
|
56
|
+
var import_rx_react2 = require("@effect-rx/rx-react");
|
|
57
|
+
var import_signals_core = require("@preact/signals-core");
|
|
58
|
+
var import_effect2 = require("effect");
|
|
60
59
|
var import_log2 = require("@dxos/log");
|
|
61
60
|
var import_util2 = require("@dxos/util");
|
|
62
|
-
var isGraphNode = (data) => data && typeof data === "object" && "id" in data && "properties" in data && data.properties ? typeof data.properties === "object" && "data" in data : false;
|
|
63
|
-
var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" : false;
|
|
64
|
-
var actionGroupSymbol = Symbol("ActionGroup");
|
|
65
|
-
var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol : false;
|
|
66
|
-
var isActionLike = (data) => isAction(data) || isActionGroup(data);
|
|
67
61
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
|
|
68
62
|
var graphSymbol = Symbol("graph");
|
|
69
63
|
var getGraph = (node) => {
|
|
70
64
|
const graph = node[graphSymbol];
|
|
71
65
|
(0, import_invariant.invariant)(graph, "Node is not associated with a graph.", {
|
|
72
66
|
F: __dxlog_file,
|
|
73
|
-
L:
|
|
67
|
+
L: 25,
|
|
74
68
|
S: void 0,
|
|
75
69
|
A: [
|
|
76
70
|
"graph",
|
|
@@ -83,637 +77,531 @@ var ROOT_ID = "root";
|
|
|
83
77
|
var ROOT_TYPE = "dxos.org/type/GraphRoot";
|
|
84
78
|
var ACTION_TYPE = "dxos.org/type/GraphAction";
|
|
85
79
|
var ACTION_GROUP_TYPE = "dxos.org/type/GraphActionGroup";
|
|
86
|
-
var
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
this.
|
|
90
|
-
this._initialized =
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
80
|
+
var Graph = class {
|
|
81
|
+
constructor({ registry, nodes, edges, onExpand, onRemoveNode } = {}) {
|
|
82
|
+
this.onNodeChanged = new import_async.Event();
|
|
83
|
+
this._expanded = import_effect.Record.empty();
|
|
84
|
+
this._initialized = import_effect.Record.empty();
|
|
85
|
+
this._initialEdges = import_effect.Record.empty();
|
|
86
|
+
this._initialNodes = import_effect.Record.fromEntries([
|
|
87
|
+
[
|
|
88
|
+
ROOT_ID,
|
|
89
|
+
this._constructNode({
|
|
90
|
+
id: ROOT_ID,
|
|
91
|
+
type: ROOT_TYPE,
|
|
92
|
+
data: null,
|
|
93
|
+
properties: {}
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
]);
|
|
97
|
+
this._node = import_rx_react.Rx.family((id) => {
|
|
98
|
+
const initial = import_effect.Option.flatten(import_effect.Record.get(this._initialNodes, id));
|
|
99
|
+
return import_rx_react.Rx.make(initial).pipe(import_rx_react.Rx.keepAlive, import_rx_react.Rx.withLabel(`graph:node:${id}`));
|
|
100
|
+
});
|
|
101
|
+
this._nodeOrThrow = import_rx_react.Rx.family((id) => {
|
|
102
|
+
return import_rx_react.Rx.make((get) => {
|
|
103
|
+
const node = get(this._node(id));
|
|
104
|
+
(0, import_invariant.invariant)(import_effect.Option.isSome(node), `Node not available: ${id}`, {
|
|
105
|
+
F: __dxlog_file,
|
|
106
|
+
L: 253,
|
|
107
|
+
S: this,
|
|
108
|
+
A: [
|
|
109
|
+
"Option.isSome(node)",
|
|
110
|
+
"`Node not available: ${id}`"
|
|
111
|
+
]
|
|
112
|
+
});
|
|
113
|
+
return node.value;
|
|
97
114
|
});
|
|
98
|
-
};
|
|
99
|
-
this._onInitialNode = onInitialNode;
|
|
100
|
-
this._onInitialNodes = onInitialNodes;
|
|
101
|
-
this._onRemoveNode = onRemoveNode;
|
|
102
|
-
this._nodes[ROOT_ID] = this._constructNode({
|
|
103
|
-
id: ROOT_ID,
|
|
104
|
-
type: ROOT_TYPE,
|
|
105
|
-
cacheable: [],
|
|
106
|
-
properties: {},
|
|
107
|
-
data: null
|
|
108
115
|
});
|
|
116
|
+
this._edges = import_rx_react.Rx.family((id) => {
|
|
117
|
+
const initial = import_effect.Record.get(this._initialEdges, id).pipe(import_effect.Option.getOrElse(() => ({
|
|
118
|
+
inbound: [],
|
|
119
|
+
outbound: []
|
|
120
|
+
})));
|
|
121
|
+
return import_rx_react.Rx.make(initial).pipe(import_rx_react.Rx.keepAlive, import_rx_react.Rx.withLabel(`graph:edges:${id}`));
|
|
122
|
+
});
|
|
123
|
+
this._connections = import_rx_react.Rx.family((key) => {
|
|
124
|
+
return import_rx_react.Rx.make((get) => {
|
|
125
|
+
const [id, relation] = key.split("$");
|
|
126
|
+
const edges2 = get(this._edges(id));
|
|
127
|
+
return edges2[relation].map((id2) => get(this._node(id2))).filter(import_effect.Option.isSome).map((o) => o.value);
|
|
128
|
+
}).pipe(import_rx_react.Rx.withLabel(`graph:connections:${key}`));
|
|
129
|
+
});
|
|
130
|
+
this._actions = import_rx_react.Rx.family((id) => {
|
|
131
|
+
return import_rx_react.Rx.make((get) => {
|
|
132
|
+
return get(this._connections(`${id}$outbound`)).filter((node) => node.type === ACTION_TYPE || node.type === ACTION_GROUP_TYPE);
|
|
133
|
+
}).pipe(import_rx_react.Rx.withLabel(`graph:actions:${id}`));
|
|
134
|
+
});
|
|
135
|
+
this._json = import_rx_react.Rx.family((id) => {
|
|
136
|
+
return import_rx_react.Rx.make((get) => {
|
|
137
|
+
const toJSON = (node, seen = []) => {
|
|
138
|
+
const nodes2 = get(this.connections(node.id));
|
|
139
|
+
const obj = {
|
|
140
|
+
id: node.id.length > 32 ? `${node.id.slice(0, 32)}...` : node.id,
|
|
141
|
+
type: node.type
|
|
142
|
+
};
|
|
143
|
+
if (node.properties.label) {
|
|
144
|
+
obj.label = node.properties.label;
|
|
145
|
+
}
|
|
146
|
+
if (nodes2.length) {
|
|
147
|
+
obj.nodes = nodes2.map((n) => {
|
|
148
|
+
const nextSeen = [
|
|
149
|
+
...seen,
|
|
150
|
+
node.id
|
|
151
|
+
];
|
|
152
|
+
return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
|
|
153
|
+
}).filter(import_util.isNonNullable);
|
|
154
|
+
}
|
|
155
|
+
return obj;
|
|
156
|
+
};
|
|
157
|
+
const root = get(this.nodeOrThrow(id));
|
|
158
|
+
return toJSON(root);
|
|
159
|
+
}).pipe(import_rx_react.Rx.withLabel(`graph:json:${id}`));
|
|
160
|
+
});
|
|
161
|
+
this._registry = registry ?? import_rx_react.Registry.make();
|
|
162
|
+
this._onExpand = onExpand;
|
|
163
|
+
this._onRemoveNode = onRemoveNode;
|
|
109
164
|
if (nodes) {
|
|
110
165
|
nodes.forEach((node) => {
|
|
111
|
-
|
|
112
|
-
if (node.type === ACTION_TYPE) {
|
|
113
|
-
this._addNode({
|
|
114
|
-
cacheable,
|
|
115
|
-
data: () => import_log.log.warn("Pickled action invocation", void 0, {
|
|
116
|
-
F: __dxlog_file,
|
|
117
|
-
L: 113,
|
|
118
|
-
S: this,
|
|
119
|
-
C: (f, a) => f(...a)
|
|
120
|
-
}),
|
|
121
|
-
...node
|
|
122
|
-
});
|
|
123
|
-
} else if (node.type === ACTION_GROUP_TYPE) {
|
|
124
|
-
this._addNode({
|
|
125
|
-
cacheable,
|
|
126
|
-
data: actionGroupSymbol,
|
|
127
|
-
...node
|
|
128
|
-
});
|
|
129
|
-
} else {
|
|
130
|
-
this._addNode({
|
|
131
|
-
cacheable,
|
|
132
|
-
...node
|
|
133
|
-
});
|
|
134
|
-
}
|
|
166
|
+
import_effect.Record.set(this._initialNodes, node.id, this._constructNode(node));
|
|
135
167
|
});
|
|
136
168
|
}
|
|
137
|
-
this._edges[ROOT_ID] = (0, import_live_object.create)({
|
|
138
|
-
inbound: [],
|
|
139
|
-
outbound: []
|
|
140
|
-
});
|
|
141
169
|
if (edges) {
|
|
142
170
|
Object.entries(edges).forEach(([source, edges2]) => {
|
|
143
|
-
|
|
144
|
-
this._addEdge({
|
|
145
|
-
source,
|
|
146
|
-
target
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
this._sortEdges(source, "outbound", edges2);
|
|
171
|
+
import_effect.Record.set(this._initialEdges, source, edges2);
|
|
150
172
|
});
|
|
151
173
|
}
|
|
152
174
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
175
|
+
toJSON(id = ROOT_ID) {
|
|
176
|
+
return this._registry.get(this._json(id));
|
|
177
|
+
}
|
|
178
|
+
json(id = ROOT_ID) {
|
|
179
|
+
return this._json(id);
|
|
180
|
+
}
|
|
181
|
+
node(id) {
|
|
182
|
+
return this._node(id);
|
|
183
|
+
}
|
|
184
|
+
nodeOrThrow(id) {
|
|
185
|
+
return this._nodeOrThrow(id);
|
|
186
|
+
}
|
|
187
|
+
connections(id, relation = "outbound") {
|
|
188
|
+
return this._connections(`${id}$${relation}`);
|
|
189
|
+
}
|
|
190
|
+
actions(id) {
|
|
191
|
+
return this._actions(id);
|
|
192
|
+
}
|
|
193
|
+
edges(id) {
|
|
194
|
+
return this._edges(id);
|
|
160
195
|
}
|
|
161
|
-
/**
|
|
162
|
-
* Alias for `findNode('root')`.
|
|
163
|
-
*/
|
|
164
196
|
get root() {
|
|
165
|
-
return this.
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
197
|
+
return this.getNodeOrThrow(ROOT_ID);
|
|
198
|
+
}
|
|
199
|
+
getNode(id) {
|
|
200
|
+
return this._registry.get(this.node(id));
|
|
201
|
+
}
|
|
202
|
+
getNodeOrThrow(id) {
|
|
203
|
+
return this._registry.get(this.nodeOrThrow(id));
|
|
204
|
+
}
|
|
205
|
+
getConnections(id, relation = "outbound") {
|
|
206
|
+
return this._registry.get(this.connections(id, relation));
|
|
207
|
+
}
|
|
208
|
+
getActions(id) {
|
|
209
|
+
return this._registry.get(this.actions(id));
|
|
210
|
+
}
|
|
211
|
+
getEdges(id) {
|
|
212
|
+
return this._registry.get(this.edges(id));
|
|
213
|
+
}
|
|
214
|
+
// TODO(wittjosiah): On initialize to restore state from cache.
|
|
215
|
+
// async initialize(id: string) {
|
|
216
|
+
// const initialized = Record.get(this._initialized, id).pipe(Option.getOrElse(() => false));
|
|
217
|
+
// log('initialize', { id, initialized });
|
|
218
|
+
// if (!initialized) {
|
|
219
|
+
// await this._onInitialize?.(id);
|
|
220
|
+
// Record.set(this._initialized, id, true);
|
|
221
|
+
// }
|
|
222
|
+
// }
|
|
223
|
+
expand(id, relation = "outbound") {
|
|
224
|
+
const key = `${id}$${relation}`;
|
|
225
|
+
const expanded = import_effect.Record.get(this._expanded, key).pipe(import_effect.Option.getOrElse(() => false));
|
|
226
|
+
(0, import_log.log)("expand", {
|
|
227
|
+
key,
|
|
228
|
+
expanded
|
|
229
|
+
}, {
|
|
193
230
|
F: __dxlog_file,
|
|
194
|
-
L:
|
|
231
|
+
L: 395,
|
|
195
232
|
S: this,
|
|
196
|
-
|
|
197
|
-
"root",
|
|
198
|
-
"`Node not found: ${id}`"
|
|
199
|
-
]
|
|
233
|
+
C: (f, a) => f(...a)
|
|
200
234
|
});
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
};
|
|
235
|
+
if (!expanded) {
|
|
236
|
+
this._onExpand?.(id, relation);
|
|
237
|
+
import_effect.Record.set(this._expanded, key, true);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
addNodes(nodes) {
|
|
241
|
+
import_rx_react.Rx.batch(() => {
|
|
242
|
+
nodes.map((node) => this.addNode(node));
|
|
210
243
|
});
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
244
|
+
}
|
|
245
|
+
addNode({ nodes, edges, ...nodeArg }) {
|
|
246
|
+
const { id, type, data = null, properties = {} } = nodeArg;
|
|
247
|
+
const nodeRx = this._node(id);
|
|
248
|
+
const node = this._registry.get(nodeRx);
|
|
249
|
+
import_effect.Option.match(node, {
|
|
250
|
+
onSome: (node2) => {
|
|
251
|
+
const typeChanged = node2.type !== type;
|
|
252
|
+
const dataChanged = node2.data !== data;
|
|
253
|
+
const propertiesChanged = Object.keys(properties).some((key) => node2.properties[key] !== properties[key]);
|
|
254
|
+
(0, import_log.log)("existing node", {
|
|
255
|
+
id,
|
|
256
|
+
typeChanged,
|
|
257
|
+
dataChanged,
|
|
258
|
+
propertiesChanged
|
|
259
|
+
}, {
|
|
260
|
+
F: __dxlog_file,
|
|
261
|
+
L: 417,
|
|
262
|
+
S: this,
|
|
263
|
+
C: (f, a) => f(...a)
|
|
264
|
+
});
|
|
265
|
+
if (typeChanged || dataChanged || propertiesChanged) {
|
|
266
|
+
(0, import_log.log)("updating node", {
|
|
267
|
+
id,
|
|
268
|
+
type,
|
|
269
|
+
data,
|
|
270
|
+
properties
|
|
271
|
+
}, {
|
|
272
|
+
F: __dxlog_file,
|
|
273
|
+
L: 419,
|
|
274
|
+
S: this,
|
|
275
|
+
C: (f, a) => f(...a)
|
|
276
|
+
});
|
|
277
|
+
const newNode = import_effect.Option.some({
|
|
278
|
+
...node2,
|
|
279
|
+
type,
|
|
280
|
+
data,
|
|
281
|
+
properties: {
|
|
282
|
+
...node2.properties,
|
|
283
|
+
...properties
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
this._registry.set(nodeRx, newNode);
|
|
287
|
+
this.onNodeChanged.emit({
|
|
288
|
+
id,
|
|
289
|
+
node: newNode
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
onNone: () => {
|
|
294
|
+
(0, import_log.log)("new node", {
|
|
295
|
+
id,
|
|
296
|
+
type,
|
|
297
|
+
data,
|
|
298
|
+
properties
|
|
299
|
+
}, {
|
|
300
|
+
F: __dxlog_file,
|
|
301
|
+
L: 426,
|
|
302
|
+
S: this,
|
|
303
|
+
C: (f, a) => f(...a)
|
|
304
|
+
});
|
|
305
|
+
const newNode = this._constructNode({
|
|
306
|
+
id,
|
|
307
|
+
type,
|
|
308
|
+
data,
|
|
309
|
+
properties
|
|
310
|
+
});
|
|
311
|
+
this._registry.set(nodeRx, newNode);
|
|
312
|
+
this.onNodeChanged.emit({
|
|
313
|
+
id,
|
|
314
|
+
node: newNode
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
if (nodes) {
|
|
319
|
+
this.addNodes(nodes);
|
|
320
|
+
const _edges = nodes.map((node2) => ({
|
|
321
|
+
source: id,
|
|
322
|
+
target: node2.id
|
|
323
|
+
}));
|
|
324
|
+
this.addEdges(_edges);
|
|
325
|
+
}
|
|
326
|
+
if (edges) {
|
|
327
|
+
(0, import_debug.todo)();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
removeNodes(ids, edges = false) {
|
|
331
|
+
import_rx_react.Rx.batch(() => {
|
|
332
|
+
ids.map((id) => this.removeNode(id, edges));
|
|
219
333
|
});
|
|
220
334
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
335
|
+
removeNode(id, edges = false) {
|
|
336
|
+
const nodeRx = this._node(id);
|
|
337
|
+
this._registry.set(nodeRx, import_effect.Option.none());
|
|
338
|
+
this.onNodeChanged.emit({
|
|
339
|
+
id,
|
|
340
|
+
node: import_effect.Option.none()
|
|
341
|
+
});
|
|
342
|
+
if (edges) {
|
|
343
|
+
const { inbound, outbound } = this._registry.get(this._edges(id));
|
|
344
|
+
const edges2 = [
|
|
345
|
+
...inbound.map((source) => ({
|
|
346
|
+
source,
|
|
347
|
+
target: id
|
|
348
|
+
})),
|
|
349
|
+
...outbound.map((target) => ({
|
|
350
|
+
source: id,
|
|
351
|
+
target
|
|
352
|
+
}))
|
|
353
|
+
];
|
|
354
|
+
this.removeEdges(edges2);
|
|
231
355
|
}
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
356
|
+
this._onRemoveNode?.(id);
|
|
357
|
+
}
|
|
358
|
+
addEdges(edges) {
|
|
359
|
+
import_rx_react.Rx.batch(() => {
|
|
360
|
+
edges.map((edge) => this.addEdge(edge));
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
addEdge(edgeArg) {
|
|
364
|
+
const sourceRx = this._edges(edgeArg.source);
|
|
365
|
+
const source = this._registry.get(sourceRx);
|
|
366
|
+
if (!source.outbound.includes(edgeArg.target)) {
|
|
367
|
+
(0, import_log.log)("add outbound edge", {
|
|
368
|
+
source: edgeArg.source,
|
|
369
|
+
target: edgeArg.target
|
|
370
|
+
}, {
|
|
371
|
+
F: __dxlog_file,
|
|
372
|
+
L: 481,
|
|
373
|
+
S: this,
|
|
374
|
+
C: (f, a) => f(...a)
|
|
375
|
+
});
|
|
376
|
+
this._registry.set(sourceRx, {
|
|
377
|
+
inbound: source.inbound,
|
|
378
|
+
outbound: [
|
|
379
|
+
...source.outbound,
|
|
380
|
+
edgeArg.target
|
|
381
|
+
]
|
|
382
|
+
});
|
|
248
383
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
384
|
+
const targetRx = this._edges(edgeArg.target);
|
|
385
|
+
const target = this._registry.get(targetRx);
|
|
386
|
+
if (!target.inbound.includes(edgeArg.source)) {
|
|
387
|
+
(0, import_log.log)("add inbound edge", {
|
|
388
|
+
source: edgeArg.source,
|
|
389
|
+
target: edgeArg.target
|
|
390
|
+
}, {
|
|
391
|
+
F: __dxlog_file,
|
|
392
|
+
L: 488,
|
|
393
|
+
S: this,
|
|
394
|
+
C: (f, a) => f(...a)
|
|
395
|
+
});
|
|
396
|
+
this._registry.set(targetRx, {
|
|
397
|
+
inbound: [
|
|
398
|
+
...target.inbound,
|
|
399
|
+
edgeArg.source
|
|
400
|
+
],
|
|
401
|
+
outbound: target.outbound
|
|
402
|
+
});
|
|
253
403
|
}
|
|
254
404
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
nodes(node, options = {}) {
|
|
259
|
-
const { relation, expansion, filter = DEFAULT_FILTER, type } = options;
|
|
260
|
-
const nodes = this._getNodes({
|
|
261
|
-
node,
|
|
262
|
-
relation,
|
|
263
|
-
expansion,
|
|
264
|
-
type
|
|
405
|
+
removeEdges(edges, removeOrphans = false) {
|
|
406
|
+
import_rx_react.Rx.batch(() => {
|
|
407
|
+
edges.map((edge) => this.removeEdge(edge, removeOrphans));
|
|
265
408
|
});
|
|
266
|
-
return nodes.filter((n) => filter(n, node));
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Edges that this node is connected to in default order.
|
|
270
|
-
*/
|
|
271
|
-
edges(node, { relation = "outbound" } = {}) {
|
|
272
|
-
return this._edges[node.id]?.[relation] ?? [];
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Actions or action groups that this node is connected to in default order.
|
|
276
|
-
*/
|
|
277
|
-
actions(node, { expansion } = {}) {
|
|
278
|
-
return [
|
|
279
|
-
...this._getNodes({
|
|
280
|
-
node,
|
|
281
|
-
expansion,
|
|
282
|
-
type: ACTION_GROUP_TYPE
|
|
283
|
-
}),
|
|
284
|
-
...this._getNodes({
|
|
285
|
-
node,
|
|
286
|
-
expansion,
|
|
287
|
-
type: ACTION_TYPE
|
|
288
|
-
})
|
|
289
|
-
];
|
|
290
409
|
}
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
const
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
410
|
+
removeEdge(edgeArg, removeOrphans = false) {
|
|
411
|
+
const sourceRx = this._edges(edgeArg.source);
|
|
412
|
+
const source = this._registry.get(sourceRx);
|
|
413
|
+
if (source.outbound.includes(edgeArg.target)) {
|
|
414
|
+
this._registry.set(sourceRx, {
|
|
415
|
+
inbound: source.inbound,
|
|
416
|
+
outbound: source.outbound.filter((id) => id !== edgeArg.target)
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
const targetRx = this._edges(edgeArg.target);
|
|
420
|
+
const target = this._registry.get(targetRx);
|
|
421
|
+
if (target.inbound.includes(edgeArg.source)) {
|
|
422
|
+
this._registry.set(targetRx, {
|
|
423
|
+
inbound: target.inbound.filter((id) => id !== edgeArg.source),
|
|
424
|
+
outbound: target.outbound
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
if (removeOrphans) {
|
|
428
|
+
const source2 = this._registry.get(sourceRx);
|
|
429
|
+
const target2 = this._registry.get(targetRx);
|
|
430
|
+
if (source2.outbound.length === 0 && source2.inbound.length === 0 && edgeArg.source !== ROOT_ID) {
|
|
431
|
+
this.removeNodes([
|
|
432
|
+
edgeArg.source
|
|
433
|
+
]);
|
|
434
|
+
}
|
|
435
|
+
if (target2.outbound.length === 0 && target2.inbound.length === 0 && edgeArg.target !== ROOT_ID) {
|
|
436
|
+
this.removeNodes([
|
|
437
|
+
edgeArg.target
|
|
438
|
+
]);
|
|
439
|
+
}
|
|
297
440
|
}
|
|
298
441
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
442
|
+
sortEdges(id, relation, order) {
|
|
443
|
+
const edgesRx = this._edges(id);
|
|
444
|
+
const edges = this._registry.get(edgesRx);
|
|
445
|
+
const unsorted = edges[relation].filter((id2) => !order.includes(id2)) ?? [];
|
|
446
|
+
const sorted = order.filter((id2) => edges[relation].includes(id2)) ?? [];
|
|
447
|
+
edges[relation].splice(0, edges[relation].length, ...[
|
|
448
|
+
...sorted,
|
|
449
|
+
...unsorted
|
|
450
|
+
]);
|
|
451
|
+
this._registry.set(edgesRx, edges);
|
|
452
|
+
}
|
|
453
|
+
traverse({ visitor, source = ROOT_ID, relation = "outbound" }, path = []) {
|
|
454
|
+
if (path.includes(source)) {
|
|
311
455
|
return;
|
|
312
456
|
}
|
|
457
|
+
const node = this.getNodeOrThrow(source);
|
|
313
458
|
const shouldContinue = visitor(node, [
|
|
314
459
|
...path,
|
|
315
|
-
|
|
460
|
+
source
|
|
316
461
|
]);
|
|
317
462
|
if (shouldContinue === false) {
|
|
318
463
|
return;
|
|
319
464
|
}
|
|
320
|
-
Object.values(this.
|
|
321
|
-
|
|
322
|
-
relation,
|
|
323
|
-
expansion
|
|
324
|
-
})).forEach((child) => this.traverse({
|
|
325
|
-
node: child,
|
|
465
|
+
Object.values(this.getConnections(source, relation)).forEach((child) => this.traverse({
|
|
466
|
+
source: child.id,
|
|
326
467
|
relation,
|
|
327
|
-
visitor
|
|
328
|
-
expansion
|
|
468
|
+
visitor
|
|
329
469
|
}, [
|
|
330
470
|
...path,
|
|
331
|
-
|
|
471
|
+
source
|
|
332
472
|
]));
|
|
333
473
|
}
|
|
334
|
-
/**
|
|
335
|
-
* Recursive depth-first traversal of the graph wrapping each visitor call in an effect.
|
|
336
|
-
*
|
|
337
|
-
* @param options.node The node to start traversing from.
|
|
338
|
-
* @param options.relation The relation to traverse graph edges.
|
|
339
|
-
* @param options.visitor A callback which is called for each node visited during traversal.
|
|
340
|
-
*/
|
|
341
|
-
subscribeTraverse({ visitor, node = this.root, relation = "outbound", expansion }, currentPath = []) {
|
|
342
|
-
return (0, import_signals_core.effect)(() => {
|
|
343
|
-
const path = [
|
|
344
|
-
...currentPath,
|
|
345
|
-
node.id
|
|
346
|
-
];
|
|
347
|
-
const result = visitor(node, path);
|
|
348
|
-
if (result === false) {
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
const nodes = this._getNodes({
|
|
352
|
-
node,
|
|
353
|
-
relation,
|
|
354
|
-
expansion
|
|
355
|
-
});
|
|
356
|
-
const nodeSubscriptions = nodes.map((n) => this.subscribeTraverse({
|
|
357
|
-
node: n,
|
|
358
|
-
visitor,
|
|
359
|
-
expansion
|
|
360
|
-
}, path));
|
|
361
|
-
return () => {
|
|
362
|
-
nodeSubscriptions.forEach((unsubscribe) => unsubscribe());
|
|
363
|
-
};
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Get the path between two nodes in the graph.
|
|
368
|
-
*/
|
|
369
474
|
getPath({ source = "root", target }) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (node.id === target) {
|
|
382
|
-
found = path;
|
|
475
|
+
return (0, import_effect.pipe)(this.getNode(source), import_effect.Option.flatMap((node) => {
|
|
476
|
+
let found = import_effect.Option.none();
|
|
477
|
+
this.traverse({
|
|
478
|
+
source: node.id,
|
|
479
|
+
visitor: (node2, path) => {
|
|
480
|
+
if (import_effect.Option.isSome(found)) {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
if (node2.id === target) {
|
|
484
|
+
found = import_effect.Option.some(path);
|
|
485
|
+
}
|
|
383
486
|
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
|
|
487
|
+
});
|
|
488
|
+
return found;
|
|
489
|
+
}));
|
|
387
490
|
}
|
|
388
|
-
/**
|
|
389
|
-
* Wait for the path between two nodes in the graph to be established.
|
|
390
|
-
*/
|
|
391
491
|
async waitForPath(params, { timeout = 5e3, interval = 500 } = {}) {
|
|
392
492
|
const path = this.getPath(params);
|
|
393
|
-
if (path) {
|
|
394
|
-
return path;
|
|
493
|
+
if (import_effect.Option.isSome(path)) {
|
|
494
|
+
return path.value;
|
|
395
495
|
}
|
|
396
496
|
const trigger = new import_async.Trigger();
|
|
397
497
|
const i = setInterval(() => {
|
|
398
498
|
const path2 = this.getPath(params);
|
|
399
|
-
if (path2) {
|
|
400
|
-
trigger.wake(path2);
|
|
499
|
+
if (import_effect.Option.isSome(path2)) {
|
|
500
|
+
trigger.wake(path2.value);
|
|
401
501
|
}
|
|
402
502
|
}, interval);
|
|
403
503
|
return trigger.wait({
|
|
404
504
|
timeout
|
|
405
505
|
}).finally(() => clearInterval(i));
|
|
406
506
|
}
|
|
407
|
-
/**
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
_addNode({ nodes, edges, ..._node }) {
|
|
416
|
-
return (0, import_signals_core.untracked)(() => {
|
|
417
|
-
const existingNode = this._nodes[_node.id];
|
|
418
|
-
const node = existingNode ?? this._constructNode({
|
|
419
|
-
data: null,
|
|
420
|
-
properties: {},
|
|
421
|
-
..._node
|
|
422
|
-
});
|
|
423
|
-
if (existingNode) {
|
|
424
|
-
const { data = null, properties, type } = _node;
|
|
425
|
-
if (data !== node.data) {
|
|
426
|
-
node.data = data;
|
|
427
|
-
}
|
|
428
|
-
if (type !== node.type) {
|
|
429
|
-
node.type = type;
|
|
430
|
-
}
|
|
431
|
-
for (const key in properties) {
|
|
432
|
-
if (properties[key] !== node.properties[key]) {
|
|
433
|
-
node.properties[key] = properties[key];
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
} else {
|
|
437
|
-
this._nodes[node.id] = node;
|
|
438
|
-
this._edges[node.id] = (0, import_live_object.create)({
|
|
439
|
-
inbound: [],
|
|
440
|
-
outbound: []
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
const trigger = this._waitingForNodes[node.id];
|
|
444
|
-
if (trigger) {
|
|
445
|
-
trigger.wake(node);
|
|
446
|
-
delete this._waitingForNodes[node.id];
|
|
447
|
-
}
|
|
448
|
-
if (nodes) {
|
|
449
|
-
nodes.forEach((subNode) => {
|
|
450
|
-
this._addNode(subNode);
|
|
451
|
-
this._addEdge({
|
|
452
|
-
source: node.id,
|
|
453
|
-
target: subNode.id
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
if (edges) {
|
|
458
|
-
edges.forEach(([id, relation]) => relation === "outbound" ? this._addEdge({
|
|
459
|
-
source: node.id,
|
|
460
|
-
target: id
|
|
461
|
-
}) : this._addEdge({
|
|
462
|
-
source: id,
|
|
463
|
-
target: node.id
|
|
464
|
-
}));
|
|
465
|
-
}
|
|
466
|
-
return node;
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Remove nodes from the graph.
|
|
471
|
-
*
|
|
472
|
-
* @param ids The id of the node to remove.
|
|
473
|
-
* @param edges Whether to remove edges connected to the node from the graph as well.
|
|
474
|
-
* @internal
|
|
475
|
-
*/
|
|
476
|
-
_removeNodes(ids, edges = false) {
|
|
477
|
-
(0, import_signals_core.batch)(() => ids.forEach((id) => this._removeNode(id, edges)));
|
|
478
|
-
}
|
|
479
|
-
_removeNode(id, edges = false) {
|
|
480
|
-
(0, import_signals_core.untracked)(() => {
|
|
481
|
-
const node = this.findNode(id, false);
|
|
482
|
-
if (!node) {
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
if (edges) {
|
|
486
|
-
this._getNodes({
|
|
487
|
-
node
|
|
488
|
-
}).forEach((node2) => {
|
|
489
|
-
this._removeEdge({
|
|
490
|
-
source: id,
|
|
491
|
-
target: node2.id
|
|
492
|
-
});
|
|
493
|
-
});
|
|
494
|
-
this._getNodes({
|
|
495
|
-
node,
|
|
496
|
-
relation: "inbound"
|
|
497
|
-
}).forEach((node2) => {
|
|
498
|
-
this._removeEdge({
|
|
499
|
-
source: node2.id,
|
|
500
|
-
target: id
|
|
501
|
-
});
|
|
502
|
-
});
|
|
503
|
-
delete this._edges[id];
|
|
504
|
-
}
|
|
505
|
-
delete this._nodes[id];
|
|
506
|
-
Object.keys(this._initialized).filter((key) => key.startsWith(id)).forEach((key) => {
|
|
507
|
-
delete this._initialized[key];
|
|
508
|
-
});
|
|
509
|
-
void this._onRemoveNode?.(id);
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* Add edges to the graph.
|
|
514
|
-
*
|
|
515
|
-
* @internal
|
|
516
|
-
*/
|
|
517
|
-
_addEdges(edges) {
|
|
518
|
-
(0, import_signals_core.batch)(() => edges.forEach((edge) => this._addEdge(edge)));
|
|
519
|
-
}
|
|
520
|
-
_addEdge({ source, target }) {
|
|
521
|
-
(0, import_signals_core.untracked)(() => {
|
|
522
|
-
if (!this._edges[source]) {
|
|
523
|
-
this._edges[source] = (0, import_live_object.create)({
|
|
524
|
-
inbound: [],
|
|
525
|
-
outbound: []
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
if (!this._edges[target]) {
|
|
529
|
-
this._edges[target] = (0, import_live_object.create)({
|
|
530
|
-
inbound: [],
|
|
531
|
-
outbound: []
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
const sourceEdges = this._edges[source];
|
|
535
|
-
if (!sourceEdges.outbound.includes(target)) {
|
|
536
|
-
sourceEdges.outbound.push(target);
|
|
537
|
-
}
|
|
538
|
-
const targetEdges = this._edges[target];
|
|
539
|
-
if (!targetEdges.inbound.includes(source)) {
|
|
540
|
-
targetEdges.inbound.push(source);
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Remove edges from the graph.
|
|
546
|
-
* @internal
|
|
547
|
-
*/
|
|
548
|
-
_removeEdges(edges, removeOrphans = false) {
|
|
549
|
-
(0, import_signals_core.batch)(() => edges.forEach((edge) => this._removeEdge(edge, removeOrphans)));
|
|
550
|
-
}
|
|
551
|
-
_removeEdge({ source, target }, removeOrphans = false) {
|
|
552
|
-
(0, import_signals_core.untracked)(() => {
|
|
553
|
-
(0, import_signals_core.batch)(() => {
|
|
554
|
-
const outboundIndex = this._edges[source]?.outbound.findIndex((id) => id === target);
|
|
555
|
-
if (outboundIndex !== void 0 && outboundIndex !== -1) {
|
|
556
|
-
this._edges[source].outbound.splice(outboundIndex, 1);
|
|
557
|
-
}
|
|
558
|
-
const inboundIndex = this._edges[target]?.inbound.findIndex((id) => id === source);
|
|
559
|
-
if (inboundIndex !== void 0 && inboundIndex !== -1) {
|
|
560
|
-
this._edges[target].inbound.splice(inboundIndex, 1);
|
|
561
|
-
}
|
|
562
|
-
if (removeOrphans) {
|
|
563
|
-
if (this._edges[source]?.outbound.length === 0 && this._edges[source]?.inbound.length === 0 && source !== ROOT_ID) {
|
|
564
|
-
this._removeNode(source, true);
|
|
565
|
-
}
|
|
566
|
-
if (this._edges[target]?.outbound.length === 0 && this._edges[target]?.inbound.length === 0 && target !== ROOT_ID) {
|
|
567
|
-
this._removeNode(target, true);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* Sort edges for a node.
|
|
575
|
-
*
|
|
576
|
-
* Edges not included in the sorted list are appended to the end of the list.
|
|
577
|
-
*
|
|
578
|
-
* @param nodeId The id of the node to sort edges for.
|
|
579
|
-
* @param relation The relation of the edges from the node to sort.
|
|
580
|
-
* @param edges The ordered list of edges.
|
|
581
|
-
* @ignore
|
|
582
|
-
*/
|
|
583
|
-
_sortEdges(nodeId, relation, edges) {
|
|
584
|
-
(0, import_signals_core.untracked)(() => {
|
|
585
|
-
(0, import_signals_core.batch)(() => {
|
|
586
|
-
const current = this._edges[nodeId];
|
|
587
|
-
if (current) {
|
|
588
|
-
const unsorted = current[relation].filter((id) => !edges.includes(id)) ?? [];
|
|
589
|
-
const sorted = edges.filter((id) => current[relation].includes(id)) ?? [];
|
|
590
|
-
current[relation].splice(0, current[relation].length, ...[
|
|
591
|
-
...sorted,
|
|
592
|
-
...unsorted
|
|
593
|
-
]);
|
|
594
|
-
}
|
|
595
|
-
});
|
|
507
|
+
/** @internal */
|
|
508
|
+
_constructNode(node) {
|
|
509
|
+
return import_effect.Option.some({
|
|
510
|
+
[graphSymbol]: this,
|
|
511
|
+
data: null,
|
|
512
|
+
properties: {},
|
|
513
|
+
...node
|
|
596
514
|
});
|
|
597
515
|
}
|
|
598
|
-
_getNodes({ node, relation = "outbound", type, expansion }) {
|
|
599
|
-
if (expansion) {
|
|
600
|
-
void this.expand(node, relation, type);
|
|
601
|
-
}
|
|
602
|
-
const edges = this._edges[node.id];
|
|
603
|
-
if (!edges) {
|
|
604
|
-
return [];
|
|
605
|
-
} else {
|
|
606
|
-
return edges[relation].map((id) => this._nodes[id]).filter(import_util.isNonNullable).filter((n) => !type || n.type === type);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
516
|
};
|
|
517
|
+
var isGraphNode = (data) => data && typeof data === "object" && "id" in data && "properties" in data && data.properties ? typeof data.properties === "object" && "data" in data : false;
|
|
518
|
+
var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" : false;
|
|
519
|
+
var actionGroupSymbol = Symbol("ActionGroup");
|
|
520
|
+
var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol : false;
|
|
521
|
+
var isActionLike = (data) => isAction(data) || isActionGroup(data);
|
|
610
522
|
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
|
|
611
|
-
var NODE_RESOLVER_TIMEOUT = 1e3;
|
|
612
523
|
var createExtension = (extension) => {
|
|
613
|
-
const { id, position = "static",
|
|
524
|
+
const { id, position = "static", relation = "outbound", connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
|
|
614
525
|
const getId = (key) => `${id}/${key}`;
|
|
526
|
+
const connector = _connector && import_rx_react2.Rx.family((node) => _connector(node).pipe(import_rx_react2.Rx.withLabel(`graph-builder:_connector:${id}`)));
|
|
527
|
+
const actionGroups = _actionGroups && import_rx_react2.Rx.family((node) => _actionGroups(node).pipe(import_rx_react2.Rx.withLabel(`graph-builder:_actionGroups:${id}`)));
|
|
528
|
+
const actions = _actions && import_rx_react2.Rx.family((node) => _actions(node).pipe(import_rx_react2.Rx.withLabel(`graph-builder:_actions:${id}`)));
|
|
615
529
|
return [
|
|
616
|
-
resolver ? {
|
|
617
|
-
id: getId("resolver"),
|
|
618
|
-
position,
|
|
619
|
-
resolver
|
|
620
|
-
} : void 0,
|
|
530
|
+
// resolver ? { id: getId('resolver'), position, resolver } : undefined,
|
|
621
531
|
connector ? {
|
|
622
|
-
...rest,
|
|
623
532
|
id: getId("connector"),
|
|
624
533
|
position,
|
|
625
|
-
|
|
534
|
+
relation,
|
|
535
|
+
connector: import_rx_react2.Rx.family((node) => import_rx_react2.Rx.make((get) => {
|
|
536
|
+
try {
|
|
537
|
+
return get(connector(node));
|
|
538
|
+
} catch {
|
|
539
|
+
import_log2.log.warn("Error in connector", {
|
|
540
|
+
id: getId("connector"),
|
|
541
|
+
node
|
|
542
|
+
}, {
|
|
543
|
+
F: __dxlog_file2,
|
|
544
|
+
L: 101,
|
|
545
|
+
S: void 0,
|
|
546
|
+
C: (f, a) => f(...a)
|
|
547
|
+
});
|
|
548
|
+
return [];
|
|
549
|
+
}
|
|
550
|
+
}).pipe(import_rx_react2.Rx.withLabel(`graph-builder:connector:${id}`)))
|
|
626
551
|
} : void 0,
|
|
627
552
|
actionGroups ? {
|
|
628
|
-
...rest,
|
|
629
553
|
id: getId("actionGroups"),
|
|
630
554
|
position,
|
|
631
|
-
type: ACTION_GROUP_TYPE,
|
|
632
555
|
relation: "outbound",
|
|
633
|
-
connector: (
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
556
|
+
connector: import_rx_react2.Rx.family((node) => import_rx_react2.Rx.make((get) => {
|
|
557
|
+
try {
|
|
558
|
+
return get(actionGroups(node)).map((arg) => ({
|
|
559
|
+
...arg,
|
|
560
|
+
data: actionGroupSymbol,
|
|
561
|
+
type: ACTION_GROUP_TYPE
|
|
562
|
+
}));
|
|
563
|
+
} catch {
|
|
564
|
+
import_log2.log.warn("Error in actionGroups", {
|
|
565
|
+
id: getId("actionGroups"),
|
|
566
|
+
node
|
|
567
|
+
}, {
|
|
568
|
+
F: __dxlog_file2,
|
|
569
|
+
L: 122,
|
|
570
|
+
S: void 0,
|
|
571
|
+
C: (f, a) => f(...a)
|
|
572
|
+
});
|
|
573
|
+
return [];
|
|
574
|
+
}
|
|
575
|
+
}).pipe(import_rx_react2.Rx.withLabel(`graph-builder:connector:actionGroups:${id}`)))
|
|
640
576
|
} : void 0,
|
|
641
577
|
actions ? {
|
|
642
|
-
...rest,
|
|
643
578
|
id: getId("actions"),
|
|
644
579
|
position,
|
|
645
|
-
type: ACTION_TYPE,
|
|
646
580
|
relation: "outbound",
|
|
647
|
-
connector: (
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
581
|
+
connector: import_rx_react2.Rx.family((node) => import_rx_react2.Rx.make((get) => {
|
|
582
|
+
try {
|
|
583
|
+
return get(actions(node)).map((arg) => ({
|
|
584
|
+
...arg,
|
|
585
|
+
type: ACTION_TYPE
|
|
586
|
+
}));
|
|
587
|
+
} catch {
|
|
588
|
+
import_log2.log.warn("Error in actions", {
|
|
589
|
+
id: getId("actions"),
|
|
590
|
+
node
|
|
591
|
+
}, {
|
|
592
|
+
F: __dxlog_file2,
|
|
593
|
+
L: 139,
|
|
594
|
+
S: void 0,
|
|
595
|
+
C: (f, a) => f(...a)
|
|
596
|
+
});
|
|
597
|
+
return [];
|
|
598
|
+
}
|
|
599
|
+
}).pipe(import_rx_react2.Rx.withLabel(`graph-builder:connector:actions:${id}`)))
|
|
653
600
|
} : void 0
|
|
654
601
|
].filter(import_util2.isNonNullable);
|
|
655
602
|
};
|
|
656
|
-
var Dispatcher = class {
|
|
657
|
-
constructor() {
|
|
658
|
-
this.stateIndex = 0;
|
|
659
|
-
this.state = {};
|
|
660
|
-
this.cleanup = [];
|
|
661
|
-
}
|
|
662
|
-
};
|
|
663
|
-
var BuilderInternal = class {
|
|
664
|
-
};
|
|
665
|
-
var memoize = (fn, key = "result") => {
|
|
666
|
-
const dispatcher = BuilderInternal.currentDispatcher;
|
|
667
|
-
(0, import_invariant2.invariant)(dispatcher?.currentExtension, "memoize must be called within an extension", {
|
|
668
|
-
F: __dxlog_file2,
|
|
669
|
-
L: 135,
|
|
670
|
-
S: void 0,
|
|
671
|
-
A: [
|
|
672
|
-
"dispatcher?.currentExtension",
|
|
673
|
-
"'memoize must be called within an extension'"
|
|
674
|
-
]
|
|
675
|
-
});
|
|
676
|
-
const all = dispatcher.state[dispatcher.currentExtension][dispatcher.stateIndex] ?? {};
|
|
677
|
-
const current = all[key];
|
|
678
|
-
const result = current ? current.result : fn();
|
|
679
|
-
dispatcher.state[dispatcher.currentExtension][dispatcher.stateIndex] = {
|
|
680
|
-
...all,
|
|
681
|
-
[key]: {
|
|
682
|
-
result
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
dispatcher.stateIndex++;
|
|
686
|
-
return result;
|
|
687
|
-
};
|
|
688
|
-
var cleanup = (fn) => {
|
|
689
|
-
memoize(() => {
|
|
690
|
-
const dispatcher = BuilderInternal.currentDispatcher;
|
|
691
|
-
(0, import_invariant2.invariant)(dispatcher, "cleanup must be called within an extension", {
|
|
692
|
-
F: __dxlog_file2,
|
|
693
|
-
L: 150,
|
|
694
|
-
S: void 0,
|
|
695
|
-
A: [
|
|
696
|
-
"dispatcher",
|
|
697
|
-
"'cleanup must be called within an extension'"
|
|
698
|
-
]
|
|
699
|
-
});
|
|
700
|
-
dispatcher.cleanup.push(fn);
|
|
701
|
-
});
|
|
702
|
-
};
|
|
703
|
-
var toSignal = (subscribe, get, key) => {
|
|
704
|
-
const thisSignal = memoize(() => {
|
|
705
|
-
return (0, import_signals_core2.signal)(get());
|
|
706
|
-
}, key);
|
|
707
|
-
const unsubscribe = memoize(() => {
|
|
708
|
-
return subscribe(() => thisSignal.value = get());
|
|
709
|
-
}, key);
|
|
710
|
-
cleanup(() => {
|
|
711
|
-
unsubscribe();
|
|
712
|
-
});
|
|
713
|
-
return thisSignal.value;
|
|
714
|
-
};
|
|
715
603
|
var flattenExtensions = (extension, acc = []) => {
|
|
716
|
-
if (Array.isArray(extension)) {
|
|
604
|
+
if (import_effect2.Array.isArray(extension)) {
|
|
717
605
|
return [
|
|
718
606
|
...acc,
|
|
719
607
|
...extension.flatMap((ext) => flattenExtensions(ext, acc))
|
|
@@ -726,107 +614,74 @@ var flattenExtensions = (extension, acc = []) => {
|
|
|
726
614
|
}
|
|
727
615
|
};
|
|
728
616
|
var GraphBuilder = class _GraphBuilder {
|
|
729
|
-
constructor(params = {}) {
|
|
730
|
-
this._dispatcher = new Dispatcher();
|
|
731
|
-
this._extensions = (0, import_live_object2.create)({});
|
|
732
|
-
this._resolverSubscriptions = /* @__PURE__ */ new Map();
|
|
617
|
+
constructor({ registry, ...params } = {}) {
|
|
733
618
|
this._connectorSubscriptions = /* @__PURE__ */ new Map();
|
|
734
|
-
this.
|
|
735
|
-
this.
|
|
619
|
+
this._extensions = import_rx_react2.Rx.make(import_effect2.Record.empty()).pipe(import_rx_react2.Rx.keepAlive, import_rx_react2.Rx.withLabel("graph-builder:extensions"));
|
|
620
|
+
this._connectors = import_rx_react2.Rx.family((key) => {
|
|
621
|
+
return import_rx_react2.Rx.make((get) => {
|
|
622
|
+
const [id, relation] = key.split("+");
|
|
623
|
+
const node = this._graph.node(id);
|
|
624
|
+
return (0, import_effect2.pipe)(
|
|
625
|
+
get(this._extensions),
|
|
626
|
+
import_effect2.Record.values,
|
|
627
|
+
// TODO(wittjosiah): Sort on write rather than read.
|
|
628
|
+
import_effect2.Array.sortBy(import_util2.byPosition),
|
|
629
|
+
import_effect2.Array.filter(({ relation: _relation = "outbound" }) => _relation === relation),
|
|
630
|
+
import_effect2.Array.map(({ connector }) => connector?.(node)),
|
|
631
|
+
import_effect2.Array.filter(import_util2.isNonNullable),
|
|
632
|
+
import_effect2.Array.flatMap((result) => get(result))
|
|
633
|
+
);
|
|
634
|
+
}).pipe(import_rx_react2.Rx.withLabel(`graph-builder:connectors:${key}`));
|
|
635
|
+
});
|
|
636
|
+
this._registry = registry ?? import_rx_react2.Registry.make();
|
|
736
637
|
this._graph = new Graph({
|
|
737
638
|
...params,
|
|
738
|
-
|
|
739
|
-
|
|
639
|
+
registry: this._registry,
|
|
640
|
+
onExpand: (id, relation) => this._onExpand(id, relation),
|
|
641
|
+
// onInitialize: (id) => this._onInitialize(id),
|
|
740
642
|
onRemoveNode: (id) => this._onRemoveNode(id)
|
|
741
643
|
});
|
|
742
644
|
}
|
|
743
|
-
static from(pickle) {
|
|
645
|
+
static from(pickle, registry) {
|
|
744
646
|
if (!pickle) {
|
|
745
|
-
return new _GraphBuilder(
|
|
647
|
+
return new _GraphBuilder({
|
|
648
|
+
registry
|
|
649
|
+
});
|
|
746
650
|
}
|
|
747
651
|
const { nodes, edges } = JSON.parse(pickle);
|
|
748
652
|
return new _GraphBuilder({
|
|
749
653
|
nodes,
|
|
750
|
-
edges
|
|
654
|
+
edges,
|
|
655
|
+
registry
|
|
751
656
|
});
|
|
752
657
|
}
|
|
753
|
-
/**
|
|
754
|
-
* If graph is being restored from a pickle, the data will be null.
|
|
755
|
-
* Initialize the data of each node by calling resolvers.
|
|
756
|
-
* Wait until all of the initial nodes have resolved.
|
|
757
|
-
*/
|
|
758
|
-
async initialize() {
|
|
759
|
-
Object.keys(this._graph._nodes).filter((id) => id !== ROOT_ID).forEach((id) => this._initialized[id] = new import_async2.Trigger());
|
|
760
|
-
Object.keys(this._graph._nodes).forEach((id) => this._onInitialNode(id));
|
|
761
|
-
await Promise.all(Object.entries(this._initialized).map(async ([id, trigger]) => {
|
|
762
|
-
try {
|
|
763
|
-
await trigger.wait({
|
|
764
|
-
timeout: NODE_RESOLVER_TIMEOUT
|
|
765
|
-
});
|
|
766
|
-
} catch {
|
|
767
|
-
import_log2.log.error("node resolver timeout", {
|
|
768
|
-
id
|
|
769
|
-
}, {
|
|
770
|
-
F: __dxlog_file2,
|
|
771
|
-
L: 244,
|
|
772
|
-
S: this,
|
|
773
|
-
C: (f, a) => f(...a)
|
|
774
|
-
});
|
|
775
|
-
this.graph._removeNodes([
|
|
776
|
-
id
|
|
777
|
-
]);
|
|
778
|
-
}
|
|
779
|
-
}));
|
|
780
|
-
}
|
|
781
658
|
get graph() {
|
|
782
659
|
return this._graph;
|
|
783
660
|
}
|
|
784
|
-
/**
|
|
785
|
-
* @reactive
|
|
786
|
-
*/
|
|
787
661
|
get extensions() {
|
|
788
|
-
return
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
const extensions = flattenExtensions(extension);
|
|
795
|
-
(0, import_signals_core2.untracked)(() => {
|
|
796
|
-
extensions.forEach((extension2) => {
|
|
797
|
-
this._dispatcher.state[extension2.id] = [];
|
|
798
|
-
this._extensions[extension2.id] = extension2;
|
|
799
|
-
});
|
|
662
|
+
return this._extensions;
|
|
663
|
+
}
|
|
664
|
+
addExtension(extensions) {
|
|
665
|
+
flattenExtensions(extensions).forEach((extension) => {
|
|
666
|
+
const extensions2 = this._registry.get(this._extensions);
|
|
667
|
+
this._registry.set(this._extensions, import_effect2.Record.set(extensions2, extension.id, extension));
|
|
800
668
|
});
|
|
801
669
|
return this;
|
|
802
670
|
}
|
|
803
|
-
/**
|
|
804
|
-
* Remove a node builder from the graph builder.
|
|
805
|
-
*/
|
|
806
671
|
removeExtension(id) {
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
});
|
|
672
|
+
const extensions = this._registry.get(this._extensions);
|
|
673
|
+
this._registry.set(this._extensions, import_effect2.Record.remove(extensions, id));
|
|
810
674
|
return this;
|
|
811
675
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
this._resolverSubscriptions.forEach((unsubscribe) => unsubscribe());
|
|
815
|
-
this._connectorSubscriptions.forEach((unsubscribe) => unsubscribe());
|
|
816
|
-
this._resolverSubscriptions.clear();
|
|
817
|
-
this._connectorSubscriptions.clear();
|
|
818
|
-
}
|
|
819
|
-
/**
|
|
820
|
-
* A graph traversal using just the connector extensions, without subscribing to any signals or persisting any nodes.
|
|
821
|
-
*/
|
|
822
|
-
async explore({ node = this._graph.root, relation = "outbound", visitor }, path = []) {
|
|
823
|
-
if (path.includes(node.id)) {
|
|
676
|
+
async explore({ registry = import_rx_react2.Registry.make(), source = ROOT_ID, relation = "outbound", visitor }, path = []) {
|
|
677
|
+
if (path.includes(source)) {
|
|
824
678
|
return;
|
|
825
679
|
}
|
|
826
680
|
if (!(0, import_util2.isNode)()) {
|
|
827
681
|
const { yieldOrContinue } = await import("main-thread-scheduling");
|
|
828
682
|
await yieldOrContinue("idle");
|
|
829
683
|
}
|
|
684
|
+
const node = registry.get(this._graph.nodeOrThrow(source));
|
|
830
685
|
const shouldContinue = await visitor(node, [
|
|
831
686
|
...path,
|
|
832
687
|
node.id
|
|
@@ -834,158 +689,104 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
834
689
|
if (shouldContinue === false) {
|
|
835
690
|
return;
|
|
836
691
|
}
|
|
837
|
-
const nodes = Object.values(this._extensions).filter((extension) => relation === (extension.relation ?? "outbound")).
|
|
838
|
-
|
|
839
|
-
this.
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
cacheable: arg.cacheable,
|
|
850
|
-
data: arg.data ?? null,
|
|
851
|
-
properties: arg.properties ?? {}
|
|
852
|
-
}));
|
|
853
|
-
await Promise.all(nodes.map((n) => this.explore({
|
|
854
|
-
node: n,
|
|
855
|
-
relation,
|
|
856
|
-
visitor
|
|
857
|
-
}, [
|
|
858
|
-
...path,
|
|
859
|
-
node.id
|
|
860
|
-
])));
|
|
861
|
-
}
|
|
862
|
-
_onInitialNode(nodeId) {
|
|
863
|
-
this._nodeChanged[nodeId] = this._nodeChanged[nodeId] ?? (0, import_signals_core2.signal)({});
|
|
864
|
-
this._resolverSubscriptions.set(nodeId, (0, import_signals_core2.effect)(() => {
|
|
865
|
-
const extensions = Object.values(this._extensions).toSorted(import_util2.byPosition);
|
|
866
|
-
for (const { id, resolver } of extensions) {
|
|
867
|
-
if (!resolver) {
|
|
868
|
-
continue;
|
|
869
|
-
}
|
|
870
|
-
this._dispatcher.currentExtension = id;
|
|
871
|
-
this._dispatcher.stateIndex = 0;
|
|
872
|
-
BuilderInternal.currentDispatcher = this._dispatcher;
|
|
873
|
-
let node;
|
|
874
|
-
try {
|
|
875
|
-
node = resolver({
|
|
876
|
-
id: nodeId
|
|
877
|
-
});
|
|
878
|
-
} catch (err) {
|
|
879
|
-
import_log2.log.catch(err, {
|
|
880
|
-
extension: id
|
|
881
|
-
}, {
|
|
882
|
-
F: __dxlog_file2,
|
|
883
|
-
L: 359,
|
|
884
|
-
S: this,
|
|
885
|
-
C: (f, a) => f(...a)
|
|
886
|
-
});
|
|
887
|
-
import_log2.log.error(`Previous error occurred in extension: ${id}`, void 0, {
|
|
888
|
-
F: __dxlog_file2,
|
|
889
|
-
L: 360,
|
|
890
|
-
S: this,
|
|
891
|
-
C: (f, a) => f(...a)
|
|
892
|
-
});
|
|
893
|
-
} finally {
|
|
894
|
-
BuilderInternal.currentDispatcher = void 0;
|
|
895
|
-
}
|
|
896
|
-
const trigger = this._initialized[nodeId];
|
|
897
|
-
if (node) {
|
|
898
|
-
this.graph._addNodes([
|
|
899
|
-
node
|
|
900
|
-
]);
|
|
901
|
-
trigger?.wake();
|
|
902
|
-
if (this._nodeChanged[node.id]) {
|
|
903
|
-
this._nodeChanged[node.id].value = {};
|
|
904
|
-
}
|
|
905
|
-
break;
|
|
906
|
-
} else if (node === false) {
|
|
907
|
-
this.graph._removeNodes([
|
|
908
|
-
nodeId
|
|
909
|
-
]);
|
|
910
|
-
trigger?.wake();
|
|
911
|
-
break;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
692
|
+
const nodes = Object.values(this._registry.get(this._extensions)).filter((extension) => relation === (extension.relation ?? "outbound")).map((extension) => extension.connector).filter(import_util2.isNonNullable).flatMap((connector) => registry.get(connector(this._graph.node(source))));
|
|
693
|
+
await Promise.all(nodes.map((nodeArg) => {
|
|
694
|
+
registry.set(this._graph._node(nodeArg.id), this._graph._constructNode(nodeArg));
|
|
695
|
+
return this.explore({
|
|
696
|
+
registry,
|
|
697
|
+
source: nodeArg.id,
|
|
698
|
+
relation,
|
|
699
|
+
visitor
|
|
700
|
+
}, [
|
|
701
|
+
...path,
|
|
702
|
+
node.id
|
|
703
|
+
]);
|
|
914
704
|
}));
|
|
705
|
+
if (registry !== this._registry) {
|
|
706
|
+
registry.reset();
|
|
707
|
+
registry.dispose();
|
|
708
|
+
}
|
|
915
709
|
}
|
|
916
|
-
|
|
917
|
-
this.
|
|
918
|
-
|
|
710
|
+
destroy() {
|
|
711
|
+
this._connectorSubscriptions.forEach((unsubscribe) => unsubscribe());
|
|
712
|
+
this._connectorSubscriptions.clear();
|
|
713
|
+
}
|
|
714
|
+
_onExpand(id, relation) {
|
|
715
|
+
(0, import_log2.log)("onExpand", {
|
|
716
|
+
id,
|
|
717
|
+
relation,
|
|
718
|
+
registry: (0, import_util2.getDebugName)(this._registry)
|
|
719
|
+
}, {
|
|
720
|
+
F: __dxlog_file2,
|
|
721
|
+
L: 301,
|
|
722
|
+
S: this,
|
|
723
|
+
C: (f, a) => f(...a)
|
|
724
|
+
});
|
|
725
|
+
const connectors = this._connectors(`${id}+${relation}`);
|
|
919
726
|
let previous = [];
|
|
920
|
-
this.
|
|
921
|
-
if (!first && !this._connectorSubscriptions.has(node.id)) {
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
924
|
-
first = false;
|
|
925
|
-
Object.keys(this._extensions);
|
|
926
|
-
this._nodeChanged[node.id].value;
|
|
927
|
-
const nodes = [];
|
|
928
|
-
const extensions = Object.values(this._extensions).toSorted(import_util2.byPosition);
|
|
929
|
-
for (const { id, connector, filter, type, relation = "outbound" } of extensions) {
|
|
930
|
-
if (!connector || relation !== nodesRelation || nodesType && type !== nodesType || filter && !filter(node)) {
|
|
931
|
-
continue;
|
|
932
|
-
}
|
|
933
|
-
this._dispatcher.currentExtension = id;
|
|
934
|
-
this._dispatcher.stateIndex = 0;
|
|
935
|
-
BuilderInternal.currentDispatcher = this._dispatcher;
|
|
936
|
-
try {
|
|
937
|
-
nodes.push(...connector({
|
|
938
|
-
node
|
|
939
|
-
}) ?? []);
|
|
940
|
-
} catch (err) {
|
|
941
|
-
import_log2.log.catch(err, {
|
|
942
|
-
extension: id
|
|
943
|
-
}, {
|
|
944
|
-
F: __dxlog_file2,
|
|
945
|
-
L: 421,
|
|
946
|
-
S: this,
|
|
947
|
-
C: (f, a) => f(...a)
|
|
948
|
-
});
|
|
949
|
-
import_log2.log.error(`Previous error occurred in extension: ${id}`, void 0, {
|
|
950
|
-
F: __dxlog_file2,
|
|
951
|
-
L: 422,
|
|
952
|
-
S: this,
|
|
953
|
-
C: (f, a) => f(...a)
|
|
954
|
-
});
|
|
955
|
-
} finally {
|
|
956
|
-
BuilderInternal.currentDispatcher = void 0;
|
|
957
|
-
}
|
|
958
|
-
}
|
|
727
|
+
const cancel = this._registry.subscribe(connectors, (nodes) => {
|
|
959
728
|
const ids = nodes.map((n) => n.id);
|
|
960
|
-
const removed = previous.filter((
|
|
729
|
+
const removed = previous.filter((id2) => !ids.includes(id2));
|
|
961
730
|
previous = ids;
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
target: node.id
|
|
973
|
-
}));
|
|
974
|
-
this.graph._sortEdges(node.id, nodesRelation, nodes.map(({ id }) => id));
|
|
975
|
-
nodes.forEach((n) => {
|
|
976
|
-
if (this._nodeChanged[n.id]) {
|
|
977
|
-
this._nodeChanged[n.id].value = {};
|
|
978
|
-
}
|
|
731
|
+
(0, import_log2.log)("update", {
|
|
732
|
+
id,
|
|
733
|
+
relation,
|
|
734
|
+
ids,
|
|
735
|
+
removed
|
|
736
|
+
}, {
|
|
737
|
+
F: __dxlog_file2,
|
|
738
|
+
L: 312,
|
|
739
|
+
S: this,
|
|
740
|
+
C: (f, a) => f(...a)
|
|
979
741
|
});
|
|
980
|
-
|
|
742
|
+
import_rx_react2.Rx.batch(() => {
|
|
743
|
+
this._graph.removeEdges(removed.map((target) => ({
|
|
744
|
+
source: id,
|
|
745
|
+
target
|
|
746
|
+
})), true);
|
|
747
|
+
this._graph.addNodes(nodes);
|
|
748
|
+
this._graph.addEdges(nodes.map((node) => relation === "outbound" ? {
|
|
749
|
+
source: id,
|
|
750
|
+
target: node.id
|
|
751
|
+
} : {
|
|
752
|
+
source: node.id,
|
|
753
|
+
target: id
|
|
754
|
+
}));
|
|
755
|
+
this._graph.sortEdges(id, relation, nodes.map(({ id: id2 }) => id2));
|
|
756
|
+
});
|
|
757
|
+
}, {
|
|
758
|
+
immediate: true
|
|
759
|
+
});
|
|
760
|
+
this._connectorSubscriptions.set(id, cancel);
|
|
981
761
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
762
|
+
// TODO(wittjosiah): On initialize to restore state from cache.
|
|
763
|
+
// private async _onInitialize(id: string) {
|
|
764
|
+
// log('onInitialize', { id });
|
|
765
|
+
// }
|
|
766
|
+
_onRemoveNode(id) {
|
|
767
|
+
this._connectorSubscriptions.get(id)?.();
|
|
768
|
+
this._connectorSubscriptions.delete(id);
|
|
987
769
|
}
|
|
988
770
|
};
|
|
771
|
+
var rxFromSignal = (cb) => {
|
|
772
|
+
return import_rx_react2.Rx.make((get) => {
|
|
773
|
+
const dispose = (0, import_signals_core.effect)(() => {
|
|
774
|
+
get.setSelf(cb());
|
|
775
|
+
});
|
|
776
|
+
get.addFinalizer(() => dispose());
|
|
777
|
+
return cb();
|
|
778
|
+
});
|
|
779
|
+
};
|
|
780
|
+
var observableFamily = import_rx_react2.Rx.family((observable) => {
|
|
781
|
+
return import_rx_react2.Rx.make((get) => {
|
|
782
|
+
const subscription = observable.subscribe((value) => get.setSelf(value));
|
|
783
|
+
get.addFinalizer(() => subscription.unsubscribe());
|
|
784
|
+
return observable.get();
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
var rxFromObservable = (observable) => {
|
|
788
|
+
return observableFamily(observable);
|
|
789
|
+
};
|
|
989
790
|
// Annotate the CommonJS export names for ESM import in node:
|
|
990
791
|
0 && (module.exports = {
|
|
991
792
|
ACTION_GROUP_TYPE,
|
|
@@ -995,7 +796,6 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
995
796
|
ROOT_ID,
|
|
996
797
|
ROOT_TYPE,
|
|
997
798
|
actionGroupSymbol,
|
|
998
|
-
cleanup,
|
|
999
799
|
createExtension,
|
|
1000
800
|
flattenExtensions,
|
|
1001
801
|
getGraph,
|
|
@@ -1003,7 +803,7 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
1003
803
|
isActionGroup,
|
|
1004
804
|
isActionLike,
|
|
1005
805
|
isGraphNode,
|
|
1006
|
-
|
|
1007
|
-
|
|
806
|
+
rxFromObservable,
|
|
807
|
+
rxFromSignal
|
|
1008
808
|
});
|
|
1009
809
|
//# sourceMappingURL=index.cjs.map
|