@dxos/app-graph 0.8.3 → 0.8.4-main.03d5cd7b56
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.
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/neutral/chunk-WJJ5KEOH.mjs +1477 -0
- package/dist/lib/neutral/chunk-WJJ5KEOH.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +40 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/scheduler.mjs +15 -0
- package/dist/lib/neutral/scheduler.mjs.map +7 -0
- package/dist/lib/neutral/testing/index.mjs +40 -0
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/atoms.d.ts +8 -0
- package/dist/types/src/atoms.d.ts.map +1 -0
- package/dist/types/src/graph-builder.d.ts +117 -60
- package/dist/types/src/graph-builder.d.ts.map +1 -1
- package/dist/types/src/graph.d.ts +188 -218
- package/dist/types/src/graph.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +7 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/node-matcher.d.ts +244 -0
- package/dist/types/src/node-matcher.d.ts.map +1 -0
- package/dist/types/src/node-matcher.test.d.ts +2 -0
- package/dist/types/src/node-matcher.test.d.ts.map +1 -0
- package/dist/types/src/node.d.ts +50 -5
- package/dist/types/src/node.d.ts.map +1 -1
- package/dist/types/src/scheduler.browser.d.ts +2 -0
- package/dist/types/src/scheduler.browser.d.ts.map +1 -0
- package/dist/types/src/scheduler.d.ts +8 -0
- package/dist/types/src/scheduler.d.ts.map +1 -0
- package/dist/types/src/stories/EchoGraph.stories.d.ts +6 -13
- package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/setup-graph-builder.d.ts +31 -0
- package/dist/types/src/testing/setup-graph-builder.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +40 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +53 -42
- package/src/atoms.ts +25 -0
- package/src/graph-builder.test.ts +1193 -126
- package/src/graph-builder.ts +753 -264
- package/src/graph.test.ts +451 -123
- package/src/graph.ts +1057 -407
- package/src/index.ts +10 -3
- package/src/node-matcher.test.ts +301 -0
- package/src/node-matcher.ts +314 -0
- package/src/node.ts +83 -7
- package/src/scheduler.browser.ts +5 -0
- package/src/scheduler.ts +17 -0
- package/src/stories/EchoGraph.stories.tsx +178 -255
- package/src/stories/Tree.tsx +1 -1
- package/src/testing/index.ts +5 -0
- package/src/testing/setup-graph-builder.ts +41 -0
- package/src/util.ts +101 -0
- package/dist/lib/browser/index.mjs +0 -778
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node/index.cjs +0 -816
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -780
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/types/src/experimental/graph-projections.test.d.ts +0 -25
- package/dist/types/src/experimental/graph-projections.test.d.ts.map +0 -1
- package/dist/types/src/signals-integration.test.d.ts +0 -2
- package/dist/types/src/signals-integration.test.d.ts.map +0 -1
- package/dist/types/src/testing.d.ts +0 -5
- package/dist/types/src/testing.d.ts.map +0 -1
- package/src/experimental/graph-projections.test.ts +0 -56
- package/src/signals-integration.test.ts +0 -218
- package/src/testing.ts +0 -20
package/src/graph.test.ts
CHANGED
|
@@ -2,32 +2,37 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { Atom, Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Option from 'effect/Option';
|
|
7
7
|
import { assert, describe, expect, onTestFinished, test } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import
|
|
10
|
-
import
|
|
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);
|
|
14
|
-
const EXAMPLE_TYPE = 'dxos.
|
|
15
|
+
const EXAMPLE_TYPE = 'org.dxos.type.example';
|
|
16
|
+
const CHILD_RELATION_KEY = Graph.relationKey('child');
|
|
17
|
+
const CHILD_INBOUND_RELATION_KEY = Graph.relationKey(Node.childRelation('inbound'));
|
|
18
|
+
const ACTIONS_RELATION_KEY = Graph.relationKey('action');
|
|
19
|
+
const ACTIONS_INBOUND_RELATION_KEY = Graph.relationKey(Node.actionRelation('inbound'));
|
|
15
20
|
|
|
16
21
|
describe('Graph', () => {
|
|
17
22
|
test('getGraph', () => {
|
|
18
23
|
const registry = Registry.make();
|
|
19
|
-
const graph =
|
|
20
|
-
const root = registry.get(graph.node(
|
|
24
|
+
const graph = Graph.make({ registry });
|
|
25
|
+
const root = registry.get(graph.node(Node.RootId));
|
|
21
26
|
assert.ok(Option.isSome(root));
|
|
22
|
-
expect(root.value.id).toEqual(
|
|
23
|
-
expect(root.value.type).toEqual(
|
|
24
|
-
expect(getGraph(root.value)).toEqual(graph);
|
|
27
|
+
expect(root.value.id).toEqual(Node.RootId);
|
|
28
|
+
expect(root.value.type).toEqual(Node.RootType);
|
|
29
|
+
expect(Graph.getGraph(root.value)).toEqual(graph);
|
|
25
30
|
});
|
|
26
31
|
|
|
27
32
|
test('add node', () => {
|
|
28
33
|
const registry = Registry.make();
|
|
29
|
-
const graph =
|
|
30
|
-
|
|
34
|
+
const graph = Graph.make({ registry });
|
|
35
|
+
Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
|
|
31
36
|
const node = registry.get(graph.node(EXAMPLE_ID));
|
|
32
37
|
assert.ok(Option.isSome(node));
|
|
33
38
|
expect(node.value.id).toEqual(EXAMPLE_ID);
|
|
@@ -36,9 +41,20 @@ describe('Graph', () => {
|
|
|
36
41
|
expect(node.value.properties).toEqual({});
|
|
37
42
|
});
|
|
38
43
|
|
|
44
|
+
test('add node curried', () => {
|
|
45
|
+
const registry = Registry.make();
|
|
46
|
+
const graph = Graph.make({ registry });
|
|
47
|
+
const result = graph.pipe(Graph.addNode({ id: EXAMPLE_ID, type: EXAMPLE_TYPE }));
|
|
48
|
+
expect(result).toEqual(graph);
|
|
49
|
+
const node = registry.get(graph.node(EXAMPLE_ID));
|
|
50
|
+
assert.ok(Option.isSome(node));
|
|
51
|
+
expect(node.value.id).toEqual(EXAMPLE_ID);
|
|
52
|
+
expect(node.value.type).toEqual(EXAMPLE_TYPE);
|
|
53
|
+
});
|
|
54
|
+
|
|
39
55
|
test('add nodes updates existing nodes', () => {
|
|
40
56
|
const registry = Registry.make();
|
|
41
|
-
const graph =
|
|
57
|
+
const graph = Graph.make({ registry });
|
|
42
58
|
const nodeKey = graph.node(EXAMPLE_ID);
|
|
43
59
|
|
|
44
60
|
let count = 0;
|
|
@@ -53,7 +69,7 @@ describe('Graph', () => {
|
|
|
53
69
|
expect(registry.get(nodeKey)).toEqual(Option.none());
|
|
54
70
|
expect(count).toEqual(1);
|
|
55
71
|
|
|
56
|
-
|
|
72
|
+
Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
|
|
57
73
|
const node = registry.get(nodeKey);
|
|
58
74
|
assert.ok(Option.isSome(node));
|
|
59
75
|
expect(node.value.id).toEqual(EXAMPLE_ID);
|
|
@@ -62,13 +78,13 @@ describe('Graph', () => {
|
|
|
62
78
|
expect(node.value.properties).toEqual({});
|
|
63
79
|
expect(count).toEqual(2);
|
|
64
80
|
|
|
65
|
-
|
|
81
|
+
Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
|
|
66
82
|
expect(count).toEqual(2);
|
|
67
83
|
});
|
|
68
84
|
|
|
69
85
|
test('remove node', () => {
|
|
70
86
|
const registry = Registry.make();
|
|
71
|
-
const graph =
|
|
87
|
+
const graph = Graph.make({ registry });
|
|
72
88
|
|
|
73
89
|
{
|
|
74
90
|
const node = registry.get(graph.node(EXAMPLE_ID));
|
|
@@ -76,125 +92,220 @@ describe('Graph', () => {
|
|
|
76
92
|
}
|
|
77
93
|
|
|
78
94
|
{
|
|
79
|
-
|
|
95
|
+
Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
|
|
80
96
|
const node = registry.get(graph.node(EXAMPLE_ID));
|
|
81
97
|
expect(Option.isSome(node)).toEqual(true);
|
|
82
98
|
}
|
|
83
99
|
|
|
84
100
|
{
|
|
85
|
-
|
|
101
|
+
Graph.removeNode(graph, EXAMPLE_ID);
|
|
86
102
|
const node = registry.get(graph.node(EXAMPLE_ID));
|
|
87
103
|
expect(Option.isNone(node)).toEqual(true);
|
|
88
104
|
}
|
|
89
105
|
});
|
|
90
106
|
|
|
107
|
+
test('remove node with edges=true removes default inbound counterparts', () => {
|
|
108
|
+
const registry = Registry.make();
|
|
109
|
+
const graph = Graph.make({ registry });
|
|
110
|
+
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
111
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
112
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
113
|
+
|
|
114
|
+
Graph.removeNode(graph, exampleId(2), true);
|
|
115
|
+
|
|
116
|
+
const sourceEdges = registry.get(graph.edges(exampleId(1)));
|
|
117
|
+
expect(sourceEdges[CHILD_RELATION_KEY]).toEqual([]);
|
|
118
|
+
expect(sourceEdges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('remove node with edges=true removes typed inbound and outbound counterparts', () => {
|
|
122
|
+
const registry = Registry.make();
|
|
123
|
+
const graph = Graph.make({ registry });
|
|
124
|
+
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
125
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
126
|
+
Graph.addNode(graph, { id: exampleId(3), type: EXAMPLE_TYPE });
|
|
127
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'action' });
|
|
128
|
+
Graph.addEdge(graph, { source: exampleId(2), target: exampleId(3), relation: 'action' });
|
|
129
|
+
|
|
130
|
+
Graph.removeNode(graph, exampleId(2), true);
|
|
131
|
+
|
|
132
|
+
const sourceEdges = registry.get(graph.edges(exampleId(1)));
|
|
133
|
+
const targetEdges = registry.get(graph.edges(exampleId(3)));
|
|
134
|
+
expect(sourceEdges[ACTIONS_RELATION_KEY]).toEqual([]);
|
|
135
|
+
expect(sourceEdges[ACTIONS_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
136
|
+
expect(targetEdges[ACTIONS_RELATION_KEY]).toBeUndefined();
|
|
137
|
+
expect(targetEdges[ACTIONS_INBOUND_RELATION_KEY]).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('remove node curried', () => {
|
|
141
|
+
const registry = Registry.make();
|
|
142
|
+
const graph = Graph.make({ registry });
|
|
143
|
+
Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
|
|
144
|
+
const result = graph.pipe(Graph.removeNode(EXAMPLE_ID));
|
|
145
|
+
expect(result).toEqual(graph);
|
|
146
|
+
const node = registry.get(graph.node(EXAMPLE_ID));
|
|
147
|
+
assert.ok(Option.isNone(node));
|
|
148
|
+
});
|
|
149
|
+
|
|
91
150
|
test('onNodeChanged', () => {
|
|
92
|
-
const graph =
|
|
151
|
+
const graph = Graph.make();
|
|
93
152
|
|
|
94
|
-
let node: Option.Option<Node> = Option.none();
|
|
153
|
+
let node: Option.Option<Node.Node> = Option.none();
|
|
95
154
|
graph.onNodeChanged.on(({ node: newNode }) => {
|
|
96
155
|
node = newNode;
|
|
97
156
|
});
|
|
98
157
|
|
|
99
|
-
|
|
158
|
+
Graph.addNode(graph, { id: EXAMPLE_ID, type: EXAMPLE_TYPE });
|
|
100
159
|
assert.ok(Option.isSome(node));
|
|
101
160
|
expect(node.value.id).toEqual(EXAMPLE_ID);
|
|
102
161
|
expect(node.value.type).toEqual(EXAMPLE_TYPE);
|
|
103
162
|
|
|
104
|
-
|
|
163
|
+
Graph.removeNode(graph, EXAMPLE_ID);
|
|
105
164
|
expect(node.pipe(Option.getOrNull)).toEqual(null);
|
|
106
165
|
});
|
|
107
166
|
|
|
108
167
|
test('add edge', () => {
|
|
109
168
|
const registry = Registry.make();
|
|
110
|
-
const graph =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
169
|
+
const graph = Graph.make({ registry });
|
|
170
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
171
|
+
|
|
172
|
+
{
|
|
173
|
+
const edges = registry.get(graph.edges(exampleId(1)));
|
|
174
|
+
expect(edges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
175
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([exampleId(2)]);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
{
|
|
179
|
+
const edges = registry.get(graph.edges(exampleId(2)));
|
|
180
|
+
expect(edges[CHILD_INBOUND_RELATION_KEY]).toEqual([exampleId(1)]);
|
|
181
|
+
expect(edges[CHILD_RELATION_KEY]).toBeUndefined();
|
|
182
|
+
}
|
|
115
183
|
});
|
|
116
184
|
|
|
117
185
|
test('add edges is idempotent', () => {
|
|
118
186
|
const registry = Registry.make();
|
|
119
|
-
const graph =
|
|
120
|
-
|
|
121
|
-
|
|
187
|
+
const graph = Graph.make({ registry });
|
|
188
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
189
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
122
190
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
123
|
-
expect(edges
|
|
124
|
-
expect(edges
|
|
191
|
+
expect(edges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
192
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([exampleId(2)]);
|
|
125
193
|
});
|
|
126
194
|
|
|
127
195
|
test('sort edges', () => {
|
|
128
196
|
const registry = Registry.make();
|
|
129
|
-
const graph =
|
|
197
|
+
const graph = Graph.make({ registry });
|
|
130
198
|
|
|
131
199
|
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
200
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
201
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(3), relation: 'child' });
|
|
202
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(4), relation: 'child' });
|
|
135
203
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
136
|
-
expect(edges
|
|
204
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([exampleId(2), exampleId(3), exampleId(4)]);
|
|
137
205
|
}
|
|
138
206
|
|
|
139
207
|
{
|
|
140
|
-
|
|
208
|
+
Graph.sortEdges(graph, exampleId(1), 'child', [exampleId(3), exampleId(2)]);
|
|
141
209
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
142
|
-
expect(edges
|
|
210
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([exampleId(3), exampleId(2), exampleId(4)]);
|
|
143
211
|
}
|
|
144
212
|
});
|
|
145
213
|
|
|
146
214
|
test('remove edge', () => {
|
|
147
215
|
const registry = Registry.make();
|
|
148
|
-
const graph =
|
|
216
|
+
const graph = Graph.make({ registry });
|
|
149
217
|
|
|
150
218
|
{
|
|
151
|
-
|
|
219
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
152
220
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
153
|
-
expect(edges
|
|
154
|
-
expect(edges
|
|
221
|
+
expect(edges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
222
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([exampleId(2)]);
|
|
155
223
|
}
|
|
156
224
|
|
|
157
225
|
{
|
|
158
|
-
|
|
226
|
+
Graph.removeEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
159
227
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
160
|
-
expect(edges
|
|
161
|
-
expect(edges
|
|
228
|
+
expect(edges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
229
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([]);
|
|
162
230
|
}
|
|
163
231
|
});
|
|
164
232
|
|
|
233
|
+
test('remove edge curried', () => {
|
|
234
|
+
const registry = Registry.make();
|
|
235
|
+
const graph = Graph.make({ registry });
|
|
236
|
+
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
237
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
238
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
239
|
+
const result = graph.pipe(Graph.removeEdge({ source: exampleId(1), target: exampleId(2), relation: 'child' }));
|
|
240
|
+
expect(result).toEqual(graph);
|
|
241
|
+
const edges = registry.get(graph.edges(exampleId(1)));
|
|
242
|
+
expect(edges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
243
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([]);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test('add edge with custom relation creates typed inbound inverse', () => {
|
|
247
|
+
const registry = Registry.make();
|
|
248
|
+
const graph = Graph.make({ registry });
|
|
249
|
+
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
250
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
251
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'action' });
|
|
252
|
+
const sourceEdges = registry.get(graph.edges(exampleId(1)));
|
|
253
|
+
expect(sourceEdges[ACTIONS_RELATION_KEY]).toEqual([exampleId(2)]);
|
|
254
|
+
expect(sourceEdges[CHILD_RELATION_KEY]).toBeUndefined();
|
|
255
|
+
const targetEdges = registry.get(graph.edges(exampleId(2)));
|
|
256
|
+
expect(targetEdges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
257
|
+
expect(targetEdges[ACTIONS_INBOUND_RELATION_KEY]).toEqual([exampleId(1)]);
|
|
258
|
+
const reverseConnections = registry.get(graph.connections(exampleId(2), Node.actionRelation('inbound')));
|
|
259
|
+
expect(reverseConnections.map(({ id }) => id)).toEqual([exampleId(1)]);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('remove edge with custom relation removes typed inbound inverse', () => {
|
|
263
|
+
const registry = Registry.make();
|
|
264
|
+
const graph = Graph.make({ registry });
|
|
265
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'action' });
|
|
266
|
+
Graph.removeEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'action' });
|
|
267
|
+
const sourceEdges = registry.get(graph.edges(exampleId(1)));
|
|
268
|
+
expect(sourceEdges[ACTIONS_RELATION_KEY]).toEqual([]);
|
|
269
|
+
const targetEdges = registry.get(graph.edges(exampleId(2)));
|
|
270
|
+
expect(targetEdges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
271
|
+
expect(targetEdges[CHILD_RELATION_KEY]).toBeUndefined();
|
|
272
|
+
expect(targetEdges[ACTIONS_RELATION_KEY]).toBeUndefined();
|
|
273
|
+
expect(targetEdges[ACTIONS_INBOUND_RELATION_KEY]).toEqual([]);
|
|
274
|
+
});
|
|
275
|
+
|
|
165
276
|
test('get connections', () => {
|
|
166
277
|
const registry = Registry.make();
|
|
167
|
-
const graph =
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const nodes = registry.get(graph.connections(exampleId(1)));
|
|
278
|
+
const graph = Graph.make({ registry });
|
|
279
|
+
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
280
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
281
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
282
|
+
const nodes = registry.get(graph.connections(exampleId(1), 'child'));
|
|
172
283
|
expect(nodes).has.length(1);
|
|
173
284
|
expect(nodes[0].id).toEqual(exampleId(2));
|
|
174
285
|
});
|
|
175
286
|
|
|
176
287
|
test('can subscribe to a node before it exists', async () => {
|
|
177
288
|
const registry = Registry.make();
|
|
178
|
-
const graph =
|
|
289
|
+
const graph = Graph.make({ registry });
|
|
179
290
|
const nodeKey = graph.node(exampleId(1));
|
|
180
291
|
|
|
181
|
-
let node: Option.Option<Node> = Option.none();
|
|
292
|
+
let node: Option.Option<Node.Node> = Option.none();
|
|
182
293
|
const cancel = registry.subscribe(nodeKey, (n) => {
|
|
183
294
|
node = n;
|
|
184
295
|
});
|
|
185
296
|
onTestFinished(() => cancel());
|
|
186
297
|
|
|
187
298
|
expect(node).toEqual(Option.none());
|
|
188
|
-
|
|
299
|
+
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
189
300
|
assert.ok(Option.isSome(node));
|
|
190
301
|
expect(node.value.id).toEqual(exampleId(1));
|
|
191
302
|
});
|
|
192
303
|
|
|
193
304
|
test('connections updates', () => {
|
|
194
305
|
const registry = Registry.make();
|
|
195
|
-
const graph =
|
|
196
|
-
assert.strictEqual(graph.connections(exampleId(1)), graph.connections(exampleId(1)));
|
|
197
|
-
const childrenKey = graph.connections(exampleId(1));
|
|
306
|
+
const graph = Graph.make({ registry });
|
|
307
|
+
assert.strictEqual(graph.connections(exampleId(1), 'child'), graph.connections(exampleId(1), 'child'));
|
|
308
|
+
const childrenKey = graph.connections(exampleId(1), 'child');
|
|
198
309
|
|
|
199
310
|
let count = 0;
|
|
200
311
|
const cancel = registry.subscribe(childrenKey, (_) => {
|
|
@@ -202,9 +313,11 @@ describe('Graph', () => {
|
|
|
202
313
|
});
|
|
203
314
|
onTestFinished(() => cancel());
|
|
204
315
|
|
|
205
|
-
graph.
|
|
206
|
-
|
|
207
|
-
|
|
316
|
+
graph.pipe(
|
|
317
|
+
Graph.addNode({ id: exampleId(1), type: EXAMPLE_TYPE }),
|
|
318
|
+
Graph.addNode({ id: exampleId(2), type: EXAMPLE_TYPE }),
|
|
319
|
+
Graph.addEdge({ source: exampleId(1), target: exampleId(2), relation: 'child' }),
|
|
320
|
+
);
|
|
208
321
|
|
|
209
322
|
expect(count).toEqual(0);
|
|
210
323
|
const children = registry.get(childrenKey);
|
|
@@ -213,55 +326,57 @@ describe('Graph', () => {
|
|
|
213
326
|
expect(count).toEqual(1);
|
|
214
327
|
|
|
215
328
|
// Updating an existing node fires an update.
|
|
216
|
-
|
|
329
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE, data: 'updated' });
|
|
217
330
|
expect(count).toEqual(2);
|
|
218
331
|
|
|
219
332
|
// Adding a node with no changes does not fire an update.
|
|
220
|
-
|
|
333
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE, data: 'updated' });
|
|
221
334
|
expect(count).toEqual(2);
|
|
222
335
|
|
|
223
336
|
// Adding an unconnected node does not fire an update.
|
|
224
|
-
|
|
337
|
+
Graph.addNode(graph, { id: exampleId(3), type: EXAMPLE_TYPE });
|
|
225
338
|
expect(count).toEqual(2);
|
|
226
339
|
|
|
227
340
|
// Connecting a node fires an update.
|
|
228
|
-
|
|
341
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(3), relation: 'child' });
|
|
229
342
|
expect(count).toEqual(3);
|
|
230
343
|
|
|
231
344
|
// Adding an edge connected to nothing fires an update.
|
|
232
345
|
// TODO(wittjosiah): Is there a way to avoid this?
|
|
233
|
-
|
|
346
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(4), relation: 'child' });
|
|
234
347
|
expect(count).toEqual(4);
|
|
235
348
|
|
|
236
349
|
// Adding a node to an existing edge fires an update.
|
|
237
|
-
|
|
350
|
+
Graph.addNode(graph, { id: exampleId(4), type: EXAMPLE_TYPE });
|
|
238
351
|
expect(count).toEqual(5);
|
|
239
352
|
|
|
240
353
|
// Batching the edge and node updates fires a single update.
|
|
241
|
-
|
|
242
|
-
graph.
|
|
243
|
-
|
|
354
|
+
Atom.batch(() => {
|
|
355
|
+
graph.pipe(
|
|
356
|
+
Graph.addEdge({ source: exampleId(1), target: exampleId(6), relation: 'child' }),
|
|
357
|
+
Graph.addNode({ id: exampleId(6), type: EXAMPLE_TYPE }),
|
|
358
|
+
);
|
|
244
359
|
});
|
|
245
360
|
expect(count).toEqual(6);
|
|
246
361
|
});
|
|
247
362
|
|
|
248
363
|
test('toJSON', () => {
|
|
249
|
-
const graph =
|
|
364
|
+
const graph = Graph.make();
|
|
250
365
|
|
|
251
|
-
|
|
252
|
-
id:
|
|
253
|
-
type:
|
|
366
|
+
Graph.addNode(graph, {
|
|
367
|
+
id: Node.RootId,
|
|
368
|
+
type: Node.RootType,
|
|
254
369
|
nodes: [
|
|
255
370
|
{ id: 'test1', type: 'test' },
|
|
256
371
|
{ id: 'test2', type: 'test' },
|
|
257
372
|
],
|
|
258
373
|
});
|
|
259
|
-
|
|
374
|
+
Graph.addEdge(graph, { source: 'test1', target: 'test2', relation: 'child' });
|
|
260
375
|
|
|
261
|
-
const json =
|
|
376
|
+
const json = Graph.toJSON(graph);
|
|
262
377
|
expect(json).to.deep.equal({
|
|
263
|
-
id:
|
|
264
|
-
type:
|
|
378
|
+
id: Node.RootId,
|
|
379
|
+
type: Node.RootType,
|
|
265
380
|
nodes: [
|
|
266
381
|
{ id: 'test1', type: 'test', nodes: [{ id: 'test2', type: 'test' }] },
|
|
267
382
|
{ id: 'test2', type: 'test' },
|
|
@@ -271,17 +386,17 @@ describe('Graph', () => {
|
|
|
271
386
|
|
|
272
387
|
test('subscribe to json', () => {
|
|
273
388
|
const registry = Registry.make();
|
|
274
|
-
const graph =
|
|
389
|
+
const graph = Graph.make({ registry });
|
|
275
390
|
|
|
276
|
-
|
|
277
|
-
id:
|
|
278
|
-
type:
|
|
391
|
+
Graph.addNode(graph, {
|
|
392
|
+
id: Node.RootId,
|
|
393
|
+
type: Node.RootType,
|
|
279
394
|
nodes: [
|
|
280
395
|
{ id: 'test1', type: 'test' },
|
|
281
396
|
{ id: 'test2', type: 'test' },
|
|
282
397
|
],
|
|
283
398
|
});
|
|
284
|
-
|
|
399
|
+
Graph.addEdge(graph, { source: 'test1', target: 'test2', relation: 'child' });
|
|
285
400
|
|
|
286
401
|
let json: any;
|
|
287
402
|
const cancel = registry.subscribe(graph.json(), (_) => {
|
|
@@ -291,19 +406,19 @@ describe('Graph', () => {
|
|
|
291
406
|
|
|
292
407
|
registry.get(graph.json());
|
|
293
408
|
expect(json).to.deep.equal({
|
|
294
|
-
id:
|
|
295
|
-
type:
|
|
409
|
+
id: Node.RootId,
|
|
410
|
+
type: Node.RootType,
|
|
296
411
|
nodes: [
|
|
297
412
|
{ id: 'test1', type: 'test', nodes: [{ id: 'test2', type: 'test' }] },
|
|
298
413
|
{ id: 'test2', type: 'test' },
|
|
299
414
|
],
|
|
300
415
|
});
|
|
301
416
|
|
|
302
|
-
|
|
303
|
-
|
|
417
|
+
Graph.addNode(graph, { id: 'test3', type: 'test' });
|
|
418
|
+
Graph.addEdge(graph, { source: 'root', target: 'test3', relation: 'child' });
|
|
304
419
|
expect(json).to.deep.equal({
|
|
305
|
-
id:
|
|
306
|
-
type:
|
|
420
|
+
id: Node.RootId,
|
|
421
|
+
type: Node.RootType,
|
|
307
422
|
nodes: [
|
|
308
423
|
{ id: 'test1', type: 'test', nodes: [{ id: 'test2', type: 'test' }] },
|
|
309
424
|
{ id: 'test2', type: 'test' },
|
|
@@ -313,35 +428,50 @@ describe('Graph', () => {
|
|
|
313
428
|
});
|
|
314
429
|
|
|
315
430
|
test('get path', () => {
|
|
316
|
-
const graph =
|
|
317
|
-
|
|
318
|
-
id:
|
|
319
|
-
type:
|
|
431
|
+
const graph = Graph.make();
|
|
432
|
+
Graph.addNode(graph, {
|
|
433
|
+
id: Node.RootId,
|
|
434
|
+
type: Node.RootType,
|
|
320
435
|
nodes: [
|
|
321
436
|
{ id: exampleId(1), type: EXAMPLE_TYPE },
|
|
322
437
|
{ id: exampleId(2), type: EXAMPLE_TYPE },
|
|
323
438
|
],
|
|
324
439
|
});
|
|
325
|
-
|
|
440
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
326
441
|
|
|
327
|
-
expect(
|
|
442
|
+
expect(Graph.getPath(graph, { target: exampleId(2) }).pipe(Option.getOrNull)).to.deep.equal([
|
|
328
443
|
'root',
|
|
329
444
|
exampleId(1),
|
|
330
445
|
exampleId(2),
|
|
331
446
|
]);
|
|
332
|
-
expect(
|
|
447
|
+
expect(Graph.getPath(graph, { source: exampleId(1), target: exampleId(2) }).pipe(Option.getOrNull)).to.deep.equal([
|
|
333
448
|
exampleId(1),
|
|
334
449
|
exampleId(2),
|
|
335
450
|
]);
|
|
336
|
-
expect(
|
|
451
|
+
expect(Graph.getPath(graph, { source: exampleId(2), target: exampleId(1) }).pipe(Option.getOrNull)).to.be.null;
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
test('get path curried', () => {
|
|
455
|
+
const graph = Graph.make();
|
|
456
|
+
Graph.addNode(graph, {
|
|
457
|
+
id: Node.RootId,
|
|
458
|
+
type: Node.RootType,
|
|
459
|
+
nodes: [
|
|
460
|
+
{ id: exampleId(1), type: EXAMPLE_TYPE },
|
|
461
|
+
{ id: exampleId(2), type: EXAMPLE_TYPE },
|
|
462
|
+
],
|
|
463
|
+
});
|
|
464
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
465
|
+
const path = graph.pipe(Graph.getPath({ target: exampleId(2) }));
|
|
466
|
+
expect(path.pipe(Option.getOrNull)).to.deep.equal(['root', exampleId(1), exampleId(2)]);
|
|
337
467
|
});
|
|
338
468
|
|
|
339
469
|
describe('traverse', () => {
|
|
340
470
|
test('can be traversed', () => {
|
|
341
|
-
const graph =
|
|
342
|
-
|
|
343
|
-
id:
|
|
344
|
-
type:
|
|
471
|
+
const graph = Graph.make();
|
|
472
|
+
Graph.addNode(graph, {
|
|
473
|
+
id: Node.RootId,
|
|
474
|
+
type: Node.RootType,
|
|
345
475
|
nodes: [
|
|
346
476
|
{ id: 'test1', type: 'test' },
|
|
347
477
|
{ id: 'test2', type: 'test' },
|
|
@@ -349,7 +479,8 @@ describe('Graph', () => {
|
|
|
349
479
|
});
|
|
350
480
|
|
|
351
481
|
const nodes: string[] = [];
|
|
352
|
-
|
|
482
|
+
Graph.traverse(graph, {
|
|
483
|
+
relation: 'child',
|
|
353
484
|
visitor: (node) => {
|
|
354
485
|
nodes.push(node.id);
|
|
355
486
|
},
|
|
@@ -358,19 +489,20 @@ describe('Graph', () => {
|
|
|
358
489
|
});
|
|
359
490
|
|
|
360
491
|
test('traversal breaks cycles', () => {
|
|
361
|
-
const graph =
|
|
362
|
-
|
|
363
|
-
id:
|
|
364
|
-
type:
|
|
492
|
+
const graph = Graph.make();
|
|
493
|
+
Graph.addNode(graph, {
|
|
494
|
+
id: Node.RootId,
|
|
495
|
+
type: Node.RootType,
|
|
365
496
|
nodes: [
|
|
366
497
|
{ id: 'test1', type: 'test' },
|
|
367
498
|
{ id: 'test2', type: 'test' },
|
|
368
499
|
],
|
|
369
500
|
});
|
|
370
|
-
|
|
501
|
+
Graph.addEdge(graph, { source: 'test1', target: 'root', relation: 'child' });
|
|
371
502
|
|
|
372
503
|
const nodes: string[] = [];
|
|
373
|
-
|
|
504
|
+
Graph.traverse(graph, {
|
|
505
|
+
relation: 'child',
|
|
374
506
|
visitor: (node) => {
|
|
375
507
|
nodes.push(node.id);
|
|
376
508
|
},
|
|
@@ -379,10 +511,10 @@ describe('Graph', () => {
|
|
|
379
511
|
});
|
|
380
512
|
|
|
381
513
|
test('traversal can be started from any node', () => {
|
|
382
|
-
const graph =
|
|
383
|
-
|
|
384
|
-
id:
|
|
385
|
-
type:
|
|
514
|
+
const graph = Graph.make();
|
|
515
|
+
Graph.addNode(graph, {
|
|
516
|
+
id: Node.RootId,
|
|
517
|
+
type: Node.RootType,
|
|
386
518
|
nodes: [
|
|
387
519
|
{
|
|
388
520
|
id: 'test1',
|
|
@@ -393,8 +525,9 @@ describe('Graph', () => {
|
|
|
393
525
|
});
|
|
394
526
|
|
|
395
527
|
const nodes: string[] = [];
|
|
396
|
-
|
|
528
|
+
Graph.traverse(graph, {
|
|
397
529
|
source: 'test2',
|
|
530
|
+
relation: 'child',
|
|
398
531
|
visitor: (node) => {
|
|
399
532
|
nodes.push(node.id);
|
|
400
533
|
},
|
|
@@ -403,10 +536,10 @@ describe('Graph', () => {
|
|
|
403
536
|
});
|
|
404
537
|
|
|
405
538
|
test('traversal can follow inbound edges', () => {
|
|
406
|
-
const graph =
|
|
407
|
-
|
|
408
|
-
id:
|
|
409
|
-
type:
|
|
539
|
+
const graph = Graph.make();
|
|
540
|
+
Graph.addNode(graph, {
|
|
541
|
+
id: Node.RootId,
|
|
542
|
+
type: Node.RootType,
|
|
410
543
|
nodes: [
|
|
411
544
|
{
|
|
412
545
|
id: 'test1',
|
|
@@ -417,9 +550,9 @@ describe('Graph', () => {
|
|
|
417
550
|
});
|
|
418
551
|
|
|
419
552
|
const nodes: string[] = [];
|
|
420
|
-
|
|
553
|
+
Graph.traverse(graph, {
|
|
421
554
|
source: 'test2',
|
|
422
|
-
relation: 'inbound',
|
|
555
|
+
relation: Node.childRelation('inbound'),
|
|
423
556
|
visitor: (node) => {
|
|
424
557
|
nodes.push(node.id);
|
|
425
558
|
},
|
|
@@ -427,11 +560,28 @@ describe('Graph', () => {
|
|
|
427
560
|
expect(nodes).to.deep.equal(['test2', 'test1', 'root']);
|
|
428
561
|
});
|
|
429
562
|
|
|
563
|
+
test('traversal can follow typed inbound edges', () => {
|
|
564
|
+
const graph = Graph.make();
|
|
565
|
+
Graph.addNode(graph, { id: 'host', type: 'test' });
|
|
566
|
+
Graph.addNode(graph, { id: 'action', type: 'test' });
|
|
567
|
+
Graph.addEdge(graph, { source: 'host', target: 'action', relation: 'action' });
|
|
568
|
+
|
|
569
|
+
const nodes: string[] = [];
|
|
570
|
+
Graph.traverse(graph, {
|
|
571
|
+
source: 'action',
|
|
572
|
+
relation: Node.actionRelation('inbound'),
|
|
573
|
+
visitor: (node) => {
|
|
574
|
+
nodes.push(node.id);
|
|
575
|
+
},
|
|
576
|
+
});
|
|
577
|
+
expect(nodes).to.deep.equal(['action', 'host']);
|
|
578
|
+
});
|
|
579
|
+
|
|
430
580
|
test('traversal can be terminated early', () => {
|
|
431
|
-
const graph =
|
|
432
|
-
|
|
433
|
-
id:
|
|
434
|
-
type:
|
|
581
|
+
const graph = Graph.make();
|
|
582
|
+
Graph.addNode(graph, {
|
|
583
|
+
id: Node.RootId,
|
|
584
|
+
type: Node.RootType,
|
|
435
585
|
nodes: [
|
|
436
586
|
{ id: 'test1', type: 'test' },
|
|
437
587
|
{ id: 'test2', type: 'test' },
|
|
@@ -439,7 +589,8 @@ describe('Graph', () => {
|
|
|
439
589
|
});
|
|
440
590
|
|
|
441
591
|
const nodes: string[] = [];
|
|
442
|
-
|
|
592
|
+
Graph.traverse(graph, {
|
|
593
|
+
relation: 'child',
|
|
443
594
|
visitor: (node) => {
|
|
444
595
|
if (nodes.length === 2) {
|
|
445
596
|
return false;
|
|
@@ -450,5 +601,182 @@ describe('Graph', () => {
|
|
|
450
601
|
});
|
|
451
602
|
expect(nodes).to.deep.equal(['root', 'test1']);
|
|
452
603
|
});
|
|
604
|
+
|
|
605
|
+
test('traversal with multiple relations follows all edge types', () => {
|
|
606
|
+
const graph = Graph.make();
|
|
607
|
+
Graph.addNode(graph, {
|
|
608
|
+
id: Node.RootId,
|
|
609
|
+
type: Node.RootType,
|
|
610
|
+
nodes: [{ id: 'child1', type: 'test' }],
|
|
611
|
+
});
|
|
612
|
+
Graph.addNode(graph, { id: 'action1', type: Node.ActionType });
|
|
613
|
+
Graph.addEdge(graph, { source: Node.RootId, target: 'action1', relation: 'action' });
|
|
614
|
+
|
|
615
|
+
const nodes: string[] = [];
|
|
616
|
+
Graph.traverse(graph, {
|
|
617
|
+
relation: ['child', 'action'],
|
|
618
|
+
visitor: (node) => {
|
|
619
|
+
nodes.push(node.id);
|
|
620
|
+
},
|
|
621
|
+
});
|
|
622
|
+
expect(nodes).to.deep.equal(['root', 'child1', 'action1']);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test('traverse curried', () => {
|
|
626
|
+
const graph = Graph.make();
|
|
627
|
+
Graph.addNode(graph, {
|
|
628
|
+
id: Node.RootId,
|
|
629
|
+
type: Node.RootType,
|
|
630
|
+
nodes: [{ id: 'test1', type: 'test' }],
|
|
631
|
+
});
|
|
632
|
+
Graph.addNode(graph, { id: 'test2', type: 'test' });
|
|
633
|
+
Graph.addEdge(graph, { source: 'test1', target: 'test2', relation: 'child' });
|
|
634
|
+
const nodes: string[] = [];
|
|
635
|
+
graph.pipe(
|
|
636
|
+
Graph.traverse({
|
|
637
|
+
source: Node.RootId,
|
|
638
|
+
relation: 'child',
|
|
639
|
+
visitor: (node, _path) => {
|
|
640
|
+
nodes.push(node.id);
|
|
641
|
+
},
|
|
642
|
+
}),
|
|
643
|
+
);
|
|
644
|
+
expect(nodes).to.deep.equal(['root', 'test1', 'test2']);
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
test('add nodes curried', () => {
|
|
649
|
+
const registry = Registry.make();
|
|
650
|
+
const graph = Graph.make({ registry });
|
|
651
|
+
const result = graph.pipe(
|
|
652
|
+
Graph.addNodes([
|
|
653
|
+
{ id: exampleId(1), type: EXAMPLE_TYPE },
|
|
654
|
+
{ id: exampleId(2), type: EXAMPLE_TYPE },
|
|
655
|
+
]),
|
|
656
|
+
);
|
|
657
|
+
expect(result).toEqual(graph);
|
|
658
|
+
const node1 = registry.get(graph.node(exampleId(1)));
|
|
659
|
+
const node2 = registry.get(graph.node(exampleId(2)));
|
|
660
|
+
assert.ok(Option.isSome(node1));
|
|
661
|
+
assert.ok(Option.isSome(node2));
|
|
662
|
+
expect(node1.value.id).toEqual(exampleId(1));
|
|
663
|
+
expect(node2.value.id).toEqual(exampleId(2));
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
test('add edges curried', () => {
|
|
667
|
+
const registry = Registry.make();
|
|
668
|
+
const graph = Graph.make({ registry });
|
|
669
|
+
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
670
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
671
|
+
Graph.addNode(graph, { id: exampleId(3), type: EXAMPLE_TYPE });
|
|
672
|
+
const result = graph.pipe(
|
|
673
|
+
Graph.addEdges([
|
|
674
|
+
{ source: exampleId(1), target: exampleId(2), relation: 'child' },
|
|
675
|
+
{ source: exampleId(1), target: exampleId(3), relation: 'child' },
|
|
676
|
+
]),
|
|
677
|
+
);
|
|
678
|
+
expect(result).toEqual(graph);
|
|
679
|
+
const edges = registry.get(graph.edges(exampleId(1)));
|
|
680
|
+
expect(edges[CHILD_RELATION_KEY]).to.have.length(2);
|
|
681
|
+
expect(edges[CHILD_RELATION_KEY]).to.include(exampleId(2));
|
|
682
|
+
expect(edges[CHILD_RELATION_KEY]).to.include(exampleId(3));
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
test('remove nodes curried', () => {
|
|
686
|
+
const registry = Registry.make();
|
|
687
|
+
const graph = Graph.make({ registry });
|
|
688
|
+
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
689
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
690
|
+
const result = graph.pipe(Graph.removeNodes([exampleId(1), exampleId(2)]));
|
|
691
|
+
expect(result).toEqual(graph);
|
|
692
|
+
const node1 = registry.get(graph.node(exampleId(1)));
|
|
693
|
+
const node2 = registry.get(graph.node(exampleId(2)));
|
|
694
|
+
assert.ok(Option.isNone(node1));
|
|
695
|
+
assert.ok(Option.isNone(node2));
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
test('remove edges curried', () => {
|
|
699
|
+
const registry = Registry.make();
|
|
700
|
+
const graph = Graph.make({ registry });
|
|
701
|
+
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
702
|
+
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
703
|
+
Graph.addNode(graph, { id: exampleId(3), type: EXAMPLE_TYPE });
|
|
704
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
705
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(3), relation: 'child' });
|
|
706
|
+
const result = graph.pipe(
|
|
707
|
+
Graph.removeEdges([
|
|
708
|
+
{ source: exampleId(1), target: exampleId(2), relation: 'child' },
|
|
709
|
+
{ source: exampleId(1), target: exampleId(3), relation: 'child' },
|
|
710
|
+
]),
|
|
711
|
+
);
|
|
712
|
+
expect(result).toEqual(graph);
|
|
713
|
+
const edges = registry.get(graph.edges(exampleId(1)));
|
|
714
|
+
expect(edges[CHILD_RELATION_KEY]).to.have.length(0);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
test('expand curried', async () => {
|
|
718
|
+
const registry = Registry.make();
|
|
719
|
+
const builder = GraphBuilder.make({ registry });
|
|
720
|
+
const graph = builder.graph;
|
|
721
|
+
let expandCalled = false;
|
|
722
|
+
GraphBuilder.addExtension(
|
|
723
|
+
builder,
|
|
724
|
+
GraphBuilder.createExtensionRaw({
|
|
725
|
+
id: 'test',
|
|
726
|
+
connector: () => {
|
|
727
|
+
expandCalled = true;
|
|
728
|
+
return Atom.make([]);
|
|
729
|
+
},
|
|
730
|
+
}),
|
|
731
|
+
);
|
|
732
|
+
await graph.pipe(Graph.expand(Node.RootId, 'child'));
|
|
733
|
+
expect(expandCalled).to.be.true;
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
test('expand defers for non-existent node and applies when node is added', () => {
|
|
737
|
+
const registry = Registry.make();
|
|
738
|
+
const expandCalls: [string, Node.Relation][] = [];
|
|
739
|
+
const graph = Graph.make({
|
|
740
|
+
registry,
|
|
741
|
+
onExpand: (id, relation) => expandCalls.push([id, relation]),
|
|
742
|
+
});
|
|
743
|
+
const childId = 'child';
|
|
744
|
+
expect(Option.isNone(registry.get(graph.node(childId)))).to.be.true;
|
|
745
|
+
|
|
746
|
+
Graph.expand(graph, childId, 'child');
|
|
747
|
+
expect(expandCalls).to.deep.equal([]);
|
|
748
|
+
|
|
749
|
+
Graph.addNode(graph, { id: childId, type: EXAMPLE_TYPE });
|
|
750
|
+
expect(expandCalls).to.deep.equal([[childId, Node.childRelation()]]);
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
test('initialize curried', async () => {
|
|
754
|
+
const registry = Registry.make();
|
|
755
|
+
const builder = GraphBuilder.make({ registry });
|
|
756
|
+
const graph = builder.graph;
|
|
757
|
+
let initializeCalled = false;
|
|
758
|
+
GraphBuilder.addExtension(
|
|
759
|
+
builder,
|
|
760
|
+
GraphBuilder.createExtensionRaw({
|
|
761
|
+
id: 'test',
|
|
762
|
+
resolver: () => {
|
|
763
|
+
initializeCalled = true;
|
|
764
|
+
return Atom.make({ id: EXAMPLE_ID, type: EXAMPLE_TYPE });
|
|
765
|
+
},
|
|
766
|
+
}),
|
|
767
|
+
);
|
|
768
|
+
await graph.pipe(Graph.initialize(EXAMPLE_ID));
|
|
769
|
+
expect(initializeCalled).to.be.true;
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
test('waitForPath curried', async () => {
|
|
773
|
+
const graph = Graph.make();
|
|
774
|
+
Graph.addNode(graph, {
|
|
775
|
+
id: Node.RootId,
|
|
776
|
+
type: Node.RootType,
|
|
777
|
+
nodes: [{ id: exampleId(1), type: EXAMPLE_TYPE }],
|
|
778
|
+
});
|
|
779
|
+
const path = await graph.pipe(Graph.waitForPath({ target: exampleId(1) }, { timeout: 1000 }));
|
|
780
|
+
expect(path).to.deep.equal(['root', exampleId(1)]);
|
|
453
781
|
});
|
|
454
782
|
});
|