@dxos/app-graph 0.8.4-main.5ea62a8 → 0.8.4-main.66e292d

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.
@@ -2,38 +2,28 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type Registry, RegistryContext, Rx, useRxValue } from '@effect-rx/rx-react';
5
+ import { Atom, type Registry, RegistryContext, useAtomValue } from '@effect-atom/atom-react';
8
6
  import { type Meta, type StoryObj } from '@storybook/react-vite';
9
- import { Option, pipe } from 'effect';
7
+ import * as Function from 'effect/Function';
8
+ import * as Option from 'effect/Option';
9
+ import type * as Schema from 'effect/Schema';
10
10
  import React, { type PropsWithChildren, useCallback, useContext, useEffect, useMemo, useState } from 'react';
11
11
 
12
- import {
13
- Expando,
14
- Filter,
15
- type Live,
16
- Query,
17
- type QueryResult,
18
- type Space,
19
- SpaceState,
20
- isSpace,
21
- live,
22
- } from '@dxos/client/echo';
23
- import { Obj, Type } from '@dxos/echo';
12
+ import { Filter, type Live, Query, type Space, SpaceState, isSpace, live } from '@dxos/client/echo';
13
+ import { type Database, Obj, Type } from '@dxos/echo';
24
14
  import { faker } from '@dxos/random';
25
15
  import { type Client, useClient } from '@dxos/react-client';
26
16
  import { withClientProvider } from '@dxos/react-client/testing';
27
17
  import { Icon, IconButton, Input, Select } from '@dxos/react-ui';
18
+ import { withTheme } from '@dxos/react-ui/testing';
28
19
  import { Path, Tree } from '@dxos/react-ui-list';
29
20
  import { getSize, mx } from '@dxos/react-ui-theme';
30
- import { withTheme } from '@dxos/storybook-utils';
31
21
  import { byPosition, isNonNullable, safeParseInt } from '@dxos/util';
32
22
 
33
23
  import { type ExpandableGraph, ROOT_ID } from '../graph';
34
- import { GraphBuilder, createExtension, rxFromObservable, rxFromSignal } from '../graph-builder';
24
+ import { GraphBuilder, atomFromObservable, atomFromSignal, createExtension } from '../graph-builder';
35
25
  import { type Node } from '../node';
36
- import { rxFromQuery } from '../testing';
26
+ import { atomFromQuery } from '../testing';
37
27
 
38
28
  import { JsonTree } from './Tree';
39
29
 
