@dxos/app-graph 0.8.3 → 0.8.4-main.28f8d3d

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,14 +1,14 @@
1
1
  import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
2
 
3
- // packages/sdk/app-graph/src/graph.ts
3
+ // src/graph.ts
4
4
  import { Registry, Rx } from "@effect-rx/rx-react";
5
- import { Option, pipe, Record } from "effect";
5
+ import { Option, Record, pipe } from "effect";
6
6
  import { Event, Trigger } from "@dxos/async";
7
7
  import { todo } from "@dxos/debug";
8
8
  import { invariant } from "@dxos/invariant";
9
9
  import { log } from "@dxos/log";
10
10
  import { isNonNullable } from "@dxos/util";
11
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
11
+ var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
12
12
  var graphSymbol = Symbol("graph");
13
13
  var getGraph = (node) => {
14
14
  const graph = node[graphSymbol];
@@ -28,90 +28,95 @@ var ROOT_TYPE = "dxos.org/type/GraphRoot";
28
28
  var ACTION_TYPE = "dxos.org/type/GraphAction";
29
29
  var ACTION_GROUP_TYPE = "dxos.org/type/GraphActionGroup";
30
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;
31
+ onNodeChanged = new Event();
32
+ _onExpand;
33
+ _onInitialize;
34
+ _onRemoveNode;
35
+ _registry;
36
+ _expanded = Record.empty();
37
+ _initialized = Record.empty();
38
+ _initialEdges = Record.empty();
39
+ _initialNodes = Record.fromEntries([
40
+ [
41
+ ROOT_ID,
42
+ this._constructNode({
43
+ id: ROOT_ID,
44
+ type: ROOT_TYPE,
45
+ data: null,
46
+ properties: {}
47
+ })
48
+ ]
49
+ ]);
50
+ /** @internal */
51
+ _node = Rx.family((id) => {
52
+ const initial = Option.flatten(Record.get(this._initialNodes, id));
53
+ return Rx.make(initial).pipe(Rx.keepAlive, Rx.withLabel(`graph:node:${id}`));
54
+ });
55
+ _nodeOrThrow = Rx.family((id) => {
56
+ return Rx.make((get) => {
57
+ const node = get(this._node(id));
58
+ invariant(Option.isSome(node), `Node not available: ${id}`, {
59
+ F: __dxlog_file,
60
+ L: 252,
61
+ S: this,
62
+ A: [
63
+ "Option.isSome(node)",
64
+ "`Node not available: ${id}`"
65
+ ]
65
66
  });
67
+ return node.value;
66
68
  });
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;
69
+ });
70
+ _edges = Rx.family((id) => {
71
+ const initial = Record.get(this._initialEdges, id).pipe(Option.getOrElse(() => ({
72
+ inbound: [],
73
+ outbound: []
74
+ })));
75
+ return Rx.make(initial).pipe(Rx.keepAlive, Rx.withLabel(`graph:edges:${id}`));
76
+ });
77
+ // NOTE: Currently the argument to the family needs to be referentially stable for the rx to be referentially stable.
78
+ // TODO(wittjosiah): Rx feature request, support for something akin to `ComplexMap` to allow for complex arguments.
79
+ _connections = Rx.family((key) => {
80
+ return Rx.make((get) => {
81
+ const [id, relation] = key.split("$");
82
+ const edges = get(this._edges(id));
83
+ return edges[relation].map((id2) => get(this._node(id2))).filter(Option.isSome).map((o) => o.value);
84
+ }).pipe(Rx.withLabel(`graph:connections:${key}`));
85
+ });
86
+ _actions = Rx.family((id) => {
87
+ return Rx.make((get) => {
88
+ return get(this._connections(`${id}$outbound`)).filter((node) => node.type === ACTION_TYPE || node.type === ACTION_GROUP_TYPE);
89
+ }).pipe(Rx.withLabel(`graph:actions:${id}`));
90
+ });
91
+ _json = Rx.family((id) => {
92
+ return Rx.make((get) => {
93
+ const toJSON = (node, seen = []) => {
94
+ const nodes = get(this.connections(node.id));
95
+ const obj = {
96
+ id: node.id.length > 32 ? `${node.id.slice(0, 32)}...` : node.id,
97
+ type: node.type
109
98
  };
110
- const root = get(this.nodeOrThrow(id));
111
- return toJSON(root);
112
- }).pipe(Rx.withLabel(`graph:json:${id}`));
113
- });
99
+ if (node.properties.label) {
100
+ obj.label = node.properties.label;
101
+ }
102
+ if (nodes.length) {
103
+ obj.nodes = nodes.map((n) => {
104
+ const nextSeen = [
105
+ ...seen,
106
+ node.id
107
+ ];
108
+ return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
109
+ }).filter(isNonNullable);
110
+ }
111
+ return obj;
112
+ };
113
+ const root = get(this.nodeOrThrow(id));
114
+ return toJSON(root);
115
+ }).pipe(Rx.withLabel(`graph:json:${id}`));
116
+ });
117
+ constructor({ registry, nodes, edges, onInitialize, onExpand, onRemoveNode } = {}) {
114
118
  this._registry = registry ?? Registry.make();
119
+ this._onInitialize = onInitialize;
115
120
  this._onExpand = onExpand;
116
121
  this._onRemoveNode = onRemoveNode;
117
122
  if (nodes) {
@@ -164,15 +169,22 @@ var Graph = class {
164
169
  getEdges(id) {
165
170
  return this._registry.get(this.edges(id));
166
171
  }
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
- // }
172
+ async initialize(id) {
173
+ const initialized = Record.get(this._initialized, id).pipe(Option.getOrElse(() => false));
174
+ log("initialize", {
175
+ id,
176
+ initialized
177
+ }, {
178
+ F: __dxlog_file,
179
+ L: 384,
180
+ S: this,
181
+ C: (f, a) => f(...a)
182
+ });
183
+ if (!initialized) {
184
+ await this._onInitialize?.(id);
185
+ Record.set(this._initialized, id, true);
186
+ }
187
+ }
176
188
  expand(id, relation = "outbound") {
177
189
  const key = `${id}$${relation}`;
178
190
  const expanded = Record.get(this._expanded, key).pipe(Option.getOrElse(() => false));
@@ -181,7 +193,7 @@ var Graph = class {
181
193
  expanded
182
194
  }, {
183
195
  F: __dxlog_file,
184
- L: 395,
196
+ L: 394,
185
197
  S: this,
186
198
  C: (f, a) => f(...a)
187
199
  });
@@ -211,7 +223,7 @@ var Graph = class {
211
223
  propertiesChanged
212
224
  }, {
213
225
  F: __dxlog_file,
214
- L: 417,
226
+ L: 416,
215
227
  S: this,
216
228
  C: (f, a) => f(...a)
217
229
  });
@@ -223,7 +235,7 @@ var Graph = class {
223
235
  properties
224
236
  }, {
225
237
  F: __dxlog_file,
226
- L: 419,
238
+ L: 418,
227
239
  S: this,
228
240
  C: (f, a) => f(...a)
229
241
  });
@@ -251,7 +263,7 @@ var Graph = class {
251
263
  properties
252
264
  }, {
253
265
  F: __dxlog_file,
254
- L: 426,
266
+ L: 425,
255
267
  S: this,
256
268
  C: (f, a) => f(...a)
257
269
  });
