@dxos/app-graph 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6
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/browser/chunk-AKBGYELG.mjs +1603 -0
- package/dist/lib/browser/chunk-AKBGYELG.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +17 -1276
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +39 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HR5S4XYH.mjs +1604 -0
- package/dist/lib/node-esm/chunk-HR5S4XYH.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +17 -1276
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +40 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/graph-builder.d.ts +11 -7
- package/dist/types/src/graph-builder.d.ts.map +1 -1
- package/dist/types/src/graph.d.ts +13 -17
- package/dist/types/src/graph.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/node-matcher.d.ts +43 -17
- package/dist/types/src/node-matcher.d.ts.map +1 -1
- package/dist/types/src/node.d.ts +21 -5
- package/dist/types/src/node.d.ts.map +1 -1
- 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 +39 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +36 -26
- package/src/graph-builder.test.ts +569 -102
- package/src/graph-builder.ts +202 -74
- package/src/graph.test.ts +187 -52
- package/src/graph.ts +174 -98
- package/src/index.ts +1 -0
- package/src/node-matcher.ts +58 -28
- package/src/node.ts +46 -5
- package/src/stories/EchoGraph.stories.tsx +90 -61
- 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 +95 -0
package/src/graph.test.ts
CHANGED
|
@@ -12,7 +12,11 @@ import * as Node from './node';
|
|
|
12
12
|
|
|
13
13
|
const exampleId = (id: number) => `dx:test:${id}`;
|
|
14
14
|
const EXAMPLE_ID = exampleId(1);
|
|
15
|
-
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'));
|
|
16
20
|
|
|
17
21
|
describe('Graph', () => {
|
|
18
22
|
test('getGraph', () => {
|
|
@@ -100,6 +104,39 @@ describe('Graph', () => {
|
|
|
100
104
|
}
|
|
101
105
|
});
|
|
102
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
|
+
|
|
103
140
|
test('remove node curried', () => {
|
|
104
141
|
const registry = Registry.make();
|
|
105
142
|
const graph = Graph.make({ registry });
|
|
@@ -130,20 +167,29 @@ describe('Graph', () => {
|
|
|
130
167
|
test('add edge', () => {
|
|
131
168
|
const registry = Registry.make();
|
|
132
169
|
const graph = Graph.make({ registry });
|
|
133
|
-
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
}
|
|
137
183
|
});
|
|
138
184
|
|
|
139
185
|
test('add edges is idempotent', () => {
|
|
140
186
|
const registry = Registry.make();
|
|
141
187
|
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) });
|
|
188
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
189
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
144
190
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
145
|
-
expect(edges
|
|
146
|
-
expect(edges
|
|
191
|
+
expect(edges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
192
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([exampleId(2)]);
|
|
147
193
|
});
|
|
148
194
|
|
|
149
195
|
test('sort edges', () => {
|
|
@@ -151,17 +197,17 @@ describe('Graph', () => {
|
|
|
151
197
|
const graph = Graph.make({ registry });
|
|
152
198
|
|
|
153
199
|
{
|
|
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) });
|
|
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' });
|
|
157
203
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
158
|
-
expect(edges
|
|
204
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([exampleId(2), exampleId(3), exampleId(4)]);
|
|
159
205
|
}
|
|
160
206
|
|
|
161
207
|
{
|
|
162
|
-
Graph.sortEdges(graph, exampleId(1), '
|
|
208
|
+
Graph.sortEdges(graph, exampleId(1), 'child', [exampleId(3), exampleId(2)]);
|
|
163
209
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
164
|
-
expect(edges
|
|
210
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([exampleId(3), exampleId(2), exampleId(4)]);
|
|
165
211
|
}
|
|
166
212
|
});
|
|
167
213
|
|
|
@@ -170,17 +216,17 @@ describe('Graph', () => {
|
|
|
170
216
|
const graph = Graph.make({ registry });
|
|
171
217
|
|
|
172
218
|
{
|
|
173
|
-
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
|
|
219
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
174
220
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
175
|
-
expect(edges
|
|
176
|
-
expect(edges
|
|
221
|
+
expect(edges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
222
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([exampleId(2)]);
|
|
177
223
|
}
|
|
178
224
|
|
|
179
225
|
{
|
|
180
|
-
Graph.removeEdge(graph, { source: exampleId(1), target: exampleId(2) });
|
|
226
|
+
Graph.removeEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
181
227
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
182
|
-
expect(edges
|
|
183
|
-
expect(edges
|
|
228
|
+
expect(edges[CHILD_INBOUND_RELATION_KEY]).toBeUndefined();
|
|
229
|
+
expect(edges[CHILD_RELATION_KEY]).toEqual([]);
|
|
184
230
|
}
|
|
185
231
|
});
|
|
186
232
|
|
|
@@ -189,12 +235,42 @@ describe('Graph', () => {
|
|
|
189
235
|
const graph = Graph.make({ registry });
|
|
190
236
|
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
191
237
|
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) }));
|
|
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' }));
|
|
194
240
|
expect(result).toEqual(graph);
|
|
195
241
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
196
|
-
expect(edges
|
|
197
|
-
expect(edges
|
|
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([]);
|
|
198
274
|
});
|
|
199
275
|
|
|
200
276
|
test('get connections', () => {
|
|
@@ -202,8 +278,8 @@ describe('Graph', () => {
|
|
|
202
278
|
const graph = Graph.make({ registry });
|
|
203
279
|
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
204
280
|
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
205
|
-
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
|
|
206
|
-
const nodes = registry.get(graph.connections(exampleId(1)));
|
|
281
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
282
|
+
const nodes = registry.get(graph.connections(exampleId(1), 'child'));
|
|
207
283
|
expect(nodes).has.length(1);
|
|
208
284
|
expect(nodes[0].id).toEqual(exampleId(2));
|
|
209
285
|
});
|
|
@@ -228,8 +304,8 @@ describe('Graph', () => {
|
|
|
228
304
|
test('connections updates', () => {
|
|
229
305
|
const registry = Registry.make();
|
|
230
306
|
const graph = Graph.make({ registry });
|
|
231
|
-
assert.strictEqual(graph.connections(exampleId(1)), graph.connections(exampleId(1)));
|
|
232
|
-
const childrenKey = graph.connections(exampleId(1));
|
|
307
|
+
assert.strictEqual(graph.connections(exampleId(1), 'child'), graph.connections(exampleId(1), 'child'));
|
|
308
|
+
const childrenKey = graph.connections(exampleId(1), 'child');
|
|
233
309
|
|
|
234
310
|
let count = 0;
|
|
235
311
|
const cancel = registry.subscribe(childrenKey, (_) => {
|
|
@@ -240,7 +316,7 @@ describe('Graph', () => {
|
|
|
240
316
|
graph.pipe(
|
|
241
317
|
Graph.addNode({ id: exampleId(1), type: EXAMPLE_TYPE }),
|
|
242
318
|
Graph.addNode({ id: exampleId(2), type: EXAMPLE_TYPE }),
|
|
243
|
-
Graph.addEdge({ source: exampleId(1), target: exampleId(2) }),
|
|
319
|
+
Graph.addEdge({ source: exampleId(1), target: exampleId(2), relation: 'child' }),
|
|
244
320
|
);
|
|
245
321
|
|
|
246
322
|
expect(count).toEqual(0);
|
|
@@ -262,12 +338,12 @@ describe('Graph', () => {
|
|
|
262
338
|
expect(count).toEqual(2);
|
|
263
339
|
|
|
264
340
|
// Connecting a node fires an update.
|
|
265
|
-
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(3) });
|
|
341
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(3), relation: 'child' });
|
|
266
342
|
expect(count).toEqual(3);
|
|
267
343
|
|
|
268
344
|
// Adding an edge connected to nothing fires an update.
|
|
269
345
|
// TODO(wittjosiah): Is there a way to avoid this?
|
|
270
|
-
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(4) });
|
|
346
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(4), relation: 'child' });
|
|
271
347
|
expect(count).toEqual(4);
|
|
272
348
|
|
|
273
349
|
// Adding a node to an existing edge fires an update.
|
|
@@ -277,7 +353,7 @@ describe('Graph', () => {
|
|
|
277
353
|
// Batching the edge and node updates fires a single update.
|
|
278
354
|
Atom.batch(() => {
|
|
279
355
|
graph.pipe(
|
|
280
|
-
Graph.addEdge({ source: exampleId(1), target: exampleId(6) }),
|
|
356
|
+
Graph.addEdge({ source: exampleId(1), target: exampleId(6), relation: 'child' }),
|
|
281
357
|
Graph.addNode({ id: exampleId(6), type: EXAMPLE_TYPE }),
|
|
282
358
|
);
|
|
283
359
|
});
|
|
@@ -295,7 +371,7 @@ describe('Graph', () => {
|
|
|
295
371
|
{ id: 'test2', type: 'test' },
|
|
296
372
|
],
|
|
297
373
|
});
|
|
298
|
-
Graph.addEdge(graph, { source: 'test1', target: 'test2' });
|
|
374
|
+
Graph.addEdge(graph, { source: 'test1', target: 'test2', relation: 'child' });
|
|
299
375
|
|
|
300
376
|
const json = Graph.toJSON(graph);
|
|
301
377
|
expect(json).to.deep.equal({
|
|
@@ -320,7 +396,7 @@ describe('Graph', () => {
|
|
|
320
396
|
{ id: 'test2', type: 'test' },
|
|
321
397
|
],
|
|
322
398
|
});
|
|
323
|
-
Graph.addEdge(graph, { source: 'test1', target: 'test2' });
|
|
399
|
+
Graph.addEdge(graph, { source: 'test1', target: 'test2', relation: 'child' });
|
|
324
400
|
|
|
325
401
|
let json: any;
|
|
326
402
|
const cancel = registry.subscribe(graph.json(), (_) => {
|
|
@@ -339,7 +415,7 @@ describe('Graph', () => {
|
|
|
339
415
|
});
|
|
340
416
|
|
|
341
417
|
Graph.addNode(graph, { id: 'test3', type: 'test' });
|
|
342
|
-
Graph.addEdge(graph, { source: 'root', target: 'test3' });
|
|
418
|
+
Graph.addEdge(graph, { source: 'root', target: 'test3', relation: 'child' });
|
|
343
419
|
expect(json).to.deep.equal({
|
|
344
420
|
id: Node.RootId,
|
|
345
421
|
type: Node.RootType,
|
|
@@ -361,7 +437,7 @@ describe('Graph', () => {
|
|
|
361
437
|
{ id: exampleId(2), type: EXAMPLE_TYPE },
|
|
362
438
|
],
|
|
363
439
|
});
|
|
364
|
-
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
|
|
440
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
365
441
|
|
|
366
442
|
expect(Graph.getPath(graph, { target: exampleId(2) }).pipe(Option.getOrNull)).to.deep.equal([
|
|
367
443
|
'root',
|
|
@@ -385,7 +461,7 @@ describe('Graph', () => {
|
|
|
385
461
|
{ id: exampleId(2), type: EXAMPLE_TYPE },
|
|
386
462
|
],
|
|
387
463
|
});
|
|
388
|
-
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2) });
|
|
464
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
389
465
|
const path = graph.pipe(Graph.getPath({ target: exampleId(2) }));
|
|
390
466
|
expect(path.pipe(Option.getOrNull)).to.deep.equal(['root', exampleId(1), exampleId(2)]);
|
|
391
467
|
});
|
|
@@ -404,6 +480,7 @@ describe('Graph', () => {
|
|
|
404
480
|
|
|
405
481
|
const nodes: string[] = [];
|
|
406
482
|
Graph.traverse(graph, {
|
|
483
|
+
relation: 'child',
|
|
407
484
|
visitor: (node) => {
|
|
408
485
|
nodes.push(node.id);
|
|
409
486
|
},
|
|
@@ -421,10 +498,11 @@ describe('Graph', () => {
|
|
|
421
498
|
{ id: 'test2', type: 'test' },
|
|
422
499
|
],
|
|
423
500
|
});
|
|
424
|
-
Graph.addEdge(graph, { source: 'test1', target: 'root' });
|
|
501
|
+
Graph.addEdge(graph, { source: 'test1', target: 'root', relation: 'child' });
|
|
425
502
|
|
|
426
503
|
const nodes: string[] = [];
|
|
427
504
|
Graph.traverse(graph, {
|
|
505
|
+
relation: 'child',
|
|
428
506
|
visitor: (node) => {
|
|
429
507
|
nodes.push(node.id);
|
|
430
508
|
},
|
|
@@ -449,6 +527,7 @@ describe('Graph', () => {
|
|
|
449
527
|
const nodes: string[] = [];
|
|
450
528
|
Graph.traverse(graph, {
|
|
451
529
|
source: 'test2',
|
|
530
|
+
relation: 'child',
|
|
452
531
|
visitor: (node) => {
|
|
453
532
|
nodes.push(node.id);
|
|
454
533
|
},
|
|
@@ -473,7 +552,7 @@ describe('Graph', () => {
|
|
|
473
552
|
const nodes: string[] = [];
|
|
474
553
|
Graph.traverse(graph, {
|
|
475
554
|
source: 'test2',
|
|
476
|
-
relation: 'inbound',
|
|
555
|
+
relation: Node.childRelation('inbound'),
|
|
477
556
|
visitor: (node) => {
|
|
478
557
|
nodes.push(node.id);
|
|
479
558
|
},
|
|
@@ -481,6 +560,23 @@ describe('Graph', () => {
|
|
|
481
560
|
expect(nodes).to.deep.equal(['test2', 'test1', 'root']);
|
|
482
561
|
});
|
|
483
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
|
+
|
|
484
580
|
test('traversal can be terminated early', () => {
|
|
485
581
|
const graph = Graph.make();
|
|
486
582
|
Graph.addNode(graph, {
|
|
@@ -494,6 +590,7 @@ describe('Graph', () => {
|
|
|
494
590
|
|
|
495
591
|
const nodes: string[] = [];
|
|
496
592
|
Graph.traverse(graph, {
|
|
593
|
+
relation: 'child',
|
|
497
594
|
visitor: (node) => {
|
|
498
595
|
if (nodes.length === 2) {
|
|
499
596
|
return false;
|
|
@@ -505,6 +602,26 @@ describe('Graph', () => {
|
|
|
505
602
|
expect(nodes).to.deep.equal(['root', 'test1']);
|
|
506
603
|
});
|
|
507
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
|
+
|
|
508
625
|
test('traverse curried', () => {
|
|
509
626
|
const graph = Graph.make();
|
|
510
627
|
Graph.addNode(graph, {
|
|
@@ -513,11 +630,12 @@ describe('Graph', () => {
|
|
|
513
630
|
nodes: [{ id: 'test1', type: 'test' }],
|
|
514
631
|
});
|
|
515
632
|
Graph.addNode(graph, { id: 'test2', type: 'test' });
|
|
516
|
-
Graph.addEdge(graph, { source: 'test1', target: 'test2' });
|
|
633
|
+
Graph.addEdge(graph, { source: 'test1', target: 'test2', relation: 'child' });
|
|
517
634
|
const nodes: string[] = [];
|
|
518
635
|
graph.pipe(
|
|
519
636
|
Graph.traverse({
|
|
520
637
|
source: Node.RootId,
|
|
638
|
+
relation: 'child',
|
|
521
639
|
visitor: (node, _path) => {
|
|
522
640
|
nodes.push(node.id);
|
|
523
641
|
},
|
|
@@ -553,15 +671,15 @@ describe('Graph', () => {
|
|
|
553
671
|
Graph.addNode(graph, { id: exampleId(3), type: EXAMPLE_TYPE });
|
|
554
672
|
const result = graph.pipe(
|
|
555
673
|
Graph.addEdges([
|
|
556
|
-
{ source: exampleId(1), target: exampleId(2) },
|
|
557
|
-
{ source: exampleId(1), target: exampleId(3) },
|
|
674
|
+
{ source: exampleId(1), target: exampleId(2), relation: 'child' },
|
|
675
|
+
{ source: exampleId(1), target: exampleId(3), relation: 'child' },
|
|
558
676
|
]),
|
|
559
677
|
);
|
|
560
678
|
expect(result).toEqual(graph);
|
|
561
679
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
562
|
-
expect(edges
|
|
563
|
-
expect(edges
|
|
564
|
-
expect(edges
|
|
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));
|
|
565
683
|
});
|
|
566
684
|
|
|
567
685
|
test('remove nodes curried', () => {
|
|
@@ -583,17 +701,17 @@ describe('Graph', () => {
|
|
|
583
701
|
Graph.addNode(graph, { id: exampleId(1), type: EXAMPLE_TYPE });
|
|
584
702
|
Graph.addNode(graph, { id: exampleId(2), type: EXAMPLE_TYPE });
|
|
585
703
|
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) });
|
|
704
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(2), relation: 'child' });
|
|
705
|
+
Graph.addEdge(graph, { source: exampleId(1), target: exampleId(3), relation: 'child' });
|
|
588
706
|
const result = graph.pipe(
|
|
589
707
|
Graph.removeEdges([
|
|
590
|
-
{ source: exampleId(1), target: exampleId(2) },
|
|
591
|
-
{ source: exampleId(1), target: exampleId(3) },
|
|
708
|
+
{ source: exampleId(1), target: exampleId(2), relation: 'child' },
|
|
709
|
+
{ source: exampleId(1), target: exampleId(3), relation: 'child' },
|
|
592
710
|
]),
|
|
593
711
|
);
|
|
594
712
|
expect(result).toEqual(graph);
|
|
595
713
|
const edges = registry.get(graph.edges(exampleId(1)));
|
|
596
|
-
expect(edges
|
|
714
|
+
expect(edges[CHILD_RELATION_KEY]).to.have.length(0);
|
|
597
715
|
});
|
|
598
716
|
|
|
599
717
|
test('expand curried', async () => {
|
|
@@ -611,10 +729,27 @@ describe('Graph', () => {
|
|
|
611
729
|
},
|
|
612
730
|
}),
|
|
613
731
|
);
|
|
614
|
-
await graph.pipe(Graph.expand(Node.RootId));
|
|
732
|
+
await graph.pipe(Graph.expand(Node.RootId, 'child'));
|
|
615
733
|
expect(expandCalled).to.be.true;
|
|
616
734
|
});
|
|
617
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
|
+
|
|
618
753
|
test('initialize curried', async () => {
|
|
619
754
|
const registry = Registry.make();
|
|
620
755
|
const builder = GraphBuilder.make({ registry });
|