@dxos/app-graph 0.6.11 → 0.6.12-main.5a87ad5

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.
@@ -0,0 +1,808 @@
1
+ import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
+
3
+ // packages/sdk/app-graph/src/graph.ts
4
+ import { batch, effect, untracked } from "@preact/signals-core";
5
+ import { asyncTimeout, Trigger } from "@dxos/async";
6
+ import { create } from "@dxos/echo-schema";
7
+ import { invariant } from "@dxos/invariant";
8
+ import { nonNullable } from "@dxos/util";
9
+
10
+ // packages/sdk/app-graph/src/node.ts
11
+ var isGraphNode = (data) => data && typeof data === "object" && "id" in data && "properties" in data && data.properties ? typeof data.properties === "object" && "data" in data : false;
12
+ var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" : false;
13
+ var actionGroupSymbol = Symbol("ActionGroup");
14
+ var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol : false;
15
+ var isActionLike = (data) => isAction(data) || isActionGroup(data);
16
+
17
+ // packages/sdk/app-graph/src/graph.ts
18
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
19
+ var graphSymbol = Symbol("graph");
20
+ var getGraph = (node) => {
21
+ const graph = node[graphSymbol];
22
+ invariant(graph, "Node is not associated with a graph.", {
23
+ F: __dxlog_file,
24
+ L: 20,
25
+ S: void 0,
26
+ A: [
27
+ "graph",
28
+ "'Node is not associated with a graph.'"
29
+ ]
30
+ });
31
+ return graph;
32
+ };
33
+ var ROOT_ID = "root";
34
+ var ROOT_TYPE = "dxos.org/type/GraphRoot";
35
+ var ACTION_TYPE = "dxos.org/type/GraphAction";
36
+ var ACTION_GROUP_TYPE = "dxos.org/type/GraphActionGroup";
37
+ var DEFAULT_FILTER = (node) => untracked(() => !isActionLike(node));
38
+ var Graph = class {
39
+ constructor({ onInitialNode, onInitialNodes, onRemoveNode } = {}) {
40
+ this._waitingForNodes = {};
41
+ this._initialized = {};
42
+ /**
43
+ * @internal
44
+ */
45
+ this._nodes = {};
46
+ /**
47
+ * @internal
48
+ */
49
+ this._edges = {};
50
+ this._constructNode = (node) => {
51
+ return create({
52
+ ...node,
53
+ [graphSymbol]: this
54
+ });
55
+ };
56
+ this._onInitialNode = onInitialNode;
57
+ this._onInitialNodes = onInitialNodes;
58
+ this._onRemoveNode = onRemoveNode;
59
+ this._nodes[ROOT_ID] = this._constructNode({
60
+ id: ROOT_ID,
61
+ type: ROOT_TYPE,
62
+ properties: {},
63
+ data: null
64
+ });
65
+ this._edges[ROOT_ID] = create({
66
+ inbound: [],
67
+ outbound: []
68
+ });
69
+ }
70
+ /**
71
+ * Alias for `findNode('root')`.
72
+ */
73
+ get root() {
74
+ return this.findNode(ROOT_ID);
75
+ }
76
+ /**
77
+ * Convert the graph to a JSON object.
78
+ */
79
+ toJSON({ id = ROOT_ID, maxLength = 32 } = {}) {
80
+ const toJSON = (node, seen = []) => {
81
+ const nodes = this.nodes(node);
82
+ const obj = {
83
+ id: node.id.length > maxLength ? `${node.id.slice(0, maxLength - 3)}...` : node.id,
84
+ type: node.type
85
+ };
86
+ if (node.properties.label) {
87
+ obj.label = node.properties.label;
88
+ }
89
+ if (nodes.length) {
90
+ obj.nodes = nodes.map((n) => {
91
+ const nextSeen = [
92
+ ...seen,
93
+ node.id
94
+ ];
95
+ return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
96
+ }).filter(nonNullable);
97
+ }
98
+ return obj;
99
+ };
100
+ const root = this.findNode(id);
101
+ invariant(root, `Node not found: ${id}`, {
102
+ F: __dxlog_file,
103
+ L: 137,
104
+ S: this,
105
+ A: [
106
+ "root",
107
+ "`Node not found: ${id}`"
108
+ ]
109
+ });
110
+ return toJSON(root);
111
+ }
112
+ /**
113
+ * Find the node with the given id in the graph.
114
+ *
115
+ * If a node is not found within the graph and an `onInitialNode` callback is provided,
116
+ * it is called with the id and type of the node, potentially initializing the node.
117
+ */
118
+ findNode(id, expansion = true) {
119
+ const existingNode = this._nodes[id];
120
+ if (!existingNode && expansion) {
121
+ void this._onInitialNode?.(id);
122
+ }
123
+ return existingNode;
124
+ }
125
+ /**
126
+ * Wait for a node to be added to the graph.
127
+ *
128
+ * If the node is already present in the graph, the promise resolves immediately.
129
+ *
130
+ * @param id The id of the node to wait for.
131
+ * @param timeout The time in milliseconds to wait for the node to be added.
132
+ */
133
+ async waitForNode(id, timeout) {
134
+ const trigger = this._waitingForNodes[id] ?? (this._waitingForNodes[id] = new Trigger());
135
+ const node = this.findNode(id);
136
+ if (node) {
137
+ delete this._waitingForNodes[id];
138
+ return node;
139
+ }
140
+ if (timeout === void 0) {
141
+ return trigger.wait();
142
+ } else {
143
+ return asyncTimeout(trigger.wait(), timeout, `Node not found: ${id}`);
144
+ }
145
+ }
146
+ /**
147
+ * Nodes that this node is connected to in default order.
148
+ */
149
+ nodes(node, options = {}) {
150
+ const { relation, expansion, filter = DEFAULT_FILTER, type } = options;
151
+ const nodes = this._getNodes({
152
+ node,
153
+ relation,
154
+ expansion,
155
+ type
156
+ });
157
+ return nodes.filter((n) => filter(n, node));
158
+ }
159
+ /**
160
+ * Edges that this node is connected to in default order.
161
+ */
162
+ edges(node, { relation = "outbound" } = {}) {
163
+ return this._edges[node.id]?.[relation] ?? [];
164
+ }
165
+ /**
166
+ * Actions or action groups that this node is connected to in default order.
167
+ */
168
+ actions(node, { expansion } = {}) {
169
+ return [
170
+ ...this._getNodes({
171
+ node,
172
+ expansion,
173
+ type: ACTION_GROUP_TYPE
174
+ }),
175
+ ...this._getNodes({
176
+ node,
177
+ expansion,
178
+ type: ACTION_TYPE
179
+ })
180
+ ];
181
+ }
182
+ async expand(node, relation = "outbound", type) {
183
+ const key = this._key(node, relation, type);
184
+ const initialized = this._initialized[key];
185
+ if (!initialized && this._onInitialNodes) {
186
+ await this._onInitialNodes(node, relation, type);
187
+ this._initialized[key] = true;
188
+ }
189
+ }
190
+ _key(node, relation, type) {
191
+ return `${node.id}-${relation}-${type}`;
192
+ }
193
+ /**
194
+ * Recursive depth-first traversal of the graph.
195
+ *
196
+ * @param options.node The node to start traversing from.
197
+ * @param options.relation The relation to traverse graph edges.
198
+ * @param options.visitor A callback which is called for each node visited during traversal.
199
+ */
200
+ traverse({ visitor, node = this.root, relation = "outbound", expansion }, path = []) {
201
+ if (path.includes(node.id)) {
202
+ return;
203
+ }
204
+ const shouldContinue = visitor(node, [
205
+ ...path,
206
+ node.id
207
+ ]);
208
+ if (shouldContinue === false) {
209
+ return;
210
+ }
211
+ Object.values(this._getNodes({
212
+ node,
213
+ relation,
214
+ expansion
215
+ })).forEach((child) => this.traverse({
216
+ node: child,
217
+ relation,
218
+ visitor,
219
+ expansion
220
+ }, [
221
+ ...path,
222
+ node.id
223
+ ]));
224
+ }
225
+ /**
226
+ * Recursive depth-first traversal of the graph wrapping each visitor call in an effect.
227
+ *
228
+ * @param options.node The node to start traversing from.
229
+ * @param options.relation The relation to traverse graph edges.
230
+ * @param options.visitor A callback which is called for each node visited during traversal.
231
+ */
232
+ subscribeTraverse({ visitor, node = this.root, relation = "outbound", expansion }, currentPath = []) {
233
+ return effect(() => {
234
+ const path = [
235
+ ...currentPath,
236
+ node.id
237
+ ];
238
+ const result = visitor(node, path);
239
+ if (result === false) {
240
+ return;
241
+ }
242
+ const nodes = this._getNodes({
243
+ node,
244
+ relation,
245
+ expansion
246
+ });
247
+ const nodeSubscriptions = nodes.map((n) => this.subscribeTraverse({
248
+ node: n,
249
+ visitor,
250
+ expansion
251
+ }, path));
252
+ return () => {
253
+ nodeSubscriptions.forEach((unsubscribe) => unsubscribe());
254
+ };
255
+ });
256
+ }
257
+ /**
258
+ * Get the path between two nodes in the graph.
259
+ */
260
+ getPath({ source = "root", target }) {
261
+ const start = this.findNode(source);
262
+ if (!start) {
263
+ return void 0;
264
+ }
265
+ let found;
266
+ this.traverse({
267
+ node: start,
268
+ visitor: (node, path) => {
269
+ if (found) {
270
+ return false;
271
+ }
272
+ if (node.id === target) {
273
+ found = path;
274
+ }
275
+ }
276
+ });
277
+ return found;
278
+ }
279
+ /**
280
+ * Add nodes to the graph.
281
+ *
282
+ * @internal
283
+ */
284
+ _addNodes(nodes) {
285
+ return batch(() => nodes.map((node) => this._addNode(node)));
286
+ }
287
+ _addNode({ nodes, edges, ..._node }) {
288
+ return untracked(() => {
289
+ const existingNode = this._nodes[_node.id];
290
+ const node = existingNode ?? this._constructNode({
291
+ data: null,
292
+ properties: {},
293
+ ..._node
294
+ });
295
+ if (existingNode) {
296
+ const { data, properties, type } = _node;
297
+ if (data && data !== node.data) {
298
+ node.data = data;
299
+ }
300
+ if (type !== node.type) {
301
+ node.type = type;
302
+ }
303
+ for (const key in properties) {
304
+ if (properties[key] !== node.properties[key]) {
305
+ node.properties[key] = properties[key];
306
+ }
307
+ }
308
+ } else {
309
+ this._nodes[node.id] = node;
310
+ this._edges[node.id] = create({
311
+ inbound: [],
312
+ outbound: []
313
+ });
314
+ }
315
+ const trigger = this._waitingForNodes[node.id];
316
+ if (trigger) {
317
+ trigger.wake(node);
318
+ delete this._waitingForNodes[node.id];
319
+ }
320
+ if (nodes) {
321
+ nodes.forEach((subNode) => {
322
+ this._addNode(subNode);
323
+ this._addEdge({
324
+ source: node.id,
325
+ target: subNode.id
326
+ });
327
+ });
328
+ }
329
+ if (edges) {
330
+ edges.forEach(([id, relation]) => relation === "outbound" ? this._addEdge({
331
+ source: node.id,
332
+ target: id
333
+ }) : this._addEdge({
334
+ source: id,
335
+ target: node.id
336
+ }));
337
+ }
338
+ return node;
339
+ });
340
+ }
341
+ /**
342
+ * Remove nodes from the graph.
343
+ *
344
+ * @param ids The id of the node to remove.
345
+ * @param edges Whether to remove edges connected to the node from the graph as well.
346
+ * @internal
347
+ */
348
+ _removeNodes(ids, edges = false) {
349
+ batch(() => ids.forEach((id) => this._removeNode(id, edges)));
350
+ }
351
+ _removeNode(id, edges = false) {
352
+ untracked(() => {
353
+ const node = this.findNode(id);
354
+ if (!node) {
355
+ return;
356
+ }
357
+ if (edges) {
358
+ this._getNodes({
359
+ node
360
+ }).forEach((node2) => {
361
+ this._removeEdge({
362
+ source: id,
363
+ target: node2.id
364
+ });
365
+ });
366
+ this._getNodes({
367
+ node,
368
+ relation: "inbound"
369
+ }).forEach((node2) => {
370
+ this._removeEdge({
371
+ source: node2.id,
372
+ target: id
373
+ });
374
+ });
375
+ delete this._edges[id];
376
+ }
377
+ delete this._nodes[id];
378
+ Object.keys(this._initialized).filter((key) => key.startsWith(id)).forEach((key) => {
379
+ delete this._initialized[key];
380
+ });
381
+ void this._onRemoveNode?.(id);
382
+ });
383
+ }
384
+ /**
385
+ * Add edges to the graph.
386
+ *
387
+ * @internal
388
+ */
389
+ _addEdges(edges) {
390
+ batch(() => edges.forEach((edge) => this._addEdge(edge)));
391
+ }
392
+ _addEdge({ source, target }) {
393
+ untracked(() => {
394
+ if (!this._edges[source]) {
395
+ this._edges[source] = create({
396
+ inbound: [],
397
+ outbound: []
398
+ });
399
+ }
400
+ if (!this._edges[target]) {
401
+ this._edges[target] = create({
402
+ inbound: [],
403
+ outbound: []
404
+ });
405
+ }
406
+ const sourceEdges = this._edges[source];
407
+ if (!sourceEdges.outbound.includes(target)) {
408
+ sourceEdges.outbound.push(target);
409
+ }
410
+ const targetEdges = this._edges[target];
411
+ if (!targetEdges.inbound.includes(source)) {
412
+ targetEdges.inbound.push(source);
413
+ }
414
+ });
415
+ }
416
+ /**
417
+ * Remove edges from the graph.
418
+ * @internal
419
+ */
420
+ _removeEdges(edges, removeOrphans = false) {
421
+ batch(() => edges.forEach((edge) => this._removeEdge(edge, removeOrphans)));
422
+ }
423
+ _removeEdge({ source, target }, removeOrphans = false) {
424
+ untracked(() => {
425
+ batch(() => {
426
+ const outboundIndex = this._edges[source]?.outbound.findIndex((id) => id === target);
427
+ if (outboundIndex !== void 0 && outboundIndex !== -1) {
428
+ this._edges[source].outbound.splice(outboundIndex, 1);
429
+ }
430
+ const inboundIndex = this._edges[target]?.inbound.findIndex((id) => id === source);
431
+ if (inboundIndex !== void 0 && inboundIndex !== -1) {
432
+ this._edges[target].inbound.splice(inboundIndex, 1);
433
+ }
434
+ if (removeOrphans) {
435
+ if (this._edges[source]?.outbound.length === 0 && this._edges[source]?.inbound.length === 0 && source !== ROOT_ID) {
436
+ this._removeNode(source, true);
437
+ }
438
+ if (this._edges[target]?.outbound.length === 0 && this._edges[target]?.inbound.length === 0 && target !== ROOT_ID) {
439
+ this._removeNode(target, true);
440
+ }
441
+ }
442
+ });
443
+ });
444
+ }
445
+ /**
446
+ * Sort edges for a node.
447
+ *
448
+ * Edges not included in the sorted list are appended to the end of the list.
449
+ *
450
+ * @param nodeId The id of the node to sort edges for.
451
+ * @param relation The relation of the edges from the node to sort.
452
+ * @param edges The ordered list of edges.
453
+ * @ignore
454
+ */
455
+ _sortEdges(nodeId, relation, edges) {
456
+ untracked(() => {
457
+ batch(() => {
458
+ const current = this._edges[nodeId];
459
+ if (current) {
460
+ const unsorted = current[relation].filter((id) => !edges.includes(id)) ?? [];
461
+ const sorted = edges.filter((id) => current[relation].includes(id)) ?? [];
462
+ current[relation].splice(0, current[relation].length, ...[
463
+ ...sorted,
464
+ ...unsorted
465
+ ]);
466
+ }
467
+ });
468
+ });
469
+ }
470
+ _getNodes({ node, relation = "outbound", type, expansion }) {
471
+ if (expansion) {
472
+ void this.expand(node, relation, type);
473
+ }
474
+ const edges = this._edges[node.id];
475
+ if (!edges) {
476
+ return [];
477
+ } else {
478
+ return edges[relation].map((id) => this._nodes[id]).filter(nonNullable).filter((n) => !type || n.type === type);
479
+ }
480
+ }
481
+ };
482
+
483
+ // packages/sdk/app-graph/src/graph-builder.ts
484
+ import { effect as effect2, signal } from "@preact/signals-core";
485
+ import { create as create2 } from "@dxos/echo-schema";
486
+ import { invariant as invariant2 } from "@dxos/invariant";
487
+ import { log } from "@dxos/log";
488
+ import { isNode, nonNullable as nonNullable2 } from "@dxos/util";
489
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
490
+ var createExtension = (extension) => {
491
+ const { id, resolver, connector, actions, actionGroups, ...rest } = extension;
492
+ const getId = (key) => `${id}/${key}`;
493
+ return [
494
+ resolver ? {
495
+ id: getId("resolver"),
496
+ resolver
497
+ } : void 0,
498
+ connector ? {
499
+ ...rest,
500
+ id: getId("connector"),
501
+ connector
502
+ } : void 0,
503
+ actionGroups ? {
504
+ ...rest,
505
+ id: getId("actionGroups"),
506
+ type: ACTION_GROUP_TYPE,
507
+ relation: "outbound",
508
+ connector: ({ node }) => actionGroups({
509
+ node
510
+ })?.map((arg) => ({
511
+ ...arg,
512
+ data: actionGroupSymbol,
513
+ type: ACTION_GROUP_TYPE
514
+ }))
515
+ } : void 0,
516
+ actions ? {
517
+ ...rest,
518
+ id: getId("actions"),
519
+ type: ACTION_TYPE,
520
+ relation: "outbound",
521
+ connector: ({ node }) => actions({
522
+ node
523
+ })?.map((arg) => ({
524
+ ...arg,
525
+ type: ACTION_TYPE
526
+ }))
527
+ } : void 0
528
+ ].filter(nonNullable2);
529
+ };
530
+ var Dispatcher = class {
531
+ constructor() {
532
+ this.stateIndex = 0;
533
+ this.state = {};
534
+ this.cleanup = [];
535
+ }
536
+ };
537
+ var BuilderInternal = class {
538
+ };
539
+ var memoize = (fn, key = "result") => {
540
+ const dispatcher = BuilderInternal.currentDispatcher;
541
+ invariant2(dispatcher?.currentExtension, "memoize must be called within an extension", {
542
+ F: __dxlog_file2,
543
+ L: 129,
544
+ S: void 0,
545
+ A: [
546
+ "dispatcher?.currentExtension",
547
+ "'memoize must be called within an extension'"
548
+ ]
549
+ });
550
+ const all = dispatcher.state[dispatcher.currentExtension][dispatcher.stateIndex] ?? {};
551
+ const current = all[key];
552
+ const result = current ? current.result : fn();
553
+ dispatcher.state[dispatcher.currentExtension][dispatcher.stateIndex] = {
554
+ ...all,
555
+ [key]: {
556
+ result
557
+ }
558
+ };
559
+ dispatcher.stateIndex++;
560
+ return result;
561
+ };
562
+ var cleanup = (fn) => {
563
+ memoize(() => {
564
+ const dispatcher = BuilderInternal.currentDispatcher;
565
+ invariant2(dispatcher, "cleanup must be called within an extension", {
566
+ F: __dxlog_file2,
567
+ L: 144,
568
+ S: void 0,
569
+ A: [
570
+ "dispatcher",
571
+ "'cleanup must be called within an extension'"
572
+ ]
573
+ });
574
+ dispatcher.cleanup.push(fn);
575
+ });
576
+ };
577
+ var toSignal = (subscribe, get, key) => {
578
+ const thisSignal = memoize(() => {
579
+ return signal(get());
580
+ }, key);
581
+ const unsubscribe = memoize(() => {
582
+ return subscribe(() => thisSignal.value = get());
583
+ }, key);
584
+ cleanup(() => {
585
+ unsubscribe();
586
+ });
587
+ return thisSignal.value;
588
+ };
589
+ var GraphBuilder = class {
590
+ constructor() {
591
+ this._dispatcher = new Dispatcher();
592
+ this._extensions = create2({});
593
+ this._resolverSubscriptions = /* @__PURE__ */ new Map();
594
+ this._connectorSubscriptions = /* @__PURE__ */ new Map();
595
+ this._nodeChanged = {};
596
+ this._graph = new Graph({
597
+ onInitialNode: (id) => this._onInitialNode(id),
598
+ onInitialNodes: (node, relation, type) => this._onInitialNodes(node, relation, type),
599
+ onRemoveNode: (id) => this._onRemoveNode(id)
600
+ });
601
+ }
602
+ get graph() {
603
+ return this._graph;
604
+ }
605
+ /**
606
+ * Register a node builder which will be called in order to construct the graph.
607
+ */
608
+ addExtension(extension) {
609
+ if (Array.isArray(extension)) {
610
+ extension.forEach((ext) => this.addExtension(ext));
611
+ return this;
612
+ }
613
+ this._dispatcher.state[extension.id] = [];
614
+ this._extensions[extension.id] = extension;
615
+ return this;
616
+ }
617
+ /**
618
+ * Remove a node builder from the graph builder.
619
+ */
620
+ removeExtension(id) {
621
+ delete this._extensions[id];
622
+ return this;
623
+ }
624
+ destroy() {
625
+ this._dispatcher.cleanup.forEach((fn) => fn());
626
+ this._resolverSubscriptions.forEach((unsubscribe) => unsubscribe());
627
+ this._connectorSubscriptions.forEach((unsubscribe) => unsubscribe());
628
+ this._resolverSubscriptions.clear();
629
+ this._connectorSubscriptions.clear();
630
+ }
631
+ /**
632
+ * A graph traversal using just the connector extensions, without subscribing to any signals or persisting any nodes.
633
+ */
634
+ async explore({ node = this._graph.root, relation = "outbound", visitor }, path = []) {
635
+ if (path.includes(node.id)) {
636
+ return;
637
+ }
638
+ if (!isNode()) {
639
+ const { yieldOrContinue } = await import("main-thread-scheduling");
640
+ await yieldOrContinue("idle");
641
+ }
642
+ const shouldContinue = await visitor(node, [
643
+ ...path,
644
+ node.id
645
+ ]);
646
+ if (shouldContinue === false) {
647
+ return;
648
+ }
649
+ const nodes = Object.values(this._extensions).filter((extension) => relation === (extension.relation ?? "outbound")).filter((extension) => !extension.filter || extension.filter(node)).flatMap((extension) => {
650
+ this._dispatcher.currentExtension = extension.id;
651
+ this._dispatcher.stateIndex = 0;
652
+ BuilderInternal.currentDispatcher = this._dispatcher;
653
+ const result = extension.connector?.({
654
+ node
655
+ }) ?? [];
656
+ BuilderInternal.currentDispatcher = void 0;
657
+ return result;
658
+ }).map((arg) => ({
659
+ id: arg.id,
660
+ type: arg.type,
661
+ data: arg.data ?? null,
662
+ properties: arg.properties ?? {}
663
+ }));
664
+ await Promise.all(nodes.map((n) => this.explore({
665
+ node: n,
666
+ relation,
667
+ visitor
668
+ }, [
669
+ ...path,
670
+ node.id
671
+ ])));
672
+ }
673
+ async _onInitialNode(nodeId) {
674
+ this._nodeChanged[nodeId] = this._nodeChanged[nodeId] ?? signal({});
675
+ this._resolverSubscriptions.set(nodeId, effect2(() => {
676
+ for (const { id, resolver } of Object.values(this._extensions)) {
677
+ if (!resolver) {
678
+ continue;
679
+ }
680
+ this._dispatcher.currentExtension = id;
681
+ this._dispatcher.stateIndex = 0;
682
+ BuilderInternal.currentDispatcher = this._dispatcher;
683
+ let node;
684
+ try {
685
+ node = resolver({
686
+ id: nodeId
687
+ });
688
+ } catch (err) {
689
+ log.catch(err, {
690
+ extension: id
691
+ }, {
692
+ F: __dxlog_file2,
693
+ L: 300,
694
+ S: this,
695
+ C: (f, a) => f(...a)
696
+ });
697
+ log.error(`Previous error occurred in extension: ${id}`, void 0, {
698
+ F: __dxlog_file2,
699
+ L: 301,
700
+ S: this,
701
+ C: (f, a) => f(...a)
702
+ });
703
+ } finally {
704
+ BuilderInternal.currentDispatcher = void 0;
705
+ }
706
+ if (node) {
707
+ this.graph._addNodes([
708
+ node
709
+ ]);
710
+ if (this._nodeChanged[node.id]) {
711
+ this._nodeChanged[node.id].value = {};
712
+ }
713
+ break;
714
+ }
715
+ }
716
+ }));
717
+ }
718
+ async _onInitialNodes(node, nodesRelation, nodesType) {
719
+ this._nodeChanged[node.id] = this._nodeChanged[node.id] ?? signal({});
720
+ let first = true;
721
+ let previous = [];
722
+ this._connectorSubscriptions.set(node.id, effect2(() => {
723
+ if (!first && !this._connectorSubscriptions.has(node.id)) {
724
+ return;
725
+ }
726
+ first = false;
727
+ Object.keys(this._extensions);
728
+ this._nodeChanged[node.id].value;
729
+ const nodes = [];
730
+ for (const { id, connector, filter, type, relation = "outbound" } of Object.values(this._extensions)) {
731
+ if (!connector || relation !== nodesRelation || nodesType && type !== nodesType || filter && !filter(node)) {
732
+ continue;
733
+ }
734
+ this._dispatcher.currentExtension = id;
735
+ this._dispatcher.stateIndex = 0;
736
+ BuilderInternal.currentDispatcher = this._dispatcher;
737
+ try {
738
+ nodes.push(...connector({
739
+ node
740
+ }) ?? []);
741
+ } catch (err) {
742
+ log.catch(err, {
743
+ extension: id
744
+ }, {
745
+ F: __dxlog_file2,
746
+ L: 355,
747
+ S: this,
748
+ C: (f, a) => f(...a)
749
+ });
750
+ log.error(`Previous error occurred in extension: ${id}`, void 0, {
751
+ F: __dxlog_file2,
752
+ L: 356,
753
+ S: this,
754
+ C: (f, a) => f(...a)
755
+ });
756
+ } finally {
757
+ BuilderInternal.currentDispatcher = void 0;
758
+ }
759
+ }
760
+ const ids = nodes.map((n) => n.id);
761
+ const removed = previous.filter((id) => !ids.includes(id));
762
+ previous = ids;
763
+ this.graph._removeEdges(removed.map((target) => ({
764
+ source: node.id,
765
+ target
766
+ })), true);
767
+ this.graph._addNodes(nodes);
768
+ this.graph._addEdges(nodes.map(({ id }) => nodesRelation === "outbound" ? {
769
+ source: node.id,
770
+ target: id
771
+ } : {
772
+ source: id,
773
+ target: node.id
774
+ }));
775
+ this.graph._sortEdges(node.id, nodesRelation, nodes.map(({ id }) => id));
776
+ nodes.forEach((n) => {
777
+ if (this._nodeChanged[n.id]) {
778
+ this._nodeChanged[n.id].value = {};
779
+ }
780
+ });
781
+ }));
782
+ }
783
+ async _onRemoveNode(nodeId) {
784
+ this._resolverSubscriptions.get(nodeId)?.();
785
+ this._connectorSubscriptions.get(nodeId)?.();
786
+ this._resolverSubscriptions.delete(nodeId);
787
+ this._connectorSubscriptions.delete(nodeId);
788
+ }
789
+ };
790
+ export {
791
+ ACTION_GROUP_TYPE,
792
+ ACTION_TYPE,
793
+ Graph,
794
+ GraphBuilder,
795
+ ROOT_ID,
796
+ ROOT_TYPE,
797
+ actionGroupSymbol,
798
+ cleanup,
799
+ createExtension,
800
+ getGraph,
801
+ isAction,
802
+ isActionGroup,
803
+ isActionLike,
804
+ isGraphNode,
805
+ memoize,
806
+ toSignal
807
+ };
808
+ //# sourceMappingURL=index.mjs.map