@dxos/app-graph 0.8.4-main.ae835ea → 0.8.4-main.bc674ce

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.
Files changed (43) hide show
  1. package/dist/lib/browser/index.mjs +1014 -553
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +1013 -553
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/atoms.d.ts +8 -0
  8. package/dist/types/src/atoms.d.ts.map +1 -0
  9. package/dist/types/src/graph-builder.d.ts +108 -66
  10. package/dist/types/src/graph-builder.d.ts.map +1 -1
  11. package/dist/types/src/graph.d.ts +182 -212
  12. package/dist/types/src/graph.d.ts.map +1 -1
  13. package/dist/types/src/index.d.ts +6 -3
  14. package/dist/types/src/index.d.ts.map +1 -1
  15. package/dist/types/src/node-matcher.d.ts +218 -0
  16. package/dist/types/src/node-matcher.d.ts.map +1 -0
  17. package/dist/types/src/node-matcher.test.d.ts +2 -0
  18. package/dist/types/src/node-matcher.test.d.ts.map +1 -0
  19. package/dist/types/src/node.d.ts +32 -3
  20. package/dist/types/src/node.d.ts.map +1 -1
  21. package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
  22. package/dist/types/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +35 -33
  24. package/src/atoms.ts +25 -0
  25. package/src/graph-builder.test.ts +520 -104
  26. package/src/graph-builder.ts +550 -255
  27. package/src/graph.test.ts +299 -106
  28. package/src/graph.ts +964 -394
  29. package/src/index.ts +9 -3
  30. package/src/node-matcher.test.ts +301 -0
  31. package/src/node-matcher.ts +284 -0
  32. package/src/node.ts +39 -6
  33. package/src/stories/EchoGraph.stories.tsx +104 -95
  34. package/src/stories/Tree.tsx +2 -2
  35. package/dist/types/src/experimental/graph-projections.test.d.ts +0 -25
  36. package/dist/types/src/experimental/graph-projections.test.d.ts.map +0 -1
  37. package/dist/types/src/signals-integration.test.d.ts +0 -2
  38. package/dist/types/src/signals-integration.test.d.ts.map +0 -1
  39. package/dist/types/src/testing.d.ts +0 -5
  40. package/dist/types/src/testing.d.ts.map +0 -1
  41. package/src/experimental/graph-projections.test.ts +0 -56
  42. package/src/signals-integration.test.ts +0 -218
  43. package/src/testing.ts +0 -20
package/src/graph.test.ts CHANGED
@@ -2,12 +2,13 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Registry, Rx } from '@effect-rx/rx-react';
5
+ import { Atom, Registry } from '@effect-atom/atom-react';
6
6
  import * as Option from 'effect/Option';
7
7
  import { assert, describe, expect, onTestFinished, test } from 'vitest';
8
8
 
9
- import { Graph, ROOT_ID, ROOT_TYPE, getGraph } from './graph';
10
- import { type Node } from './node';
9
+ import * as Graph from './graph';
10
+ import * as GraphBuilder from './graph-builder';
11
+ import * as Node from './node';
11
12
 
12
13
  const exampleId = (id: number) => `dx:test:${id}`;
13
14
  const EXAMPLE_ID = exampleId(1);
