@dxos/app-graph 0.8.4-main.c85a9c8dae → 0.8.4-main.d05673bc65

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.
@@ -59,7 +59,7 @@ import * as Pipeable from "effect/Pipeable";
59
59
  import * as Record from "effect/Record";
60
60
  import { Event, Trigger } from "@dxos/async";
61
61
  import { todo } from "@dxos/debug";
62
- import { invariant } from "@dxos/invariant";
62
+ import { invariant as invariant2 } from "@dxos/invariant";
63
63
  import { log } from "@dxos/log";
64
64
  import { isNonNullable } from "@dxos/util";
65
65
 
@@ -80,9 +80,9 @@ __export(node_exports, {
80
80
  relation: () => relation
81
81
  });
82
82
  var RootId = "root";
83
- var RootType = "dxos.org/type/GraphRoot";
84
- var ActionType = "dxos.org/type/GraphAction";
85
- var ActionGroupType = "dxos.org/type/GraphActionGroup";
83
+ var RootType = "org.dxos.type.graph-root";
84
+ var ActionType = "org.dxos.type.graph-action";
85
+ var ActionGroupType = "org.dxos.type.graph-action-group";
86
86
  var relation = (kind, direction = "outbound") => ({
87
87
  kind,
88
88
  direction
@@ -96,9 +96,12 @@ var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbo
96
96
  var isActionLike = (data) => isAction(data) || isActionGroup(data);
97
97
 
98
98
  // src/util.ts
99
+ import { invariant } from "@dxos/invariant";
100
+ var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-graph/src/util.ts";
99
101
  var Separators = {
100
102
  primary: "",
101
- secondary: ""
103
+ secondary: "",
104
+ path: "/"
102
105
  };
103
106
  var normalizeRelation = (relation2) => relation2 == null ? childRelation() : typeof relation2 === "string" ? relation(relation2) : relation2;
104
107
  var shallowEqual = (a, b) => {
@@ -120,14 +123,26 @@ var nodeArgsUnchanged = (prev, next) => {
120
123
  return prevNode.id === nextNode.id && prevNode.type === nextNode.type && shallowEqual(prevNode.data, nextNode.data) && shallowEqual(prevNode.properties, nextNode.properties);
121
124
  });
122
125
  };
126
+ var qualifyId = (parentId, segmentId) => `${parentId}${Separators.path}${segmentId}`;
127
+ var validateSegmentId = (id) => {
128
+ invariant(!id.includes(Separators.path), `Node segment ID must not contain '${Separators.path}': ${id}`, {
129
+ F: __dxlog_file,
130
+ L: 70,
131
+ S: void 0,
132
+ A: [
133
+ "!id.includes(Separators.path)",
134
+ "`Node segment ID must not contain '${Separators.path}': ${id}`"
135
+ ]
136
+ });
137
+ };
123
138
 
124
139
  // src/graph.ts
125
- var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
140
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
126
141
  var graphSymbol = /* @__PURE__ */ Symbol("graph");
127
142
  var getGraph = (node) => {
128
143
  const graph = node[graphSymbol];
129
- invariant(graph, "Node is not associated with a graph.", {
130
- F: __dxlog_file,
144
+ invariant2(graph, "Node is not associated with a graph.", {
145
+ F: __dxlog_file2,
131
146
  L: 33,
132
147
  S: void 0,
133
148
  A: [
@@ -173,8 +188,8 @@ var GraphImpl = class {
173
188
  _nodeOrThrow = Atom2.family((id) => {
174
189
  return Atom2.make((get2) => {
175
190
  const node = get2(this._node(id));
176
- invariant(Option.isSome(node), `Node not available: ${id}`, {
177
- F: __dxlog_file,
191
+ invariant2(Option.isSome(node), `Node not available: ${id}`, {
192
+ F: __dxlog_file2,
178
193
  L: 172,
179
194
  S: this,
180
195
  A: [
@@ -193,6 +208,9 @@ var GraphImpl = class {
193
208
  // TODO(wittjosiah): Atom feature request, support for something akin to `ComplexMap` to allow for complex arguments.
194
209
  _connections = Atom2.family((key) => {
195
210
  return Atom2.make((get2) => {
211
+ if (!key || key.indexOf(Separators.primary) <= 0) {
212
+ return [];
213
+ }
196
214
  const { id, relation: relation2 } = relationFromConnectionKey(key);
197
215
  const edges = get2(this._edges(id));
198
216
  return (edges[relationKey(relation2)] ?? []).map((id2) => get2(this._node(id2))).filter(Option.isSome).map((o) => o.value);
@@ -200,6 +218,9 @@ var GraphImpl = class {
200
218
  });
201
219
  _actions = Atom2.family((id) => {
202
220
  return Atom2.make((get2) => {
221
+ if (!id) {
222
+ return [];
223
+ }
203
224
  return get2(this._connections(connectionKey(id, actionRelation())));
204
225
  }).pipe(Atom2.withLabel(`graph:actions:${id}`));
205
226
  });
@@ -345,9 +366,9 @@ function getConnections(graphOrId, idOrRelation, relation2) {
345
366
  } else {
346
367
  const graph = graphOrId;
347
368
  const id = idOrRelation;
348
- invariant(relation2 !== void 0, "Relation is required.", {
349
- F: __dxlog_file,
350
- L: 440,
369
+ invariant2(relation2 !== void 0, "Relation is required.", {
370
+ F: __dxlog_file2,
371
+ L: 446,
351
372
  S: this,
352
373
  A: [
353
374
  "relation !== undefined",
@@ -397,14 +418,25 @@ var traverseImpl = (graph, options, path = []) => {
397
418
  if (shouldContinue === false) {
398
419
  return;
399
420
  }
400
- Object.values(getConnections(graph, source, relation2)).forEach((child) => traverseImpl(graph, {
401
- source: child.id,
402
- relation: relation2,
403
- visitor
404
- }, [
405
- ...path,
406
- source
407
- ]));
421
+ const relations = Array.isArray(relation2) ? relation2 : [
422
+ relation2
423
+ ];
424
+ const seen = /* @__PURE__ */ new Set();
425
+ for (const rel of relations) {
426
+ for (const connected of getConnections(graph, source, rel)) {
427
+ if (!seen.has(connected.id)) {
428
+ seen.add(connected.id);
429
+ traverseImpl(graph, {
430
+ source: connected.id,
431
+ relation: relation2,
432
+ visitor
433
+ }, [
434
+ ...path,
435
+ source
436
+ ]);
437
+ }
438
+ }
439
+ }
408
440
  };
409
441
  function traverse(graphOrOptions, optionsOrPath, path) {
410
442
  if (typeof graphOrOptions === "object" && "visitor" in graphOrOptions) {
@@ -480,8 +512,8 @@ var initializeImpl = async (graph, id) => {
480
512
  id,
481
513
  initialized
482
514
  }, {
483
- F: __dxlog_file,
484
- L: 655,
515
+ F: __dxlog_file2,
516
+ L: 668,
485
517
  S: void 0,
486
518
  C: (f, a) => f(...a)
487
519
  });
@@ -511,8 +543,8 @@ var expandImpl = (graph, id, relation2) => {
511
543
  key,
512
544
  deferred: true
513
545
  }, {
514
- F: __dxlog_file,
515
- L: 701,
546
+ F: __dxlog_file2,
547
+ L: 714,
516
548
  S: void 0,
517
549
  C: (f, a) => f(...a)
518
550
  });
@@ -523,8 +555,8 @@ var expandImpl = (graph, id, relation2) => {
523
555
  key,
524
556
  expanded
525
557
  }, {
526
- F: __dxlog_file,
527
- L: 706,
558
+ F: __dxlog_file2,
559
+ L: 719,
528
560
  S: void 0,
529
561
  C: (f, a) => f(...a)
530
562
  });
@@ -542,9 +574,9 @@ function expand(graphOrId, idOrRelation, relation2) {
542
574
  } else {
543
575
  const graph = graphOrId;
544
576
  const id = idOrRelation;
545
- invariant(relation2 !== void 0, "Relation is required.", {
546
- F: __dxlog_file,
547
- L: 742,
577
+ invariant2(relation2 !== void 0, "Relation is required.", {
578
+ F: __dxlog_file2,
579
+ L: 755,
548
580
  S: this,
549
581
  A: [
550
582
  "relation !== undefined",
@@ -620,8 +652,8 @@ var addNodeImpl = (graph, nodeArg) => {
620
652
  dataChanged,
621
653
  propertiesChanged
622
654
  }, {
623
- F: __dxlog_file,
624
- L: 864,
655
+ F: __dxlog_file2,
656
+ L: 877,
625
657
  S: void 0,
626
658
  C: (f, a) => f(...a)
627
659
  });
@@ -632,8 +664,8 @@ var addNodeImpl = (graph, nodeArg) => {
632
664
  data,
633
665
  properties
634
666
  }, {
635
- F: __dxlog_file,
636
- L: 871,
667
+ F: __dxlog_file2,
668
+ L: 884,
637
669
  S: void 0,
638
670
  C: (f, a) => f(...a)
639
671
  });
@@ -661,8 +693,8 @@ var addNodeImpl = (graph, nodeArg) => {
661
693
  data,
662
694
  properties
663
695
  }, {
664
- F: __dxlog_file,
665
- L: 884,
696
+ F: __dxlog_file2,
697
+ L: 897,
666
698
  S: void 0,
667
699
  C: (f, a) => f(...a)
668
700
  });
@@ -808,8 +840,8 @@ var addEdgeImpl = (graph, edgeArg) => {
808
840
  target: edgeArg.target,
809
841
  relation: relationId
810
842
  }, {
811
- F: __dxlog_file,
812
- L: 1068,
843
+ F: __dxlog_file2,
844
+ L: 1081,
813
845
  S: void 0,
814
846
  C: (f, a) => f(...a)
815
847
  });
@@ -830,8 +862,8 @@ var addEdgeImpl = (graph, edgeArg) => {
830
862
  target: edgeArg.target,
831
863
  relation: inverseId
832
864
  }, {
833
- F: __dxlog_file,
834
- L: 1076,
865
+ F: __dxlog_file2,
866
+ L: 1089,
835
867
  S: void 0,
836
868
  C: (f, a) => f(...a)
837
869
  });
@@ -934,9 +966,9 @@ var relationKey = (relation2) => {
934
966
  };
935
967
  var relationFromKey = (encoded) => {
936
968
  const separatorIndex = encoded.lastIndexOf(Separators.secondary);
937
- invariant(separatorIndex > 0 && separatorIndex < encoded.length - 1, `Invalid relation key: ${encoded}`, {
938
- F: __dxlog_file,
939
- L: 1221,
969
+ invariant2(separatorIndex > 0 && separatorIndex < encoded.length - 1, `Invalid relation key: ${encoded}`, {
970
+ F: __dxlog_file2,
971
+ L: 1234,
940
972
  S: void 0,
941
973
  A: [
942
974
  "separatorIndex > 0 && separatorIndex < encoded.length - 1",
@@ -945,9 +977,9 @@ var relationFromKey = (encoded) => {
945
977
  });
946
978
  const kind = encoded.slice(0, separatorIndex);
947
979
  const directionRaw = encoded.slice(separatorIndex + 1);
948
- invariant(directionRaw === "outbound" || directionRaw === "inbound", `Invalid relation direction: ${directionRaw}`, {
949
- F: __dxlog_file,
950
- L: 1224,
980
+ invariant2(directionRaw === "outbound" || directionRaw === "inbound", `Invalid relation direction: ${directionRaw}`, {
981
+ F: __dxlog_file2,
982
+ L: 1237,
951
983
  S: void 0,
952
984
  A: [
953
985
  "directionRaw === 'outbound' || directionRaw === 'inbound'",
@@ -959,9 +991,9 @@ var relationFromKey = (encoded) => {
959
991
  var connectionKey = (id, relation2) => `${id}${Separators.primary}${relationKey(relation2)}`;
960
992
  var relationFromConnectionKey = (key) => {
961
993
  const separatorIndex = key.indexOf(Separators.primary);
962
- invariant(separatorIndex > 0 && separatorIndex < key.length - 1, `Invalid connection key: ${key}`, {
963
- F: __dxlog_file,
964
- L: 1233,
994
+ invariant2(separatorIndex > 0 && separatorIndex < key.length - 1, `Invalid connection key: ${key}`, {
995
+ F: __dxlog_file2,
996
+ L: 1246,
965
997
  S: void 0,
966
998
  A: [
967
999
  "separatorIndex > 0 && separatorIndex < key.length - 1",
@@ -1050,7 +1082,7 @@ var whenEchoObjectMatches = (node) => Obj.isObject(node.data) ? Option2.some(nod
1050
1082
  var whenNot = (matcher) => (node) => Option2.isNone(matcher(node)) ? Option2.some(node) : Option2.none();
1051
1083
 
1052
1084
  // src/graph-builder.ts
1053
- var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
1085
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
1054
1086
  var GraphBuilderTypeId = /* @__PURE__ */ Symbol.for("@dxos/app-graph/GraphBuilder");
1055
1087
  var GraphBuilderImpl = class {
1056
1088
  [GraphBuilderTypeId] = GraphBuilderTypeId;
@@ -1058,15 +1090,25 @@ var GraphBuilderImpl = class {
1058
1090
  return Pipeable2.pipeArguments(this, arguments);
1059
1091
  }
1060
1092
  // TODO(wittjosiah): Use Context.
1093
+ /** Active subscriptions keyed by composite ID, cleaned up on node removal. */
1061
1094
  _subscriptions = /* @__PURE__ */ new Map();
1095
+ /** Connector updates pending flush, keyed by connector key. */
1062
1096
  _dirtyConnectors = /* @__PURE__ */ new Map();
1097
+ /** Last-flushed node IDs per connector key, used for edge removal on update. */
1063
1098
  _connectorPrevious = /* @__PURE__ */ new Map();
1099
+ /** Last-flushed node args per connector key, used for change detection. */
1064
1100
  _connectorPreviousArgs = /* @__PURE__ */ new Map();
1101
+ /** Whether a dirty-flush task is already scheduled. */
1065
1102
  _flushScheduled = false;
1103
+ /** Resolves when the current flush completes. */
1066
1104
  _flushPromise = Promise.resolve();
1105
+ /** Registered builder extensions keyed by extension ID. */
1067
1106
  _extensions = Atom3.make(Record2.empty()).pipe(Atom3.keepAlive, Atom3.withLabel("graph-builder:extensions"));
1107
+ /** Triggers signalling that a node's resolver has fired at least once. */
1068
1108
  _initialized = {};
1109
+ /** Shared atom registry for reactive subscriptions. */
1069
1110
  _registry;
1111
+ /** Backing graph with internal accessors for node atoms and construction. */
1070
1112
  _graph;
1071
1113
  constructor({ registry, ...params } = {}) {
1072
1114
  this._registry = registry ?? Registry2.make();
@@ -1159,8 +1201,8 @@ var GraphBuilderImpl = class {
1159
1201
  relation: relation2,
1160
1202
  registry: getDebugName(this._registry)
1161
1203
  }, {
1162
- F: __dxlog_file2,
1163
- L: 251,
1204
+ F: __dxlog_file3,
1205
+ L: 261,
1164
1206
  S: this,
1165
1207
  C: (f, a) => f(...a)
1166
1208
  });
@@ -1172,7 +1214,8 @@ var GraphBuilderImpl = class {
1172
1214
  _expandRelation(id, relation2) {
1173
1215
  const key = connectorKey(id, relation2);
1174
1216
  const connectors = this._connectors(key);
1175
- const cancel = this._registry.subscribe(connectors, (nodes) => {
1217
+ const cancel = this._registry.subscribe(connectors, (rawNodes) => {
1218
+ const nodes = qualifyNodeArgs(id)(rawNodes);
1176
1219
  const previous = this._connectorPrevious.get(key) ?? [];
1177
1220
  const ids = nodes.map((n) => n.id);
1178
1221
  if (ids.length === previous.length && ids.every((nodeId, idx) => nodeId === previous[idx])) {
@@ -1186,8 +1229,8 @@ var GraphBuilderImpl = class {
1186
1229
  relation: relation2,
1187
1230
  ids
1188
1231
  }, {
1189
- F: __dxlog_file2,
1190
- L: 277,
1232
+ F: __dxlog_file3,
1233
+ L: 288,
1191
1234
  S: this,
1192
1235
  C: (f, a) => f(...a)
1193
1236
  });
@@ -1206,8 +1249,8 @@ var GraphBuilderImpl = class {
1206
1249
  log2("onInitialize", {
1207
1250
  id
1208
1251
  }, {
1209
- F: __dxlog_file2,
1210
- L: 289,
1252
+ F: __dxlog_file3,
1253
+ L: 300,
1211
1254
  S: this,
1212
1255
  C: (f, a) => f(...a)
1213
1256
  });
@@ -1216,9 +1259,14 @@ var GraphBuilderImpl = class {
1216
1259
  const trigger = this._initialized[id];
1217
1260
  Option3.match(node, {
1218
1261
  onSome: (node2) => {
1219
- addNodes(this._graph, [
1220
- node2
1221
- ]);
1262
+ const connectorOwned = [
1263
+ ...this._connectorPrevious.values()
1264
+ ].some((ids) => ids.includes(id));
1265
+ if (!connectorOwned) {
1266
+ addNodes(this._graph, [
1267
+ node2
1268
+ ]);
1269
+ }
1222
1270
  trigger?.wake();
1223
1271
  },
1224
1272
  onNone: () => {
@@ -1306,7 +1354,7 @@ var exploreImpl = async (builder, options, path = []) => {
1306
1354
  if (shouldContinue === false) {
1307
1355
  return;
1308
1356
  }
1309
- const nodes = Object.values(internal._registry.get(internal._extensions)).filter((extension) => relationKey(extension.relation ?? "child") === relationKey(relation2)).map((extension) => extension.connector).filter(isNonNullable2).flatMap((connector) => registry.get(connector(internal._graph.node(source))));
1357
+ const nodes = Function2.pipe(internal._registry.get(internal._extensions), Record2.values, Array2.map((extension) => extension.connector), Array2.filter(isNonNullable2), Array2.flatMap((connector) => registry.get(connector(internal._graph.node(source)))), qualifyNodeArgs(source));
1310
1358
  await Promise.all(nodes.map((nodeArg) => {
1311
1359
  registry.set(internal._graph._node(nodeArg.id), internal._graph._constructNode(nodeArg));
1312
1360
  return exploreImpl(builder, {
@@ -1378,8 +1426,8 @@ var createExtensionRaw = (extension) => {
1378
1426
  node,
1379
1427
  error
1380
1428
  }, {
1381
- F: __dxlog_file2,
1382
- L: 579,
1429
+ F: __dxlog_file3,
1430
+ L: 596,
1383
1431
  S: void 0,
1384
1432
  C: (f, a) => f(...a)
1385
1433
  });
@@ -1404,8 +1452,8 @@ var createExtensionRaw = (extension) => {
1404
1452
  node,
1405
1453
  error
1406
1454
  }, {
1407
- F: __dxlog_file2,
1408
- L: 600,
1455
+ F: __dxlog_file3,
1456
+ L: 617,
1409
1457
  S: void 0,
1410
1458
  C: (f, a) => f(...a)
1411
1459
  });
@@ -1429,8 +1477,8 @@ var createExtensionRaw = (extension) => {
1429
1477
  node,
1430
1478
  error
1431
1479
  }, {
1432
- F: __dxlog_file2,
1433
- L: 617,
1480
+ F: __dxlog_file3,
1481
+ L: 634,
1434
1482
  S: void 0,
1435
1483
  C: (f, a) => f(...a)
1436
1484
  });
@@ -1446,8 +1494,8 @@ var runEffectSyncWithFallback = (effect, context2, extensionId, fallback) => {
1446
1494
  extension: extensionId,
1447
1495
  error
1448
1496
  }, {
1449
- F: __dxlog_file2,
1450
- L: 660,
1497
+ F: __dxlog_file3,
1498
+ L: 677,
1451
1499
  S: void 0,
1452
1500
  C: (f, a) => f(...a)
1453
1501
  });
@@ -1489,6 +1537,15 @@ var createTypeExtension = (options) => {
1489
1537
  position
1490
1538
  });
1491
1539
  };
1540
+ var qualifyNodeArgs = (parentId) => (nodes) => nodes.map((node) => {
1541
+ validateSegmentId(node.id);
1542
+ const qualified = qualifyId(parentId, node.id);
1543
+ return {
1544
+ ...node,
1545
+ id: qualified,
1546
+ nodes: node.nodes ? qualifyNodeArgs(qualified)(node.nodes) : void 0
1547
+ };
1548
+ });
1492
1549
  var connectorKey = (id, relation2) => `${id}${Separators.primary}${relationKey(relation2)}`;
1493
1550
  var relationFromConnectorKey = (key) => {
1494
1551
  const separatorIndex = key.indexOf(Separators.primary);