@@ -322,7 +334,7 @@ var Graph = class {
322
334
  target: edgeArg.target
323
335
  }, {
324
336
  F: __dxlog_file,
325
- L: 481,
337
+ L: 480,
326
338
  S: this,
327
339
  C: (f, a) => f(...a)
328
340
  });
@@ -342,7 +354,7 @@ var Graph = class {
342
354
  target: edgeArg.target
343
355
  }, {
344
356
  F: __dxlog_file,
345
- L: 488,
357
+ L: 487,
346
358
  S: this,
347
359
  C: (f, a) => f(...a)
348
360
  });
@@ -468,30 +480,35 @@ var Graph = class {
468
480
  }
469
481
  };
470
482
 
471
- // packages/sdk/app-graph/src/graph-builder.ts
483
+ // src/graph-builder.ts
472
484
  import { Registry as Registry2, Rx as Rx2 } from "@effect-rx/rx-react";
473
485
  import { effect } from "@preact/signals-core";
474
- import { Array, pipe as pipe2, Record as Record2 } from "effect";
486
+ import { Array, Option as Option2, Record as Record2, pipe as pipe2 } from "effect";
475
487
  import { log as log2 } from "@dxos/log";
476
488
  import { byPosition, getDebugName, isNode, isNonNullable as isNonNullable2 } from "@dxos/util";
