@dxos/graph 0.8.4-main.72ec0f3 → 0.8.4-main.765dc60934

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,35 +1,132 @@
1
1
  import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
2
7
 
3
- // src/model.ts
4
- import { effect } from "@preact/signals-core";
5
- import { inspectCustom } from "@dxos/debug";
6
- import { failedInvariant, invariant as invariant2 } from "@dxos/invariant";
7
- import { live } from "@dxos/live-object";
8
- import { isTruthy, removeBy } from "@dxos/util";
9
-
10
- // src/util.ts
8
+ // src/selection.ts
9
+ import { Atom, Registry } from "@effect-atom/atom-react";
11
10
  import { invariant } from "@dxos/invariant";
12
- var __dxlog_file = "/__w/dxos/dxos/packages/common/graph/src/util.ts";
11
+ var __dxlog_file = "/__w/dxos/dxos/packages/common/graph/src/selection.ts";
12
+ var SelectionModel = class {
13
+ _registry;
14
+ _selected;
15
+ _mode;
16
+ constructor({ mode = "multi" } = {}) {
17
+ this._registry = Registry.make();
18
+ this._selected = Atom.make(/* @__PURE__ */ new Set());
19
+ this._mode = mode;
20
+ }
21
+ /**
22
+ * Returns the selected IDs atom for subscription.
23
+ */
24
+ get selected() {
25
+ return this._selected;
26
+ }
27
+ /**
28
+ * Gets the current selected IDs as a Set.
29
+ */
30
+ getSelected() {
31
+ return this._registry.get(this._selected);
32
+ }
33
+ /**
34
+ * Gets the current selected IDs as an array.
35
+ */
36
+ getSelectedIds() {
37
+ return Array.from(this._registry.get(this._selected).values());
38
+ }
39
+ /**
40
+ * Subscribe to selection changes.
41
+ */
42
+ subscribe(cb) {
43
+ this._registry.get(this._selected);
44
+ return this._registry.subscribe(this._selected, () => {
45
+ cb(this._registry.get(this._selected));
46
+ });
47
+ }
48
+ toJSON() {
49
+ return {
50
+ selected: this.getSelectedIds()
51
+ };
52
+ }
53
+ /**
54
+ * Gets the current selection size.
55
+ */
56
+ getSize() {
57
+ return this._registry.get(this._selected).size;
58
+ }
59
+ contains(id) {
60
+ return this._registry.get(this._selected).has(id);
61
+ }
62
+ clear() {
63
+ this._registry.set(this._selected, /* @__PURE__ */ new Set());
64
+ }
65
+ add(id) {
66
+ invariant(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 59, S: this, A: ["id", ""] });
67
+ const current = this._registry.get(this._selected);
68
+ this._registry.set(this._selected, new Set(this._mode === "single" ? [
69
+ id
70
+ ] : [
71
+ ...Array.from(current.values()),
72
+ id
73
+ ]));
74
+ }
75
+ remove(id) {
76
+ invariant(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 69, S: this, A: ["id", ""] });
77
+ const current = this._registry.get(this._selected);
78
+ this._registry.set(this._selected, new Set(Array.from(current.values()).filter((_id) => _id !== id)));
79
+ }
80
+ // TODO(burdon): Handle single select.
81
+ setSelected(ids, subtract = false) {
82
+ const current = this._registry.get(this._selected);
83
+ this._registry.set(this._selected, /* @__PURE__ */ new Set([
84
+ ...subtract ? Array.from(current.values()) : [],
85
+ ...ids
86
+ ]));
87
+ }
88
+ toggleSelected(ids, subtract = false) {
89
+ const current = this._registry.get(this._selected);
90
+ const set = new Set(subtract ? Array.from(current.values()) : void 0);
91
+ ids.forEach((id) => {
92
+ if (current.has(id)) {
93
+ set.delete(id);
94
+ } else {
95
+ set.add(id);
96
+ }
97
+ });
98
+ this._registry.set(this._selected, set);
99
+ }
100
+ };
101
+
102
+ // src/Graph.ts
103
+ var Graph_exports = {};
104
+ __export(Graph_exports, {
105
+ Edge: () => Edge,
106
+ Graph: () => Graph,
107
+ Node: () => Node,
108
+ createEdgeId: () => createEdgeId,
109
+ parseEdgeId: () => parseEdgeId
110
+ });
111
+ import * as Schema from "effect/Schema";
112
+ import { invariant as invariant2 } from "@dxos/invariant";
113
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/common/graph/src/Graph.ts";
114
+ var Node = Schema.Struct({
115
+ id: Schema.String,
116
+ type: Schema.optional(Schema.String),
117
+ data: Schema.optional(Schema.Any)
118
+ });
119
+ var Edge = Schema.Struct({
120
+ id: Schema.String,
121
+ type: Schema.optional(Schema.String),
122
+ source: Schema.String,
123
+ target: Schema.String,
124
+ data: Schema.optional(Schema.Any)
125
+ });
13
126
  var KEY_REGEX = /\w+/;
