@dxos/react-client 0.8.4-main.fd6878d → 0.8.4-main.fffef41
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/.storybook/main.mts +11 -0
- package/.storybook/preview.mts +8 -0
- package/.swc/plugins/linux_x86_64_19.0.0/727453fb3a62f7f1d952a41e051ca8a6f88cadc45cee43c6a4d1aa45f9b75665.wasmer-v7 +0 -0
- package/dist/lib/browser/{chunk-BXOLJYAY.mjs → chunk-LO3TU5CK.mjs} +4 -4
- package/dist/lib/browser/chunk-LO3TU5CK.mjs.map +7 -0
- package/dist/lib/browser/{chunk-FQAPI3I4.mjs → chunk-TA2HGRAF.mjs} +58 -61
- package/dist/lib/browser/chunk-TA2HGRAF.mjs.map +7 -0
- package/dist/lib/browser/devtools/index.mjs +1 -1
- package/dist/lib/browser/echo/index.mjs +22 -22
- package/dist/lib/browser/echo/index.mjs.map +3 -3
- package/dist/lib/browser/halo/index.mjs +3 -4
- package/dist/lib/browser/halo/index.mjs.map +2 -2
- package/dist/lib/browser/index.mjs +1 -1
- package/dist/lib/browser/invitations/index.mjs +1 -1
- package/dist/lib/browser/mesh/index.mjs +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +49 -48
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/{chunk-256IHY6F.mjs → chunk-AQJH2UNZ.mjs} +4 -4
- package/dist/lib/node-esm/chunk-AQJH2UNZ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-MNTJNI2M.mjs → chunk-ZJMNF7GC.mjs} +58 -61
- package/dist/lib/node-esm/chunk-ZJMNF7GC.mjs.map +7 -0
- package/dist/lib/node-esm/devtools/index.mjs +1 -1
- package/dist/lib/node-esm/echo/index.mjs +22 -22
- package/dist/lib/node-esm/echo/index.mjs.map +3 -3
- package/dist/lib/node-esm/halo/index.mjs +3 -4
- package/dist/lib/node-esm/halo/index.mjs.map +2 -2
- package/dist/lib/node-esm/index.mjs +1 -1
- package/dist/lib/node-esm/invitations/index.mjs +1 -1
- package/dist/lib/node-esm/mesh/index.mjs +1 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +49 -48
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/client/ClientProvider.d.ts.map +1 -1
- package/dist/types/src/echo/useQuery.d.ts +3 -3
- package/dist/types/src/echo/useQuery.d.ts.map +1 -1
- package/dist/types/src/echo/useSchema.d.ts +1 -1
- package/dist/types/src/echo/useSchema.d.ts.map +1 -1
- package/dist/types/src/echo/useSpaces.d.ts.map +1 -1
- package/dist/types/src/echo/useSubscription.d.ts +1 -1
- package/dist/types/src/echo/useSubscription.d.ts.map +1 -1
- package/dist/types/src/halo/Passkey.stories.d.ts +5 -5
- package/dist/types/src/halo/Passkey.stories.d.ts.map +1 -1
- package/dist/types/src/invitations/useInvitationStatus.d.ts +1 -1
- package/dist/types/src/invitations/useInvitationStatus.d.ts.map +1 -1
- package/dist/types/src/testing/ClientRepeater.d.ts +2 -2
- package/dist/types/src/testing/ClientRepeater.d.ts.map +1 -1
- package/dist/types/src/testing/ClientRepeater.stories.d.ts +6 -10
- package/dist/types/src/testing/ClientRepeater.stories.d.ts.map +1 -1
- package/dist/types/src/testing/withClientProvider.d.ts +5 -6
- package/dist/types/src/testing/withClientProvider.d.ts.map +1 -1
- package/dist/types/src/testing/withClientProvider.stories.d.ts +7 -9
- package/dist/types/src/testing/withClientProvider.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/moon.yml +4 -0
- package/package.json +30 -31
- package/src/client/ClientProvider.tsx +29 -33
- package/src/echo/useQuery.test.tsx +7 -7
- package/src/echo/useQuery.ts +20 -12
- package/src/echo/useSchema.ts +2 -2
- package/src/echo/useSpaces.ts +5 -9
- package/src/echo/useSubscription.ts +1 -1
- package/src/halo/Passkey.stories.tsx +12 -10
- package/src/invitations/useInvitationStatus.ts +9 -8
- package/src/testing/ClientRepeater.stories.tsx +10 -7
- package/src/testing/ClientRepeater.tsx +23 -28
- package/src/testing/withClientProvider.stories.tsx +23 -15
- package/src/testing/withClientProvider.tsx +19 -17
- package/tsconfig.json +0 -3
- package/vitest.config.ts +12 -16
- package/.swc/plugins/v7_linux_x86_64_13.0.0/c614d7475354583212fbd7669acbae95b9832c305bf51bdaabe2e6de05abb6bf +0 -0
- package/dist/lib/browser/chunk-BXOLJYAY.mjs.map +0 -7
- package/dist/lib/browser/chunk-FQAPI3I4.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-256IHY6F.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MNTJNI2M.mjs.map +0 -7
package/moon.yml
CHANGED
|
@@ -6,6 +6,7 @@ tags:
|
|
|
6
6
|
- ts-test
|
|
7
7
|
- typedoc
|
|
8
8
|
- pack
|
|
9
|
+
- storybook
|
|
9
10
|
tasks:
|
|
10
11
|
compile:
|
|
11
12
|
args:
|
|
@@ -17,3 +18,6 @@ tasks:
|
|
|
17
18
|
- '--entryPoint=src/mesh/index.ts'
|
|
18
19
|
- '--entryPoint=src/testing/index.ts'
|
|
19
20
|
- '--entryPoint=src/worker.ts'
|
|
21
|
+
test:
|
|
22
|
+
inputs:
|
|
23
|
+
- .storybook/*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-client",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.fffef41",
|
|
4
4
|
"description": "React client API",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -86,43 +86,42 @@
|
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"react-error-boundary": "^4.0.13",
|
|
89
|
-
"@dxos/
|
|
90
|
-
"@dxos/
|
|
91
|
-
"@dxos/context": "0.8.4-main.
|
|
92
|
-
"@dxos/
|
|
93
|
-
"@dxos/debug": "0.8.4-main.
|
|
94
|
-
"@dxos/
|
|
95
|
-
"@dxos/
|
|
96
|
-
"@dxos/echo-
|
|
97
|
-
"@dxos/
|
|
98
|
-
"@dxos/
|
|
99
|
-
"@dxos/
|
|
100
|
-
"@dxos/
|
|
101
|
-
"@dxos/
|
|
102
|
-
"@dxos/util": "0.8.4-main.
|
|
103
|
-
"@dxos/log": "0.8.4-main.fd6878d"
|
|
89
|
+
"@dxos/async": "0.8.4-main.fffef41",
|
|
90
|
+
"@dxos/client": "0.8.4-main.fffef41",
|
|
91
|
+
"@dxos/context": "0.8.4-main.fffef41",
|
|
92
|
+
"@dxos/codec-protobuf": "0.8.4-main.fffef41",
|
|
93
|
+
"@dxos/debug": "0.8.4-main.fffef41",
|
|
94
|
+
"@dxos/config": "0.8.4-main.fffef41",
|
|
95
|
+
"@dxos/echo": "0.8.4-main.fffef41",
|
|
96
|
+
"@dxos/echo-signals": "0.8.4-main.fffef41",
|
|
97
|
+
"@dxos/invariant": "0.8.4-main.fffef41",
|
|
98
|
+
"@dxos/log": "0.8.4-main.fffef41",
|
|
99
|
+
"@dxos/node-std": "0.8.4-main.fffef41",
|
|
100
|
+
"@dxos/protocols": "0.8.4-main.fffef41",
|
|
101
|
+
"@dxos/react-hooks": "0.8.4-main.fffef41",
|
|
102
|
+
"@dxos/util": "0.8.4-main.fffef41"
|
|
104
103
|
},
|
|
105
104
|
"devDependencies": {
|
|
106
|
-
"@types/react": "~
|
|
107
|
-
"@types/react-dom": "~
|
|
108
|
-
"react": "~
|
|
109
|
-
"react-dom": "~
|
|
105
|
+
"@types/react": "~19.2.2",
|
|
106
|
+
"@types/react-dom": "~19.2.2",
|
|
107
|
+
"react": "~19.2.0",
|
|
108
|
+
"react-dom": "~19.2.0",
|
|
110
109
|
"typedoc": "0.28.1",
|
|
111
|
-
"@dxos/random": "0.8.4-main.
|
|
112
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
113
|
-
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.
|
|
114
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
115
|
-
"@dxos/storybook-utils": "0.8.4-main.
|
|
116
|
-
"@dxos/test-utils": "0.8.4-main.
|
|
110
|
+
"@dxos/random": "0.8.4-main.fffef41",
|
|
111
|
+
"@dxos/react-ui": "0.8.4-main.fffef41",
|
|
112
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.fffef41",
|
|
113
|
+
"@dxos/react-ui-theme": "0.8.4-main.fffef41",
|
|
114
|
+
"@dxos/storybook-utils": "0.8.4-main.fffef41",
|
|
115
|
+
"@dxos/test-utils": "0.8.4-main.fffef41"
|
|
117
116
|
},
|
|
118
117
|
"peerDependencies": {
|
|
119
|
-
"react": "
|
|
120
|
-
"react-dom": "
|
|
118
|
+
"react": "^19.0.0",
|
|
119
|
+
"react-dom": "^19.0.0"
|
|
121
120
|
},
|
|
122
121
|
"optionalDependencies": {
|
|
123
|
-
"@dxos/random": "0.8.4-main.
|
|
124
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
125
|
-
"@dxos/react-ui
|
|
122
|
+
"@dxos/random": "0.8.4-main.fffef41",
|
|
123
|
+
"@dxos/react-ui-theme": "0.8.4-main.fffef41",
|
|
124
|
+
"@dxos/react-ui": "0.8.4-main.fffef41"
|
|
126
125
|
},
|
|
127
126
|
"publishConfig": {
|
|
128
127
|
"access": "public"
|
|
@@ -15,7 +15,7 @@ import { Client, type ClientOptions, type ClientServicesProvider, SystemStatus }
|
|
|
15
15
|
import { type Config } from '@dxos/config';
|
|
16
16
|
import { registerSignalsRuntime } from '@dxos/echo-signals/react';
|
|
17
17
|
import { log } from '@dxos/log';
|
|
18
|
-
import { useControlledState } from '@dxos/react-hooks';
|
|
18
|
+
import { useAsyncEffect, useControlledState } from '@dxos/react-hooks';
|
|
19
19
|
import { type MaybePromise, type Provider, getAsyncProviderValue } from '@dxos/util';
|
|
20
20
|
|
|
21
21
|
import { printBanner } from '../banner';
|
|
@@ -81,10 +81,10 @@ export const ClientProvider = forwardRef<Client | undefined, ClientProviderProps
|
|
|
81
81
|
(
|
|
82
82
|
{
|
|
83
83
|
children,
|
|
84
|
-
config:
|
|
85
|
-
client:
|
|
86
|
-
services:
|
|
87
|
-
status:
|
|
84
|
+
config: configParam,
|
|
85
|
+
client: clientParam,
|
|
86
|
+
services: servicesParam,
|
|
87
|
+
status: statusParam,
|
|
88
88
|
fallback: Fallback = () => null,
|
|
89
89
|
signalsRuntime = true,
|
|
90
90
|
noBanner,
|
|
@@ -109,13 +109,13 @@ export const ClientProvider = forwardRef<Client | undefined, ClientProviderProps
|
|
|
109
109
|
throw error;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
const [client, setClient] = useState(
|
|
112
|
+
const [client, setClient] = useState(clientParam instanceof Client ? clientParam : undefined);
|
|
113
113
|
|
|
114
114
|
// Provide external access.
|
|
115
115
|
useImperativeHandle(forwardedRef, () => client, [client]);
|
|
116
116
|
|
|
117
117
|
// Client status subscription.
|
|
118
|
-
const [status, setStatus] = useControlledState(
|
|
118
|
+
const [status, setStatus] = useControlledState(statusParam);
|
|
119
119
|
useEffect(() => {
|
|
120
120
|
if (!client) {
|
|
121
121
|
return;
|
|
@@ -126,7 +126,7 @@ export const ClientProvider = forwardRef<Client | undefined, ClientProviderProps
|
|
|
126
126
|
}, [client]);
|
|
127
127
|
|
|
128
128
|
// Create and/or initialize client.
|
|
129
|
-
|
|
129
|
+
useAsyncEffect(async () => {
|
|
130
130
|
let disposed = false;
|
|
131
131
|
const initialize = async (client: Client) => {
|
|
132
132
|
if (!client.initialized) {
|
|
@@ -141,42 +141,38 @@ export const ClientProvider = forwardRef<Client | undefined, ClientProviderProps
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
setClient(client);
|
|
144
|
-
|
|
145
144
|
if (!noBanner) {
|
|
146
145
|
printBanner(client);
|
|
147
146
|
}
|
|
148
147
|
};
|
|
149
148
|
|
|
150
149
|
let client: Client;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
await initialize(client);
|
|
166
|
-
}
|
|
167
|
-
} catch (err) {
|
|
168
|
-
if (!disposed) {
|
|
169
|
-
log.catch(err);
|
|
170
|
-
}
|
|
150
|
+
try {
|
|
151
|
+
if (clientParam) {
|
|
152
|
+
// Asynchronously request client.
|
|
153
|
+
client = await getAsyncProviderValue(clientParam);
|
|
154
|
+
await initialize(client);
|
|
155
|
+
} else {
|
|
156
|
+
// Asynchronously construct client (config may be undefined).
|
|
157
|
+
const config = await getAsyncProviderValue(configParam);
|
|
158
|
+
log('resolved config', { config });
|
|
159
|
+
const services = await getAsyncProviderValue(servicesParam, config);
|
|
160
|
+
log('created services', { services });
|
|
161
|
+
client = new Client({ config, services, ...options });
|
|
162
|
+
log('created client');
|
|
163
|
+
await initialize(client);
|
|
171
164
|
}
|
|
172
|
-
})
|
|
165
|
+
} catch (err) {
|
|
166
|
+
if (!disposed) {
|
|
167
|
+
log.catch(err);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
173
170
|
|
|
174
171
|
return () => {
|
|
175
172
|
log('clean up');
|
|
176
173
|
disposed = true;
|
|
177
|
-
clearTimeout(t);
|
|
178
174
|
// Only destroy if the client is not provided by the parent.
|
|
179
|
-
if (!
|
|
175
|
+
if (!clientParam) {
|
|
180
176
|
void client
|
|
181
177
|
?.destroy()
|
|
182
178
|
.then(() => {
|
|
@@ -185,7 +181,7 @@ export const ClientProvider = forwardRef<Client | undefined, ClientProviderProps
|
|
|
185
181
|
.catch((err) => log.catch(err));
|
|
186
182
|
}
|
|
187
183
|
};
|
|
188
|
-
}, [
|
|
184
|
+
}, [configParam, clientParam, servicesParam, noBanner]);
|
|
189
185
|
|
|
190
186
|
if (!client?.initialized || status !== SystemStatus.ACTIVE) {
|
|
191
187
|
return <Fallback client={client} status={status} />;
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
import { renderHook } from '@testing-library/react';
|
|
6
6
|
import { describe, expect, test } from 'vitest';
|
|
7
7
|
|
|
8
|
-
import { Expando, Filter
|
|
8
|
+
import { Expando, Filter } from '@dxos/client/echo';
|
|
9
|
+
import { Obj, Type } from '@dxos/echo';
|
|
9
10
|
|
|
10
11
|
import { createClient, createClientContextProvider } from '../testing/util';
|
|
11
12
|
|
|
@@ -19,9 +20,9 @@ describe('useQuery', () => {
|
|
|
19
20
|
const wrapper = await createClientContextProvider(client);
|
|
20
21
|
|
|
21
22
|
// Create 3 test objects: Alice, Bob, Charlie.
|
|
22
|
-
const alice =
|
|
23
|
-
const bob =
|
|
24
|
-
const charlie =
|
|
23
|
+
const alice = Obj.make(Type.Expando, { name: 'Alice' });
|
|
24
|
+
const bob = Obj.make(Type.Expando, { name: 'Bob' });
|
|
25
|
+
const charlie = Obj.make(Type.Expando, { name: 'Charlie' });
|
|
25
26
|
|
|
26
27
|
space!.db.add(alice);
|
|
27
28
|
space!.db.add(bob);
|
|
@@ -65,7 +66,7 @@ describe('useQuery', () => {
|
|
|
65
66
|
* Actual: 4 renders
|
|
66
67
|
* 1. [] (empty)
|
|
67
68
|
* 2. ['Alice', 'Bob', 'Charlie'] (all loaded)
|
|
68
|
-
* 3. ['Alice', 'Charlie', 'Bob'] (FLASH BUG - Bob moves to end!)
|
|
69
|
+
* 3. ['Alice', 'Charlie', 'Bob'] (FLASH BUG - Bob moves to end!)x
|
|
69
70
|
* 4. ['Alice', 'Charlie'] (Bob finally removed)
|
|
70
71
|
*/
|
|
71
72
|
|
|
@@ -82,7 +83,7 @@ describe('useQuery', () => {
|
|
|
82
83
|
const wrapper = await createClientContextProvider(client);
|
|
83
84
|
|
|
84
85
|
// Create 10 test objects: 1, 2, 3, ..., 10.
|
|
85
|
-
const objects = Array.from({ length: 10 }, (_, i) =>
|
|
86
|
+
const objects = Array.from({ length: 10 }, (_, i) => Obj.make(Type.Expando, { value: i + 1 }));
|
|
86
87
|
|
|
87
88
|
objects.forEach((obj) => space!.db.add(obj));
|
|
88
89
|
await space!.db.flush();
|
|
@@ -122,7 +123,6 @@ describe('useQuery', () => {
|
|
|
122
123
|
new Set([]), // Initial loading state.
|
|
123
124
|
new Set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), // All objects loaded.
|
|
124
125
|
// TODO(dmaretskyi): Fix this with query ordering.
|
|
125
|
-
new Set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), // Getting another render for some reason.
|
|
126
126
|
new Set([]), // All items deleted.
|
|
127
127
|
]);
|
|
128
128
|
});
|
package/src/echo/useQuery.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { useMemo, useSyncExternalStore } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type Echo, Filter, type Live, Query, type Space, isSpace } from '@dxos/client/echo';
|
|
7
|
+
import { type Echo, Filter, type Live, Query, type Queryable, type Space, isSpace } from '@dxos/client/echo';
|
|
8
8
|
|
|
9
9
|
const EMPTY_ARRAY: never[] = [];
|
|
10
10
|
|
|
@@ -12,30 +12,38 @@ const noop = () => {};
|
|
|
12
12
|
|
|
13
13
|
// TODO(dmaretskyi): Queries are fully serializable, so we can remove `deps` argument.
|
|
14
14
|
interface UseQueryFn {
|
|
15
|
-
<Q extends Query.Any>(
|
|
15
|
+
<Q extends Query.Any>(
|
|
16
|
+
spaceOrEcho: Space | Echo | Queryable | undefined,
|
|
17
|
+
query: Q,
|
|
18
|
+
deps?: any[],
|
|
19
|
+
): Live<Query.Type<Q>>[];
|
|
16
20
|
|
|
17
|
-
<F extends Filter.Any>(
|
|
21
|
+
<F extends Filter.Any>(
|
|
22
|
+
spaceOrEcho: Space | Echo | Queryable | undefined,
|
|
23
|
+
filter: F,
|
|
24
|
+
deps?: any[],
|
|
25
|
+
): Live<Filter.Type<F>>[];
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
/**
|
|
21
29
|
* Create subscription.
|
|
22
30
|
*/
|
|
31
|
+
// TODO(burdon): Sort?
|
|
23
32
|
export const useQuery: UseQueryFn = (
|
|
24
|
-
|
|
33
|
+
// TODO(burdon): CRITICAL: Remove Space and just requre Queryable.
|
|
34
|
+
resource: Space | Echo | Queryable | undefined,
|
|
25
35
|
queryOrFilter: Query.Any | Filter.Any,
|
|
26
36
|
deps?: any[],
|
|
27
37
|
): Live<unknown>[] => {
|
|
28
38
|
const query = Filter.is(queryOrFilter) ? Query.select(queryOrFilter) : queryOrFilter;
|
|
29
39
|
|
|
30
40
|
const { getObjects, subscribe } = useMemo(() => {
|
|
31
|
-
let
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
: isSpace(spaceOrEcho)
|
|
36
|
-
? spaceOrEcho.db.query(query)
|
|
37
|
-
: spaceOrEcho.query(query);
|
|
41
|
+
let queryResult = undefined;
|
|
42
|
+
if (resource) {
|
|
43
|
+
queryResult = isSpace(resource) ? resource.db.query(query) : resource.query(query);
|
|
44
|
+
}
|
|
38
45
|
|
|
46
|
+
let subscribed = false;
|
|
39
47
|
return {
|
|
40
48
|
getObjects: () => (subscribed && queryResult ? queryResult.objects : EMPTY_ARRAY),
|
|
41
49
|
subscribe: (cb: () => void) => {
|
|
@@ -47,7 +55,7 @@ export const useQuery: UseQueryFn = (
|
|
|
47
55
|
};
|
|
48
56
|
},
|
|
49
57
|
};
|
|
50
|
-
}, [
|
|
58
|
+
}, [resource, JSON.stringify(query.ast), ...(deps ?? [])]);
|
|
51
59
|
|
|
52
60
|
// https://beta.reactjs.org/reference/react/useSyncExternalStore
|
|
53
61
|
// NOTE: This hook will resubscribe whenever the callback passed to the first argument changes; make sure it is stable.
|
package/src/echo/useSchema.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import type * as Schema from 'effect/Schema';
|
|
6
6
|
import { useMemo, useSyncExternalStore } from 'react';
|
|
7
7
|
|
|
8
8
|
import { type Client } from '@dxos/client';
|
|
9
9
|
import { type Space } from '@dxos/client/echo';
|
|
10
|
-
import { type BaseSchema } from '@dxos/echo
|
|
10
|
+
import { type BaseSchema } from '@dxos/echo/internal';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Subscribe to and retrieve schema changes from a space's schema registry.
|
package/src/echo/useSpaces.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { useState } from 'react';
|
|
6
6
|
|
|
7
7
|
import { type PublicKeyLike } from '@dxos/client';
|
|
8
8
|
import { type Space, SpaceState } from '@dxos/client/echo';
|
|
9
|
-
import { useMulticastObservable } from '@dxos/react-hooks';
|
|
9
|
+
import { useAsyncEffect, useMulticastObservable } from '@dxos/react-hooks';
|
|
10
10
|
|
|
11
11
|
import { useClient } from '../client';
|
|
12
12
|
|
|
@@ -23,18 +23,14 @@ export const useSpace = (spaceKeyLike?: PublicKeyLike): Space | undefined => {
|
|
|
23
23
|
const spaces = useMulticastObservable<Space[]>(client.spaces);
|
|
24
24
|
const [ready, setReady] = useState(false);
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
useAsyncEffect(async () => {
|
|
27
27
|
// Only wait for ready if looking for the default space.
|
|
28
28
|
if (spaceKeyLike) {
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
setReady(true);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
return () => clearTimeout(timeout);
|
|
32
|
+
await client.spaces.waitUntilReady();
|
|
33
|
+
setReady(true);
|
|
38
34
|
}, [client, spaceKeyLike]);
|
|
39
35
|
|
|
40
36
|
if (spaceKeyLike) {
|
|
@@ -18,7 +18,7 @@ export const useSubscription = (cb: () => void, selection: Selection) => {
|
|
|
18
18
|
const callbackRef = useRef(cb);
|
|
19
19
|
callbackRef.current = cb;
|
|
20
20
|
|
|
21
|
-
const subscriptionRef = useRef<SubscriptionHandle>();
|
|
21
|
+
const subscriptionRef = useRef<SubscriptionHandle>(null);
|
|
22
22
|
|
|
23
23
|
useEffect(() => {
|
|
24
24
|
subscriptionRef.current = createSubscription(() => {
|
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
6
|
import React, { useCallback } from 'react';
|
|
8
7
|
|
|
9
8
|
import { Config, PublicKey } from '@dxos/client';
|
|
10
9
|
import { invariant } from '@dxos/invariant';
|
|
11
10
|
import { log } from '@dxos/log';
|
|
12
11
|
import { Button } from '@dxos/react-ui';
|
|
12
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
13
13
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
14
|
-
import { withTheme } from '@dxos/storybook-utils';
|
|
15
14
|
|
|
16
15
|
import { useClient } from '../client';
|
|
17
16
|
import { withClientProvider } from '../testing';
|
|
@@ -126,11 +125,6 @@ const Test = () => {
|
|
|
126
125
|
);
|
|
127
126
|
};
|
|
128
127
|
|
|
129
|
-
export default {
|
|
130
|
-
title: 'sdk/react-client/Passkeys',
|
|
131
|
-
render: Test,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
128
|
const config = new Config({
|
|
135
129
|
runtime: {
|
|
136
130
|
client: {
|
|
@@ -151,6 +145,14 @@ const config = new Config({
|
|
|
151
145
|
},
|
|
152
146
|
});
|
|
153
147
|
|
|
154
|
-
|
|
148
|
+
const meta = {
|
|
149
|
+
title: 'sdk/react-client/Passkeys',
|
|
150
|
+
render: Test,
|
|
155
151
|
decorators: [withClientProvider({ config }), withTheme],
|
|
156
|
-
};
|
|
152
|
+
} satisfies Meta;
|
|
153
|
+
|
|
154
|
+
export default meta;
|
|
155
|
+
|
|
156
|
+
type Story = StoryObj<typeof meta>;
|
|
157
|
+
|
|
158
|
+
export const Default: Story = {};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { useCallback, useEffect, useMemo, useReducer } from 'react';
|
|
6
6
|
|
|
7
7
|
import { type PublicKey } from '@dxos/client';
|
|
8
8
|
import {
|
|
@@ -24,9 +24,10 @@ interface InvitationReducerState {
|
|
|
24
24
|
status: Invitation.State; // TODO(burdon): Rename state.
|
|
25
25
|
haltedAt?: Invitation.State;
|
|
26
26
|
result: InvitationResult;
|
|
27
|
-
error?:
|
|
27
|
+
error?: Error;
|
|
28
28
|
id?: string;
|
|
29
29
|
multiUse?: boolean;
|
|
30
|
+
shareable?: boolean;
|
|
30
31
|
invitationCode?: string;
|
|
31
32
|
authCode?: string;
|
|
32
33
|
authMethod?: Invitation.AuthMethod;
|
|
@@ -72,7 +73,7 @@ export type InvitationStatus = {
|
|
|
72
73
|
multiUse?: boolean;
|
|
73
74
|
shareable?: boolean;
|
|
74
75
|
result: InvitationResult;
|
|
75
|
-
error?:
|
|
76
|
+
error?: Error;
|
|
76
77
|
cancel(): void;
|
|
77
78
|
// TODO(wittjosiah): Remove?
|
|
78
79
|
connect(observable: CancellableInvitationObservable): void;
|
|
@@ -82,11 +83,11 @@ export type InvitationStatus = {
|
|
|
82
83
|
// Without private key, the invitation code cannot be created.
|
|
83
84
|
// These invitations are only available to be accepted but not shared.
|
|
84
85
|
const isShareableInvitation = (invitation: Invitation) =>
|
|
85
|
-
invitation.authMethod !== Invitation.AuthMethod.KNOWN_PUBLIC_KEY || invitation.guestKeypair?.privateKey;
|
|
86
|
+
invitation.authMethod !== Invitation.AuthMethod.KNOWN_PUBLIC_KEY || !!invitation.guestKeypair?.privateKey;
|
|
86
87
|
|
|
87
88
|
export const useInvitationStatus = (observable?: CancellableInvitationObservable): InvitationStatus => {
|
|
88
|
-
const [state, dispatch] = useReducer
|
|
89
|
-
(prev, action) => {
|
|
89
|
+
const [state, dispatch] = useReducer(
|
|
90
|
+
(prev: InvitationReducerState, action: InvitationAction): InvitationReducerState => {
|
|
90
91
|
log('useInvitationStatus', { action });
|
|
91
92
|
const invitationProps =
|
|
92
93
|
'invitation' in action
|
|
@@ -115,10 +116,10 @@ export const useInvitationStatus = (observable?: CancellableInvitationObservable
|
|
|
115
116
|
action.status === Invitation.State.TIMEOUT) && {
|
|
116
117
|
haltedAt: typeof prev.haltedAt === 'undefined' ? action.haltedAt : prev.haltedAt,
|
|
117
118
|
}),
|
|
118
|
-
}
|
|
119
|
+
};
|
|
119
120
|
},
|
|
120
121
|
null,
|
|
121
|
-
(_arg: null) => {
|
|
122
|
+
(_arg: null): InvitationReducerState => {
|
|
122
123
|
const invitation = observable?.get();
|
|
123
124
|
return {
|
|
124
125
|
status: Invitation.State.INIT,
|
|
@@ -2,23 +2,26 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
6
|
import React from 'react';
|
|
8
7
|
|
|
9
8
|
import { Input } from '@dxos/react-ui';
|
|
9
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
10
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
11
|
-
import { withTheme } from '@dxos/storybook-utils';
|
|
12
11
|
|
|
13
12
|
import { useClient } from '../client';
|
|
14
13
|
import { useSpace } from '../echo';
|
|
15
14
|
|
|
16
15
|
import { type ClientRepeatedComponentProps, ClientRepeater } from './ClientRepeater';
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
const meta = {
|
|
19
18
|
title: 'sdk/react-client/ClientRepeater',
|
|
20
19
|
decorators: [withTheme],
|
|
21
|
-
}
|
|
20
|
+
} satisfies Meta<typeof Input>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
|
|
24
|
+
type Story = StoryObj<typeof meta>;
|
|
22
25
|
|
|
23
26
|
const JsonPanel = ({ value }: { value: any }) => (
|
|
24
27
|
<SyntaxHighlighter language='json'>{JSON.stringify(value, undefined, 2)}</SyntaxHighlighter>
|
|
@@ -50,10 +53,10 @@ const ClientSpace = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
|
50
53
|
);
|
|
51
54
|
};
|
|
52
55
|
|
|
53
|
-
export const Default = {
|
|
56
|
+
export const Default: Story = {
|
|
54
57
|
render: () => <ClientRepeater component={ClientStory} count={2} />,
|
|
55
58
|
};
|
|
56
59
|
|
|
57
|
-
export const Space = {
|
|
60
|
+
export const Space: Story = {
|
|
58
61
|
render: () => <ClientRepeater component={ClientSpace} count={2} createSpace />,
|
|
59
62
|
};
|
|
@@ -6,9 +6,10 @@ import React, { type FC, useEffect, useRef, useState } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { Client, type PublicKey } from '@dxos/client';
|
|
8
8
|
import { TestBuilder, performInvitation } from '@dxos/client/testing';
|
|
9
|
-
import { type TypedObject } from '@dxos/echo
|
|
9
|
+
import { type TypedObject } from '@dxos/echo/internal';
|
|
10
10
|
import { registerSignalsRuntime } from '@dxos/echo-signals/react';
|
|
11
11
|
import { faker } from '@dxos/random';
|
|
12
|
+
import { useAsyncEffect } from '@dxos/react-hooks';
|
|
12
13
|
|
|
13
14
|
import { ClientProvider } from '../client';
|
|
14
15
|
|
|
@@ -27,7 +28,7 @@ export type ClientRepeaterProps<P extends ClientRepeatedComponentProps> = {
|
|
|
27
28
|
clients?: Client[];
|
|
28
29
|
types?: TypedObject[];
|
|
29
30
|
args?: Omit<P, 'id' | 'count'>;
|
|
30
|
-
} & Pick<WithClientProviderProps, 'createIdentity' | 'createSpace' | '
|
|
31
|
+
} & Pick<WithClientProviderProps, 'createIdentity' | 'createSpace' | 'onCreateSpace'>;
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Utility component for Storybook stories which sets up clients for n peers.
|
|
@@ -47,7 +48,7 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
47
48
|
types,
|
|
48
49
|
createIdentity,
|
|
49
50
|
createSpace,
|
|
50
|
-
|
|
51
|
+
onCreateSpace,
|
|
51
52
|
} = props;
|
|
52
53
|
useEffect(() => {
|
|
53
54
|
registerSignalsRuntime();
|
|
@@ -57,31 +58,25 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
57
58
|
const [spaceKey, setSpaceKey] = useState<PublicKey>();
|
|
58
59
|
|
|
59
60
|
const testBuilder = useRef(new TestBuilder());
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
setClients(clients);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
return () => clearTimeout(timeout);
|
|
61
|
+
useAsyncEffect(async () => {
|
|
62
|
+
const clients = [...Array(count)].map(
|
|
63
|
+
(_) => new Client({ services: testBuilder.current.createLocalClientServices(), types }),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
await Promise.all(clients.map((client) => client.initialize()));
|
|
67
|
+
if (createIdentity || createSpace) {
|
|
68
|
+
await Promise.all(clients.map((client) => client.halo.createIdentity()));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (createSpace) {
|
|
72
|
+
const client = clients[0];
|
|
73
|
+
const space = await client.spaces.create({ name: faker.commerce.productName() });
|
|
74
|
+
setSpaceKey(space.key);
|
|
75
|
+
await onCreateSpace?.({ client, space }, {});
|
|
76
|
+
await Promise.all(clients.slice(1).flatMap((client) => performInvitation({ host: space, guest: client.spaces })));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setClients(clients);
|
|
85
80
|
}, []);
|
|
86
81
|
|
|
87
82
|
if (clients.length === 0) {
|