477
489
 
478
- // packages/sdk/app-graph/src/node.ts
490
+ // src/node.ts
479
491
  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;
492
+ var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" && data.type === ACTION_TYPE : false;
481
493
  var actionGroupSymbol = Symbol("ActionGroup");
482
- var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol : false;
494
+ var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol && data.type === ACTION_GROUP_TYPE : false;
483
495
  var isActionLike = (data) => isAction(data) || isActionGroup(data);
484
496
 
485
- // packages/sdk/app-graph/src/graph-builder.ts
486
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
497
+ // src/graph-builder.ts
498
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
487
499
  var createExtension = (extension) => {
488
- const { id, position = "static", relation = "outbound", connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
500
+ const { id, position = "static", relation = "outbound", resolver: _resolver, connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
489
501
  const getId = (key) => `${id}/${key}`;
502
+ const resolver = _resolver && Rx2.family((id2) => _resolver(id2).pipe(Rx2.withLabel(`graph-builder:_resolver:${id2}`)));
490
503
  const connector = _connector && Rx2.family((node) => _connector(node).pipe(Rx2.withLabel(`graph-builder:_connector:${id}`)));
491
504
  const actionGroups = _actionGroups && Rx2.family((node) => _actionGroups(node).pipe(Rx2.withLabel(`graph-builder:_actionGroups:${id}`)));
492
505
  const actions = _actions && Rx2.family((node) => _actions(node).pipe(Rx2.withLabel(`graph-builder:_actions:${id}`)));
493
506
  return [
494
- // resolver ? { id: getId('resolver'), position, resolver } : undefined,
507
+ resolver ? {
508
+ id: getId("resolver"),
509
+ position,
510
+ resolver
511
+ } : void 0,
495
512
  connector ? {
496
513
  id: getId("connector"),
497
514
  position,
@@ -505,7 +522,7 @@ var createExtension = (extension) => {
505
522
  node
506
523
  }, {
507
524
  F: __dxlog_file2,
508
- L: 101,
525
+ L: 109,
509
526
  S: void 0,
510
527
  C: (f, a) => f(...a)
511
528
  });
@@ -530,7 +547,7 @@ var createExtension = (extension) => {
530
547
  node
531
548
  }, {
532
549
  F: __dxlog_file2,
533
- L: 122,
550
+ L: 130,
534
551
  S: void 0,
535
552
  C: (f, a) => f(...a)
536
553
  });
@@ -554,7 +571,7 @@ var createExtension = (extension) => {
554
571
  node
555
572
  }, {
556
573
  F: __dxlog_file2,
557
- L: 139,
574
+ L: 147,
558
575
  S: void 0,
559
576
  C: (f, a) => f(...a)
560
577
  });
@@ -578,32 +595,19 @@ var flattenExtensions = (extension, acc = []) => {
578
595
  }
579
596
  };
580
597
  var GraphBuilder = class _GraphBuilder {
598
+ // TODO(wittjosiah): Use Context.
599
+ _subscriptions = /* @__PURE__ */ new Map();
600
+ _extensions = Rx2.make(Record2.empty()).pipe(Rx2.keepAlive, Rx2.withLabel("graph-builder:extensions"));
601
+ _initialized = {};
602
+ _registry;
603
+ _graph;
581
604
  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
605
  this._registry = registry ?? Registry2.make();
602
606
  this._graph = new Graph({
603
607
  ...params,
604
608
  registry: this._registry,
605
609
  onExpand: (id, relation) => this._onExpand(id, relation),
606
- // onInitialize: (id) => this._onInitialize(id),
610
+ onInitialize: (id) => this._onInitialize(id),
607
611
  onRemoveNode: (id) => this._onRemoveNode(id)
608
612
  });
609
613
  }
@@ -673,9 +677,30 @@ var GraphBuilder = class _GraphBuilder {
673
677
  }
674
678
  }