14
127
  var createEdgeId = ({ source, target, relation }) => {
15
- invariant(source.match(KEY_REGEX), `invalid source: ${source}`, {
16
- F: __dxlog_file,
17
- L: 13,
18
- S: void 0,
19
- A: [
20
- "source.match(KEY_REGEX)",
21
- "`invalid source: ${source}`"
22
- ]
23
- });
24
- invariant(target.match(KEY_REGEX), `invalid target: ${target}`, {
25
- F: __dxlog_file,
26
- L: 14,
27
- S: void 0,
28
- A: [
29
- "target.match(KEY_REGEX)",
30
- "`invalid target: ${target}`"
31
- ]
32
- });
128
+ invariant2(source.match(KEY_REGEX), `invalid source: ${source}`, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 27, S: void 0, A: ["source.match(KEY_REGEX)", "`invalid source: ${source}`"] });
129
+ invariant2(target.match(KEY_REGEX), `invalid target: ${target}`, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 28, S: void 0, A: ["target.match(KEY_REGEX)", "`invalid target: ${target}`"] });
33
130
  return [
34
131
  source,
35
132
  relation,
@@ -38,34 +135,52 @@ var createEdgeId = ({ source, target, relation }) => {
38
135
  };
39
136
  var parseEdgeId = (id) => {
40
137
  const [source, relation, target] = id.split("_");
41
- invariant(source.length && target.length, void 0, {
42
- F: __dxlog_file,
43
- L: 20,
44
- S: void 0,
45
- A: [
46
- "source.length && target.length",
47
- ""
48
- ]
49
- });
138
+ invariant2(source.length && target.length, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 37, S: void 0, A: ["source.length && target.length", ""] });
50
139
  return {
51
140
  source,
52
141
  relation: relation.length ? relation : void 0,
53
142
  target
54
143
  };
55
144
  };
145
+ var Graph = Schema.Struct({
146
+ id: Schema.optional(Schema.String),
147
+ nodes: Schema.mutable(Schema.Array(Node)),
148
+ edges: Schema.mutable(Schema.Array(Edge))
149
+ });
56
150
 
