@dxos/app-graph 0.8.4-main.b97322e → 0.8.4-main.c4373fc

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/app-graph",
3
- "version": "0.8.4-main.b97322e",
3
+ "version": "0.8.4-main.c4373fc",
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",
@@ -10,6 +10,7 @@
10
10
  "type": "module",
11
11
  "exports": {
12
12
  ".": {
13
+ "source": "./src/index.ts",
13
14
  "types": "./dist/types/src/index.d.ts",
14
15
  "browser": "./dist/lib/browser/index.mjs",
15
16
  "node": "./dist/lib/node-esm/index.mjs"
@@ -24,44 +25,43 @@
24
25
  "src"
25
26
  ],
26
27
  "dependencies": {
27
- "@preact/signals-core": "^1.9.0",
28
+ "@preact/signals-core": "^1.12.1",
28
29
  "main-thread-scheduling": "^14.1.1",
29
- "@dxos/async": "0.8.4-main.b97322e",
30
- "@dxos/debug": "0.8.4-main.b97322e",
31
- "@dxos/echo": "0.8.4-main.b97322e",
32
- "@dxos/echo-schema": "0.8.4-main.b97322e",
33
- "@dxos/echo-signals": "0.8.4-main.b97322e",
34
- "@dxos/invariant": "0.8.4-main.b97322e",
35
- "@dxos/live-object": "0.8.4-main.b97322e",
36
- "@dxos/log": "0.8.4-main.b97322e",
37
- "@dxos/util": "0.8.4-main.b97322e"
30
+ "@dxos/async": "0.8.4-main.c4373fc",
31
+ "@dxos/debug": "0.8.4-main.c4373fc",
32
+ "@dxos/echo": "0.8.4-main.c4373fc",
33
+ "@dxos/echo-signals": "0.8.4-main.c4373fc",
34
+ "@dxos/log": "0.8.4-main.c4373fc",
35
+ "@dxos/live-object": "0.8.4-main.c4373fc",
36
+ "@dxos/util": "0.8.4-main.c4373fc",
37
+ "@dxos/invariant": "0.8.4-main.c4373fc"
38
38
  },
39
39
  "devDependencies": {
40
- "@effect-rx/rx-react": "0.38.0",
41
- "@effect/platform": "0.89.0",
42
- "@types/react": "~18.2.0",
43
- "@types/react-dom": "~18.2.0",
44
- "effect": "3.17.0",
45
- "react": "~18.2.0",
46
- "react-dom": "~18.2.0",
47
- "vite": "5.4.7",
48
- "@dxos/random": "0.8.4-main.b97322e",
49
- "@dxos/echo-db": "0.8.4-main.b97322e",
50
- "@dxos/react-ui": "0.8.4-main.b97322e",
51
- "@dxos/react-client": "0.8.4-main.b97322e",
52
- "@dxos/react-ui-list": "0.8.4-main.b97322e",
53
- "@dxos/react-ui-tabs": "0.8.4-main.b97322e",
54
- "@dxos/storybook-utils": "0.8.4-main.b97322e",
55
- "@dxos/react-ui-theme": "0.8.4-main.b97322e"
40
+ "@effect-rx/rx-react": "0.42.4",
41
+ "@effect/platform": "0.92.1",
42
+ "@types/react": "~19.2.2",
43
+ "@types/react-dom": "~19.2.1",
44
+ "effect": "3.18.3",
45
+ "react": "~19.2.0",
46
+ "react-dom": "~19.2.0",
47
+ "vite": "7.1.9",
48
+ "@dxos/echo-db": "0.8.4-main.c4373fc",
49
+ "@dxos/random": "0.8.4-main.c4373fc",
50
+ "@dxos/react-client": "0.8.4-main.c4373fc",
51
+ "@dxos/react-ui": "0.8.4-main.c4373fc",
52
+ "@dxos/react-ui-theme": "0.8.4-main.c4373fc",
53
+ "@dxos/react-ui-tabs": "0.8.4-main.c4373fc",
54
+ "@dxos/react-ui-list": "0.8.4-main.c4373fc",
55
+ "@dxos/storybook-utils": "0.8.4-main.c4373fc"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@effect-rx/rx-react": "^0.34.1",
59
59
  "@effect/platform": "^0.80.12",
60
60
  "effect": "3.14.21",
61
- "react": "~18.2.0",
62
- "react-dom": "~18.2.0",
63
- "@dxos/react-ui": "0.8.4-main.b97322e",
64
- "@dxos/react-ui-theme": "0.8.4-main.b97322e"
61
+ "react": "^19.0.0",
62
+ "react-dom": "^19.0.0",
63
+ "@dxos/react-ui": "0.8.4-main.c4373fc",
64
+ "@dxos/react-ui-theme": "0.8.4-main.c4373fc"
65
65
  },
66
66
  "publishConfig": {
67
67
  "access": "public"
@@ -3,13 +3,14 @@
3
3
  //
4
4
 
5
5
  import { Registry, Rx } from '@effect-rx/rx-react';
6
- import { Option, pipe } from 'effect';
6
+ import * as Function from 'effect/Function';
7
+ import * as Option from 'effect/Option';
7
8
  import { describe, expect, onTestFinished, test } from 'vitest';
8
9
 
9
- import { sleep, Trigger } from '@dxos/async';
10
+ import { Trigger, sleep } from '@dxos/async';
10
11
 
11
12
  import { ROOT_ID } from './graph';
12
- import { createExtension, GraphBuilder } from './graph-builder';
13
+ import { GraphBuilder, createExtension } from './graph-builder';
13
14
  import { type Node } from './node';
14
15
 
15
16
  const exampleId = (id: number) => `dx:test:${id}`;
@@ -17,6 +18,63 @@ const EXAMPLE_ID = exampleId(1);
17
18
  const EXAMPLE_TYPE = 'dxos.org/type/example';
18
19
 
19
20
  describe('GraphBuilder', () => {
21
+ describe('resolver', () => {
22
+ test('works', async () => {
23
+ const registry = Registry.make();
24
+ const builder = new GraphBuilder({ registry });
25
+ const graph = builder.graph;
26
+
27
+ {
28
+ const node = graph.getNode(EXAMPLE_ID).pipe(Option.getOrNull);
29
+ expect(node).to.be.null;
30
+ }
31
+
32
+ builder.addExtension(
33
+ createExtension({
34
+ id: 'resolver',
35
+ resolver: () => {
36
+ console.log('resolver');
37
+ return Rx.make({ id: EXAMPLE_ID, type: EXAMPLE_TYPE, data: 1 });
38
+ },
39
+ }),
40
+ );
41
+ await graph.initialize(EXAMPLE_ID);
42
+
43
+ {
44
+ const node = graph.getNode(EXAMPLE_ID).pipe(Option.getOrNull);
45
+ expect(node?.id).to.equal(EXAMPLE_ID);
46
+ expect(node?.type).to.equal(EXAMPLE_TYPE);
47
+ expect(node?.data).to.equal(1);
48
+ }
49
+ });
50
+
51
+ test('updates', async () => {
52
+ const registry = Registry.make();
53
+ const builder = new GraphBuilder({ registry });
54
+ const name = Rx.make('default');
55
+ builder.addExtension(
56
+ createExtension({
57
+ id: 'resolver',
58
+ resolver: () => Rx.make((get) => ({ id: EXAMPLE_ID, type: EXAMPLE_TYPE, data: get(name) })),
59
+ }),
60
+ );
61
+ const graph = builder.graph;
62
+ await graph.initialize(EXAMPLE_ID);
63
+
64
+ {
65
+ const node = graph.getNode(EXAMPLE_ID).pipe(Option.getOrNull);
66
+ expect(node?.data).to.equal('default');
67
+ }
68
+
69
+ registry.set(name, 'updated');
70
+
71
+ {
72
+ const node = graph.getNode(EXAMPLE_ID).pipe(Option.getOrNull);
73
+ expect(node?.data).to.equal('updated');
74
+ }
75
+ });
76
+ });
77
+
20
78
  describe('connector', () => {
21
79
  test('works', () => {
22
80
  const registry = Registry.make();
@@ -181,7 +239,7 @@ describe('GraphBuilder', () => {
181
239
  id: 'root',
182
240
  connector: (node) =>
183
241
  Rx.make((get) =>
184
- pipe(
242
+ Function.pipe(
185
243
  get(node),
186
244
  Option.flatMap((node) => (node.id === 'root' ? Option.some(get(name)) : Option.none())),
187
245
  Option.filter((name) => name !== 'removed'),
@@ -273,7 +331,7 @@ describe('GraphBuilder', () => {
273
331
  id: 'root',
274
332
  connector: (node) =>
275
333
  Rx.make((get) =>
276
- pipe(
334
+ Function.pipe(
277
335
  get(node),
278
336
  Option.flatMap((node) => (node.id === 'root' ? Option.some(get(name)) : Option.none())),
279
337
  Option.filter((name) => name !== 'removed'),
@@ -286,7 +344,7 @@ describe('GraphBuilder', () => {
286
344
  id: 'connector1',
287
345
  connector: (node) =>
288
346
  Rx.make((get) =>
289
- pipe(
347
+ Function.pipe(
290
348
  get(node),
291
349
  Option.flatMap((node) => (node.id === EXAMPLE_ID ? Option.some(get(sub)) : Option.none())),
292
350
  Option.map((sub) => [{ id: exampleId(2), type: EXAMPLE_TYPE, data: sub }]),
@@ -298,7 +356,7 @@ describe('GraphBuilder', () => {
298
356
  id: 'connector2',
299
357
  connector: (node) =>
300
358
  Rx.make((get) =>
301
- pipe(
359
+ Function.pipe(
302
360
  get(node),
303
361
  Option.flatMap((node) => (node.id === EXAMPLE_ID ? Option.some(node.data) : Option.none())),
304
362
  Option.map((data) => [{ id: exampleId(3), type: EXAMPLE_TYPE, data }]),
@@ -382,7 +440,7 @@ describe('GraphBuilder', () => {
382
440
  id: 'connector',
383
441
  connector: (node) => {
384
442
  return Rx.make((get) =>
385
- pipe(
443
+ Function.pipe(
386
444
  get(node),
387
445
  Option.map((node) => (node.data ? node.data + 1 : 1)),
388
446
  Option.filter((data) => data <= 5),
@@ -418,7 +476,7 @@ describe('GraphBuilder', () => {
418
476
  id: 'connector',
419
477
  connector: (node) =>
420
478
  Rx.make((get) =>
421
- pipe(
479
+ Function.pipe(
422
480
  get(node),
423
481
  Option.map((node) => (node.data ? node.data + 1 : 1)),
424
482
  Option.filter((data) => data <= 5),
@@ -4,14 +4,22 @@
4
4
 
5
5
  import { Registry, Rx } from '@effect-rx/rx-react';
6
6
  import { effect } from '@preact/signals-core';
7
- import { Array, type Option, pipe, Record } from 'effect';
7
+ import * as Array from 'effect/Array';
8
+ import * as Function from 'effect/Function';
9
+ import * as Option from 'effect/Option';
10
+ import * as Record from 'effect/Record';
8
11
 
9
- import { type MulticastObservable, type CleanupFn } from '@dxos/async';
12
+ import { type CleanupFn, type MulticastObservable, type Trigger } from '@dxos/async';
10
13
  import { log } from '@dxos/log';
11
- import { byPosition, getDebugName, isNode, isNonNullable, type MaybePromise, type Position } from '@dxos/util';
14
+ import { type MaybePromise, type Position, byPosition, getDebugName, isNode, isNonNullable } from '@dxos/util';
12
15
 
13
- import { ACTION_GROUP_TYPE, ACTION_TYPE, Graph, ROOT_ID, type GraphParams, type ExpandableGraph } from './graph';
14
- import { actionGroupSymbol, type ActionData, type Node, type NodeArg, type Relation } from './node';
16
+ import { ACTION_GROUP_TYPE, ACTION_TYPE, type ExpandableGraph, Graph, type GraphParams, ROOT_ID } from './graph';
17
+ import { type ActionData, type Node, type NodeArg, type Relation, actionGroupSymbol } from './node';
18
+
19
+ /**
20
+ * Graph builder extension for adding nodes to the graph based on a node id.
21
+ */
22
+ export type ResolverExtension = (id: string) => Rx.Rx<NodeArg<any> | null>;
15
23
 
16
24
  /**
17
25
  * Graph builder extension for adding nodes to the graph based on a connection to an existing node.
@@ -49,8 +57,7 @@ export type CreateExtensionOptions = {
49
57
  id: string;
50
58
  relation?: Relation;
51
59
  position?: Position;
52
- // TODO(wittjosiah): On initialize to restore state from cache.
53
- // resolver?: ResolverExtension;
60
+ resolver?: ResolverExtension;
54
61
  connector?: ConnectorExtension;
55
62
  actions?: ActionsExtension;
56
63
  actionGroups?: ActionGroupsExtension;
@@ -64,12 +71,16 @@ export const createExtension = (extension: CreateExtensionOptions): BuilderExten
64
71
  id,
65
72
  position = 'static',
66
73
  relation = 'outbound',
74
+ resolver: _resolver,
67
75
  connector: _connector,
68
76
  actions: _actions,
69
77
  actionGroups: _actionGroups,
70
78
  } = extension;
71
79
  const getId = (key: string) => `${id}/${key}`;
72
80
 
81
+ const resolver =
82
+ _resolver && Rx.family((id: string) => _resolver(id).pipe(Rx.withLabel(`graph-builder:_resolver:${id}`)));
83
+
73
84
  const connector =
74
85
  _connector &&
75
86
  Rx.family((node: Rx.Rx<Option.Option<Node>>) =>
@@ -87,7 +98,7 @@ export const createExtension = (extension: CreateExtensionOptions): BuilderExten
87
98
  Rx.family((node: Rx.Rx<Option.Option<Node>>) => _actions(node).pipe(Rx.withLabel(`graph-builder:_actions:${id}`)));
88
99
 
89
100
  return [
90
- // resolver ? { id: getId('resolver'), position, resolver } : undefined,
101
+ resolver ? { id: getId('resolver'), position, resolver } : undefined,
91
102
  connector
92
103
  ? ({
93
104
  id: getId('connector'),
@@ -157,7 +168,7 @@ export type BuilderExtension = Readonly<{
157
168
  id: string;
158
169
  position: Position;
159
170
  relation?: Relation; // Only for connector.
160
- // resolver?: ResolverExtension;
171
+ resolver?: ResolverExtension;
161
172
  connector?: (node: Rx.Rx<Option.Option<Node>>) => Rx.Rx<NodeArg<any>[]>;
162
173
  }>;
163
174
 
@@ -179,12 +190,12 @@ export const flattenExtensions = (extension: BuilderExtensions, acc: BuilderExte
179
190
  // Should track LRU nodes that are not in the set/radius and remove them beyond a certain threshold.
180
191
  export class GraphBuilder {
181
192
  // TODO(wittjosiah): Use Context.
182
- private readonly _connectorSubscriptions = new Map<string, CleanupFn>();
193
+ private readonly _subscriptions = new Map<string, CleanupFn>();
183
194
  private readonly _extensions = Rx.make(Record.empty<string, BuilderExtension>()).pipe(
184
195
  Rx.keepAlive,
185
196
  Rx.withLabel('graph-builder:extensions'),
186
197
  );
187
-
198
+ private readonly _initialized: Record<string, Trigger> = {};
188
199
  private readonly _registry: Registry.Registry;
189
200
  private readonly _graph: Graph;
190
201
 
@@ -194,7 +205,7 @@ export class GraphBuilder {
194
205
  ...params,
195
206
  registry: this._registry,
196
207
  onExpand: (id, relation) => this._onExpand(id, relation),
197
- // onInitialize: (id) => this._onInitialize(id),
208
+ onInitialize: (id) => this._onInitialize(id),
198
209
  onRemoveNode: (id) => this._onRemoveNode(id),
199
210
  });
200
211
  }
@@ -275,16 +286,31 @@ export class GraphBuilder {
275
286
  }
276
287
 
277
288
  destroy(): void {
278
- this._connectorSubscriptions.forEach((unsubscribe) => unsubscribe());
279
- this._connectorSubscriptions.clear();
289
+ this._subscriptions.forEach((unsubscribe) => unsubscribe());
290
+ this._subscriptions.clear();
280
291
  }
281
292
 
293
+ private readonly _resolvers = Rx.family<string, Rx.Rx<Option.Option<NodeArg<any>>>>((id) => {
294
+ return Rx.make((get) => {
295
+ return Function.pipe(
296
+ get(this._extensions),
297
+ Record.values,
298
+ Array.sortBy(byPosition),
299
+ Array.map(({ resolver }) => resolver),
300
+ Array.filter(isNonNullable),
301
+ Array.map((resolver) => get(resolver(id))),
302
+ Array.filter(isNonNullable),
303
+ Array.head,
304
+ );
305
+ });
306
+ });
307
+
282
308
  private readonly _connectors = Rx.family<string, Rx.Rx<NodeArg<any>[]>>((key) => {
283
309
  return Rx.make((get) => {
284
310
  const [id, relation] = key.split('+');
285
311
  const node = this._graph.node(id);
286
312
 
287
- return pipe(
313
+ return Function.pipe(
288
314
  get(this._extensions),
289
315
  Record.values,
290
316
  // TODO(wittjosiah): Sort on write rather than read.
@@ -341,17 +367,38 @@ export class GraphBuilder {
341
367
  { immediate: true },
342
368
  );
343
369
 
344
- this._connectorSubscriptions.set(id, cancel);
370
+ this._subscriptions.set(id, cancel);
345
371
  }
346
372
 
347
- // TODO(wittjosiah): On initialize to restore state from cache.
348
- // private async _onInitialize(id: string) {
349
- // log('onInitialize', { id });
350
- // }
373
+ // TODO(wittjosiah): If the same node is added by a connector, the resolver should probably cancel itself?
374
+ private async _onInitialize(id: string) {
375
+ log('onInitialize', { id });
376
+ const resolver = this._resolvers(id);
377
+
378
+ const cancel = this._registry.subscribe(
379
+ resolver,
380
+ (node) => {
381
+ const trigger = this._initialized[id];
382
+ Option.match(node, {
383
+ onSome: (node) => {
384
+ this._graph.addNodes([node]);
385
+ trigger?.wake();
386
+ },
387
+ onNone: () => {
388
+ trigger?.wake();
389
+ this._graph.removeNodes([id]);
390
+ },
391
+ });
392
+ },
393
+ { immediate: true },
394
+ );
395
+
396
+ this._subscriptions.set(id, cancel);
397
+ }
351
398
 
352
399
  private _onRemoveNode(id: string): void {
353
- this._connectorSubscriptions.get(id)?.();
354
- this._connectorSubscriptions.delete(id);
400
+ this._subscriptions.get(id)?.();
401
+ this._subscriptions.delete(id);
355
402
  }
356
403
  }
357
404
 
package/src/graph.test.ts CHANGED
@@ -3,10 +3,10 @@
3
3
  //
4
4
 
5
5
  import { Registry, Rx } from '@effect-rx/rx-react';
6
- import { Option } from 'effect';
6
+ import * as Option from 'effect/Option';
7
7
  import { assert, describe, expect, onTestFinished, test } from 'vitest';
8
8
 
9
- import { getGraph, Graph, ROOT_ID, ROOT_TYPE } from './graph';
9
+ import { Graph, ROOT_ID, ROOT_TYPE, getGraph } from './graph';
10
10
  import { type Node } from './node';
11
11
 
12
12
  const exampleId = (id: number) => `dx:test:${id}`;
package/src/graph.ts CHANGED
@@ -3,15 +3,17 @@
3
3
  //
4
4
 
5
5
  import { Registry, Rx } from '@effect-rx/rx-react';
6
- import { Option, pipe, Record } from 'effect';
6
+ import * as Function from 'effect/Function';
7
+ import * as Option from 'effect/Option';
8
+ import * as Record from 'effect/Record';
7
9
 
8
10
  import { Event, Trigger } from '@dxos/async';
9
11
  import { todo } from '@dxos/debug';
10
12
  import { invariant } from '@dxos/invariant';
11
13
  import { log } from '@dxos/log';
12
- import { isNonNullable, type MakeOptional } from '@dxos/util';
14
+ import { type MakeOptional, isNonNullable } from '@dxos/util';
13
15
 
14
- import { type NodeArg, type Node, type Relation, type Action, type ActionGroup } from './node';
16
+ import { type Action, type ActionGroup, type Node, type NodeArg, type Relation } from './node';
15
17
 
16
18
  const graphSymbol = Symbol('graph');
17
19
  type DeepWriteable<T> = { -readonly [K in keyof T]: T[K] extends object ? DeepWriteable<T[K]> : T[K] };
@@ -59,8 +61,7 @@ export type GraphParams = {
59
61
  nodes?: MakeOptional<Node, 'data' | 'cacheable'>[];
60
62
  edges?: Record<string, Edges>;
61
63
  onExpand?: Graph['_onExpand'];
62
- // TODO(wittjosiah): On initialize to restore state from cache.
63
- // onInitialize?: Graph['_onInitialize'];
64
+ onInitialize?: Graph['_onInitialize'];
64
65
  onRemoveNode?: Graph['_onRemoveNode'];
65
66
  };
66
67
 
@@ -166,7 +167,7 @@ export interface ExpandableGraph extends ReadableGraph {
166
167
  *
167
168
  * Fires the `onInitialize` callback to provide initial data for a node.
168
169
  */
169
- // initialize(id: string): Promise<void>;
170
+ initialize(id: string): Promise<void>;
170
171
 
171
172
  /**
172
173
  * Expand a node in the graph.
@@ -230,7 +231,7 @@ export class Graph implements WritableGraph {
230
231
  readonly onNodeChanged = new Event<{ id: string; node: Option.Option<Node> }>();
231
232
 
232
233
  private readonly _onExpand?: (id: string, relation: Relation) => void;
233
- // private readonly _onInitialize?: (id: string) => Promise<void>;
234
+ private readonly _onInitialize?: (id: string) => Promise<void>;
234
235
  private readonly _onRemoveNode?: (id: string) => void;
235
236
 
236
237
  private readonly _registry: Registry.Registry;
@@ -309,8 +310,9 @@ export class Graph implements WritableGraph {
309
310
  }).pipe(Rx.withLabel(`graph:json:${id}`));
310
311
  });
311
312
 
312
- constructor({ registry, nodes, edges, onExpand, onRemoveNode }: GraphParams = {}) {
313
+ constructor({ registry, nodes, edges, onInitialize, onExpand, onRemoveNode }: GraphParams = {}) {
313
314
  this._registry = registry ?? Registry.make();
315
+ this._onInitialize = onInitialize;
314
316
  this._onExpand = onExpand;
315
317
  this._onRemoveNode = onRemoveNode;
316
318
 
@@ -379,15 +381,14 @@ export class Graph implements WritableGraph {
379
381
  return this._registry.get(this.edges(id));
380
382
  }
381
383
 
382
- // TODO(wittjosiah): On initialize to restore state from cache.
383
- // async initialize(id: string) {
384
- // const initialized = Record.get(this._initialized, id).pipe(Option.getOrElse(() => false));
385
- // log('initialize', { id, initialized });
386
- // if (!initialized) {
387
- // await this._onInitialize?.(id);
388
- // Record.set(this._initialized, id, true);
389
- // }
390
- // }
384
+ async initialize(id: string) {
385
+ const initialized = Record.get(this._initialized, id).pipe(Option.getOrElse(() => false));
386
+ log('initialize', { id, initialized });
387
+ if (!initialized) {
388
+ await this._onInitialize?.(id);
389
+ Record.set(this._initialized, id, true);
390
+ }
391
+ }
391
392
 
392
393
  expand(id: string, relation: Relation = 'outbound'): void {
393
394
  const key = `${id}$${relation}`;
@@ -554,7 +555,7 @@ export class Graph implements WritableGraph {
554
555
  }
555
556
 
556
557
  getPath({ source = 'root', target }: { source?: string; target: string }): Option.Option<string[]> {
557
- return pipe(
558
+ return Function.pipe(
558
559
  this.getNode(source),
559
560
  Option.flatMap((node) => {
560
561
  let found: Option.Option<string[]> = Option.none();
package/src/node.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type MaybePromise, type MakeOptional } from '@dxos/util';
5
+ import { type MakeOptional, type MaybePromise } from '@dxos/util';
6
6
 
7
7
  import { ACTION_GROUP_TYPE, ACTION_TYPE } from './graph';
8
8
 
@@ -7,14 +7,14 @@ import { signal } from '@preact/signals-core';
7
7
  import { afterEach, beforeEach, describe, expect, onTestFinished, test } from 'vitest';
8
8
 
9
9
  import { Trigger } from '@dxos/async';
10
+ import { Obj, Type } from '@dxos/echo';
11
+ import { Ref } from '@dxos/echo/internal';
10
12
  import { Filter } from '@dxos/echo-db';
11
13
  import { EchoTestBuilder } from '@dxos/echo-db/testing';
12
- import { Expando, Ref } from '@dxos/echo-schema';
13
14
  import { registerSignalsRuntime } from '@dxos/echo-signals';
14
- import { live } from '@dxos/live-object';
15
15
 
16
16
  import { ROOT_ID } from './graph';
17
- import { createExtension, GraphBuilder, rxFromSignal } from './graph-builder';
17
+ import { GraphBuilder, createExtension, rxFromSignal } from './graph-builder';
18
18
  import { rxFromQuery } from './testing';
19
19
 
20
20
  registerSignalsRuntime();
@@ -174,15 +174,15 @@ describe('signals integration', () => {
174
174
  const registry = Registry.make();
175
175
  await using peer = await dbBuilder.createPeer();
176
176
  await using db = await peer.createDatabase();
177
- db.add(live(Expando, { name: 'a' }));
178
- db.add(live(Expando, { name: 'b' }));
177
+ db.add(Obj.make(Type.Expando, { name: 'a' }));
178
+ db.add(Obj.make(Type.Expando, { name: 'b' }));
179
179
 
180
180
  const builder = new GraphBuilder({ registry });
181
181
  builder.addExtension(
182
182
  createExtension({
183
183
  id: 'expando',
184
184
  connector: () => {
185
- const query = db.query(Filter.type(Expando));
185
+ const query = db.query(Filter.type(Type.Expando));
186
186
 
187
187
  return Rx.make((get) => {
188
188
  const objects = get(rxFromQuery(query));
@@ -205,7 +205,7 @@ describe('signals integration', () => {
205
205
  graph.expand(ROOT_ID);
206
206
  expect(count).to.eq(2);
207
207
 
208
- const object = db.add(live(Expando, { name: 'c' }));
208
+ const object = db.add(Obj.make(Type.Expando, { name: 'c' }));
209
209
  await db.flush();
210
210
  expect(count).to.eq(3);
211
211
 
@@ -2,40 +2,40 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Registry, RegistryContext, Rx, useRxValue } from '@effect-rx/rx-react';
8
- import { type Meta } from '@storybook/react-vite';
9
- import { Option, pipe } from 'effect';
6
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
+ import * as Function from 'effect/Function';
8
+ import * as Option from 'effect/Option';
10
9
  import React, { type PropsWithChildren, useCallback, useContext, useEffect, useMemo, useState } from 'react';
11
10
 
12
11
  import {
13
- live,
14
- isSpace,
12
+ Expando,
13
+ Filter,
14
+ type Live,
15
15
  Query,
16
16
  type QueryResult,
17
17
  type Space,
18
18
  SpaceState,
19
- Expando,
20
- type Live,
21
- Filter,
19
+ isSpace,
20
+ live,
22
21
  } from '@dxos/client/echo';
23
22
  import { Obj, Type } from '@dxos/echo';
24
23
  import { faker } from '@dxos/random';
25
24
  import { type Client, useClient } from '@dxos/react-client';
26
25
  import { withClientProvider } from '@dxos/react-client/testing';
27
- import { Input, Select, Icon, IconButton } from '@dxos/react-ui';
26
+ import { Icon, IconButton, Input, Select } from '@dxos/react-ui';
27
+ import { withTheme } from '@dxos/react-ui/testing';
28
28
  import { Path, Tree } from '@dxos/react-ui-list';
29
29
  import { getSize, mx } from '@dxos/react-ui-theme';
30
- import { withTheme } from '@dxos/storybook-utils';
31
30
  import { byPosition, isNonNullable, safeParseInt } from '@dxos/util';
32
31
 
33
- import { JsonTree } from './Tree';
34
32
  import { type ExpandableGraph, ROOT_ID } from '../graph';
35
33
  import { GraphBuilder, createExtension, rxFromObservable, rxFromSignal } from '../graph-builder';
36
34
  import { type Node } from '../node';
37
35
  import { rxFromQuery } from '../testing';
38
36
 
37
+ import { JsonTree } from './Tree';
38
+
39
39
  const DEFAULT_PERIOD = 500;
40
40
 
41
41
  enum Action {
@@ -61,7 +61,7 @@ const createGraph = (client: Client, registry: Registry.Registry): ExpandableGra
61
61
  id: 'space',
62
62
  connector: (node) =>
63
63
  Rx.make((get) =>
64
- pipe(
64
+ Function.pipe(
65
65
  get(node),
66
66
  Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
67
67
  Option.map(() => {
@@ -85,7 +85,7 @@ const createGraph = (client: Client, registry: Registry.Registry): ExpandableGra
85
85
  connector: (node) => {
86
86
  let query: QueryResult<Live<Expando>> | undefined;
87
87
  return Rx.make((get) =>
88
- pipe(
88
+ Function.pipe(
89
89
  get(node),
90
90
  Option.flatMap((node) => (isSpace(node.data) ? Option.some(node.data) : Option.none())),
91
91
  Option.map((space) => {
@@ -246,23 +246,25 @@ const Controls = ({ children }: PropsWithChildren) => {
246
246
  );
247
247
  };
248
248
 
249
- const meta: Meta = {
249
+ const meta = {
250
250
  title: 'sdk/app-graph/EchoGraph',
251
251
  decorators: [
252
+ withTheme,
252
253
  withClientProvider({
253
254
  createIdentity: true,
254
- onIdentityCreated: async ({ client }) => {
255
+ onCreateIdentity: async ({ client }) => {
255
256
  await client.spaces.create();
256
257
  await client.spaces.create();
257
258
  },
258
259
  }),
259
- withTheme,
260
260
  ],
261
- };
261
+ } satisfies Meta<typeof Registry>;
262
262
 
263
263
  export default meta;
264
264
 
265
- export const JsonView = {
265
+ type Story = StoryObj<typeof meta>;
266
+
267
+ export const JsonView: Story = {
266
268
  render: () => {
267
269
  const client = useClient();
268
270
  const registry = useContext(RegistryContext);
@@ -278,7 +280,7 @@ export const JsonView = {
278
280
  },
279
281
  };
280
282
 
281
- export const TreeView = {
283
+ export const TreeView: Story = {
282
284
  render: () => {
283
285
  const client = useClient();
284
286
  const registry = useContext(RegistryContext);