675
679
  destroy() {
676
- this._connectorSubscriptions.forEach((unsubscribe) => unsubscribe());
677
- this._connectorSubscriptions.clear();
680
+ this._subscriptions.forEach((unsubscribe) => unsubscribe());
681
+ this._subscriptions.clear();
678
682
  }
683
+ _resolvers = Rx2.family((id) => {
684
+ return Rx2.make((get) => {
685
+ return pipe2(get(this._extensions), Record2.values, Array.sortBy(byPosition), Array.map(({ resolver }) => resolver), Array.filter(isNonNullable2), Array.map((resolver) => get(resolver(id))), Array.filter(isNonNullable2), Array.head);
686
+ });
687
+ });
688
+ _connectors = Rx2.family((key) => {
689
+ return Rx2.make((get) => {
690
+ const [id, relation] = key.split("+");
691
+ const node = this._graph.node(id);
692
+ return pipe2(
693
+ get(this._extensions),
694
+ Record2.values,
695
+ // TODO(wittjosiah): Sort on write rather than read.
696
+ Array.sortBy(byPosition),
697
+ Array.filter(({ relation: _relation = "outbound" }) => _relation === relation),
698
+ Array.map(({ connector }) => connector?.(node)),
699
+ Array.filter(isNonNullable2),
700
+ Array.flatMap((result) => get(result))
701
+ );
702
+ }).pipe(Rx2.withLabel(`graph-builder:connectors:${key}`));
703
+ });
679
704
  _onExpand(id, relation) {
680
705
  log2("onExpand", {
681
706
  id,
@@ -683,7 +708,7 @@ var GraphBuilder = class _GraphBuilder {
683
708
  registry: getDebugName(this._registry)
684
709
  }, {
685
710
  F: __dxlog_file2,
686
- L: 301,
711
+ L: 324,
687
712
  S: this,
688
713
  C: (f, a) => f(...a)
689
714
  });
@@ -700,7 +725,7 @@ var GraphBuilder = class _GraphBuilder {
700
725
  removed
701
726
  }, {
702
727
  F: __dxlog_file2,
703
- L: 312,
728
+ L: 335,
704
729
  S: this,
705
730
  C: (f, a) => f(...a)
706
731
  });
@@ -729,15 +754,43 @@ var GraphBuilder = class _GraphBuilder {
729
754
  }, {
730
755
  immediate: true
731
756
  });
732
- this._connectorSubscriptions.set(id, cancel);
757
+ this._subscriptions.set(id, cancel);
758
+ }
759
+ // TODO(wittjosiah): If the same node is added by a connector, the resolver should probably cancel itself?
760
+ async _onInitialize(id) {
761
+ log2("onInitialize", {
762
+ id
763
+ }, {
764
+ F: __dxlog_file2,
765
+ L: 372,
766
+ S: this,
767
+ C: (f, a) => f(...a)
768
+ });
769
+ const resolver = this._resolvers(id);
770
+ const cancel = this._registry.subscribe(resolver, (node) => {
771
+ const trigger = this._initialized[id];
772
+ Option2.match(node, {
773
+ onSome: (node2) => {
774
+ this._graph.addNodes([
775
+ node2
776
+ ]);
777
+ trigger?.wake();
778
+ },
779
+ onNone: () => {
780
+ trigger?.wake();
781
+ this._graph.removeNodes([
782
+ id
783
+ ]);
784
+ }
785
+ });
786
+ }, {
787
+ immediate: true
788
+ });
789
+ this._subscriptions.set(id, cancel);
733
790
  }
734
- // TODO(wittjosiah): On initialize to restore state from cache.
735
- // private async _onInitialize(id: string) {
736
- // log('onInitialize', { id });
737
- // }
738
791
  _onRemoveNode(id) {
739
- this._connectorSubscriptions.get(id)?.();
740
- this._connectorSubscriptions.delete(id);
792
+ this._subscriptions.get(id)?.();
793
+ this._subscriptions.delete(id);
741
794
  }
742
795
  };
743
796
  var rxFromSignal = (cb) => {