@@ -16,18 +17,18 @@ const EXAMPLE_TYPE = 'dxos.org/type/example';
16
17
  describe('Graph', () => {
17
18
  test('getGraph', () => {
18
19
  const registry = Registry.make();
19
- const graph = new Graph({ registry });
20
- const root = registry.get(graph.node(ROOT_ID));
20
+ const graph = Graph.make({ registry });
21
+ const root = registry.get(graph.node(Node.RootId));
21
22
  assert.ok(Option.isSome(root));
22
- expect(root.value.id).toEqual(ROOT_ID);
23
- expect(root.value.type).toEqual(ROOT_TYPE);
24
- expect(getGraph(root.value)).toEqual(graph);
23
+ expect(root.value.id).toEqual(Node.RootId);
24
+ expect(root.value.type).toEqual(Node.RootType);
25
+ expect(Graph.getGraph(root.value)).toEqual(graph);
25
26
  });
26
27
 
27
28
  test('add node', () => {
28
29
  const registry = Registry.make();
29
- const graph = new Graph({ registry });
30
- graph.addNode({ id: EXAMPLE_ID, type: EXAMPLE_TYPE });
30
+ const graph = Graph.make({ registry });
31
+ Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
31
32
  const node = registry.get(graph.node(EXAMPLE_ID));
32
33
  assert.ok(Option.isSome(node));
33
34
  expect(node.value.id).toEqual(EXAMPLE_ID);
@@ -36,9 +37,20 @@ describe('Graph', () => {
36
37
  expect(node.value.properties).toEqual({});
37
38
  });
38
39
 
40
+ test('add node curried', () => {
41
+ const registry = Registry.make();
42
+ const graph = Graph.make({ registry });
43
+ const result = graph.pipe(Graph.addNode({ id: EXAMPLE_ID, type: EXAMPLE_TYPE }));
44
+ expect(result).toEqual(graph);
45
+ const node = registry.get(graph.node(EXAMPLE_ID));
46
+ assert.ok(Option.isSome(node));
47
+ expect(node.value.id).toEqual(EXAMPLE_ID);
48
+ expect(node.value.type).toEqual(EXAMPLE_TYPE);
49
+ });
50
+
39
51
  test('add nodes updates existing nodes', () => {
40
52
  const registry = Registry.make();
41
- const graph = new Graph({ registry });
53
+ const graph = Graph.make({ registry });
42
54
  const nodeKey = graph.node(EXAMPLE_ID);
43
55
 
44
56
  let count = 0;
@@ -53,7 +65,7 @@ describe('Graph', () => {
53
65
  expect(registry.get(nodeKey)).toEqual(Option.none());
54
66
  expect(count).toEqual(1);
55
67
 
56
- graph.addNode({ id: EXAMPLE_ID, type: EXAMPLE_TYPE });
68
+ Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
57
69
  const node = registry.get(nodeKey);
58
70
  assert.ok(Option.isSome(node));
59
71
  expect(node.value.id).toEqual(EXAMPLE_ID);
@@ -62,13 +74,13 @@ describe('Graph', () => {
62
74
  expect(node.value.properties).toEqual({});
63
75
  expect(count).toEqual(2);
64
76
 
65
- graph.addNode({ id: EXAMPLE_ID, type: EXAMPLE_TYPE });
77
+ Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
66
78
  expect(count).toEqual(2);
67
79
  });
68
80
 
69
81
  test('remove node', () => {
70
82
  const registry = Registry.make();
71
- const graph = new Graph({ registry });
83
+ const graph = Graph.make({ registry });
72
84
 
73
85
  {
74
86
  const node = registry.get(graph.node(EXAMPLE_ID));
@@ -76,39 +88,49 @@ describe('Graph', () => {
76
88
  }
77
89
 
78
90
  {
79
- graph.addNode({ id: EXAMPLE_ID, type: EXAMPLE_TYPE });
91
+ Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
80
92
  const node = registry.get(graph.node(EXAMPLE_ID));
81
93
  expect(Option.isSome(node)).toEqual(true);
82
94
  }
83
95
 
84
96
  {
85
- graph.removeNode(EXAMPLE_ID);
97
+ Graph.removeNode(graph, EXAMPLE_ID);
86
98
  const node = registry.get(graph.node(EXAMPLE_ID));
87
99
  expect(Option.isNone(node)).toEqual(true);
88
100
  }
89
101
  });
90
102
 
103
+ test('remove node curried', () => {
104
+ const registry = Registry.make();
105
+ const graph = Graph.make({ registry });
106
+ Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
107
+ const result = graph.pipe(Graph.removeNode(EXAMPLE_ID));
108
+ expect(result).toEqual(graph);
109
+ const node = registry.get(graph.node(EXAMPLE_ID));
110
+ assert.ok(Option.isNone(node));
111
+ });
112
+
91
113
  test('onNodeChanged', () => {
92
- const graph = new Graph();
114
+ const graph = Graph.make();
93
115
 
94
- let node: Option.Option<Node> = Option.none();
116
+ let node: Option.Option<Node.Node> = Option.none();
95
117
  graph.onNodeChanged.on(({ node: newNode }) => {
96
118
  node = newNode;
97
119
  });
98
120
 
99
- graph.addNode({ id: EXAMPLE_ID, type: EXAMPLE_TYPE });
121
+ Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
100
122
  assert.ok(Option.isSome(node));
101
123
  expect(node.value.id).toEqual(EXAMPLE_ID);
102
124
  expect(node.value.type).toEqual(EXAMPLE_TYPE);
103
125
 
104
- graph.removeNode(EXAMPLE_ID);
126
+ Graph.removeNode(graph, EXAMPLE_ID);
105
127
  expect(node.pipe(Option.getOrNull)).toEqual(null);
106
128
  });
107
129
 
108
130
  test('add edge', () => {
109
131
  const registry = Registry.make();
110
- const graph = new Graph({ registry });
111
- graph.addEdge({ source: exampleId(1), target: exampleId(2) });
132
+ const graph = Graph.make({ registry });
133
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
112
134
  const edges = registry.get(graph.edges(exampleId(1)));
113
135
  expect(edges.inbound).toEqual([]);
114
136
  expect(edges.outbound).toEqual([exampleId(2)]);
@@ -116,9 +138,9 @@ describe('Graph', () => {
116
138
 
117
139
  test('add edges is idempotent', () => {
118
140
  const registry = Registry.make();
119
- const graph = new Graph({ registry });
120
- graph.addEdge({ source: exampleId(1), target: exampleId(2) });
121
- graph.addEdge({ source: exampleId(1), target: exampleId(2) });
141
+ const graph = Graph.make({ registry });
142
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
143
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
122
144
  const edges = registry.get(graph.edges(exampleId(1)));
123
145
  expect(edges.inbound).toEqual([]);
124
146
  expect(edges.outbound).toEqual([exampleId(2)]);
@@ -126,18 +148,18 @@ describe('Graph', () => {
126
148
 
127
149
  test('sort edges', () => {
128
150
  const registry = Registry.make();
129
- const graph = new Graph({ registry });
151
+ const graph = Graph.make({ registry });
130
152
 
131
153
  {
132
- graph.addEdge({ source: exampleId(1), target: exampleId(2) });
133
- graph.addEdge({ source: exampleId(1), target: exampleId(3) });
134
- graph.addEdge({ source: exampleId(1), target: exampleId(4) });
154
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
155
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(3) });
156
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(4) });
135
157
  const edges = registry.get(graph.edges(exampleId(1)));
136
158
  expect(edges.outbound).toEqual([exampleId(2), exampleId(3), exampleId(4)]);
137
159
  }
138
160
 
139
161
  {
140
- graph.sortEdges(exampleId(1), 'outbound', [exampleId(3), exampleId(2)]);
162
+ Graph.sortEdges(graph, exampleId(1), 'outbound', [exampleId(3), exampleId(2)]);
141
163
  const edges = registry.get(graph.edges(exampleId(1)));
142
164
  expect(edges.outbound).toEqual([exampleId(3), exampleId(2), exampleId(4)]);
143
165
  }
@@ -145,29 +167,42 @@ describe('Graph', () => {
145
167
 
146
168
  test('remove edge', () => {
147
169
  const registry = Registry.make();
148
- const graph = new Graph({ registry });
170
+ const graph = Graph.make({ registry });
149
171
 
150
172
  {
151
- graph.addEdge({ source: exampleId(1), target: exampleId(2) });
173
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
152
174
  const edges = registry.get(graph.edges(exampleId(1)));
153
175
  expect(edges.inbound).toEqual([]);
154
176
  expect(edges.outbound).toEqual([exampleId(2)]);
155
177
  }
156
178
 
157
179
  {
158
- graph.removeEdge({ source: exampleId(1), target: exampleId(2) });
180
+ Graph.removeEdge(graph, { source: exampleId(1), target: exampleId(2) });
159
181
  const edges = registry.get(graph.edges(exampleId(1)));
160
182
  expect(edges.inbound).toEqual([]);
161
183
  expect(edges.outbound).toEqual([]);
162
184
  }
163
185
  });
164
186
 
187
+ test('remove edge curried', () => {
188
+ const registry = Registry.make();
189
+ const graph = Graph.make({ registry });
190
+ Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
191
+ Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
192
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
193
+ const result = graph.pipe(Graph.removeEdge({ source: exampleId(1), target: exampleId(2) }));
194
+ expect(result).toEqual(graph);
195
+ const edges = registry.get(graph.edges(exampleId(1)));
196
+ expect(edges.inbound).toEqual([]);
197
+ expect(edges.outbound).toEqual([]);
198
+ });
199
+
165
200
  test('get connections', () => {
166
201
  const registry = Registry.make();
167
- const graph = new Graph({ registry });
168
- graph.addNode({ id: exampleId(1), type: EXAMPLE_TYPE });
169
- graph.addNode({ id: exampleId(2), type: EXAMPLE_TYPE });
170
- graph.addEdge({ source: exampleId(1), target: exampleId(2) });
202
+ const graph = Graph.make({ registry });
203
+ Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
204
+ Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
205
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
171
206
  const nodes = registry.get(graph.connections(exampleId(1)));
172
207
  expect(nodes).has.length(1);
173
208
  expect(nodes[0].id).toEqual(exampleId(2));
@@ -175,24 +210,24 @@ describe('Graph', () => {
175
210
 
176
211
  test('can subscribe to a node before it exists', async () => {
177
212
  const registry = Registry.make();
178
- const graph = new Graph({ registry });
213
+ const graph = Graph.make({ registry });
179
214
  const nodeKey = graph.node(exampleId(1));
180
215
 
181
- let node: Option.Option<Node> = Option.none();
216
+ let node: Option.Option<Node.Node> = Option.none();
182
217
  const cancel = registry.subscribe(nodeKey, (n) => {
183
218
  node = n;
184
219
  });
185
220
  onTestFinished(() => cancel());
186
221
 
187
222
  expect(node).toEqual(Option.none());
188
- graph.addNode({ id: exampleId(1), type: EXAMPLE_TYPE });
223
+ Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
189
224
  assert.ok(Option.isSome(node));
190
225
  expect(node.value.id).toEqual(exampleId(1));
191
226
  });
192
227
 
193
228
  test('connections updates', () => {
194
229
  const registry = Registry.make();
195
- const graph = new Graph({ registry });
230
+ const graph = Graph.make({ registry });
196
231
  assert.strictEqual(graph.connections(exampleId(1)), graph.connections(exampleId(1)));
197
232
  const childrenKey = graph.connections(exampleId(1));
198
233
 
@@ -202,9 +237,11 @@ describe('Graph', () => {
202
237
  });
203
238
  onTestFinished(() => cancel());
204
239
 
205
- graph.addNode({ id: exampleId(1), type: EXAMPLE_TYPE });
206
- graph.addNode({ id: exampleId(2), type: EXAMPLE_TYPE });
207
- graph.addEdge({ source: exampleId(1), target: exampleId(2) });
240
+ graph.pipe(
241
+ Graph.addNode({ id: exampleId(1), type: EXAMPLE_TYPE }),
242
+ Graph.addNode({ id: exampleId(2), type: EXAMPLE_TYPE }),
243
+ Graph.addEdge({ source: exampleId(1), target: exampleId(2) }),
244
+ );
208
245
 
209
246
  expect(count).toEqual(0);
210
247
  const children = registry.get(childrenKey);
@@ -213,55 +250,57 @@ describe('Graph', () => {
213
250
  expect(count).toEqual(1);
214
251
 
215
252
  // Updating an existing node fires an update.
216
- graph.addNode({ id: exampleId(2), type: EXAMPLE_TYPE, data: 'updated' });
253
+ Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE, data: 'updated' });
217
254
  expect(count).toEqual(2);
218
255
 
219
256
  // Adding a node with no changes does not fire an update.
220
- graph.addNode({ id: exampleId(2), type: EXAMPLE_TYPE, data: 'updated' });
257
+ Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE, data: 'updated' });
221
258
  expect(count).toEqual(2);
222
259
 
223
260
  // Adding an unconnected node does not fire an update.
224
- graph.addNode({ id: exampleId(3), type: EXAMPLE_TYPE });
261
+ Graph.addNode(graph, { id: exampleId(3), type: EXAMPLE_TYPE });
225
262
  expect(count).toEqual(2);
226
263
 
227
264
  // Connecting a node fires an update.
228
- graph.addEdge({ source: exampleId(1), target: exampleId(3) });
265
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(3) });
229
266
  expect(count).toEqual(3);
230
267
 
231
268
  // Adding an edge connected to nothing fires an update.
232
269
  // TODO(wittjosiah): Is there a way to avoid this?
233
- graph.addEdge({ source: exampleId(1), target: exampleId(4) });
270
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(4) });
234
271
  expect(count).toEqual(4);
235
272
 
236
273
  // Adding a node to an existing edge fires an update.
237
- graph.addNode({ id: exampleId(4), type: EXAMPLE_TYPE });
274
+ Graph.addNode(graph, { id: exampleId(4), type: EXAMPLE_TYPE });
238
275
  expect(count).toEqual(5);
239
276
 
240
277
  // Batching the edge and node updates fires a single update.
241
- Rx.batch(() => {
242
- graph.addEdge({ source: exampleId(1), target: exampleId(6) });
243
- graph.addNode({ id: exampleId(6), type: EXAMPLE_TYPE });
278
+ Atom.batch(() => {
279
+ graph.pipe(
280
+ Graph.addEdge({ source: exampleId(1), target: exampleId(6) }),
281
+ Graph.addNode({ id: exampleId(6), type: EXAMPLE_TYPE }),
282
+ );
244
283
  });
245
284
  expect(count).toEqual(6);
246
285
  });
247
286
 
248
287
  test('toJSON', () => {
249
- const graph = new Graph();
288
+ const graph = Graph.make();
250
289
 
251
- graph.addNode({
252
- id: ROOT_ID,
253
- type: ROOT_TYPE,
290
+ Graph.addNode(graph, {
291
+ id: Node.RootId,
292
+ type: Node.RootType,
254
293
  nodes: [
255
294
  { id: 'test1', type: 'test' },
256
295
  { id: 'test2', type: 'test' },
257
296
  ],
258
297
  });
259
- graph.addEdge({ source: 'test1', target: 'test2' });
298
+ Graph.addEdge(graph, { source: 'test1', target: 'test2' });
260
299
 
261
- const json = graph.toJSON();
300
+ const json = Graph.toJSON(graph);
262
301
  expect(json).to.deep.equal({
263
- id: ROOT_ID,
264
- type: ROOT_TYPE,
302
+ id: Node.RootId,
303
+ type: Node.RootType,
265
304
  nodes: [
266
305
  { id: 'test1', type: 'test', nodes: [{ id: 'test2', type: 'test' }] },
267
306
  { id: 'test2', type: 'test' },
@@ -271,17 +310,17 @@ describe('Graph', () => {
271
310
 
272
311
  test('subscribe to json', () => {
273
312
  const registry = Registry.make();
274
- const graph = new Graph({ registry });
313
+ const graph = Graph.make({ registry });
275
314
 
276
- graph.addNode({
277
- id: ROOT_ID,
278
- type: ROOT_TYPE,
315
+ Graph.addNode(graph, {
316
+ id: Node.RootId,
317
+ type: Node.RootType,
279
318
  nodes: [
280
319
  { id: 'test1', type: 'test' },
281
320
  { id: 'test2', type: 'test' },
282
321
  ],
283
322
  });
284
- graph.addEdge({ source: 'test1', target: 'test2' });
323
+ Graph.addEdge(graph, { source: 'test1', target: 'test2' });
285
324
 
286
325
  let json: any;
287
326
  const cancel = registry.subscribe(graph.json(), (_) => {
@@ -291,19 +330,19 @@ describe('Graph', () => {
291
330
 
292
331
  registry.get(graph.json());
293
332
  expect(json).to.deep.equal({
294
- id: ROOT_ID,
295
- type: ROOT_TYPE,
333
+ id: Node.RootId,
334
+ type: Node.RootType,
296
335
  nodes: [
297
336
  { id: 'test1', type: 'test', nodes: [{ id: 'test2', type: 'test' }] },
298
337
  { id: 'test2', type: 'test' },
299
338
  ],
300
339
  });
301
340
 
302
- graph.addNode({ id: 'test3', type: 'test' });
303
- graph.addEdge({ source: 'root', target: 'test3' });
341
+ Graph.addNode(graph, { id: 'test3', type: 'test' });
342
+ Graph.addEdge(graph, { source: 'root', target: 'test3' });
304
343
  expect(json).to.deep.equal({
305
- id: ROOT_ID,
306
- type: ROOT_TYPE,
344
+ id: Node.RootId,
345
+ type: Node.RootType,
307
346
  nodes: [
308
347
  { id: 'test1', type: 'test', nodes: [{ id: 'test2', type: 'test' }] },
309
348
  { id: 'test2', type: 'test' },
@@ -313,35 +352,50 @@ describe('Graph', () => {
313
352
  });
314
353
 
315
354
  test('get path', () => {
316
- const graph = new Graph();
317
- graph.addNode({
318
- id: ROOT_ID,
319
- type: ROOT_TYPE,
355
+ const graph = Graph.make();
356
+ Graph.addNode(graph, {
357
+ id: Node.RootId,
358
+ type: Node.RootType,
320
359
  nodes: [
321
360
  { id: exampleId(1), type: EXAMPLE_TYPE },
322
361
  { id: exampleId(2), type: EXAMPLE_TYPE },
323
362
  ],
324
363
  });
325
- graph.addEdge({ source: exampleId(1), target: exampleId(2) });
364
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
326
365
 
327
- expect(graph.getPath({ target: exampleId(2) }).pipe(Option.getOrNull)).to.deep.equal([
366
+ expect(Graph.getPath(graph, { target: exampleId(2) }).pipe(Option.getOrNull)).to.deep.equal([
328
367
  'root',
329
368
  exampleId(1),
330
369
  exampleId(2),
331
370
  ]);
332
- expect(graph.getPath({ source: exampleId(1), target: exampleId(2) }).pipe(Option.getOrNull)).to.deep.equal([
371
+ expect(Graph.getPath(graph, { source: exampleId(1), target: exampleId(2) }).pipe(Option.getOrNull)).to.deep.equal([
333
372
  exampleId(1),
334
373
  exampleId(2),
335
374
  ]);
336
- expect(graph.getPath({ source: exampleId(2), target: exampleId(1) }).pipe(Option.getOrNull)).to.be.null;
375
+ expect(Graph.getPath(graph, { source: exampleId(2), target: exampleId(1) }).pipe(Option.getOrNull)).to.be.null;
376
+ });
377
+
378
+ test('get path curried', () => {
379
+ const graph = Graph.make();
380
+ Graph.addNode(graph, {
381
+ id: Node.RootId,
382
+ type: Node.RootType,
383
+ nodes: [
384
+ { id: exampleId(1), type: EXAMPLE_TYPE },
385
+ { id: exampleId(2), type: EXAMPLE_TYPE },
386
+ ],
387
+ });
388
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
389
+ const path = graph.pipe(Graph.getPath({ target: exampleId(2) }));
390
+ expect(path.pipe(Option.getOrNull)).to.deep.equal(['root', exampleId(1), exampleId(2)]);
337
391
  });
338
392
 
339
393
  describe('traverse', () => {
340
394
  test('can be traversed', () => {
341
- const graph = new Graph();
342
- graph.addNode({
343
- id: ROOT_ID,
344
- type: ROOT_TYPE,
395
+ const graph = Graph.make();
396
+ Graph.addNode(graph, {
397
+ id: Node.RootId,
398
+ type: Node.RootType,
345
399
  nodes: [
346
400
  { id: 'test1', type: 'test' },
347
401
  { id: 'test2', type: 'test' },
@@ -349,7 +403,7 @@ describe('Graph', () => {
349
403
  });
350
404
 
351
405
  const nodes: string[] = [];
352
- graph.traverse({
406
+ Graph.traverse(graph, {
353
407
  visitor: (node) => {
354
408
  nodes.push(node.id);
355
409
  },
@@ -358,19 +412,19 @@ describe('Graph', () => {
358
412
  });
359
413
 
360
414
  test('traversal breaks cycles', () => {
361
- const graph = new Graph();
362
- graph.addNode({
363
- id: ROOT_ID,
364
- type: ROOT_TYPE,
415
+ const graph = Graph.make();
416
+ Graph.addNode(graph, {
417
+ id: Node.RootId,
418
+ type: Node.RootType,
365
419
  nodes: [
366
420
  { id: 'test1', type: 'test' },
367
421
  { id: 'test2', type: 'test' },
368
422
  ],
369
423
  });
370
- graph.addEdge({ source: 'test1', target: 'root' });
424
+ Graph.addEdge(graph, { source: 'test1', target: 'root' });
371
425
 
372
426
  const nodes: string[] = [];
373
- graph.traverse({
427
+ Graph.traverse(graph, {
374
428
  visitor: (node) => {
375
429
  nodes.push(node.id);
376
430
  },
@@ -379,10 +433,10 @@ describe('Graph', () => {
379
433
  });
380
434
 
381
435
  test('traversal can be started from any node', () => {
382
- const graph = new Graph();
383
- graph.addNode({
384
- id: ROOT_ID,
385
- type: ROOT_TYPE,
436
+ const graph = Graph.make();
437
+ Graph.addNode(graph, {
438
+ id: Node.RootId,
439
+ type: Node.RootType,
386
440
  nodes: [
387
441
  {
388
442
  id: 'test1',
@@ -393,7 +447,7 @@ describe('Graph', () => {
393
447
  });
394
448
 
395
449
  const nodes: string[] = [];
396
- graph.traverse({
450
+ Graph.traverse(graph, {
397
451
  source: 'test2',
398
452
  visitor: (node) => {
399
453
  nodes.push(node.id);
@@ -403,10 +457,10 @@ describe('Graph', () => {
403
457
  });
404
458
 
405
459
  test('traversal can follow inbound edges', () => {
406
- const graph = new Graph();
407
- graph.addNode({
408
- id: ROOT_ID,
409
- type: ROOT_TYPE,
460
+ const graph = Graph.make();
461
+ Graph.addNode(graph, {
462
+ id: Node.RootId,
463
+ type: Node.RootType,
410
464
  nodes: [
411
465
  {
412
466
  id: 'test1',
@@ -417,7 +471,7 @@ describe('Graph', () => {
417
471
  });
418
472
 
419
473
  const nodes: string[] = [];
420
- graph.traverse({
474
+ Graph.traverse(graph, {
421
475
  source: 'test2',
422
476
  relation: 'inbound',
423
477
  visitor: (node) => {
@@ -428,10 +482,10 @@ describe('Graph', () => {
428
482
  });
429
483
 
430
484
  test('traversal can be terminated early', () => {
431
- const graph = new Graph();
432
- graph.addNode({
433
- id: ROOT_ID,
434
- type: ROOT_TYPE,
485
+ const graph = Graph.make();
486
+ Graph.addNode(graph, {
487
+ id: Node.RootId,
488
+ type: Node.RootType,
435
489
  nodes: [
436
490
  { id: 'test1', type: 'test' },
437
491
  { id: 'test2', type: 'test' },
@@ -439,7 +493,7 @@ describe('Graph', () => {
439
493
  });
440
494
 
441
495
  const nodes: string[] = [];
442
- graph.traverse({
496
+ Graph.traverse(graph, {
443
497
  visitor: (node) => {
444
498
  if (nodes.length === 2) {
445
499
  return false;
@@ -450,5 +504,144 @@ describe('Graph', () => {
450
504
  });
451
505
  expect(nodes).to.deep.equal(['root', 'test1']);
452
506
  });
507
+
508
+ test('traverse curried', () => {
509
+ const graph = Graph.make();
510
+ Graph.addNode(graph, {
511
+ id: Node.RootId,
512
+ type: Node.RootType,
513
+ nodes: [{ id: 'test1', type: 'test' }],
514
+ });
515
+ Graph.addNode(graph, { id: 'test2', type: 'test' });
516
+ Graph.addEdge(graph, { source: 'test1', target: 'test2' });
517
+ const nodes: string[] = [];
518
+ graph.pipe(
519
+ Graph.traverse({
520
+ source: Node.RootId,
521
+ visitor: (node, _path) => {
522
+ nodes.push(node.id);
523
+ },
524
+ }),
525
+ );
526
+ expect(nodes).to.deep.equal(['root', 'test1', 'test2']);
527
+ });
528
+ });
529
+
530
+ test('add nodes curried', () => {
531
+ const registry = Registry.make();
532
+ const graph = Graph.make({ registry });
533
+ const result = graph.pipe(
534
+ Graph.addNodes([
535
+ { id: exampleId(1), type: EXAMPLE_TYPE },
536
+ { id: exampleId(2), type: EXAMPLE_TYPE },
537
+ ]),
538
+ );
539
+ expect(result).toEqual(graph);
540
+ const node1 = registry.get(graph.node(exampleId(1)));
541
+ const node2 = registry.get(graph.node(exampleId(2)));
542
+ assert.ok(Option.isSome(node1));
543
+ assert.ok(Option.isSome(node2));
544
+ expect(node1.value.id).toEqual(exampleId(1));
545
+ expect(node2.value.id).toEqual(exampleId(2));
546
+ });
547
+
548
+ test('add edges curried', () => {
549
+ const registry = Registry.make();
550
+ const graph = Graph.make({ registry });
551
+ Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
552
+ Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
553
+ Graph.addNode(graph, { id: exampleId(3), type: EXAMPLE_TYPE });
554
+ const result = graph.pipe(
555
+ Graph.addEdges([
556
+ { source: exampleId(1), target: exampleId(2) },
557
+ { source: exampleId(1), target: exampleId(3) },
558
+ ]),
559
+ );
560
+ expect(result).toEqual(graph);
561
+ const edges = registry.get(graph.edges(exampleId(1)));
562
+ expect(edges.outbound).to.have.length(2);
563
+ expect(edges.outbound).to.include(exampleId(2));
564
+ expect(edges.outbound).to.include(exampleId(3));
565
+ });
566
+
567
+ test('remove nodes curried', () => {
568
+ const registry = Registry.make();
569
+ const graph = Graph.make({ registry });
570
+ Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
571
+ Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
572
+ const result = graph.pipe(Graph.removeNodes([exampleId(1), exampleId(2)]));
573
+ expect(result).toEqual(graph);
574
+ const node1 = registry.get(graph.node(exampleId(1)));
575
+ const node2 = registry.get(graph.node(exampleId(2)));
576
+ assert.ok(Option.isNone(node1));
577
+ assert.ok(Option.isNone(node2));
578
+ });
579
+
580
+ test('remove edges curried', () => {
581
+ const registry = Registry.make();
582
+ const graph = Graph.make({ registry });
583
+ Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
584
+ Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
585
+ Graph.addNode(graph, { id: exampleId(3), type: EXAMPLE_TYPE });
586
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
587
+ Graph.addEdge(graph, { source: exampleId(1), target: exampleId(3) });
588
+ const result = graph.pipe(
589
+ Graph.removeEdges([
590
+ { source: exampleId(1), target: exampleId(2) },
591
+ { source: exampleId(1), target: exampleId(3) },
592
+ ]),
593
+ );
594
+ expect(result).toEqual(graph);
595
+ const edges = registry.get(graph.edges(exampleId(1)));
596
+ expect(edges.outbound).to.have.length(0);
597
+ });
598
+
599
+ test('expand curried', async () => {
600
+ const registry = Registry.make();
601
+ const builder = GraphBuilder.make({ registry });
602
+ const graph = builder.graph;
603
+ let expandCalled = false;
604
+ GraphBuilder.addExtension(
605
+ builder,
606
+ GraphBuilder.createExtensionRaw({
607
+ id: 'test',
608
+ connector: () => {
609
+ expandCalled = true;
610
+ return Atom.make([]);
611
+ },
612
+ }),
613
+ );
614
+ await graph.pipe(Graph.expand(Node.RootId));
615
+ expect(expandCalled).to.be.true;
616
+ });
617
+
618
+ test('initialize curried', async () => {
619
+ const registry = Registry.make();
620
+ const builder = GraphBuilder.make({ registry });
621
+ const graph = builder.graph;
622
+ let initializeCalled = false;
623
+ GraphBuilder.addExtension(
624
+ builder,
625
+ GraphBuilder.createExtensionRaw({
626
+ id: 'test',
627
+ resolver: () => {
628
+ initializeCalled = true;
629
+ return Atom.make({ id: EXAMPLE_ID, type: EXAMPLE_TYPE });
630
+ },
631
+ }),
632
+ );
633
+ await graph.pipe(Graph.initialize(EXAMPLE_ID));
634
+ expect(initializeCalled).to.be.true;
635
+ });
636
+
637
+ test('waitForPath curried', async () => {
638
+ const graph = Graph.make();
639
+ Graph.addNode(graph, {
640
+ id: Node.RootId,
641
+ type: Node.RootType,
642
+ nodes: [{ id: exampleId(1), type: EXAMPLE_TYPE }],
643
+ });
644
+ const path = await graph.pipe(Graph.waitForPath({ target: exampleId(1) }, { timeout: 1000 }));
645
+ expect(path).to.deep.equal(['root', exampleId(1)]);
453
646
  });
454
647
  });