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