57
- // src/model.ts
58
- var __dxlog_file2 = "/__w/dxos/dxos/packages/common/graph/src/model.ts";
151
+ // src/GraphModel.ts
152
+ var GraphModel_exports = {};
153
+ __export(GraphModel_exports, {
154
+ AbstractBuilder: () => AbstractBuilder,
155
+ AbstractGraphModel: () => AbstractGraphModel,
156
+ Builder: () => Builder,
157
+ GraphModel: () => GraphModel,
158
+ ReactiveGraphModel: () => ReactiveGraphModel,
159
+ ReadonlyGraphModel: () => ReadonlyGraphModel
160
+ });
161
+ import { Atom as Atom2 } from "@effect-atom/atom-react";
162
+ import { inspectCustom } from "@dxos/debug";
163
+ import { failedInvariant, invariant as invariant3 } from "@dxos/invariant";
164
+ import { isTruthy, removeBy } from "@dxos/util";
165
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/common/graph/src/GraphModel.ts";
59
166
  var ReadonlyGraphModel = class {
60
167
  _graph;
61
168
  /**
169
+ * Optional function to wrap mutations.
170
+ * When set, all graph mutations are wrapped in this function.
171
+ */
172
+ _change;
173
+ /**
62
174
  * NOTE: Pass in simple Graph or Live.
175
+ * @param graph - The graph data.
176
+ * @param change - Optional function to wrap mutations (e.g., Obj.update for ECHO objects).
63
177
  */
64
- constructor(graph) {
178
+ constructor(graph, change) {
65
179
  this._graph = graph ?? {
66
180
  nodes: [],
67
181
  edges: []
68
182
  };
183
+ this._change = change;
69
184
  }
70
185
  [inspectCustom]() {
71
186
  return this.toJSON();
@@ -133,9 +248,22 @@ var ReadonlyGraphModel = class {
133
248
  }
134
249
  };
135
250
  var AbstractGraphModel = class extends ReadonlyGraphModel {
251
+ /**
252
+ * Execute a mutation on the graph.
253
+ * If a change function is set, wraps the mutation in it.
254
+ */
255
+ _mutate(fn) {
256
+ if (this._change != null) {
257
+ this._change(() => fn(this._graph));
258
+ } else {
259
+ fn(this._graph);
260
+ }
261
+ }
136
262
  clear() {
137
- this._graph.nodes.length = 0;
138
- this._graph.edges.length = 0;
263
+ this._mutate((graph) => {
264
+ graph.nodes.length = 0;
265
+ graph.edges.length = 0;
266
+ });
139
267
  return this;
140
268
  }
141
269
  addGraph(graph) {
@@ -151,76 +279,44 @@ var AbstractGraphModel = class extends ReadonlyGraphModel {
151
279
  return this;
152
280
  }
153
281
  addNode(node) {
154
- invariant2(node.id, "ID is required", {
155
- F: __dxlog_file2,
156
- L: 157,
157
- S: this,
158
- A: [
159
- "node.id",
160
- "'ID is required'"
161
- ]
162
- });
163
- invariant2(!this.findNode(node.id), `node already exists: ${node.id}`, {
164
- F: __dxlog_file2,
165
- L: 158,
166
- S: this,
167
- A: [
168
- "!this.findNode(node.id)",
169
- "`node already exists: ${node.id}`"
170
- ]
282
+ invariant3(node.id, "ID is required", { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 126, S: this, A: ["node.id", "'ID is required'"] });
283
+ invariant3(!this.findNode(node.id), `node already exists: ${node.id}`, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 127, S: this, A: ["!this.findNode(node.id)", "`node already exists: ${node.id}`"] });
284
+ this._mutate((graph) => {
285
+ graph.nodes.push(node);
171
286
  });
172
- this._graph.nodes.push(node);
173
287
  return node;
174
288
  }
175
289
  addNodes(nodes) {
176
290
  return nodes.map((node) => this.addNode(node));
177
291
  }
178
292
  addEdge(edge) {
179
- invariant2(edge.source, void 0, {
180
- F: __dxlog_file2,
181
- L: 168,
182
- S: this,
183
- A: [
184
- "edge.source",
185
- ""
186
- ]
187
- });
188
- invariant2(edge.target, void 0, {
189
- F: __dxlog_file2,
190
- L: 169,
191
- S: this,
192
- A: [
193
- "edge.target",
194
- ""
195
- ]
196
- });
293
+ invariant3(edge.source, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 137, S: this, A: ["edge.source", ""] });
294
+ invariant3(edge.target, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 138, S: this, A: ["edge.target", ""] });
197
295
  if (!edge.id) {
198
296
  edge = {
199
297
  id: createEdgeId(edge),
200
298
  ...edge
201
299
  };
202
300
  }
203
- invariant2(!this.findNode(edge.id), void 0, {
204
- F: __dxlog_file2,
205
- L: 174,
206
- S: this,
207
- A: [
208
- "!this.findNode(edge.id!)",
209
- ""
210
- ]
301
+ invariant3(!this.findNode(edge.id), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 146, S: this, A: ["!this.findNode(edge.id)", ""] });
302
+ this._mutate((graph) => {
303
+ graph.edges.push(edge);
211
304
  });
212
- this._graph.edges.push(edge);
213
305
  return edge;
214
306
  }
215
307
  addEdges(edges) {
216
308
  return edges.map((edge) => this.addEdge(edge));
217
309
  }
218
310
  removeNode(id) {
219
- const edges = removeBy(this._graph.edges, (edge) => edge.source === id || edge.target === id);
220
- const nodes = removeBy(this._graph.nodes, (node) => node.id === id);
311
+ let removedEdges = [];
312
+ let removedNodes = [];
313
+ this._mutate((graph) => {
314
+ removedEdges = removeBy(graph.edges, (edge) => edge.source === id || edge.target === id);
315
+ removedNodes = removeBy(graph.nodes, (node) => node.id === id);
316
+ });
221
317
  return this.copy({
222
- nodes,
223
- edges
318
+ nodes: removedNodes,
319
+ edges: removedEdges
224
320
  });
225
321
  }
226
322
  removeNodes(ids) {
@@ -228,10 +324,13 @@ var AbstractGraphModel = class extends ReadonlyGraphModel {
228
324
  return this.copy().addGraphs(graphs);
229
325
  }
230
326
  removeEdge(id) {
231
- const edges = removeBy(this._graph.edges, (edge) => edge.id === id);
327
+ let removedEdges = [];
328
+ this._mutate((graph) => {
329
+ removedEdges = removeBy(graph.edges, (edge) => edge.id === id);
330
+ });
232
331
  return this.copy({
233
332
  nodes: [],
234
- edges
333
+ edges: removedEdges
235
334
  });
236
335
  }
237
336
  removeEdges(ids) {
@@ -239,7 +338,7 @@ var AbstractGraphModel = class extends ReadonlyGraphModel {
239
338
  return this.copy().addGraphs(graphs);
240
339
  }
241
340
  };
242
- var AbstractGraphBuilder = class {
341
+ var AbstractBuilder = class {
243
342
  _model;
244
343
  constructor(_model) {
245
344
  this._model = _model;
@@ -273,153 +372,138 @@ var AbstractGraphBuilder = class {
273
372
  };
274
373
  var GraphModel = class _GraphModel extends AbstractGraphModel {
275
374
  get builder() {
276
- return new GraphBuilder(this);
375
+ return new Builder(this);
277
376
  }
278
377
  copy(graph) {
279
- return new _GraphModel({
280
- nodes: graph?.nodes ?? [],
281
- edges: graph?.edges ?? []
282
- });
378
+ return new _GraphModel(graph);
283
379
  }
284
380
  };
285
- var subscribe = (model, cb, fire = false) => {
286
- if (fire) {
287
- cb(model, model.graph);
288
- }
289
- return effect(() => {
290
- cb(model, model.graph);
291
- });
292
- };
293
381
  var ReactiveGraphModel = class _ReactiveGraphModel extends GraphModel {
294
- constructor(graph) {
295
- super(live({
382
+ _registry;
383
+ _graphAtom;
384
+ constructor(_registry, graph) {
385
+ const initialGraph = {
296
386
  nodes: graph?.nodes ?? [],
297
387
  edges: graph?.edges ?? []
298
- }));
299
- }
300
- copy(graph) {
301
- return new _ReactiveGraphModel(graph);
302
- }
303
- subscribe(cb, fire = false) {
304
- return subscribe(this, cb, fire);
388
+ };
389
+ super(initialGraph), this._registry = _registry;
390
+ this._graphAtom = Atom2.make(initialGraph);
305
391
  }
306
- };
307
- var GraphBuilder = class extends AbstractGraphBuilder {
308
- call(cb) {
309
- cb(this);
310
- return this;
392
+ get registry() {
393
+ return this._registry;
311
394
  }
312
- };
313
-
314
- // src/selection.ts
315
- import { computed, signal } from "@preact/signals-core";
316
- import { invariant as invariant3 } from "@dxos/invariant";
317
- var __dxlog_file3 = "/__w/dxos/dxos/packages/common/graph/src/selection.ts";
318
- var SelectionModel = class {
319
- _singleSelect;
320
- _selected = signal(/* @__PURE__ */ new Set());
321
- _selectedIds = computed(() => Array.from(this._selected.value.values()));
322
- constructor(_singleSelect = false) {
323
- this._singleSelect = _singleSelect;
395
+ /**
396
+ * Get the graph atom for reactive subscriptions.
397
+ */
398
+ get graphAtom() {
399
+ return this._graphAtom;
324
400
  }
325
- toJSON() {
326
- return {
327
- selected: Array.from(this._selected.value.values())
328
- };
401
+ get graph() {
402
+ return this._registry.get(this._graphAtom);
329
403
  }
330
- get size() {
331
- return this._selected.value.size;
404
+ get nodes() {
405
+ return this.graph.nodes;
332
406
  }
333
- get selected() {
334
- return this._selectedIds;
407
+ get edges() {
408
+ return this.graph.edges;
335
409
  }
336
- contains(id) {
337
- return this._selected.value.has(id);
410
+ copy(graph) {
411
+ return new _ReactiveGraphModel(this._registry, graph);
338
412
  }
339
413
  clear() {
340
- this._selected.value = /* @__PURE__ */ new Set();
414
+ this._registry.set(this._graphAtom, {
415
+ nodes: [],
416
+ edges: []
417
+ });
418
+ return this;
341
419
  }
342
- add(id) {
343
- invariant3(id, void 0, {
344
- F: __dxlog_file3,
345
- L: 41,
346
- S: this,
347
- A: [
348
- "id",
349
- ""
420
+ /**
421
+ * Set the entire graph at once, triggering a single notification.
422
+ */
423
+ setGraph(graph) {
424
+ this._registry.set(this._graphAtom, graph);
425
+ return this;
426
+ }
427
+ addNode(node) {
428
+ invariant3(node.id, "ID is required", { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 280, S: this, A: ["node.id", "'ID is required'"] });
429
+ invariant3(!this.findNode(node.id), `node already exists: ${node.id}`, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 281, S: this, A: ["!this.findNode(node.id)", "`node already exists: ${node.id}`"] });
430
+ const current = this._registry.get(this._graphAtom);
431
+ this._registry.set(this._graphAtom, {
432
+ ...current,
433
+ nodes: [
434
+ ...current.nodes,
435
+ node
350
436
  ]
351
437
  });
352
- this._selected.value = new Set(this._singleSelect ? [
353
- id
354
- ] : [
355
- ...Array.from(this._selected.value.values()),
356
- id
357
- ]);
438
+ return node;
358
439
  }
359
- remove(id) {
360
- invariant3(id, void 0, {
361
- F: __dxlog_file3,
362
- L: 48,
363
- S: this,
364
- A: [
365
- "id",
366
- ""
440
+ addEdge(edge) {
441
+ invariant3(edge.source, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 293, S: this, A: ["edge.source", ""] });
442
+ invariant3(edge.target, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 294, S: this, A: ["edge.target", ""] });
443
+ if (!edge.id) {
444
+ edge = {
445
+ id: createEdgeId(edge),
446
+ ...edge
447
+ };
448
+ }
449
+ invariant3(!this.findNode(edge.id), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 301, S: this, A: ["!this.findNode(edge.id)", ""] });
450
+ const current = this._registry.get(this._graphAtom);
451
+ this._registry.set(this._graphAtom, {
452
+ ...current,
453
+ edges: [
454
+ ...current.edges,
455
+ edge
367
456
  ]
368
457
  });
369
- this._selected.value = new Set(Array.from(this._selected.value.values()).filter((_id) => _id !== id));
458
+ return edge;
370
459
  }
371
- // TODO(burdon): Handle single select.
372
- setSelected(ids, shift = false) {
373
- this._selected.value = /* @__PURE__ */ new Set([
374
- ...shift ? Array.from(this._selected.value.values()) : [],
375
- ...ids
376
- ]);
460
+ removeNode(id) {
461
+ const current = this._registry.get(this._graphAtom);
462
+ const removedEdges = current.edges.filter((edge) => edge.source === id || edge.target === id);
463
+ const removedNodes = current.nodes.filter((node) => node.id === id);
464
+ this._registry.set(this._graphAtom, {
465
+ nodes: current.nodes.filter((node) => node.id !== id),
466
+ edges: current.edges.filter((edge) => edge.source !== id && edge.target !== id)
467
+ });
468
+ return this.copy({
469
+ nodes: removedNodes,
470
+ edges: removedEdges
471
+ });
377
472
  }
378
- toggleSelected(ids, shift = false) {
379
- const set = new Set(shift ? Array.from(this._selected.value.values()) : void 0);
380
- ids.forEach((id) => {
381
- if (this._selected.value.has(id)) {
382
- set.delete(id);
383
- } else {
384
- set.add(id);
385
- }
473
+ removeEdge(id) {
474
+ const current = this._registry.get(this._graphAtom);
475
+ const removedEdges = current.edges.filter((edge) => edge.id === id);
476
+ this._registry.set(this._graphAtom, {
477
+ ...current,
478
+ edges: current.edges.filter((edge) => edge.id !== id)
479
+ });
480
+ return this.copy({
481
+ nodes: [],
482
+ edges: removedEdges
483
+ });
484
+ }
485
+ /**
486
+ * Subscribe to graph changes.
487
+ */
488
+ subscribe(cb, fire = false) {
489
+ if (fire) {
490
+ cb(this, this.graph);
491
+ }
492
+ this._registry.get(this._graphAtom);
493
+ return this._registry.subscribe(this._graphAtom, () => {
494
+ cb(this, this.graph);
386
495
  });
387
- this._selected.value = set;
388
496
  }
389
497
  };
390
-
391
- // src/types.ts
392
- import * as Schema from "effect/Schema";
393
- var BaseGraphNode = Schema.Struct({
394
- id: Schema.String,
395
- type: Schema.optional(Schema.String),
396
- data: Schema.optional(Schema.Any)
397
- });
398
- var BaseGraphEdge = Schema.Struct({
399
- id: Schema.String,
400
- type: Schema.optional(Schema.String),
401
- source: Schema.String,
402
- target: Schema.String,
403
- data: Schema.optional(Schema.Any)
404
- });
405
- var Graph = Schema.Struct({
406
- id: Schema.optional(Schema.String),
407
- nodes: Schema.mutable(Schema.Array(BaseGraphNode)),
408
- edges: Schema.mutable(Schema.Array(BaseGraphEdge))
409
- });
498
+ var Builder = class extends AbstractBuilder {
499
+ call(cb) {
500
+ cb(this);
501
+ return this;
502
+ }
503
+ };
410
504
  export {
411
- AbstractGraphBuilder,
412
- AbstractGraphModel,
413
- BaseGraphEdge,
414
- BaseGraphNode,
415
- Graph,
416
- GraphBuilder,
417
- GraphModel,
418
- ReactiveGraphModel,
419
- ReadonlyGraphModel,
420
- SelectionModel,
421
- createEdgeId,
422
- parseEdgeId,
423
- subscribe
505
+ Graph_exports as Graph,
506
+ GraphModel_exports as GraphModel,
507
+ SelectionModel
424
508
  };
425
509
  //# sourceMappingURL=index.mjs.map