@dxos/app-graph 0.8.4-main.f9ba587 → 0.8.4-main.fffef41

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.
@@ -1,8 +1,10 @@
1
1
  import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
2
 
3
3
  // src/graph.ts
4
- import { Registry, Rx } from "@effect-rx/rx-react";
5
- import { Option, pipe, Record } from "effect";
4
+ import { Atom, Registry } from "@effect-atom/atom-react";
5
+ import * as Function from "effect/Function";
6
+ import * as Option from "effect/Option";
7
+ import * as Record from "effect/Record";
6
8
  import { Event, Trigger } from "@dxos/async";
7
9
  import { todo } from "@dxos/debug";
8
10
  import { invariant } from "@dxos/invariant";
@@ -14,7 +16,7 @@ var getGraph = (node) => {
14
16
  const graph = node[graphSymbol];
15
17
  invariant(graph, "Node is not associated with a graph.", {
16
18
  F: __dxlog_file,
17
- L: 25,
19
+ L: 29,
18
20
  S: void 0,
19
21
  A: [
20
22
  "graph",
@@ -28,90 +30,95 @@ var ROOT_TYPE = "dxos.org/type/GraphRoot";
28
30
  var ACTION_TYPE = "dxos.org/type/GraphAction";
29
31
  var ACTION_GROUP_TYPE = "dxos.org/type/GraphActionGroup";
30
32
  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;
33
+ onNodeChanged = new Event();
34
+ _onExpand;
35
+ _onInitialize;
36
+ _onRemoveNode;
37
+ _registry;
38
+ _expanded = Record.empty();
39
+ _initialized = Record.empty();
40
+ _initialEdges = Record.empty();
41
+ _initialNodes = Record.fromEntries([
42
+ [
43
+ ROOT_ID,
44
+ this._constructNode({
45
+ id: ROOT_ID,
46
+ type: ROOT_TYPE,
47
+ data: null,
48
+ properties: {}
49
+ })
50
+ ]
51
+ ]);
52
+ /** @internal */
53
+ _node = Atom.family((id) => {
54
+ const initial = Option.flatten(Record.get(this._initialNodes, id));
55
+ return Atom.make(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:node:${id}`));
56
+ });
57
+ _nodeOrThrow = Atom.family((id) => {
58
+ return Atom.make((get2) => {
59
+ const node = get2(this._node(id));
60
+ invariant(Option.isSome(node), `Node not available: ${id}`, {
61
+ F: __dxlog_file,
62
+ L: 267,
63
+ S: this,
64
+ A: [
65
+ "Option.isSome(node)",
66
+ "`Node not available: ${id}`"
67
+ ]
65
68
  });
69
+ return node.value;
66
70
  });
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;
71
+ });
72
+ _edges = Atom.family((id) => {
73
+ const initial = Record.get(this._initialEdges, id).pipe(Option.getOrElse(() => ({
74
+ inbound: [],
75
+ outbound: []
76
+ })));
77
+ return Atom.make(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:edges:${id}`));
78
+ });
79
+ // NOTE: Currently the argument to the family needs to be referentially stable for the atom to be referentially stable.
80
+ // TODO(wittjosiah): Atom feature request, support for something akin to `ComplexMap` to allow for complex arguments.
81
+ _connections = Atom.family((key) => {
82
+ return Atom.make((get2) => {
83
+ const [id, relation] = key.split("$");
84
+ const edges = get2(this._edges(id));
85
+ return edges[relation].map((id2) => get2(this._node(id2))).filter(Option.isSome).map((o) => o.value);
86
+ }).pipe(Atom.withLabel(`graph:connections:${key}`));
87
+ });
88
+ _actions = Atom.family((id) => {
89
+ return Atom.make((get2) => {
90
+ return get2(this._connections(`${id}$outbound`)).filter((node) => node.type === ACTION_TYPE || node.type === ACTION_GROUP_TYPE);
91
+ }).pipe(Atom.withLabel(`graph:actions:${id}`));
92
+ });
93
+ _json = Atom.family((id) => {
94
+ return Atom.make((get2) => {
95
+ const toJSON = (node, seen = []) => {
96
+ const nodes = get2(this.connections(node.id));
97
+ const obj = {
98
+ id: node.id,
99
+ type: node.type
109
100
  };
110
- const root = get(this.nodeOrThrow(id));
111
- return toJSON(root);
112
- }).pipe(Rx.withLabel(`graph:json:${id}`));
113
- });
101
+ if (node.properties.label) {
102
+ obj.label = node.properties.label;
103
+ }
104
+ if (nodes.length) {
105
+ obj.nodes = nodes.map((n) => {
106
+ const nextSeen = [
107
+ ...seen,
108
+ node.id
109
+ ];
110
+ return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
111
+ }).filter(isNonNullable);
112
+ }
113
+ return obj;
114
+ };
115
+ const root = get2(this.nodeOrThrow(id));
116
+ return toJSON(root);
117
+ }).pipe(Atom.withLabel(`graph:json:${id}`));
118
+ });
119
+ constructor({ registry, nodes, edges, onInitialize, onExpand, onRemoveNode } = {}) {
114
120
  this._registry = registry ?? Registry.make();
121
+ this._onInitialize = onInitialize;
115
122
  this._onExpand = onExpand;
116
123
  this._onRemoveNode = onRemoveNode;
117
124
  if (nodes) {
@@ -164,15 +171,22 @@ var Graph = class {
164
171
  getEdges(id) {
165
172
  return this._registry.get(this.edges(id));
166
173
  }
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
- // }
174
+ async initialize(id) {
175
+ const initialized = Record.get(this._initialized, id).pipe(Option.getOrElse(() => false));
176
+ log("initialize", {
177
+ id,
178
+ initialized
179
+ }, {
180
+ F: __dxlog_file,
181
+ L: 399,
182
+ S: this,
183
+ C: (f, a) => f(...a)
184
+ });
185
+ if (!initialized) {
186
+ await this._onInitialize?.(id);
187
+ Record.set(this._initialized, id, true);
188
+ }
189
+ }
176
190
  expand(id, relation = "outbound") {
177
191
  const key = `${id}$${relation}`;
178
192
  const expanded = Record.get(this._expanded, key).pipe(Option.getOrElse(() => false));
@@ -181,7 +195,7 @@ var Graph = class {
181
195
  expanded
182
196
  }, {
183
197
  F: __dxlog_file,
184
- L: 395,
198
+ L: 409,
185
199
  S: this,
186
200
  C: (f, a) => f(...a)
187
201
  });
@@ -191,14 +205,14 @@ var Graph = class {
191
205
  }
192
206
  }
193
207
  addNodes(nodes) {
194
- Rx.batch(() => {
208
+ Atom.batch(() => {
195
209
  nodes.map((node) => this.addNode(node));
196
210
  });
197
211
  }
198
212
  addNode({ nodes, edges, ...nodeArg }) {
199
213
  const { id, type, data = null, properties = {} } = nodeArg;
200
- const nodeRx = this._node(id);
201
- const node = this._registry.get(nodeRx);
214
+ const nodeAtom = this._node(id);
215
+ const node = this._registry.get(nodeAtom);
202
216
  Option.match(node, {
203
217
  onSome: (node2) => {
204
218
  const typeChanged = node2.type !== type;
@@ -211,7 +225,7 @@ var Graph = class {
211
225
  propertiesChanged
212
226
  }, {
213
227
  F: __dxlog_file,
214
- L: 417,
228
+ L: 431,
215
229
  S: this,
216
230
  C: (f, a) => f(...a)
217
231
  });
@@ -223,7 +237,7 @@ var Graph = class {
223
237
  properties
224
238
  }, {
225
239
  F: __dxlog_file,
226
- L: 419,
240
+ L: 438,
227
241
  S: this,
228
242
  C: (f, a) => f(...a)
229
243
  });
@@ -236,7 +250,7 @@ var Graph = class {
236
250
  ...properties
237
251
  }
238
252
  });
239
- this._registry.set(nodeRx, newNode);
253
+ this._registry.set(nodeAtom, newNode);
240
254
  this.onNodeChanged.emit({
241
255
  id,
242
256
  node: newNode
@@ -251,7 +265,7 @@ var Graph = class {
251
265
  properties
252
266
  }, {
253
267
  F: __dxlog_file,
254
- L: 426,
268
+ L: 450,
255
269
  S: this,
256
270
  C: (f, a) => f(...a)
257
271
  });
@@ -261,7 +275,7 @@ var Graph = class {
261
275
  data,
262
276
  properties
263
277
  });
264
- this._registry.set(nodeRx, newNode);
278
+ this._registry.set(nodeAtom, newNode);
265
279
  this.onNodeChanged.emit({
266
280
  id,
267
281
  node: newNode
@@ -281,13 +295,13 @@ var Graph = class {
281
295
  }
282
296
  }
283
297
  removeNodes(ids, edges = false) {
284
- Rx.batch(() => {
298
+ Atom.batch(() => {
285
299
  ids.map((id) => this.removeNode(id, edges));
286
300
  });
287
301
  }
288
302
  removeNode(id, edges = false) {
289
- const nodeRx = this._node(id);
290
- this._registry.set(nodeRx, Option.none());
303
+ const nodeAtom = this._node(id);
304
+ this._registry.set(nodeAtom, Option.none());
291
305
  this.onNodeChanged.emit({
292
306
  id,
293
307
  node: Option.none()
@@ -309,24 +323,24 @@ var Graph = class {
309
323
  this._onRemoveNode?.(id);
310
324
  }
311
325
  addEdges(edges) {
312
- Rx.batch(() => {
326
+ Atom.batch(() => {
313
327
  edges.map((edge) => this.addEdge(edge));
314
328
  });
315
329
  }
316
330
  addEdge(edgeArg) {
317
- const sourceRx = this._edges(edgeArg.source);
318
- const source = this._registry.get(sourceRx);
331
+ const sourceAtom = this._edges(edgeArg.source);
332
+ const source = this._registry.get(sourceAtom);
319
333
  if (!source.outbound.includes(edgeArg.target)) {
320
334
  log("add outbound edge", {
321
335
  source: edgeArg.source,
322
336
  target: edgeArg.target
323
337
  }, {
324
338
  F: __dxlog_file,
325
- L: 481,
339
+ L: 505,
326
340
  S: this,
327
341
  C: (f, a) => f(...a)
328
342
  });
329
- this._registry.set(sourceRx, {
343
+ this._registry.set(sourceAtom, {
330
344
  inbound: source.inbound,
331
345
  outbound: [
332
346
  ...source.outbound,
@@ -334,19 +348,19 @@ var Graph = class {
334
348
  ]
335
349
  });
336
350
  }
337
- const targetRx = this._edges(edgeArg.target);
338
- const target = this._registry.get(targetRx);
351
+ const targetAtom = this._edges(edgeArg.target);
352
+ const target = this._registry.get(targetAtom);
339
353
  if (!target.inbound.includes(edgeArg.source)) {
340
354
  log("add inbound edge", {
341
355
  source: edgeArg.source,
342
356
  target: edgeArg.target
343
357
  }, {
344
358
  F: __dxlog_file,
345
- L: 488,
359
+ L: 518,
346
360
  S: this,
347
361
  C: (f, a) => f(...a)
348
362
  });
349
- this._registry.set(targetRx, {
363
+ this._registry.set(targetAtom, {
350
364
  inbound: [
351
365
  ...target.inbound,
352
366
  edgeArg.source
@@ -356,30 +370,30 @@ var Graph = class {
356
370
  }
357
371
  }
358
372
  removeEdges(edges, removeOrphans = false) {
359
- Rx.batch(() => {
373
+ Atom.batch(() => {
360
374
  edges.map((edge) => this.removeEdge(edge, removeOrphans));
361
375
  });
362
376
  }
363
377
  removeEdge(edgeArg, removeOrphans = false) {
364
- const sourceRx = this._edges(edgeArg.source);
365
- const source = this._registry.get(sourceRx);
378
+ const sourceAtom = this._edges(edgeArg.source);
379
+ const source = this._registry.get(sourceAtom);
366
380
  if (source.outbound.includes(edgeArg.target)) {
367
- this._registry.set(sourceRx, {
381
+ this._registry.set(sourceAtom, {
368
382
  inbound: source.inbound,
369
383
  outbound: source.outbound.filter((id) => id !== edgeArg.target)
370
384
  });
371
385
  }
372
- const targetRx = this._edges(edgeArg.target);
373
- const target = this._registry.get(targetRx);
386
+ const targetAtom = this._edges(edgeArg.target);
387
+ const target = this._registry.get(targetAtom);
374
388
  if (target.inbound.includes(edgeArg.source)) {
375
- this._registry.set(targetRx, {
389
+ this._registry.set(targetAtom, {
376
390
  inbound: target.inbound.filter((id) => id !== edgeArg.source),
377
391
  outbound: target.outbound
378
392
  });
379
393
  }
380
394
  if (removeOrphans) {
381
- const source2 = this._registry.get(sourceRx);
382
- const target2 = this._registry.get(targetRx);
395
+ const source2 = this._registry.get(sourceAtom);
396
+ const target2 = this._registry.get(targetAtom);
383
397
  if (source2.outbound.length === 0 && source2.inbound.length === 0 && edgeArg.source !== ROOT_ID) {
384
398
  this.removeNodes([
385
399
  edgeArg.source
@@ -393,15 +407,15 @@ var Graph = class {
393
407
  }
394
408
  }
395
409
  sortEdges(id, relation, order) {
396
- const edgesRx = this._edges(id);
397
- const edges = this._registry.get(edgesRx);
410
+ const edgesAtom = this._edges(id);
411
+ const edges = this._registry.get(edgesAtom);
398
412
  const unsorted = edges[relation].filter((id2) => !order.includes(id2)) ?? [];
399
413
  const sorted = order.filter((id2) => edges[relation].includes(id2)) ?? [];
400
414
  edges[relation].splice(0, edges[relation].length, ...[
401
415
  ...sorted,
402
416
  ...unsorted
403
417
  ]);
404
- this._registry.set(edgesRx, edges);
418
+ this._registry.set(edgesAtom, edges);
405
419
  }
406
420
  traverse({ visitor, source = ROOT_ID, relation = "outbound" }, path = []) {
407
421
  if (path.includes(source)) {
@@ -425,7 +439,7 @@ var Graph = class {
425
439
  ]));
426
440
  }
427
441
  getPath({ source = "root", target }) {
428
- return pipe(this.getNode(source), Option.flatMap((node) => {
442
+ return Function.pipe(this.getNode(source), Option.flatMap((node) => {
429
443
  let found = Option.none();
430
444
  this.traverse({
431
445
  source: node.id,
@@ -469,57 +483,65 @@ var Graph = class {
469
483
  };
470
484
 
471
485
  // src/graph-builder.ts
472
- import { Registry as Registry2, Rx as Rx2 } from "@effect-rx/rx-react";
486
+ import { Atom as Atom2, Registry as Registry2 } from "@effect-atom/atom-react";
473
487
  import { effect } from "@preact/signals-core";
474
- import { Array, pipe as pipe2, Record as Record2 } from "effect";
488
+ import * as Array from "effect/Array";
489
+ import * as Function2 from "effect/Function";
490
+ import * as Option2 from "effect/Option";
491
+ import * as Record2 from "effect/Record";
475
492
  import { log as log2 } from "@dxos/log";
476
493
  import { byPosition, getDebugName, isNode, isNonNullable as isNonNullable2 } from "@dxos/util";
477
494
 
478
495
  // src/node.ts
479
496
  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;
497
+ var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" && data.type === ACTION_TYPE : false;
481
498
  var actionGroupSymbol = Symbol("ActionGroup");
482
- var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol : false;
499
+ var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol && data.type === ACTION_GROUP_TYPE : false;
483
500
  var isActionLike = (data) => isAction(data) || isActionGroup(data);
484
501
 
485
502
  // src/graph-builder.ts
486
503
  var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
487
504
  var createExtension = (extension) => {
488
- const { id, position = "static", relation = "outbound", connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
505
+ const { id, position = "static", relation = "outbound", resolver: _resolver, connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
489
506
  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}`)));
507
+ const resolver = _resolver && Atom2.family((id2) => _resolver(id2).pipe(Atom2.withLabel(`graph-builder:_resolver:${id2}`)));
508
+ const connector = _connector && Atom2.family((node) => _connector(node).pipe(Atom2.withLabel(`graph-builder:_connector:${id}`)));
509
+ const actionGroups = _actionGroups && Atom2.family((node) => _actionGroups(node).pipe(Atom2.withLabel(`graph-builder:_actionGroups:${id}`)));
510
+ const actions = _actions && Atom2.family((node) => _actions(node).pipe(Atom2.withLabel(`graph-builder:_actions:${id}`)));
493
511
  return [
494
- // resolver ? { id: getId('resolver'), position, resolver } : undefined,
512
+ resolver ? {
513
+ id: getId("resolver"),
514
+ position,
515
+ resolver
516
+ } : void 0,
495
517
  connector ? {
496
518
  id: getId("connector"),
497
519
  position,
498
520
  relation,
499
- connector: Rx2.family((node) => Rx2.make((get) => {
521
+ connector: Atom2.family((node) => Atom2.make((get2) => {
500
522
  try {
501
- return get(connector(node));
523
+ return get2(connector(node));
502
524
  } catch {
503
525
  log2.warn("Error in connector", {
504
526
  id: getId("connector"),
505
527
  node
506
528
  }, {
507
529
  F: __dxlog_file2,
508
- L: 101,
530
+ L: 114,
509
531
  S: void 0,
510
532
  C: (f, a) => f(...a)
511
533
  });
512
534
  return [];
513
535
  }
514
- }).pipe(Rx2.withLabel(`graph-builder:connector:${id}`)))
536
+ }).pipe(Atom2.withLabel(`graph-builder:connector:${id}`)))
515
537
  } : void 0,
516
538
  actionGroups ? {
517
539
  id: getId("actionGroups"),
518
540
  position,
519
541
  relation: "outbound",
520
- connector: Rx2.family((node) => Rx2.make((get) => {
542
+ connector: Atom2.family((node) => Atom2.make((get2) => {
521
543
  try {
522
- return get(actionGroups(node)).map((arg) => ({
544
+ return get2(actionGroups(node)).map((arg) => ({
523
545
  ...arg,
524
546
  data: actionGroupSymbol,
525
547
  type: ACTION_GROUP_TYPE
@@ -530,21 +552,21 @@ var createExtension = (extension) => {
530
552
  node
531
553
  }, {
532
554
  F: __dxlog_file2,
533
- L: 122,
555
+ L: 135,
534
556
  S: void 0,
535
557
  C: (f, a) => f(...a)
536
558
  });
537
559
  return [];
538
560
  }
539
- }).pipe(Rx2.withLabel(`graph-builder:connector:actionGroups:${id}`)))
561
+ }).pipe(Atom2.withLabel(`graph-builder:connector:actionGroups:${id}`)))
540
562
  } : void 0,
541
563
  actions ? {
542
564
  id: getId("actions"),
543
565
  position,
544
566
  relation: "outbound",
545
- connector: Rx2.family((node) => Rx2.make((get) => {
567
+ connector: Atom2.family((node) => Atom2.make((get2) => {
546
568
  try {
547
- return get(actions(node)).map((arg) => ({
569
+ return get2(actions(node)).map((arg) => ({
548
570
  ...arg,
549
571
  type: ACTION_TYPE
550
572
  }));
@@ -554,13 +576,13 @@ var createExtension = (extension) => {
554
576
  node
555
577
  }, {
556
578
  F: __dxlog_file2,
557
- L: 139,
579
+ L: 152,
558
580
  S: void 0,
559
581
  C: (f, a) => f(...a)
560
582
  });
561
583
  return [];
562
584
  }
563
- }).pipe(Rx2.withLabel(`graph-builder:connector:actions:${id}`)))
585
+ }).pipe(Atom2.withLabel(`graph-builder:connector:actions:${id}`)))
564
586
  } : void 0
565
587
  ].filter(isNonNullable2);
566
588
  };
@@ -578,32 +600,19 @@ var flattenExtensions = (extension, acc = []) => {
578
600
  }
579
601
  };
580
602
  var GraphBuilder = class _GraphBuilder {
603
+ // TODO(wittjosiah): Use Context.
604
+ _subscriptions = /* @__PURE__ */ new Map();
605
+ _extensions = Atom2.make(Record2.empty()).pipe(Atom2.keepAlive, Atom2.withLabel("graph-builder:extensions"));
606
+ _initialized = {};
607
+ _registry;
608
+ _graph;
581
609
  constructor({ registry, ...params } = {}) {
582
- // TODO(wittjosiah): Use Context.
583
- this._connectorSubscriptions = /* @__PURE__ */ new Map();
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
610
  this._registry = registry ?? Registry2.make();
602
611
  this._graph = new Graph({
603
612
  ...params,
604
613
  registry: this._registry,
605
614
  onExpand: (id, relation) => this._onExpand(id, relation),
606
- // onInitialize: (id) => this._onInitialize(id),
615
+ onInitialize: (id) => this._onInitialize(id),
607
616
  onRemoveNode: (id) => this._onRemoveNode(id)
608
617
  });
609
618
  }
@@ -673,9 +682,30 @@ var GraphBuilder = class _GraphBuilder {
673
682
  }
674
683
  }
675
684
  destroy() {
676
- this._connectorSubscriptions.forEach((unsubscribe) => unsubscribe());
677
- this._connectorSubscriptions.clear();
685
+ this._subscriptions.forEach((unsubscribe) => unsubscribe());
686
+ this._subscriptions.clear();
678
687
  }
688
+ _resolvers = Atom2.family((id) => {
689
+ return Atom2.make((get2) => {
690
+ return Function2.pipe(get2(this._extensions), Record2.values, Array.sortBy(byPosition), Array.map(({ resolver }) => resolver), Array.filter(isNonNullable2), Array.map((resolver) => get2(resolver(id))), Array.filter(isNonNullable2), Array.head);
691
+ });
692
+ });
693
+ _connectors = Atom2.family((key) => {
694
+ return Atom2.make((get2) => {
695
+ const [id, relation] = key.split("+");
696
+ const node = this._graph.node(id);
697
+ return Function2.pipe(
698
+ get2(this._extensions),
699
+ Record2.values,
700
+ // TODO(wittjosiah): Sort on write rather than read.
701
+ Array.sortBy(byPosition),
702
+ Array.filter(({ relation: _relation = "outbound" }) => _relation === relation),
703
+ Array.map(({ connector }) => connector?.(node)),
704
+ Array.filter(isNonNullable2),
705
+ Array.flatMap((result) => get2(result))
706
+ );
707
+ }).pipe(Atom2.withLabel(`graph-builder:connectors:${key}`));
708
+ });
679
709
  _onExpand(id, relation) {
680
710
  log2("onExpand", {
681
711
  id,
@@ -683,7 +713,7 @@ var GraphBuilder = class _GraphBuilder {
683
713
  registry: getDebugName(this._registry)
684
714
  }, {
685
715
  F: __dxlog_file2,
686
- L: 301,
716
+ L: 329,
687
717
  S: this,
688
718
  C: (f, a) => f(...a)
689
719
  });
@@ -700,12 +730,12 @@ var GraphBuilder = class _GraphBuilder {
700
730
  removed
701
731
  }, {
702
732
  F: __dxlog_file2,
703
- L: 312,
733
+ L: 340,
704
734
  S: this,
705
735
  C: (f, a) => f(...a)
706
736
  });
707
737
  const update = () => {
708
- Rx2.batch(() => {
738
+ Atom2.batch(() => {
709
739
  this._graph.removeEdges(removed.map((target) => ({
710
740
  source: id,
711
741
  target
@@ -729,34 +759,62 @@ var GraphBuilder = class _GraphBuilder {
729
759
  }, {
730
760
  immediate: true
731
761
  });
732
- this._connectorSubscriptions.set(id, cancel);
762
+ this._subscriptions.set(id, cancel);
763
+ }
764
+ // TODO(wittjosiah): If the same node is added by a connector, the resolver should probably cancel itself?
765
+ async _onInitialize(id) {
766
+ log2("onInitialize", {
767
+ id
768
+ }, {
769
+ F: __dxlog_file2,
770
+ L: 377,
771
+ S: this,
772
+ C: (f, a) => f(...a)
773
+ });
774
+ const resolver = this._resolvers(id);
775
+ const cancel = this._registry.subscribe(resolver, (node) => {
776
+ const trigger = this._initialized[id];
777
+ Option2.match(node, {
778
+ onSome: (node2) => {
779
+ this._graph.addNodes([
780
+ node2
781
+ ]);
782
+ trigger?.wake();
783
+ },
784
+ onNone: () => {
785
+ trigger?.wake();
786
+ this._graph.removeNodes([
787
+ id
788
+ ]);
789
+ }
790
+ });
791
+ }, {
792
+ immediate: true
793
+ });
794
+ this._subscriptions.set(id, cancel);
733
795
  }
734
- // TODO(wittjosiah): On initialize to restore state from cache.
735
- // private async _onInitialize(id: string) {
736
- // log('onInitialize', { id });
737
- // }
738
796
  _onRemoveNode(id) {
739
- this._connectorSubscriptions.get(id)?.();
740
- this._connectorSubscriptions.delete(id);
797
+ this._subscriptions.get(id)?.();
798
+ this._subscriptions.delete(id);
741
799
  }
742
800
  };
743
- var rxFromSignal = (cb) => {
744
- return Rx2.make((get) => {
801
+ var atomFromSignal = (cb) => {
802
+ return Atom2.make((get2) => {
745
803
  const dispose = effect(() => {
746
- get.setSelf(cb());
804
+ get2.setSelf(cb());
747
805
  });
748
- get.addFinalizer(() => dispose());
806
+ get2.addFinalizer(() => dispose());
749
807
  return cb();
750
808
  });
751
809
  };
752
- var observableFamily = Rx2.family((observable) => {
753
- return Rx2.make((get) => {
754
- const subscription = observable.subscribe((value) => get.setSelf(value));
755
- get.addFinalizer(() => subscription.unsubscribe());
810
+ var observableFamily = Atom2.family((observable) => {
811
+ return Atom2.make((get2) => {
812
+ const subscription = observable.subscribe((value) => get2.setSelf(value));
813
+ get2.addFinalizer(() => subscription.unsubscribe());
756
814
  return observable.get();
757
815
  });
758
816
  });
759
- var rxFromObservable = (observable) => {
817
+ var atomFromObservable = (observable) => {
760
818
  return observableFamily(observable);
761
819
  };
762
820
  export {
@@ -767,14 +825,14 @@ export {
767
825
  ROOT_ID,
768
826
  ROOT_TYPE,
769
827
  actionGroupSymbol,
828
+ atomFromObservable,
829
+ atomFromSignal,
770
830
  createExtension,
771
831
  flattenExtensions,
772
832
  getGraph,
773
833
  isAction,
774
834
  isActionGroup,
775
835
  isActionLike,
776
- isGraphNode,
777
- rxFromObservable,
778
- rxFromSignal
836
+ isGraphNode
779
837
  };
780
838
  //# sourceMappingURL=index.mjs.map