@dxos/app-graph 0.8.2-main.fbd8ed0 → 0.8.2-staging.7ac8446
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 +789 -541
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +780 -533
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +789 -541
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/graph-builder.d.ts +91 -48
- package/dist/types/src/graph-builder.d.ts.map +1 -1
- package/dist/types/src/graph.d.ts +98 -191
- 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/stories/EchoGraph.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -23
- package/src/graph-builder.test.ts +310 -293
- package/src/graph-builder.ts +317 -209
- package/src/graph.test.ts +463 -314
- package/src/graph.ts +455 -452
- package/src/node.ts +4 -4
- package/src/stories/EchoGraph.stories.tsx +78 -57
- 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
package/dist/lib/node/index.cjs
CHANGED
|
@@ -35,6 +35,7 @@ __export(node_exports, {
|
|
|
35
35
|
ROOT_ID: () => ROOT_ID,
|
|
36
36
|
ROOT_TYPE: () => ROOT_TYPE,
|
|
37
37
|
actionGroupSymbol: () => actionGroupSymbol,
|
|
38
|
+
cleanup: () => cleanup,
|
|
38
39
|
createExtension: () => createExtension,
|
|
39
40
|
flattenExtensions: () => flattenExtensions,
|
|
40
41
|
getGraph: () => getGraph,
|
|
@@ -42,29 +43,34 @@ __export(node_exports, {
|
|
|
42
43
|
isActionGroup: () => isActionGroup,
|
|
43
44
|
isActionLike: () => isActionLike,
|
|
44
45
|
isGraphNode: () => isGraphNode,
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
memoize: () => memoize,
|
|
47
|
+
toSignal: () => toSignal
|
|
47
48
|
});
|
|
48
49
|
module.exports = __toCommonJS(node_exports);
|
|
49
|
-
var
|
|
50
|
-
var import_effect = require("effect");
|
|
50
|
+
var import_signals_core = require("@preact/signals-core");
|
|
51
51
|
var import_async = require("@dxos/async");
|
|
52
|
-
var import_debug = require("@dxos/debug");
|
|
53
52
|
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
|
|
56
|
+
var import_signals_core2 = require("@preact/signals-core");
|
|
57
|
+
var import_async2 = require("@dxos/async");
|
|
58
|
+
var import_invariant2 = require("@dxos/invariant");
|
|
59
|
+
var import_live_object2 = require("@dxos/live-object");
|
|
59
60
|
var import_log2 = require("@dxos/log");
|
|
60
61
|
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);
|
|
61
67
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
|
|
62
68
|
var graphSymbol = Symbol("graph");
|
|
63
69
|
var getGraph = (node) => {
|
|
64
70
|
const graph = node[graphSymbol];
|
|
65
71
|
(0, import_invariant.invariant)(graph, "Node is not associated with a graph.", {
|
|
66
72
|
F: __dxlog_file,
|
|
67
|
-
L:
|
|
73
|
+
L: 21,
|
|
68
74
|
S: void 0,
|
|
69
75
|
A: [
|
|
70
76
|
"graph",
|
|
@@ -77,484 +83,637 @@ var ROOT_ID = "root";
|
|
|
77
83
|
var ROOT_TYPE = "dxos.org/type/GraphRoot";
|
|
78
84
|
var ACTION_TYPE = "dxos.org/type/GraphAction";
|
|
79
85
|
var ACTION_GROUP_TYPE = "dxos.org/type/GraphActionGroup";
|
|
80
|
-
var
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.
|
|
84
|
-
this._initialized =
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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;
|
|
86
|
+
var DEFAULT_FILTER = (node) => (0, import_signals_core.untracked)(() => !isActionLike(node));
|
|
87
|
+
var Graph = class _Graph {
|
|
88
|
+
constructor({ nodes, edges, onInitialNode, onInitialNodes, onRemoveNode } = {}) {
|
|
89
|
+
this._waitingForNodes = {};
|
|
90
|
+
this._initialized = {};
|
|
91
|
+
this._nodes = {};
|
|
92
|
+
this._edges = {};
|
|
93
|
+
this._constructNode = (node) => {
|
|
94
|
+
return (0, import_live_object.create)({
|
|
95
|
+
...node,
|
|
96
|
+
[graphSymbol]: this
|
|
114
97
|
});
|
|
115
|
-
}
|
|
116
|
-
this.
|
|
117
|
-
|
|
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;
|
|
98
|
+
};
|
|
99
|
+
this._onInitialNode = onInitialNode;
|
|
100
|
+
this._onInitialNodes = onInitialNodes;
|
|
163
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
|
+
});
|
|
164
109
|
if (nodes) {
|
|
165
110
|
nodes.forEach((node) => {
|
|
166
|
-
|
|
111
|
+
const cacheable = Object.keys(node.properties ?? {});
|
|
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
|
+
}
|
|
167
135
|
});
|
|
168
136
|
}
|
|
137
|
+
this._edges[ROOT_ID] = (0, import_live_object.create)({
|
|
138
|
+
inbound: [],
|
|
139
|
+
outbound: []
|
|
140
|
+
});
|
|
169
141
|
if (edges) {
|
|
170
142
|
Object.entries(edges).forEach(([source, edges2]) => {
|
|
171
|
-
|
|
143
|
+
edges2.forEach((target) => {
|
|
144
|
+
this._addEdge({
|
|
145
|
+
source,
|
|
146
|
+
target
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
this._sortEdges(source, "outbound", edges2);
|
|
172
150
|
});
|
|
173
151
|
}
|
|
174
152
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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);
|
|
153
|
+
static from(pickle, options = {}) {
|
|
154
|
+
const { nodes, edges } = JSON.parse(pickle);
|
|
155
|
+
return new _Graph({
|
|
156
|
+
nodes,
|
|
157
|
+
edges,
|
|
158
|
+
...options
|
|
159
|
+
});
|
|
195
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Alias for `findNode('root')`.
|
|
163
|
+
*/
|
|
196
164
|
get root() {
|
|
197
|
-
return this.
|
|
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
|
-
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
|
-
}, {
|
|
165
|
+
return this.findNode(ROOT_ID);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Convert the graph to a JSON object.
|
|
169
|
+
*/
|
|
170
|
+
toJSON({ id = ROOT_ID, maxLength = 32 } = {}) {
|
|
171
|
+
const toJSON = (node, seen = []) => {
|
|
172
|
+
const nodes = this.nodes(node);
|
|
173
|
+
const obj = {
|
|
174
|
+
id: node.id.length > maxLength ? `${node.id.slice(0, maxLength - 3)}...` : node.id,
|
|
175
|
+
type: node.type
|
|
176
|
+
};
|
|
177
|
+
if (node.properties.label) {
|
|
178
|
+
obj.label = node.properties.label;
|
|
179
|
+
}
|
|
180
|
+
if (nodes.length) {
|
|
181
|
+
obj.nodes = nodes.map((n) => {
|
|
182
|
+
const nextSeen = [
|
|
183
|
+
...seen,
|
|
184
|
+
node.id
|
|
185
|
+
];
|
|
186
|
+
return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
|
|
187
|
+
}).filter(import_util.isNonNullable);
|
|
188
|
+
}
|
|
189
|
+
return obj;
|
|
190
|
+
};
|
|
191
|
+
const root = this.findNode(id);
|
|
192
|
+
(0, import_invariant.invariant)(root, `Node not found: ${id}`, {
|
|
230
193
|
F: __dxlog_file,
|
|
231
|
-
L:
|
|
194
|
+
L: 171,
|
|
232
195
|
S: this,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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));
|
|
243
|
-
});
|
|
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
|
-
typeChanged,
|
|
256
|
-
dataChanged,
|
|
257
|
-
propertiesChanged
|
|
258
|
-
}, {
|
|
259
|
-
F: __dxlog_file,
|
|
260
|
-
L: 417,
|
|
261
|
-
S: this,
|
|
262
|
-
C: (f, a) => f(...a)
|
|
263
|
-
});
|
|
264
|
-
if (typeChanged || dataChanged || propertiesChanged) {
|
|
265
|
-
(0, import_log.log)("updating node", {
|
|
266
|
-
id,
|
|
267
|
-
type,
|
|
268
|
-
data,
|
|
269
|
-
properties
|
|
270
|
-
}, {
|
|
271
|
-
F: __dxlog_file,
|
|
272
|
-
L: 419,
|
|
273
|
-
S: this,
|
|
274
|
-
C: (f, a) => f(...a)
|
|
275
|
-
});
|
|
276
|
-
const newNode = import_effect.Option.some({
|
|
277
|
-
...node2,
|
|
278
|
-
type,
|
|
279
|
-
data,
|
|
280
|
-
properties: {
|
|
281
|
-
...node2.properties,
|
|
282
|
-
...properties
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
this._registry.set(nodeRx, newNode);
|
|
286
|
-
this.onNodeChanged.emit({
|
|
287
|
-
id,
|
|
288
|
-
node: newNode
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
onNone: () => {
|
|
293
|
-
(0, import_log.log)("new node", {
|
|
294
|
-
id,
|
|
295
|
-
type,
|
|
296
|
-
data,
|
|
297
|
-
properties
|
|
298
|
-
}, {
|
|
299
|
-
F: __dxlog_file,
|
|
300
|
-
L: 426,
|
|
301
|
-
S: this,
|
|
302
|
-
C: (f, a) => f(...a)
|
|
303
|
-
});
|
|
304
|
-
const newNode = this._constructNode({
|
|
305
|
-
id,
|
|
306
|
-
type,
|
|
307
|
-
data,
|
|
308
|
-
properties
|
|
309
|
-
});
|
|
310
|
-
this._registry.set(nodeRx, newNode);
|
|
311
|
-
this.onNodeChanged.emit({
|
|
312
|
-
id,
|
|
313
|
-
node: newNode
|
|
314
|
-
});
|
|
315
|
-
}
|
|
196
|
+
A: [
|
|
197
|
+
"root",
|
|
198
|
+
"`Node not found: ${id}`"
|
|
199
|
+
]
|
|
316
200
|
});
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
(0, import_debug.todo)();
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
removeNodes(ids, edges = false) {
|
|
330
|
-
import_rx_react.Rx.batch(() => {
|
|
331
|
-
ids.map((id) => this.removeNode(id, edges));
|
|
201
|
+
return toJSON(root);
|
|
202
|
+
}
|
|
203
|
+
pickle() {
|
|
204
|
+
const nodes = Object.values(this._nodes).filter((node) => !!node.cacheable).map((node) => {
|
|
205
|
+
return {
|
|
206
|
+
id: node.id,
|
|
207
|
+
type: node.type,
|
|
208
|
+
properties: (0, import_util.pick)(node.properties, node.cacheable)
|
|
209
|
+
};
|
|
332
210
|
});
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const nodeRx = this._node(id);
|
|
336
|
-
this._registry.set(nodeRx, import_effect.Option.none());
|
|
337
|
-
this.onNodeChanged.emit({
|
|
211
|
+
const cacheable = new Set(nodes.map((node) => node.id));
|
|
212
|
+
const edges = Object.fromEntries(Object.entries(this._edges).filter(([id]) => cacheable.has(id)).map(([id, { outbound }]) => [
|
|
338
213
|
id,
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
...inbound.map((source) => ({
|
|
345
|
-
source,
|
|
346
|
-
target: id
|
|
347
|
-
})),
|
|
348
|
-
...outbound.map((target) => ({
|
|
349
|
-
source: id,
|
|
350
|
-
target
|
|
351
|
-
}))
|
|
352
|
-
];
|
|
353
|
-
this.removeEdges(edges2);
|
|
354
|
-
}
|
|
355
|
-
this._onRemoveNode?.(id);
|
|
356
|
-
}
|
|
357
|
-
addEdges(edges) {
|
|
358
|
-
import_rx_react.Rx.batch(() => {
|
|
359
|
-
edges.map((edge) => this.addEdge(edge));
|
|
214
|
+
outbound.filter((nodeId) => cacheable.has(nodeId))
|
|
215
|
+
]).toSorted(([a], [b]) => a.localeCompare(b)));
|
|
216
|
+
return JSON.stringify({
|
|
217
|
+
nodes,
|
|
218
|
+
edges
|
|
360
219
|
});
|
|
361
220
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
S: this,
|
|
373
|
-
C: (f, a) => f(...a)
|
|
374
|
-
});
|
|
375
|
-
this._registry.set(sourceRx, {
|
|
376
|
-
inbound: source.inbound,
|
|
377
|
-
outbound: [
|
|
378
|
-
...source.outbound,
|
|
379
|
-
edgeArg.target
|
|
380
|
-
]
|
|
381
|
-
});
|
|
221
|
+
/**
|
|
222
|
+
* Find the node with the given id in the graph.
|
|
223
|
+
*
|
|
224
|
+
* If a node is not found within the graph and an `onInitialNode` callback is provided,
|
|
225
|
+
* it is called with the id and type of the node, potentially initializing the node.
|
|
226
|
+
*/
|
|
227
|
+
findNode(id, expansion = true) {
|
|
228
|
+
const existingNode = this._nodes[id];
|
|
229
|
+
if (!existingNode && expansion) {
|
|
230
|
+
void this._onInitialNode?.(id);
|
|
382
231
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
232
|
+
return existingNode;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Wait for a node to be added to the graph.
|
|
236
|
+
*
|
|
237
|
+
* If the node is already present in the graph, the promise resolves immediately.
|
|
238
|
+
*
|
|
239
|
+
* @param id The id of the node to wait for.
|
|
240
|
+
* @param timeout The time in milliseconds to wait for the node to be added.
|
|
241
|
+
*/
|
|
242
|
+
async waitForNode(id, timeout) {
|
|
243
|
+
const trigger = this._waitingForNodes[id] ?? (this._waitingForNodes[id] = new import_async.Trigger());
|
|
244
|
+
const node = this.findNode(id);
|
|
245
|
+
if (node) {
|
|
246
|
+
delete this._waitingForNodes[id];
|
|
247
|
+
return node;
|
|
248
|
+
}
|
|
249
|
+
if (timeout === void 0) {
|
|
250
|
+
return trigger.wait();
|
|
251
|
+
} else {
|
|
252
|
+
return (0, import_async.asyncTimeout)(trigger.wait(), timeout, `Node not found: ${id}`);
|
|
402
253
|
}
|
|
403
254
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Nodes that this node is connected to in default order.
|
|
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
|
|
407
265
|
});
|
|
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
|
+
];
|
|
408
290
|
}
|
|
409
|
-
|
|
410
|
-
const
|
|
411
|
-
const
|
|
412
|
-
if (
|
|
413
|
-
this.
|
|
414
|
-
|
|
415
|
-
outbound: source.outbound.filter((id) => id !== edgeArg.target)
|
|
416
|
-
});
|
|
291
|
+
async expand(node, relation = "outbound", type) {
|
|
292
|
+
const key = this._key(node, relation, type);
|
|
293
|
+
const initialized = this._initialized[key];
|
|
294
|
+
if (!initialized && this._onInitialNodes) {
|
|
295
|
+
await this._onInitialNodes(node, relation, type);
|
|
296
|
+
this._initialized[key] = true;
|
|
417
297
|
}
|
|
418
|
-
const targetRx = this._edges(edgeArg.target);
|
|
419
|
-
const target = this._registry.get(targetRx);
|
|
420
|
-
if (target.inbound.includes(edgeArg.source)) {
|
|
421
|
-
this._registry.set(targetRx, {
|
|
422
|
-
inbound: target.inbound.filter((id) => id !== edgeArg.source),
|
|
423
|
-
outbound: target.outbound
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
if (removeOrphans) {
|
|
427
|
-
const source2 = this._registry.get(sourceRx);
|
|
428
|
-
const target2 = this._registry.get(targetRx);
|
|
429
|
-
if (source2.outbound.length === 0 && source2.inbound.length === 0 && edgeArg.source !== ROOT_ID) {
|
|
430
|
-
this.removeNodes([
|
|
431
|
-
edgeArg.source
|
|
432
|
-
]);
|
|
433
|
-
}
|
|
434
|
-
if (target2.outbound.length === 0 && target2.inbound.length === 0 && edgeArg.target !== ROOT_ID) {
|
|
435
|
-
this.removeNodes([
|
|
436
|
-
edgeArg.target
|
|
437
|
-
]);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
sortEdges(id, relation, order) {
|
|
442
|
-
const edgesRx = this._edges(id);
|
|
443
|
-
const edges = this._registry.get(edgesRx);
|
|
444
|
-
const unsorted = edges[relation].filter((id2) => !order.includes(id2)) ?? [];
|
|
445
|
-
const sorted = order.filter((id2) => edges[relation].includes(id2)) ?? [];
|
|
446
|
-
edges[relation].splice(0, edges[relation].length, ...[
|
|
447
|
-
...sorted,
|
|
448
|
-
...unsorted
|
|
449
|
-
]);
|
|
450
|
-
this._registry.set(edgesRx, edges);
|
|
451
298
|
}
|
|
452
|
-
|
|
453
|
-
|
|
299
|
+
_key(node, relation, type) {
|
|
300
|
+
return `${node.id}-${relation}-${type}`;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Recursive depth-first traversal of the graph.
|
|
304
|
+
*
|
|
305
|
+
* @param options.node The node to start traversing from.
|
|
306
|
+
* @param options.relation The relation to traverse graph edges.
|
|
307
|
+
* @param options.visitor A callback which is called for each node visited during traversal.
|
|
308
|
+
*/
|
|
309
|
+
traverse({ visitor, node = this.root, relation = "outbound", expansion }, path = []) {
|
|
310
|
+
if (path.includes(node.id)) {
|
|
454
311
|
return;
|
|
455
312
|
}
|
|
456
|
-
const node = this.getNodeOrThrow(source);
|
|
457
313
|
const shouldContinue = visitor(node, [
|
|
458
314
|
...path,
|
|
459
|
-
|
|
315
|
+
node.id
|
|
460
316
|
]);
|
|
461
317
|
if (shouldContinue === false) {
|
|
462
318
|
return;
|
|
463
319
|
}
|
|
464
|
-
Object.values(this.
|
|
465
|
-
|
|
320
|
+
Object.values(this._getNodes({
|
|
321
|
+
node,
|
|
466
322
|
relation,
|
|
467
|
-
|
|
323
|
+
expansion
|
|
324
|
+
})).forEach((child) => this.traverse({
|
|
325
|
+
node: child,
|
|
326
|
+
relation,
|
|
327
|
+
visitor,
|
|
328
|
+
expansion
|
|
468
329
|
}, [
|
|
469
330
|
...path,
|
|
470
|
-
|
|
331
|
+
node.id
|
|
471
332
|
]));
|
|
472
333
|
}
|
|
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
|
+
*/
|
|
473
369
|
getPath({ source = "root", target }) {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
370
|
+
const start = this.findNode(source);
|
|
371
|
+
if (!start) {
|
|
372
|
+
return void 0;
|
|
373
|
+
}
|
|
374
|
+
let found;
|
|
375
|
+
this.traverse({
|
|
376
|
+
node: start,
|
|
377
|
+
visitor: (node, path) => {
|
|
378
|
+
if (found) {
|
|
379
|
+
return false;
|
|
485
380
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
381
|
+
if (node.id === target) {
|
|
382
|
+
found = path;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
return found;
|
|
489
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Wait for the path between two nodes in the graph to be established.
|
|
390
|
+
*/
|
|
490
391
|
async waitForPath(params, { timeout = 5e3, interval = 500 } = {}) {
|
|
491
392
|
const path = this.getPath(params);
|
|
492
|
-
if (
|
|
493
|
-
return path
|
|
393
|
+
if (path) {
|
|
394
|
+
return path;
|
|
494
395
|
}
|
|
495
396
|
const trigger = new import_async.Trigger();
|
|
496
397
|
const i = setInterval(() => {
|
|
497
398
|
const path2 = this.getPath(params);
|
|
498
|
-
if (
|
|
499
|
-
trigger.wake(path2
|
|
399
|
+
if (path2) {
|
|
400
|
+
trigger.wake(path2);
|
|
500
401
|
}
|
|
501
402
|
}, interval);
|
|
502
403
|
return trigger.wait({
|
|
503
404
|
timeout
|
|
504
405
|
}).finally(() => clearInterval(i));
|
|
505
406
|
}
|
|
506
|
-
/**
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
407
|
+
/**
|
|
408
|
+
* Add nodes to the graph.
|
|
409
|
+
*
|
|
410
|
+
* @internal
|
|
411
|
+
*/
|
|
412
|
+
_addNodes(nodes) {
|
|
413
|
+
return (0, import_signals_core.batch)(() => nodes.map((node) => this._addNode(node)));
|
|
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;
|
|
513
467
|
});
|
|
514
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
|
+
});
|
|
596
|
+
});
|
|
597
|
+
}
|
|
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
|
+
}
|
|
515
609
|
};
|
|
516
|
-
var isGraphNode = (data) => data && typeof data === "object" && "id" in data && "properties" in data && data.properties ? typeof data.properties === "object" && "data" in data : false;
|
|
517
|
-
var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" : false;
|
|
518
|
-
var actionGroupSymbol = Symbol("ActionGroup");
|
|
519
|
-
var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol : false;
|
|
520
|
-
var isActionLike = (data) => isAction(data) || isActionGroup(data);
|
|
521
610
|
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
|
|
611
|
+
var NODE_RESOLVER_TIMEOUT = 1e3;
|
|
522
612
|
var createExtension = (extension) => {
|
|
523
|
-
const { id, position = "static",
|
|
613
|
+
const { id, position = "static", resolver, connector, actions, actionGroups, ...rest } = extension;
|
|
524
614
|
const getId = (key) => `${id}/${key}`;
|
|
525
|
-
const actionGroups = _actionGroups && import_rx_react2.Rx.family((node) => _actionGroups(node).pipe(import_rx_react2.Rx.withLabel(`graph-builder:actionGroups:${id}`)));
|
|
526
|
-
const actions = _actions && import_rx_react2.Rx.family((node) => _actions(node).pipe(import_rx_react2.Rx.withLabel(`graph-builder:actions:${id}`)));
|
|
527
615
|
return [
|
|
528
|
-
|
|
616
|
+
resolver ? {
|
|
617
|
+
id: getId("resolver"),
|
|
618
|
+
position,
|
|
619
|
+
resolver
|
|
620
|
+
} : void 0,
|
|
529
621
|
connector ? {
|
|
622
|
+
...rest,
|
|
530
623
|
id: getId("connector"),
|
|
531
624
|
position,
|
|
532
|
-
|
|
533
|
-
connector: import_rx_react2.Rx.family((key) => connector(key).pipe(import_rx_react2.Rx.withLabel(`graph-builder:connector:${id}`)))
|
|
625
|
+
connector
|
|
534
626
|
} : void 0,
|
|
535
627
|
actionGroups ? {
|
|
628
|
+
...rest,
|
|
536
629
|
id: getId("actionGroups"),
|
|
537
630
|
position,
|
|
631
|
+
type: ACTION_GROUP_TYPE,
|
|
538
632
|
relation: "outbound",
|
|
539
|
-
connector:
|
|
633
|
+
connector: ({ node }) => actionGroups({
|
|
634
|
+
node
|
|
635
|
+
})?.map((arg) => ({
|
|
540
636
|
...arg,
|
|
541
637
|
data: actionGroupSymbol,
|
|
542
638
|
type: ACTION_GROUP_TYPE
|
|
543
|
-
}))
|
|
639
|
+
}))
|
|
544
640
|
} : void 0,
|
|
545
641
|
actions ? {
|
|
642
|
+
...rest,
|
|
546
643
|
id: getId("actions"),
|
|
547
644
|
position,
|
|
645
|
+
type: ACTION_TYPE,
|
|
548
646
|
relation: "outbound",
|
|
549
|
-
connector:
|
|
647
|
+
connector: ({ node }) => actions({
|
|
648
|
+
node
|
|
649
|
+
})?.map((arg) => ({
|
|
550
650
|
...arg,
|
|
551
651
|
type: ACTION_TYPE
|
|
552
|
-
}))
|
|
652
|
+
}))
|
|
553
653
|
} : void 0
|
|
554
654
|
].filter(import_util2.isNonNullable);
|
|
555
655
|
};
|
|
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
|
+
};
|
|
556
715
|
var flattenExtensions = (extension, acc = []) => {
|
|
557
|
-
if (
|
|
716
|
+
if (Array.isArray(extension)) {
|
|
558
717
|
return [
|
|
559
718
|
...acc,
|
|
560
719
|
...extension.flatMap((ext) => flattenExtensions(ext, acc))
|
|
@@ -567,74 +726,107 @@ var flattenExtensions = (extension, acc = []) => {
|
|
|
567
726
|
}
|
|
568
727
|
};
|
|
569
728
|
var GraphBuilder = class _GraphBuilder {
|
|
570
|
-
constructor(
|
|
729
|
+
constructor(params = {}) {
|
|
730
|
+
this._dispatcher = new Dispatcher();
|
|
731
|
+
this._extensions = (0, import_live_object2.create)({});
|
|
732
|
+
this._resolverSubscriptions = /* @__PURE__ */ new Map();
|
|
571
733
|
this._connectorSubscriptions = /* @__PURE__ */ new Map();
|
|
572
|
-
this.
|
|
573
|
-
this.
|
|
574
|
-
return import_rx_react2.Rx.make((get) => {
|
|
575
|
-
const [id, relation] = key.split("+");
|
|
576
|
-
const node = this._graph.node(id);
|
|
577
|
-
return (0, import_effect2.pipe)(
|
|
578
|
-
get(this._extensions),
|
|
579
|
-
import_effect2.Record.values,
|
|
580
|
-
// TODO(wittjosiah): Sort on write rather than read.
|
|
581
|
-
import_effect2.Array.sortBy(import_util2.byPosition),
|
|
582
|
-
import_effect2.Array.filter(({ relation: _relation = "outbound" }) => _relation === relation),
|
|
583
|
-
import_effect2.Array.map(({ connector }) => connector?.(node)),
|
|
584
|
-
import_effect2.Array.filter(import_util2.isNonNullable),
|
|
585
|
-
import_effect2.Array.flatMap((result) => get(result))
|
|
586
|
-
);
|
|
587
|
-
}).pipe(import_rx_react2.Rx.withLabel(`graph-builder:connectors:${key}`));
|
|
588
|
-
});
|
|
589
|
-
this._registry = registry ?? import_rx_react2.Registry.make();
|
|
734
|
+
this._nodeChanged = {};
|
|
735
|
+
this._initialized = {};
|
|
590
736
|
this._graph = new Graph({
|
|
591
737
|
...params,
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
// onInitialize: (id) => this._onInitialize(id),
|
|
738
|
+
onInitialNode: async (id) => this._onInitialNode(id),
|
|
739
|
+
onInitialNodes: async (node, relation, type) => this._onInitialNodes(node, relation, type),
|
|
595
740
|
onRemoveNode: (id) => this._onRemoveNode(id)
|
|
596
741
|
});
|
|
597
742
|
}
|
|
598
|
-
static from(pickle
|
|
743
|
+
static from(pickle) {
|
|
599
744
|
if (!pickle) {
|
|
600
|
-
return new _GraphBuilder(
|
|
601
|
-
registry
|
|
602
|
-
});
|
|
745
|
+
return new _GraphBuilder();
|
|
603
746
|
}
|
|
604
747
|
const { nodes, edges } = JSON.parse(pickle);
|
|
605
748
|
return new _GraphBuilder({
|
|
606
749
|
nodes,
|
|
607
|
-
edges
|
|
608
|
-
registry
|
|
750
|
+
edges
|
|
609
751
|
});
|
|
610
752
|
}
|
|
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
|
+
}
|
|
611
781
|
get graph() {
|
|
612
782
|
return this._graph;
|
|
613
783
|
}
|
|
784
|
+
/**
|
|
785
|
+
* @reactive
|
|
786
|
+
*/
|
|
614
787
|
get extensions() {
|
|
615
|
-
return this._extensions;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
788
|
+
return Object.values(this._extensions);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Register a node builder which will be called in order to construct the graph.
|
|
792
|
+
*/
|
|
793
|
+
addExtension(extension) {
|
|
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
|
+
});
|
|
621
800
|
});
|
|
622
801
|
return this;
|
|
623
802
|
}
|
|
803
|
+
/**
|
|
804
|
+
* Remove a node builder from the graph builder.
|
|
805
|
+
*/
|
|
624
806
|
removeExtension(id) {
|
|
625
|
-
|
|
626
|
-
|
|
807
|
+
(0, import_signals_core2.untracked)(() => {
|
|
808
|
+
delete this._extensions[id];
|
|
809
|
+
});
|
|
627
810
|
return this;
|
|
628
811
|
}
|
|
629
|
-
|
|
630
|
-
|
|
812
|
+
destroy() {
|
|
813
|
+
this._dispatcher.cleanup.forEach((fn) => fn());
|
|
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)) {
|
|
631
824
|
return;
|
|
632
825
|
}
|
|
633
826
|
if (!(0, import_util2.isNode)()) {
|
|
634
827
|
const { yieldOrContinue } = await import("main-thread-scheduling");
|
|
635
828
|
await yieldOrContinue("idle");
|
|
636
829
|
}
|
|
637
|
-
const node = registry.get(this._graph.nodeOrThrow(source));
|
|
638
830
|
const shouldContinue = await visitor(node, [
|
|
639
831
|
...path,
|
|
640
832
|
node.id
|
|
@@ -642,104 +834,158 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
642
834
|
if (shouldContinue === false) {
|
|
643
835
|
return;
|
|
644
836
|
}
|
|
645
|
-
const nodes = Object.values(this.
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
837
|
+
const nodes = Object.values(this._extensions).filter((extension) => relation === (extension.relation ?? "outbound")).filter((extension) => !extension.filter || extension.filter(node)).flatMap((extension) => {
|
|
838
|
+
this._dispatcher.currentExtension = extension.id;
|
|
839
|
+
this._dispatcher.stateIndex = 0;
|
|
840
|
+
BuilderInternal.currentDispatcher = this._dispatcher;
|
|
841
|
+
const result = extension.connector?.({
|
|
842
|
+
node
|
|
843
|
+
}) ?? [];
|
|
844
|
+
BuilderInternal.currentDispatcher = void 0;
|
|
845
|
+
return result;
|
|
846
|
+
}).map((arg) => ({
|
|
847
|
+
id: arg.id,
|
|
848
|
+
type: arg.type,
|
|
849
|
+
cacheable: arg.cacheable,
|
|
850
|
+
data: arg.data ?? null,
|
|
851
|
+
properties: arg.properties ?? {}
|
|
657
852
|
}));
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
registry.dispose();
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
destroy() {
|
|
664
|
-
this._connectorSubscriptions.forEach((unsubscribe) => unsubscribe());
|
|
665
|
-
this._connectorSubscriptions.clear();
|
|
666
|
-
}
|
|
667
|
-
_onExpand(id, relation) {
|
|
668
|
-
(0, import_log2.log)("onExpand", {
|
|
669
|
-
id,
|
|
853
|
+
await Promise.all(nodes.map((n) => this.explore({
|
|
854
|
+
node: n,
|
|
670
855
|
relation,
|
|
671
|
-
|
|
672
|
-
},
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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
|
+
}
|
|
914
|
+
}));
|
|
915
|
+
}
|
|
916
|
+
_onInitialNodes(node, nodesRelation, nodesType) {
|
|
917
|
+
this._nodeChanged[node.id] = this._nodeChanged[node.id] ?? (0, import_signals_core2.signal)({});
|
|
918
|
+
let first = true;
|
|
679
919
|
let previous = [];
|
|
680
|
-
|
|
920
|
+
this._connectorSubscriptions.set(node.id, (0, import_signals_core2.effect)(() => {
|
|
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
|
+
}
|
|
681
959
|
const ids = nodes.map((n) => n.id);
|
|
682
|
-
const removed = previous.filter((
|
|
960
|
+
const removed = previous.filter((id) => !ids.includes(id));
|
|
683
961
|
previous = ids;
|
|
684
|
-
(
|
|
685
|
-
id,
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
this._graph.addEdges(nodes.map((node) => relation === "outbound" ? {
|
|
702
|
-
source: id,
|
|
703
|
-
target: node.id
|
|
704
|
-
} : {
|
|
705
|
-
source: node.id,
|
|
706
|
-
target: id
|
|
707
|
-
}));
|
|
708
|
-
this._graph.sortEdges(id, relation, nodes.map(({ id: id2 }) => id2));
|
|
962
|
+
this.graph._removeEdges(removed.map((target) => ({
|
|
963
|
+
source: node.id,
|
|
964
|
+
target
|
|
965
|
+
})), true);
|
|
966
|
+
this.graph._addNodes(nodes);
|
|
967
|
+
this.graph._addEdges(nodes.map(({ id }) => nodesRelation === "outbound" ? {
|
|
968
|
+
source: node.id,
|
|
969
|
+
target: id
|
|
970
|
+
} : {
|
|
971
|
+
source: id,
|
|
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
|
+
}
|
|
709
979
|
});
|
|
710
|
-
}
|
|
711
|
-
immediate: true
|
|
712
|
-
});
|
|
713
|
-
this._connectorSubscriptions.set(id, cancel);
|
|
980
|
+
}));
|
|
714
981
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
this._connectorSubscriptions.get(id)?.();
|
|
721
|
-
this._connectorSubscriptions.delete(id);
|
|
982
|
+
async _onRemoveNode(nodeId) {
|
|
983
|
+
this._resolverSubscriptions.get(nodeId)?.();
|
|
984
|
+
this._connectorSubscriptions.get(nodeId)?.();
|
|
985
|
+
this._resolverSubscriptions.delete(nodeId);
|
|
986
|
+
this._connectorSubscriptions.delete(nodeId);
|
|
722
987
|
}
|
|
723
988
|
};
|
|
724
|
-
var rxFromSignal = (cb) => {
|
|
725
|
-
return import_rx_react2.Rx.make((get) => {
|
|
726
|
-
const dispose = (0, import_signals_core.effect)(() => {
|
|
727
|
-
get.setSelf(cb());
|
|
728
|
-
});
|
|
729
|
-
get.addFinalizer(() => dispose());
|
|
730
|
-
return cb();
|
|
731
|
-
});
|
|
732
|
-
};
|
|
733
|
-
var observableFamily = import_rx_react2.Rx.family((observable) => {
|
|
734
|
-
return import_rx_react2.Rx.make((get) => {
|
|
735
|
-
const subscription = observable.subscribe((value) => get.setSelf(value));
|
|
736
|
-
get.addFinalizer(() => subscription.unsubscribe());
|
|
737
|
-
return observable.get();
|
|
738
|
-
});
|
|
739
|
-
});
|
|
740
|
-
var rxFromObservable = (observable) => {
|
|
741
|
-
return observableFamily(observable);
|
|
742
|
-
};
|
|
743
989
|
// Annotate the CommonJS export names for ESM import in node:
|
|
744
990
|
0 && (module.exports = {
|
|
745
991
|
ACTION_GROUP_TYPE,
|
|
@@ -749,6 +995,7 @@ var rxFromObservable = (observable) => {
|
|
|
749
995
|
ROOT_ID,
|
|
750
996
|
ROOT_TYPE,
|
|
751
997
|
actionGroupSymbol,
|
|
998
|
+
cleanup,
|
|
752
999
|
createExtension,
|
|
753
1000
|
flattenExtensions,
|
|
754
1001
|
getGraph,
|
|
@@ -756,7 +1003,7 @@ var rxFromObservable = (observable) => {
|
|
|
756
1003
|
isActionGroup,
|
|
757
1004
|
isActionLike,
|
|
758
1005
|
isGraphNode,
|
|
759
|
-
|
|
760
|
-
|
|
1006
|
+
memoize,
|
|
1007
|
+
toSignal
|
|
761
1008
|
});
|
|
762
1009
|
//# sourceMappingURL=index.cjs.map
|