@@ -61,18 +51,20 @@ const createGraph = (client: Client, registry: Registry.Registry): ExpandableGra
61
51
  const spaceBuilderExtension = createExtension({
62
52
  id: 'space',
63
53
  connector: (node) =>
64
- Rx.make((get) =>
65
- pipe(
54
+ Atom.make((get) =>
55
+ Function.pipe(
66
56
  get(node),
67
57
  Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
68
58
  Option.map(() => {
69
- const spaces = get(rxFromObservable(client.spaces)) ?? [];
59
+ const spaces = get(atomFromObservable(client.spaces)) ?? [];
70
60
  return spaces
71
- .filter((space) => get(rxFromObservable(space.state)) === SpaceState.SPACE_READY)
61
+ .filter((space) => get(atomFromObservable(space.state)) === SpaceState.SPACE_READY)
72
62
  .map((space) => ({
73
63
  id: space.id,
74
64
  type: 'dxos.org/type/Space',
75
- properties: { label: get(rxFromSignal(() => space.properties.name)) },
65
+ properties: {
66
+ label: get(atomFromSignal(() => space.properties.name)),
67
+ },
76
68
  data: space,
77
69
  }));
78
70
  }),
@@ -84,16 +76,18 @@ const createGraph = (client: Client, registry: Registry.Registry): ExpandableGra
84
76
  const objectBuilderExtension = createExtension({
85
77
  id: 'object',
86
78
  connector: (node) => {
87
- let query: QueryResult<Live<Expando>> | undefined;
88
- return Rx.make((get) =>
89
- pipe(
79
+ // TODO(wittjosiah): Find a simpler way to define this type.
80
+ let result: Database.QueryResult<Schema.Schema.Type<typeof Type.Expando>> | undefined;
81
+ return Atom.make((get) =>
82
+ Function.pipe(
90
83
  get(node),
91
84
  Option.flatMap((node) => (isSpace(node.data) ? Option.some(node.data) : Option.none())),
92
85
  Option.map((space) => {
93
- if (!query) {
94
- query = space.db.query(Query.type(Expando, { type: 'test' }));
86
+ if (!result) {
87
+ result = space.db.query(Query.type(Type.Expando, { type: 'test' }));
95
88
  }
96
- const objects = get(rxFromQuery(query));
89
+
90
+ const objects = get(atomFromQuery(result));
97
91
  return objects.map((object) => ({
98
92
  id: object.id,
99
93
  type: 'dxos.org/type/test',
@@ -115,7 +109,6 @@ const createGraph = (client: Client, registry: Registry.Registry): ExpandableGra
115
109
  });
116
110
  graph.expand(ROOT_ID);
117
111
  (window as any).graph = graph;
118
-
119
112
  return graph;
120
113
  };
121
114
 
@@ -135,7 +128,7 @@ const getRandomSpace = (client: Client): Space | undefined => {
135
128
  const getSpaceWithObjects = async (client: Client): Promise<Space | undefined> => {
136
129
  const readySpaces = client.spaces.get().filter((space) => space.state.get() === SpaceState.SPACE_READY);
137
130
  const spaceQueries = await Promise.all(
138
- readySpaces.map((space) => space.db.query(Filter.type(Expando, { type: 'test' })).run()),
131
+ readySpaces.map((space) => space.db.query(Filter.type(Type.Expando, { type: 'test' })).run()),
139
132
  );
140
133
  const spaces = readySpaces.filter((space, index) => spaceQueries[index].objects.length > 0);
141
134
  return spaces[Math.floor(Math.random() * spaces.length)];
@@ -160,13 +153,18 @@ const runAction = async (client: Client, action: Action) => {
160
153
  }
161
154
 
162
155
  case Action.ADD_OBJECT:
163
- getRandomSpace(client)?.db.add(Obj.make(Type.Expando, { type: 'test', name: faker.commerce.productName() }));
156
+ getRandomSpace(client)?.db.add(
157
+ Obj.make(Type.Expando, {
158
+ type: 'test',
159
+ name: faker.commerce.productName(),
160
+ }),
161
+ );
164
162
  break;
165
163
 
166
164
  case Action.REMOVE_OBJECT: {
167
165
  const space = await getSpaceWithObjects(client);
168
166
  if (space) {
169
- const { objects } = await space.db.query(Filter.type(Expando, { type: 'test' })).run();
167
+ const { objects } = await space.db.query(Filter.type(Type.Expando, { type: 'test' })).run();
170
168
  space.db.remove(objects[Math.floor(Math.random() * objects.length)]);
171
169
  }
172
170
  break;
@@ -175,7 +173,7 @@ const runAction = async (client: Client, action: Action) => {
175
173
  case Action.RENAME_OBJECT: {
176
174
  const space = await getSpaceWithObjects(client);
177
175
  if (space) {
178
- const { objects } = await space.db.query(Filter.type(Expando, { type: 'test' })).run();
176
+ const { objects } = await space.db.query(Filter.type(Type.Expando, { type: 'test' })).run();
179
177
  objects[Math.floor(Math.random() * objects.length)].name = faker.commerce.productName();
180
178
  }
181
179
  break;
@@ -215,7 +213,7 @@ const Controls = ({ children }: PropsWithChildren) => {
215
213
  <Input.TextInput
216
214
  autoComplete='off'
217
215
  size={5}
218
- classNames='w-[100px] text-right pie-[22px]'
216
+ classNames='is-[100px] text-right pie-[22px]'
219
217
  placeholder='Interval'
220
218
  value={actionInterval}
221
219
  onChange={({ target: { value } }) => setActionInterval(value)}
@@ -250,16 +248,16 @@ const Controls = ({ children }: PropsWithChildren) => {
250
248
  const meta = {
251
249
  title: 'sdk/app-graph/EchoGraph',
252
250
  decorators: [
251
+ withTheme,
253
252
  withClientProvider({
254
253
  createIdentity: true,
255
- onIdentityCreated: async ({ client }) => {
254
+ onCreateIdentity: async ({ client }) => {
256
255
  await client.spaces.create();
257
256
  await client.spaces.create();
258
257
  },
259
258
  }),
260
- withTheme,
261
259
  ],
262
- } satisfies Meta<typeof Registry>;
260
+ } satisfies Meta;
263
261
 
264
262
  export default meta;
265
263
 
@@ -270,7 +268,7 @@ export const JsonView: Story = {
270
268
  const client = useClient();
271
269
  const registry = useContext(RegistryContext);
272
270
  const graph = useMemo(() => createGraph(client, registry), [client, registry]);
273
- const data = useRxValue(graph.json());
271
+ const data = useAtomValue(graph.json());
274
272
 
275
273
  return (
276
274
  <>
@@ -290,7 +288,7 @@ export const TreeView: Story = {
290
288
 
291
289
  const useItems = useCallback(
292
290
  (node?: Node, options?: { disposition?: string; sort?: boolean }) => {
293
- const connections = useRxValue(graph.connections(node?.id ?? ROOT_ID));
291
+ const connections = useAtomValue(graph.connections(node?.id ?? ROOT_ID));
294
292
  return options?.sort ? connections.toSorted((a, b) => byPosition(a.properties, b.properties)) : connections;
295
293
  },
296
294
  [graph],
@@ -72,7 +72,7 @@ const Scalar: FC<{ value: any }> = ({ value }) => {
72
72
 
73
73
  const Box: FC<HTMLAttributes<HTMLDivElement>> = ({ children, className, ...props }) => {
74
74
  return (
75
- <div className={mx('flex px-2 border border-l-0 font-mono truncate', className)} {...props}>
75
+ <div className={mx('flex pli-2 border border-l-0 font-mono truncate', className)} {...props}>
76
76
  {children}
77
77
  </div>
78
78
  );
package/src/testing.ts CHANGED
@@ -2,13 +2,14 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Rx } from '@effect-rx/rx-react';
5
+ import { Atom } from '@effect-atom/atom-react';
6
6
 
7
- import { type QueryResult } from '@dxos/echo-db';
8
- import { type AnyEchoObject } from '@dxos/echo-schema';
7
+ import { type Database, type Entity } from '@dxos/echo';
9
8
 
10
- export const rxFromQuery = <T extends AnyEchoObject>(query: QueryResult<T>): Rx.Rx<T[]> => {
11
- return Rx.make((get) => {
9
+ export const atomFromQuery = <T extends Entity.Unknown = Entity.Unknown>(
10
+ query: Database.QueryResult<T>,
11
+ ): Atom.Atom<T[]> => {
12
+ return Atom.make((get) => {
12
13
  const unsubscribe = query.subscribe((result) => {
13
14
  get.setSelf(result.objects);
14
15
  });
@@ -1,25 +0,0 @@
1
- type Cb = () => void;
2
- interface Query {
3
- id?: string[];
4
- typename?: string[];
5
- }
6
- /**
7
- * Lazy query result that can be executed.
8
- * Does not run without the run function being called.
9
- */
10
- interface QueryResult<T> {
11
- /**
12
- * Execute the query and subscribe to the result.
13
- * @param next Called at least once with the first value (maybe synchronously) and then for every subsequent update.
14
- * @param error Called on error. `next` is never called after that.
15
- * @returns Function to dispose the query and unsubscribe.
16
- */
17
- run(onData: (value?: T[]) => void, onError: (err: Error) => void): Cb;
18
- }
19
- declare const QueryResult: Readonly<{
20
- fromPromise: <T>(run: (onDispose: (cb: Cb) => void) => Promise<T[]>) => QueryResult<T>;
21
- }>;
22
- interface _Resolver<T> {
23
- query(query: Query): QueryResult<T>;
24
- }
25
- //# sourceMappingURL=graph-projections.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"graph-projections.test.d.ts","sourceRoot":"","sources":["../../../../src/experimental/graph-projections.test.ts"],"names":[],"mappings":"AAIA,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC;AAErB,UAAU,KAAK;IACb,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;GAGG;AACH,UAAU,WAAW,CAAC,CAAC;IACrB;;;;;OAKG;IACH,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,GAAG,EAAE,CAAC;CACvE;AAED,QAAA,MAAM,WAAW;kBACD,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,IAAI,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,KAAG,WAAW,CAAC,CAAC,CAAC;EAyBpF,CAAC;AAEH,UAAU,SAAS,CAAC,CAAC;IACnB,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;CACrC"}
@@ -1,56 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- type Cb = () => void;
6
-
7
- interface Query {
8
- id?: string[];
9
- typename?: string[];
10
- }
11
-
12
- /**
13
- * Lazy query result that can be executed.
14
- * Does not run without the run function being called.
15
- */
16
- interface QueryResult<T> {
17
- /**
18
- * Execute the query and subscribe to the result.
19
- * @param next Called at least once with the first value (maybe synchronously) and then for every subsequent update.
20
- * @param error Called on error. `next` is never called after that.
21
- * @returns Function to dispose the query and unsubscribe.
22
- */
23
- run(onData: (value?: T[]) => void, onError: (err: Error) => void): Cb;
24
- }
25
-
26
- const QueryResult = Object.freeze({
27
- fromPromise: <T>(run: (onDispose: (cb: Cb) => void) => Promise<T[]>): QueryResult<T> => {
28
- return {
29
- run: (onData, onError) => {
30
- const cbs: Cb[] = [];
31
- let disposed = false;
32
- const dispose = () => {
33
- cbs.forEach((cb) => cb());
34
- disposed = true;
35
- };
36
- run((cb) => (disposed ? cb() : cbs.push(cb))).then(
37
- (data) => {
38
- if (disposed) {
39
- return;
40
- }
41
- onData(data);
42
- },
43
- (err) => {
44
- dispose();
45
- onError(err);
46
- },
47
- );
48
- return dispose;
49
- },
50
- };
51
- },
52
- });
53
-
54
- interface _Resolver<T> {
55
- query(query: Query): QueryResult<T>;
56
- }