@dxos/app-graph 0.8.4-main.84f28bd → 0.8.4-main.ae835ea

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.
@@ -2,7 +2,9 @@ import { createRequire } from 'node:module';const require = createRequire(import
2
2
 
3
3
  // src/graph.ts
4
4
  import { Registry, Rx } from "@effect-rx/rx-react";
5
- import { Option, pipe, Record } from "effect";
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: 27,
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 = Rx.family((id) => {
54
+ const initial = Option.flatten(Record.get(this._initialNodes, id));
55
+ return Rx.make(initial).pipe(Rx.keepAlive, Rx.withLabel(`graph:node:${id}`));
56
+ });
57
+ _nodeOrThrow = Rx.family((id) => {
58
+ return Rx.make((get2) => {
59
+ const node = get2(this._node(id));
60
+ invariant(Option.isSome(node), `Node not available: ${id}`, {
61
+ F: __dxlog_file,
62
+ L: 254,
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 = Rx.family((id) => {
73
+ const initial = Record.get(this._initialEdges, id).pipe(Option.getOrElse(() => ({
74
+ inbound: [],
75
+ outbound: []
76
+ })));
77
+ return Rx.make(initial).pipe(Rx.keepAlive, Rx.withLabel(`graph:edges:${id}`));
78
+ });
79
+ // NOTE: Currently the argument to the family needs to be referentially stable for the rx to be referentially stable.
80
+ // TODO(wittjosiah): Rx feature request, support for something akin to `ComplexMap` to allow for complex arguments.
81
+ _connections = Rx.family((key) => {
82
+ return Rx.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(Rx.withLabel(`graph:connections:${key}`));
87
+ });
88
+ _actions = Rx.family((id) => {
89
+ return Rx.make((get2) => {
90
+ return get2(this._connections(`${id}$outbound`)).filter((node) => node.type === ACTION_TYPE || node.type === ACTION_GROUP_TYPE);
91
+ }).pipe(Rx.withLabel(`graph:actions:${id}`));
92
+ });
93
+ _json = Rx.family((id) => {
94
+ return Rx.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(Rx.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: 386,
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: 396,
185
199
  S: this,
186
200
  C: (f, a) => f(...a)
187
201
  });
@@ -211,7 +225,7 @@ var Graph = class {
211
225
  propertiesChanged
212
226
  }, {
213
227
  F: __dxlog_file,
214
- L: 417,
228
+ L: 418,
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: 420,
227
241
  S: this,
228
242
  C: (f, a) => f(...a)
229
243
  });
@@ -251,7 +265,7 @@ var Graph = class {
251
265
  properties
252
266
  }, {
253
267
  F: __dxlog_file,
254
- L: 426,
268
+ L: 427,
255
269
  S: this,
256
270
  C: (f, a) => f(...a)
257
271
  });
@@ -322,7 +336,7 @@ var Graph = class {
322
336
  target: edgeArg.target
323
337
  }, {
324
338
  F: __dxlog_file,
325
- L: 481,
339
+ L: 482,
326
340
  S: this,
327
341
  C: (f, a) => f(...a)
328
342
  });
@@ -342,7 +356,7 @@ var Graph = class {
342
356
  target: edgeArg.target
343
357
  }, {
344
358
  F: __dxlog_file,
345
- L: 488,
359
+ L: 489,
346
360
  S: this,
347
361
  C: (f, a) => f(...a)
348
362
  });
@@ -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,
@@ -471,41 +485,49 @@ var Graph = class {
471
485
  // src/graph-builder.ts
472
486
  import { Registry as Registry2, Rx as Rx2 } from "@effect-rx/rx-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}`;
507
+ const resolver = _resolver && Rx2.family((id2) => _resolver(id2).pipe(Rx2.withLabel(`graph-builder:_resolver:${id2}`)));
490
508
  const connector = _connector && Rx2.family((node) => _connector(node).pipe(Rx2.withLabel(`graph-builder:_connector:${id}`)));
491
509
  const actionGroups = _actionGroups && Rx2.family((node) => _actionGroups(node).pipe(Rx2.withLabel(`graph-builder:_actionGroups:${id}`)));
492
510
  const actions = _actions && Rx2.family((node) => _actions(node).pipe(Rx2.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: Rx2.family((node) => Rx2.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: 112,
509
531
  S: void 0,
510
532
  C: (f, a) => f(...a)
511
533
  });
@@ -517,9 +539,9 @@ var createExtension = (extension) => {
517
539
  id: getId("actionGroups"),
518
540
  position,
519
541
  relation: "outbound",
520
- connector: Rx2.family((node) => Rx2.make((get) => {
542
+ connector: Rx2.family((node) => Rx2.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,7 +552,7 @@ var createExtension = (extension) => {
530
552
  node
531
553
  }, {
532
554
  F: __dxlog_file2,
533
- L: 122,
555
+ L: 133,
534
556
  S: void 0,
535
557
  C: (f, a) => f(...a)
536
558
  });
@@ -542,9 +564,9 @@ var createExtension = (extension) => {
542
564
  id: getId("actions"),
543
565
  position,
544
566
  relation: "outbound",
545
- connector: Rx2.family((node) => Rx2.make((get) => {
567
+ connector: Rx2.family((node) => Rx2.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,7 +576,7 @@ var createExtension = (extension) => {
554
576
  node
555
577
  }, {
556
578
  F: __dxlog_file2,
557
- L: 139,
579
+ L: 150,
558
580
  S: void 0,
559
581
  C: (f, a) => f(...a)
560
582
  });
@@ -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 = Rx2.make(Record2.empty()).pipe(Rx2.keepAlive, Rx2.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 = Rx2.family((id) => {
689
+ return Rx2.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 = Rx2.family((key) => {
694
+ return Rx2.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(Rx2.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: 327,
687
717
  S: this,
688
718
  C: (f, a) => f(...a)
689
719
  });
@@ -700,7 +730,7 @@ var GraphBuilder = class _GraphBuilder {
700
730
  removed
701
731
  }, {
702
732
  F: __dxlog_file2,
703
- L: 312,
733
+ L: 338,
704
734
  S: this,
705
735
  C: (f, a) => f(...a)
706
736
  });
@@ -729,30 +759,58 @@ 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: 375,
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
801
  var rxFromSignal = (cb) => {
744
- return Rx2.make((get) => {
802
+ return Rx2.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
810
  var observableFamily = Rx2.family((observable) => {
753
- return Rx2.make((get) => {
754
- const subscription = observable.subscribe((value) => get.setSelf(value));
755
- get.addFinalizer(() => subscription.unsubscribe());
811
+ return Rx2.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
  });