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