@dxos/app-graph 0.8.4-main.406dc2a → 0.8.4-main.548089c
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/index.mjs +173 -195
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +173 -195
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/graph-builder.d.ts +13 -13
- package/dist/types/src/graph-builder.d.ts.map +1 -1
- package/dist/types/src/graph.d.ts +18 -18
- package/dist/types/src/graph.d.ts.map +1 -1
- package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
- package/dist/types/src/testing.d.ts +2 -2
- package/dist/types/src/testing.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -22
- package/src/graph-builder.test.ts +26 -26
- package/src/graph-builder.ts +42 -40
- package/src/graph.test.ts +2 -2
- package/src/graph.ts +89 -49
- package/src/signals-integration.test.ts +4 -4
- package/src/stories/EchoGraph.stories.tsx +15 -8
- package/src/stories/Tree.tsx +1 -1
- package/src/testing.ts +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/app-graph",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.548089c",
|
|
4
4
|
"description": "Constructs knowledge graphs for the purpose of building applications on top of",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -27,41 +27,41 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@preact/signals-core": "^1.12.1",
|
|
29
29
|
"main-thread-scheduling": "^14.1.1",
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/
|
|
32
|
-
"@dxos/echo": "0.8.4-main.
|
|
33
|
-
"@dxos/echo-signals": "0.8.4-main.
|
|
34
|
-
"@dxos/invariant": "0.8.4-main.
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
30
|
+
"@dxos/debug": "0.8.4-main.548089c",
|
|
31
|
+
"@dxos/async": "0.8.4-main.548089c",
|
|
32
|
+
"@dxos/echo": "0.8.4-main.548089c",
|
|
33
|
+
"@dxos/echo-signals": "0.8.4-main.548089c",
|
|
34
|
+
"@dxos/invariant": "0.8.4-main.548089c",
|
|
35
|
+
"@dxos/log": "0.8.4-main.548089c",
|
|
36
|
+
"@dxos/util": "0.8.4-main.548089c",
|
|
37
|
+
"@dxos/live-object": "0.8.4-main.548089c"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@effect-
|
|
40
|
+
"@effect-atom/atom-react": "^0.3.4",
|
|
41
41
|
"@effect/platform": "0.92.1",
|
|
42
42
|
"@types/react": "~19.2.2",
|
|
43
|
-
"@types/react-dom": "~19.2.
|
|
43
|
+
"@types/react-dom": "~19.2.2",
|
|
44
44
|
"effect": "3.18.3",
|
|
45
45
|
"react": "~19.2.0",
|
|
46
46
|
"react-dom": "~19.2.0",
|
|
47
47
|
"vite": "7.1.9",
|
|
48
|
-
"@dxos/echo-db": "0.8.4-main.
|
|
49
|
-
"@dxos/random": "0.8.4-main.
|
|
50
|
-
"@dxos/react-client": "0.8.4-main.
|
|
51
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
52
|
-
"@dxos/react-ui-
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/react-ui-list": "0.8.4-main.
|
|
55
|
-
"@dxos/
|
|
48
|
+
"@dxos/echo-db": "0.8.4-main.548089c",
|
|
49
|
+
"@dxos/random": "0.8.4-main.548089c",
|
|
50
|
+
"@dxos/react-client": "0.8.4-main.548089c",
|
|
51
|
+
"@dxos/react-ui": "0.8.4-main.548089c",
|
|
52
|
+
"@dxos/react-ui-theme": "0.8.4-main.548089c",
|
|
53
|
+
"@dxos/storybook-utils": "0.8.4-main.548089c",
|
|
54
|
+
"@dxos/react-ui-list": "0.8.4-main.548089c",
|
|
55
|
+
"@dxos/react-ui-tabs": "0.8.4-main.548089c"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@effect-
|
|
58
|
+
"@effect-atom/atom-react": "^0.3.4",
|
|
59
59
|
"@effect/platform": "^0.80.12",
|
|
60
60
|
"effect": "3.14.21",
|
|
61
61
|
"react": "^19.0.0",
|
|
62
62
|
"react-dom": "^19.0.0",
|
|
63
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
64
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
63
|
+
"@dxos/react-ui": "0.8.4-main.548089c",
|
|
64
|
+
"@dxos/react-ui-theme": "0.8.4-main.548089c"
|
|
65
65
|
},
|
|
66
66
|
"publishConfig": {
|
|
67
67
|
"access": "public"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Atom, Registry } from '@effect-atom/atom-react';
|
|
6
6
|
import * as Function from 'effect/Function';
|
|
7
7
|
import * as Option from 'effect/Option';
|
|
8
8
|
import { describe, expect, onTestFinished, test } from 'vitest';
|
|
@@ -34,7 +34,7 @@ describe('GraphBuilder', () => {
|
|
|
34
34
|
id: 'resolver',
|
|
35
35
|
resolver: () => {
|
|
36
36
|
console.log('resolver');
|
|
37
|
-
return
|
|
37
|
+
return Atom.make({ id: EXAMPLE_ID, type: EXAMPLE_TYPE, data: 1 });
|
|
38
38
|
},
|
|
39
39
|
}),
|
|
40
40
|
);
|
|
@@ -51,11 +51,11 @@ describe('GraphBuilder', () => {
|
|
|
51
51
|
test('updates', async () => {
|
|
52
52
|
const registry = Registry.make();
|
|
53
53
|
const builder = new GraphBuilder({ registry });
|
|
54
|
-
const name =
|
|
54
|
+
const name = Atom.make('default');
|
|
55
55
|
builder.addExtension(
|
|
56
56
|
createExtension({
|
|
57
57
|
id: 'resolver',
|
|
58
|
-
resolver: () =>
|
|
58
|
+
resolver: () => Atom.make((get) => ({ id: EXAMPLE_ID, type: EXAMPLE_TYPE, data: get(name) })),
|
|
59
59
|
}),
|
|
60
60
|
);
|
|
61
61
|
const graph = builder.graph;
|
|
@@ -82,14 +82,14 @@ describe('GraphBuilder', () => {
|
|
|
82
82
|
builder.addExtension(
|
|
83
83
|
createExtension({
|
|
84
84
|
id: 'outbound-connector',
|
|
85
|
-
connector: () =>
|
|
85
|
+
connector: () => Atom.make([{ id: 'child', type: EXAMPLE_TYPE, data: 2 }]),
|
|
86
86
|
}),
|
|
87
87
|
);
|
|
88
88
|
builder.addExtension(
|
|
89
89
|
createExtension({
|
|
90
90
|
id: 'inbound-connector',
|
|
91
91
|
relation: 'inbound',
|
|
92
|
-
connector: () =>
|
|
92
|
+
connector: () => Atom.make([{ id: 'parent', type: EXAMPLE_TYPE, data: 0 }]),
|
|
93
93
|
}),
|
|
94
94
|
);
|
|
95
95
|
|
|
@@ -111,11 +111,11 @@ describe('GraphBuilder', () => {
|
|
|
111
111
|
test('updates', () => {
|
|
112
112
|
const registry = Registry.make();
|
|
113
113
|
const builder = new GraphBuilder({ registry });
|
|
114
|
-
const state =
|
|
114
|
+
const state = Atom.make(0);
|
|
115
115
|
builder.addExtension(
|
|
116
116
|
createExtension({
|
|
117
117
|
id: 'connector',
|
|
118
|
-
connector: () =>
|
|
118
|
+
connector: () => Atom.make((get) => [{ id: EXAMPLE_ID, type: EXAMPLE_TYPE, data: get(state) }]),
|
|
119
119
|
}),
|
|
120
120
|
);
|
|
121
121
|
const graph = builder.graph;
|
|
@@ -136,11 +136,11 @@ describe('GraphBuilder', () => {
|
|
|
136
136
|
test('subscribes to updates', () => {
|
|
137
137
|
const registry = Registry.make();
|
|
138
138
|
const builder = new GraphBuilder({ registry });
|
|
139
|
-
const state =
|
|
139
|
+
const state = Atom.make(0);
|
|
140
140
|
builder.addExtension(
|
|
141
141
|
createExtension({
|
|
142
142
|
id: 'connector',
|
|
143
|
-
connector: () =>
|
|
143
|
+
connector: () => Atom.make((get) => [{ id: EXAMPLE_ID, type: EXAMPLE_TYPE, data: get(state) }]),
|
|
144
144
|
}),
|
|
145
145
|
);
|
|
146
146
|
const graph = builder.graph;
|
|
@@ -167,7 +167,7 @@ describe('GraphBuilder', () => {
|
|
|
167
167
|
builder.addExtension(
|
|
168
168
|
createExtension({
|
|
169
169
|
id: 'connector',
|
|
170
|
-
connector: () =>
|
|
170
|
+
connector: () => Atom.make([{ id: EXAMPLE_ID, type: EXAMPLE_TYPE }]),
|
|
171
171
|
}),
|
|
172
172
|
);
|
|
173
173
|
const graph = builder.graph;
|
|
@@ -190,7 +190,7 @@ describe('GraphBuilder', () => {
|
|
|
190
190
|
builder.addExtension(
|
|
191
191
|
createExtension({
|
|
192
192
|
id: 'connector-2',
|
|
193
|
-
connector: () =>
|
|
193
|
+
connector: () => Atom.make([{ id: exampleId(2), type: EXAMPLE_TYPE }]),
|
|
194
194
|
}),
|
|
195
195
|
);
|
|
196
196
|
expect(nodes).has.length(2);
|
|
@@ -200,14 +200,14 @@ describe('GraphBuilder', () => {
|
|
|
200
200
|
test('removes', () => {
|
|
201
201
|
const registry = Registry.make();
|
|
202
202
|
const builder = new GraphBuilder({ registry });
|
|
203
|
-
const nodes =
|
|
203
|
+
const nodes = Atom.make([
|
|
204
204
|
{ id: exampleId(1), type: EXAMPLE_TYPE },
|
|
205
205
|
{ id: exampleId(2), type: EXAMPLE_TYPE },
|
|
206
206
|
]);
|
|
207
207
|
builder.addExtension(
|
|
208
208
|
createExtension({
|
|
209
209
|
id: 'connector',
|
|
210
|
-
connector: () =>
|
|
210
|
+
connector: () => Atom.make((get) => get(nodes)),
|
|
211
211
|
}),
|
|
212
212
|
);
|
|
213
213
|
const graph = builder.graph;
|
|
@@ -232,13 +232,13 @@ describe('GraphBuilder', () => {
|
|
|
232
232
|
test('nodes are updated when removed', () => {
|
|
233
233
|
const registry = Registry.make();
|
|
234
234
|
const builder = new GraphBuilder({ registry });
|
|
235
|
-
const name =
|
|
235
|
+
const name = Atom.make('removed');
|
|
236
236
|
|
|
237
237
|
builder.addExtension([
|
|
238
238
|
createExtension({
|
|
239
239
|
id: 'root',
|
|
240
240
|
connector: (node) =>
|
|
241
|
-
|
|
241
|
+
Atom.make((get) =>
|
|
242
242
|
Function.pipe(
|
|
243
243
|
get(node),
|
|
244
244
|
Option.flatMap((node) => (node.id === 'root' ? Option.some(get(name)) : Option.none())),
|
|
@@ -280,7 +280,7 @@ describe('GraphBuilder', () => {
|
|
|
280
280
|
test('sort edges', async () => {
|
|
281
281
|
const registry = Registry.make();
|
|
282
282
|
const builder = new GraphBuilder({ registry });
|
|
283
|
-
const nodes =
|
|
283
|
+
const nodes = Atom.make([
|
|
284
284
|
{ id: exampleId(1), type: EXAMPLE_TYPE, data: 1 },
|
|
285
285
|
{ id: exampleId(2), type: EXAMPLE_TYPE, data: 2 },
|
|
286
286
|
{ id: exampleId(3), type: EXAMPLE_TYPE, data: 3 },
|
|
@@ -288,7 +288,7 @@ describe('GraphBuilder', () => {
|
|
|
288
288
|
builder.addExtension(
|
|
289
289
|
createExtension({
|
|
290
290
|
id: 'connector',
|
|
291
|
-
connector: () =>
|
|
291
|
+
connector: () => Atom.make((get) => get(nodes)),
|
|
292
292
|
}),
|
|
293
293
|
);
|
|
294
294
|
const graph = builder.graph;
|
|
@@ -323,14 +323,14 @@ describe('GraphBuilder', () => {
|
|
|
323
323
|
test('updates are constrained', () => {
|
|
324
324
|
const registry = Registry.make();
|
|
325
325
|
const builder = new GraphBuilder({ registry });
|
|
326
|
-
const name =
|
|
327
|
-
const sub =
|
|
326
|
+
const name = Atom.make('default');
|
|
327
|
+
const sub = Atom.make('default');
|
|
328
328
|
|
|
329
329
|
builder.addExtension([
|
|
330
330
|
createExtension({
|
|
331
331
|
id: 'root',
|
|
332
332
|
connector: (node) =>
|
|
333
|
-
|
|
333
|
+
Atom.make((get) =>
|
|
334
334
|
Function.pipe(
|
|
335
335
|
get(node),
|
|
336
336
|
Option.flatMap((node) => (node.id === 'root' ? Option.some(get(name)) : Option.none())),
|
|
@@ -343,7 +343,7 @@ describe('GraphBuilder', () => {
|
|
|
343
343
|
createExtension({
|
|
344
344
|
id: 'connector1',
|
|
345
345
|
connector: (node) =>
|
|
346
|
-
|
|
346
|
+
Atom.make((get) =>
|
|
347
347
|
Function.pipe(
|
|
348
348
|
get(node),
|
|
349
349
|
Option.flatMap((node) => (node.id === EXAMPLE_ID ? Option.some(get(sub)) : Option.none())),
|
|
@@ -355,7 +355,7 @@ describe('GraphBuilder', () => {
|
|
|
355
355
|
createExtension({
|
|
356
356
|
id: 'connector2',
|
|
357
357
|
connector: (node) =>
|
|
358
|
-
|
|
358
|
+
Atom.make((get) =>
|
|
359
359
|
Function.pipe(
|
|
360
360
|
get(node),
|
|
361
361
|
Option.flatMap((node) => (node.id === EXAMPLE_ID ? Option.some(node.data) : Option.none())),
|
|
@@ -411,7 +411,7 @@ describe('GraphBuilder', () => {
|
|
|
411
411
|
expect(dependentCount).to.equal(2);
|
|
412
412
|
|
|
413
413
|
// Independent count should update if its state changes even if the parent is removed.
|
|
414
|
-
|
|
414
|
+
Atom.batch(() => {
|
|
415
415
|
registry.set(name, 'removed');
|
|
416
416
|
registry.set(sub, 'batch');
|
|
417
417
|
});
|
|
@@ -439,7 +439,7 @@ describe('GraphBuilder', () => {
|
|
|
439
439
|
createExtension({
|
|
440
440
|
id: 'connector',
|
|
441
441
|
connector: (node) => {
|
|
442
|
-
return
|
|
442
|
+
return Atom.make((get) =>
|
|
443
443
|
Function.pipe(
|
|
444
444
|
get(node),
|
|
445
445
|
Option.map((node) => (node.data ? node.data + 1 : 1)),
|
|
@@ -475,7 +475,7 @@ describe('GraphBuilder', () => {
|
|
|
475
475
|
createExtension({
|
|
476
476
|
id: 'connector',
|
|
477
477
|
connector: (node) =>
|
|
478
|
-
|
|
478
|
+
Atom.make((get) =>
|
|
479
479
|
Function.pipe(
|
|
480
480
|
get(node),
|
|
481
481
|
Option.map((node) => (node.data ? node.data + 1 : 1)),
|
package/src/graph-builder.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Atom, Registry } from '@effect-atom/atom-react';
|
|
6
6
|
import { effect } from '@preact/signals-core';
|
|
7
7
|
import * as Array from 'effect/Array';
|
|
8
8
|
import * as Function from 'effect/Function';
|
|
@@ -19,28 +19,28 @@ import { type ActionData, type Node, type NodeArg, type Relation, actionGroupSym
|
|
|
19
19
|
/**
|
|
20
20
|
* Graph builder extension for adding nodes to the graph based on a node id.
|
|
21
21
|
*/
|
|
22
|
-
export type ResolverExtension = (id: string) =>
|
|
22
|
+
export type ResolverExtension = (id: string) => Atom.Atom<NodeArg<any> | null>;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Graph builder extension for adding nodes to the graph based on a connection to an existing node.
|
|
26
26
|
*
|
|
27
27
|
* @param params.node The existing node the returned nodes will be connected to.
|
|
28
28
|
*/
|
|
29
|
-
export type ConnectorExtension = (node:
|
|
29
|
+
export type ConnectorExtension = (node: Atom.Atom<Option.Option<Node>>) => Atom.Atom<NodeArg<any>[]>;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Constrained case of the connector extension for more easily adding actions to the graph.
|
|
33
33
|
*/
|
|
34
34
|
export type ActionsExtension = (
|
|
35
|
-
node:
|
|
36
|
-
) =>
|
|
35
|
+
node: Atom.Atom<Option.Option<Node>>,
|
|
36
|
+
) => Atom.Atom<Omit<NodeArg<ActionData>, 'type' | 'nodes' | 'edges'>[]>;
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Constrained case of the connector extension for more easily adding action groups to the graph.
|
|
40
40
|
*/
|
|
41
41
|
export type ActionGroupsExtension = (
|
|
42
|
-
node:
|
|
43
|
-
) =>
|
|
42
|
+
node: Atom.Atom<Option.Option<Node>>,
|
|
43
|
+
) => Atom.Atom<Omit<NodeArg<typeof actionGroupSymbol>, 'type' | 'data' | 'nodes' | 'edges'>[]>;
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* A graph builder extension is used to add nodes to the graph.
|
|
@@ -79,23 +79,25 @@ export const createExtension = (extension: CreateExtensionOptions): BuilderExten
|
|
|
79
79
|
const getId = (key: string) => `${id}/${key}`;
|
|
80
80
|
|
|
81
81
|
const resolver =
|
|
82
|
-
_resolver &&
|
|
82
|
+
_resolver && Atom.family((id: string) => _resolver(id).pipe(Atom.withLabel(`graph-builder:_resolver:${id}`)));
|
|
83
83
|
|
|
84
84
|
const connector =
|
|
85
85
|
_connector &&
|
|
86
|
-
|
|
87
|
-
_connector(node).pipe(
|
|
86
|
+
Atom.family((node: Atom.Atom<Option.Option<Node>>) =>
|
|
87
|
+
_connector(node).pipe(Atom.withLabel(`graph-builder:_connector:${id}`)),
|
|
88
88
|
);
|
|
89
89
|
|
|
90
90
|
const actionGroups =
|
|
91
91
|
_actionGroups &&
|
|
92
|
-
|
|
93
|
-
_actionGroups(node).pipe(
|
|
92
|
+
Atom.family((node: Atom.Atom<Option.Option<Node>>) =>
|
|
93
|
+
_actionGroups(node).pipe(Atom.withLabel(`graph-builder:_actionGroups:${id}`)),
|
|
94
94
|
);
|
|
95
95
|
|
|
96
96
|
const actions =
|
|
97
97
|
_actions &&
|
|
98
|
-
|
|
98
|
+
Atom.family((node: Atom.Atom<Option.Option<Node>>) =>
|
|
99
|
+
_actions(node).pipe(Atom.withLabel(`graph-builder:_actions:${id}`)),
|
|
100
|
+
);
|
|
99
101
|
|
|
100
102
|
return [
|
|
101
103
|
resolver ? { id: getId('resolver'), position, resolver } : undefined,
|
|
@@ -104,15 +106,15 @@ export const createExtension = (extension: CreateExtensionOptions): BuilderExten
|
|
|
104
106
|
id: getId('connector'),
|
|
105
107
|
position,
|
|
106
108
|
relation,
|
|
107
|
-
connector:
|
|
108
|
-
|
|
109
|
+
connector: Atom.family((node) =>
|
|
110
|
+
Atom.make((get) => {
|
|
109
111
|
try {
|
|
110
112
|
return get(connector(node));
|
|
111
113
|
} catch {
|
|
112
114
|
log.warn('Error in connector', { id: getId('connector'), node });
|
|
113
115
|
return [];
|
|
114
116
|
}
|
|
115
|
-
}).pipe(
|
|
117
|
+
}).pipe(Atom.withLabel(`graph-builder:connector:${id}`)),
|
|
116
118
|
),
|
|
117
119
|
} satisfies BuilderExtension)
|
|
118
120
|
: undefined,
|
|
@@ -121,8 +123,8 @@ export const createExtension = (extension: CreateExtensionOptions): BuilderExten
|
|
|
121
123
|
id: getId('actionGroups'),
|
|
122
124
|
position,
|
|
123
125
|
relation: 'outbound',
|
|
124
|
-
connector:
|
|
125
|
-
|
|
126
|
+
connector: Atom.family((node) =>
|
|
127
|
+
Atom.make((get) => {
|
|
126
128
|
try {
|
|
127
129
|
return get(actionGroups(node)).map((arg) => ({
|
|
128
130
|
...arg,
|
|
@@ -133,7 +135,7 @@ export const createExtension = (extension: CreateExtensionOptions): BuilderExten
|
|
|
133
135
|
log.warn('Error in actionGroups', { id: getId('actionGroups'), node });
|
|
134
136
|
return [];
|
|
135
137
|
}
|
|
136
|
-
}).pipe(
|
|
138
|
+
}).pipe(Atom.withLabel(`graph-builder:connector:actionGroups:${id}`)),
|
|
137
139
|
),
|
|
138
140
|
} satisfies BuilderExtension)
|
|
139
141
|
: undefined,
|
|
@@ -142,15 +144,15 @@ export const createExtension = (extension: CreateExtensionOptions): BuilderExten
|
|
|
142
144
|
id: getId('actions'),
|
|
143
145
|
position,
|
|
144
146
|
relation: 'outbound',
|
|
145
|
-
connector:
|
|
146
|
-
|
|
147
|
+
connector: Atom.family((node) =>
|
|
148
|
+
Atom.make((get) => {
|
|
147
149
|
try {
|
|
148
150
|
return get(actions(node)).map((arg) => ({ ...arg, type: ACTION_TYPE }));
|
|
149
151
|
} catch {
|
|
150
152
|
log.warn('Error in actions', { id: getId('actions'), node });
|
|
151
153
|
return [];
|
|
152
154
|
}
|
|
153
|
-
}).pipe(
|
|
155
|
+
}).pipe(Atom.withLabel(`graph-builder:connector:actions:${id}`)),
|
|
154
156
|
),
|
|
155
157
|
} satisfies BuilderExtension)
|
|
156
158
|
: undefined,
|
|
@@ -169,7 +171,7 @@ export type BuilderExtension = Readonly<{
|
|
|
169
171
|
position: Position;
|
|
170
172
|
relation?: Relation; // Only for connector.
|
|
171
173
|
resolver?: ResolverExtension;
|
|
172
|
-
connector?: (node:
|
|
174
|
+
connector?: (node: Atom.Atom<Option.Option<Node>>) => Atom.Atom<NodeArg<any>[]>;
|
|
173
175
|
}>;
|
|
174
176
|
|
|
175
177
|
export type BuilderExtensions = BuilderExtension | BuilderExtension[] | BuilderExtensions[];
|
|
@@ -191,9 +193,9 @@ export const flattenExtensions = (extension: BuilderExtensions, acc: BuilderExte
|
|
|
191
193
|
export class GraphBuilder {
|
|
192
194
|
// TODO(wittjosiah): Use Context.
|
|
193
195
|
private readonly _subscriptions = new Map<string, CleanupFn>();
|
|
194
|
-
private readonly _extensions =
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
private readonly _extensions = Atom.make(Record.empty<string, BuilderExtension>()).pipe(
|
|
197
|
+
Atom.keepAlive,
|
|
198
|
+
Atom.withLabel('graph-builder:extensions'),
|
|
197
199
|
);
|
|
198
200
|
private readonly _initialized: Record<string, Trigger> = {};
|
|
199
201
|
private readonly _registry: Registry.Registry;
|
|
@@ -290,8 +292,8 @@ export class GraphBuilder {
|
|
|
290
292
|
this._subscriptions.clear();
|
|
291
293
|
}
|
|
292
294
|
|
|
293
|
-
private readonly _resolvers =
|
|
294
|
-
return
|
|
295
|
+
private readonly _resolvers = Atom.family<string, Atom.Atom<Option.Option<NodeArg<any>>>>((id) => {
|
|
296
|
+
return Atom.make((get) => {
|
|
295
297
|
return Function.pipe(
|
|
296
298
|
get(this._extensions),
|
|
297
299
|
Record.values,
|
|
@@ -305,8 +307,8 @@ export class GraphBuilder {
|
|
|
305
307
|
});
|
|
306
308
|
});
|
|
307
309
|
|
|
308
|
-
private readonly _connectors =
|
|
309
|
-
return
|
|
310
|
+
private readonly _connectors = Atom.family<string, Atom.Atom<NodeArg<any>[]>>((key) => {
|
|
311
|
+
return Atom.make((get) => {
|
|
310
312
|
const [id, relation] = key.split('+');
|
|
311
313
|
const node = this._graph.node(id);
|
|
312
314
|
|
|
@@ -320,7 +322,7 @@ export class GraphBuilder {
|
|
|
320
322
|
Array.filter(isNonNullable),
|
|
321
323
|
Array.flatMap((result) => get(result)),
|
|
322
324
|
);
|
|
323
|
-
}).pipe(
|
|
325
|
+
}).pipe(Atom.withLabel(`graph-builder:connectors:${key}`));
|
|
324
326
|
});
|
|
325
327
|
|
|
326
328
|
private _onExpand(id: string, relation: Relation): void {
|
|
@@ -337,7 +339,7 @@ export class GraphBuilder {
|
|
|
337
339
|
|
|
338
340
|
log('update', { id, relation, ids, removed });
|
|
339
341
|
const update = () => {
|
|
340
|
-
|
|
342
|
+
Atom.batch(() => {
|
|
341
343
|
this._graph.removeEdges(
|
|
342
344
|
removed.map((target) => ({ source: id, target })),
|
|
343
345
|
true,
|
|
@@ -403,11 +405,11 @@ export class GraphBuilder {
|
|
|
403
405
|
}
|
|
404
406
|
|
|
405
407
|
/**
|
|
406
|
-
* Creates an
|
|
408
|
+
* Creates an Atom.Atom<T> from a callback which accesses signals.
|
|
407
409
|
* Will return a new rx instance each time.
|
|
408
410
|
*/
|
|
409
|
-
export const rxFromSignal = <T>(cb: () => T):
|
|
410
|
-
return
|
|
411
|
+
export const rxFromSignal = <T>(cb: () => T): Atom.Atom<T> => {
|
|
412
|
+
return Atom.make((get) => {
|
|
411
413
|
const dispose = effect(() => {
|
|
412
414
|
get.setSelf(cb());
|
|
413
415
|
});
|
|
@@ -418,8 +420,8 @@ export const rxFromSignal = <T>(cb: () => T): Rx.Rx<T> => {
|
|
|
418
420
|
});
|
|
419
421
|
};
|
|
420
422
|
|
|
421
|
-
const observableFamily =
|
|
422
|
-
return
|
|
423
|
+
const observableFamily = Atom.family((observable: MulticastObservable<any>) => {
|
|
424
|
+
return Atom.make((get) => {
|
|
423
425
|
const subscription = observable.subscribe((value) => get.setSelf(value));
|
|
424
426
|
|
|
425
427
|
get.addFinalizer(() => subscription.unsubscribe());
|
|
@@ -429,9 +431,9 @@ const observableFamily = Rx.family((observable: MulticastObservable<any>) => {
|
|
|
429
431
|
});
|
|
430
432
|
|
|
431
433
|
/**
|
|
432
|
-
* Creates an
|
|
434
|
+
* Creates an Atom.Atom<T> from a MulticastObservable<T>
|
|
433
435
|
* Will return the same rx instance for the same observable.
|
|
434
436
|
*/
|
|
435
|
-
export const rxFromObservable = <T>(observable: MulticastObservable<T>):
|
|
436
|
-
return observableFamily(observable) as
|
|
437
|
+
export const rxFromObservable = <T>(observable: MulticastObservable<T>): Atom.Atom<T> => {
|
|
438
|
+
return observableFamily(observable) as Atom.Atom<T>;
|
|
437
439
|
};
|
package/src/graph.test.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
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
|
|
|
@@ -238,7 +238,7 @@ describe('Graph', () => {
|
|
|
238
238
|
expect(count).toEqual(5);
|
|
239
239
|
|
|
240
240
|
// Batching the edge and node updates fires a single update.
|
|
241
|
-
|
|
241
|
+
Atom.batch(() => {
|
|
242
242
|
graph.addEdge({ source: exampleId(1), target: exampleId(6) });
|
|
243
243
|
graph.addNode({ id: exampleId(6), type: EXAMPLE_TYPE });
|
|
244
244
|
});
|