@dxos/react-client 0.7.5-labs.a279d8c → 0.7.5-labs.ea4b4c2
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-Z6OJATVN.mjs → chunk-72QD3PXL.mjs} +15 -13
- package/dist/lib/browser/{chunk-Z6OJATVN.mjs.map → chunk-72QD3PXL.mjs.map} +3 -3
- package/dist/lib/browser/devtools/index.mjs +1 -1
- package/dist/lib/browser/echo/index.mjs +37 -3
- package/dist/lib/browser/echo/index.mjs.map +4 -4
- package/dist/lib/browser/halo/index.mjs +15 -7
- package/dist/lib/browser/halo/index.mjs.map +4 -4
- package/dist/lib/browser/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 +1 -1
- package/dist/lib/node/{chunk-DITMVTAF.cjs → chunk-5RUIFUEC.cjs} +17 -15
- package/dist/lib/node/{chunk-DITMVTAF.cjs.map → chunk-5RUIFUEC.cjs.map} +3 -3
- package/dist/lib/node/devtools/index.cjs +2 -2
- package/dist/lib/node/echo/index.cjs +41 -8
- package/dist/lib/node/echo/index.cjs.map +4 -4
- package/dist/lib/node/halo/index.cjs +16 -9
- package/dist/lib/node/halo/index.cjs.map +4 -4
- package/dist/lib/node/index.cjs +12 -12
- package/dist/lib/node/mesh/index.cjs +2 -2
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +4 -4
- package/dist/lib/node-esm/{chunk-6QHTUQ4Z.mjs → chunk-NBTZZNQ5.mjs} +15 -13
- package/dist/lib/node-esm/{chunk-6QHTUQ4Z.mjs.map → chunk-NBTZZNQ5.mjs.map} +3 -3
- package/dist/lib/node-esm/devtools/index.mjs +1 -1
- package/dist/lib/node-esm/echo/index.mjs +37 -3
- package/dist/lib/node-esm/echo/index.mjs.map +4 -4
- package/dist/lib/node-esm/halo/index.mjs +15 -7
- package/dist/lib/node-esm/halo/index.mjs.map +4 -4
- package/dist/lib/node-esm/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 +1 -1
- package/dist/types/src/client/ClientProvider.d.ts.map +1 -1
- package/dist/types/src/echo/index.d.ts +1 -0
- package/dist/types/src/echo/index.d.ts.map +1 -1
- package/dist/types/src/echo/useSchema.d.ts +7 -0
- package/dist/types/src/echo/useSchema.d.ts.map +1 -0
- package/dist/types/src/halo/Passkey.stories.d.ts +11 -0
- package/dist/types/src/halo/Passkey.stories.d.ts.map +1 -0
- package/dist/types/src/halo/index.d.ts +1 -0
- package/dist/types/src/halo/index.d.ts.map +1 -1
- package/dist/types/src/halo/useCredentials.d.ts +3 -0
- package/dist/types/src/halo/useCredentials.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/src/client/ClientProvider.test.tsx +3 -2
- package/src/client/ClientProvider.tsx +11 -8
- package/src/echo/index.ts +1 -0
- package/src/echo/useSchema.ts +39 -0
- package/src/halo/Passkey.stories.tsx +151 -0
- package/src/halo/index.ts +1 -0
- package/src/halo/useCredentials.ts +13 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-client",
|
|
3
|
-
"version": "0.7.5-labs.
|
|
3
|
+
"version": "0.7.5-labs.ea4b4c2",
|
|
4
4
|
"description": "React client API",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -77,40 +77,40 @@
|
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
79
|
"react-error-boundary": "^4.0.13",
|
|
80
|
-
"@dxos/async": "0.7.5-labs.
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/codec-protobuf": "0.7.5-labs.
|
|
83
|
-
"@dxos/
|
|
84
|
-
"@dxos/
|
|
85
|
-
"@dxos/echo-
|
|
86
|
-
"@dxos/
|
|
87
|
-
"@dxos/
|
|
88
|
-
"@dxos/
|
|
89
|
-
"@dxos/
|
|
90
|
-
"@dxos/
|
|
91
|
-
"@dxos/
|
|
92
|
-
"@dxos/
|
|
80
|
+
"@dxos/async": "0.7.5-labs.ea4b4c2",
|
|
81
|
+
"@dxos/client": "0.7.5-labs.ea4b4c2",
|
|
82
|
+
"@dxos/codec-protobuf": "0.7.5-labs.ea4b4c2",
|
|
83
|
+
"@dxos/config": "0.7.5-labs.ea4b4c2",
|
|
84
|
+
"@dxos/debug": "0.7.5-labs.ea4b4c2",
|
|
85
|
+
"@dxos/echo-schema": "0.7.5-labs.ea4b4c2",
|
|
86
|
+
"@dxos/invariant": "0.7.5-labs.ea4b4c2",
|
|
87
|
+
"@dxos/log": "0.7.5-labs.ea4b4c2",
|
|
88
|
+
"@dxos/echo-signals": "0.7.5-labs.ea4b4c2",
|
|
89
|
+
"@dxos/react-hooks": "0.7.5-labs.ea4b4c2",
|
|
90
|
+
"@dxos/node-std": "0.7.5-labs.ea4b4c2",
|
|
91
|
+
"@dxos/protocols": "0.7.5-labs.ea4b4c2",
|
|
92
|
+
"@dxos/util": "0.7.5-labs.ea4b4c2"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@types/react": "~18.2.0",
|
|
96
96
|
"@types/react-dom": "~18.2.0",
|
|
97
97
|
"react": "~18.2.0",
|
|
98
98
|
"react-dom": "~18.2.0",
|
|
99
|
-
"@dxos/random": "0.7.5-labs.
|
|
100
|
-
"@dxos/react-ui-
|
|
101
|
-
"@dxos/react-ui-
|
|
102
|
-
"@dxos/react-ui": "0.7.5-labs.
|
|
103
|
-
"@dxos/storybook-utils": "0.7.5-labs.
|
|
104
|
-
"@dxos/test-utils": "0.7.5-labs.
|
|
99
|
+
"@dxos/random": "0.7.5-labs.ea4b4c2",
|
|
100
|
+
"@dxos/react-ui-syntax-highlighter": "0.7.5-labs.ea4b4c2",
|
|
101
|
+
"@dxos/react-ui-theme": "0.7.5-labs.ea4b4c2",
|
|
102
|
+
"@dxos/react-ui": "0.7.5-labs.ea4b4c2",
|
|
103
|
+
"@dxos/storybook-utils": "0.7.5-labs.ea4b4c2",
|
|
104
|
+
"@dxos/test-utils": "0.7.5-labs.ea4b4c2"
|
|
105
105
|
},
|
|
106
106
|
"peerDependencies": {
|
|
107
107
|
"react": "~18.2.0",
|
|
108
108
|
"react-dom": "~18.2.0"
|
|
109
109
|
},
|
|
110
110
|
"optionalDependencies": {
|
|
111
|
-
"@dxos/random": "0.7.5-labs.
|
|
112
|
-
"@dxos/react-ui": "0.7.5-labs.
|
|
113
|
-
"@dxos/react-ui-theme": "0.7.5-labs.
|
|
111
|
+
"@dxos/random": "0.7.5-labs.ea4b4c2",
|
|
112
|
+
"@dxos/react-ui": "0.7.5-labs.ea4b4c2",
|
|
113
|
+
"@dxos/react-ui-theme": "0.7.5-labs.ea4b4c2"
|
|
114
114
|
},
|
|
115
115
|
"publishConfig": {
|
|
116
116
|
"access": "public"
|
|
@@ -151,10 +151,11 @@ describe('ClientProvider', () => {
|
|
|
151
151
|
|
|
152
152
|
await act(async () => {
|
|
153
153
|
await waitForCondition({
|
|
154
|
-
condition: () =>
|
|
154
|
+
condition: () => newClient.status.get() === SystemStatus.ACTIVE,
|
|
155
155
|
});
|
|
156
156
|
});
|
|
157
|
-
|
|
157
|
+
// If client is provided externally, the provider will not destroy it.
|
|
158
|
+
expect(client.initialized).toBe(true);
|
|
158
159
|
expect(() => screen.getByText('Identity is NOT there')).not.toThrow();
|
|
159
160
|
});
|
|
160
161
|
});
|
|
@@ -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 {
|
|
18
|
+
import { useControlledState } from '@dxos/react-hooks';
|
|
19
19
|
import { getAsyncProviderValue, type MaybePromise, type Provider } from '@dxos/util';
|
|
20
20
|
|
|
21
21
|
import { ClientContext, type ClientContextProps } from './context';
|
|
@@ -114,7 +114,7 @@ export const ClientProvider = forwardRef<Client | undefined, ClientProviderProps
|
|
|
114
114
|
useImperativeHandle(forwardedRef, () => client, [client]);
|
|
115
115
|
|
|
116
116
|
// Client status subscription.
|
|
117
|
-
const [status, setStatus] =
|
|
117
|
+
const [status, setStatus] = useControlledState(controlledStatus);
|
|
118
118
|
useEffect(() => {
|
|
119
119
|
if (!client) {
|
|
120
120
|
return;
|
|
@@ -174,12 +174,15 @@ export const ClientProvider = forwardRef<Client | undefined, ClientProviderProps
|
|
|
174
174
|
log('clean up');
|
|
175
175
|
disposed = true;
|
|
176
176
|
clearTimeout(t);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
// Only destroy if the client is not provided by the parent.
|
|
178
|
+
if (!clientProvider) {
|
|
179
|
+
void client
|
|
180
|
+
?.destroy()
|
|
181
|
+
.then(() => {
|
|
182
|
+
log('destroyed');
|
|
183
|
+
})
|
|
184
|
+
.catch((err) => log.catch(err));
|
|
185
|
+
}
|
|
183
186
|
};
|
|
184
187
|
}, [configProvider, clientProvider, servicesProvider, noBanner]);
|
|
185
188
|
|
package/src/echo/index.ts
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useMemo, useSyncExternalStore } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Space } from '@dxos/client/echo';
|
|
8
|
+
import { type EchoSchema } from '@dxos/echo-schema';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to and retrieve schema changes from a space's schema registry.
|
|
12
|
+
*/
|
|
13
|
+
export const useSchema = (space: Space | undefined, typename: string | undefined): EchoSchema | undefined => {
|
|
14
|
+
const { subscribe, getSchema } = useMemo(() => {
|
|
15
|
+
if (!typename || !space) {
|
|
16
|
+
return {
|
|
17
|
+
subscribe: () => () => {},
|
|
18
|
+
getSchema: () => undefined,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const query = space.db.schemaRegistry.query({ typename });
|
|
23
|
+
const initialResult = query.runSync()[0];
|
|
24
|
+
let currentSchema = initialResult;
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
subscribe: (onStoreChange: () => void) => {
|
|
28
|
+
const unsubscribe = query.subscribe(() => {
|
|
29
|
+
currentSchema = query.results[0];
|
|
30
|
+
onStoreChange();
|
|
31
|
+
});
|
|
32
|
+
return unsubscribe;
|
|
33
|
+
},
|
|
34
|
+
getSchema: () => currentSchema,
|
|
35
|
+
};
|
|
36
|
+
}, [typename, space]);
|
|
37
|
+
|
|
38
|
+
return useSyncExternalStore(subscribe, getSchema);
|
|
39
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxos-theme';
|
|
6
|
+
|
|
7
|
+
import React, { useCallback } from 'react';
|
|
8
|
+
|
|
9
|
+
import { Config, PublicKey } from '@dxos/client';
|
|
10
|
+
import { invariant } from '@dxos/invariant';
|
|
11
|
+
import { log } from '@dxos/log';
|
|
12
|
+
import { Button } from '@dxos/react-ui';
|
|
13
|
+
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
14
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
15
|
+
|
|
16
|
+
import { useCredentials } from './useCredentials';
|
|
17
|
+
import { useIdentity } from './useIdentity';
|
|
18
|
+
import { useClient } from '../client';
|
|
19
|
+
import { withClientProvider } from '../testing';
|
|
20
|
+
|
|
21
|
+
const getNewChallenge = () => Math.random().toString(36).substring(2);
|
|
22
|
+
|
|
23
|
+
const Test = () => {
|
|
24
|
+
const client = useClient();
|
|
25
|
+
const identity = useIdentity();
|
|
26
|
+
const credentials = useCredentials();
|
|
27
|
+
|
|
28
|
+
const handleCreateIdentity = useCallback(async () => {
|
|
29
|
+
await client.halo.createIdentity();
|
|
30
|
+
invariant(client.services.services.EdgeAgentService, 'Missing EdgeAgentService');
|
|
31
|
+
await client.services.services.EdgeAgentService.createAgent(null as any, { timeout: 10_000 });
|
|
32
|
+
}, [client]);
|
|
33
|
+
|
|
34
|
+
// TODO(wittjosiah): Consider factoring out passkey creation to the halo api.
|
|
35
|
+
const handleCreatePassKey = useCallback(async () => {
|
|
36
|
+
invariant(identity, 'Identity not available');
|
|
37
|
+
const challenge = getNewChallenge();
|
|
38
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialCreationOptions
|
|
39
|
+
const credential = await navigator.credentials
|
|
40
|
+
.create({
|
|
41
|
+
publicKey: {
|
|
42
|
+
challenge: new TextEncoder().encode(challenge),
|
|
43
|
+
rp: { id: location.hostname, name: 'Test' },
|
|
44
|
+
user: {
|
|
45
|
+
id: new TextEncoder().encode(identity.did),
|
|
46
|
+
name: identity.did,
|
|
47
|
+
displayName: identity.profile?.displayName ?? '',
|
|
48
|
+
},
|
|
49
|
+
pubKeyCredParams: [
|
|
50
|
+
{ type: 'public-key', alg: -8 }, // Ed25519 (not yet supported across all browsers)
|
|
51
|
+
{ type: 'public-key', alg: -7 }, // ES256
|
|
52
|
+
],
|
|
53
|
+
// https://web.dev/articles/webauthn-discoverable-credentials#resident-key
|
|
54
|
+
authenticatorSelection: {
|
|
55
|
+
residentKey: 'required',
|
|
56
|
+
requireResidentKey: true,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
.catch(log.error);
|
|
61
|
+
|
|
62
|
+
invariant(credential, 'Credential not available');
|
|
63
|
+
const recoveryKey = PublicKey.from(new Uint8Array((credential as any).response.getPublicKey()));
|
|
64
|
+
const algorithm = (credential as any).response.getPublicKeyAlgorithm() === -7 ? 'ES256' : 'ED25519';
|
|
65
|
+
|
|
66
|
+
invariant(client.services.services.IdentityService, 'IdentityService not available');
|
|
67
|
+
// TODO(wittjosiah): This needs a proper api.
|
|
68
|
+
await client.services.services.IdentityService.createRecoveryCredential({
|
|
69
|
+
recoveryKey,
|
|
70
|
+
algorithm,
|
|
71
|
+
});
|
|
72
|
+
}, [client, identity]);
|
|
73
|
+
|
|
74
|
+
const handleAuthenticate = useCallback(async () => {
|
|
75
|
+
invariant(client.services.services.IdentityService, 'IdentityService not available');
|
|
76
|
+
const { deviceKey, controlFeedKey, challenge } =
|
|
77
|
+
await client.services.services.IdentityService.requestRecoveryChallenge();
|
|
78
|
+
const credential = await navigator.credentials
|
|
79
|
+
.get({
|
|
80
|
+
publicKey: {
|
|
81
|
+
challenge: Buffer.from(challenge, 'base64'),
|
|
82
|
+
rpId: location.hostname,
|
|
83
|
+
// NOTE: Don't prompt for password in storybook for test purposes.
|
|
84
|
+
// In practice, this should be set to 'required' for identity recovery.
|
|
85
|
+
userVerification: 'discouraged',
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
.catch(log.error);
|
|
89
|
+
const identityDid = new TextDecoder().decode((credential as any).response.userHandle);
|
|
90
|
+
await client.services.services.IdentityService.recoverIdentity({
|
|
91
|
+
external: {
|
|
92
|
+
identityDid,
|
|
93
|
+
deviceKey,
|
|
94
|
+
controlFeedKey,
|
|
95
|
+
signature: Buffer.from((credential as any).response.signature),
|
|
96
|
+
clientDataJson: Buffer.from((credential as any).response.clientDataJSON),
|
|
97
|
+
authenticatorData: Buffer.from((credential as any).response.authenticatorData),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}, []);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<>
|
|
104
|
+
<div className='mbe-4 flex gap-2'>
|
|
105
|
+
<Button disabled={!!identity} onClick={handleCreateIdentity}>
|
|
106
|
+
Create Identity
|
|
107
|
+
</Button>
|
|
108
|
+
<Button disabled={!identity} onClick={handleCreatePassKey}>
|
|
109
|
+
Create Passkey
|
|
110
|
+
</Button>
|
|
111
|
+
<Button disabled={!!identity} onClick={handleAuthenticate}>
|
|
112
|
+
Authenticate with Passkey
|
|
113
|
+
</Button>
|
|
114
|
+
</div>
|
|
115
|
+
<div className='flex flex-col min-w-[28rem] divide-y divide-separator border border-separator rounded'>
|
|
116
|
+
<SyntaxHighlighter language='json'>
|
|
117
|
+
{JSON.stringify({ identity, credentials: credentials.length }, null, 2)}
|
|
118
|
+
</SyntaxHighlighter>
|
|
119
|
+
</div>
|
|
120
|
+
</>
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export default {
|
|
125
|
+
title: 'sdk/react-client/Passkeys',
|
|
126
|
+
render: Test,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const config = new Config({
|
|
130
|
+
runtime: {
|
|
131
|
+
client: {
|
|
132
|
+
edgeFeatures: {
|
|
133
|
+
agents: true,
|
|
134
|
+
echoReplicator: true,
|
|
135
|
+
feedReplicator: true,
|
|
136
|
+
signaling: true,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
services: {
|
|
140
|
+
edge: {
|
|
141
|
+
// url: 'wss://edge.dxos.workers.dev/',
|
|
142
|
+
url: 'ws://localhost:8787',
|
|
143
|
+
},
|
|
144
|
+
iceProviders: [{ urls: 'https://edge-production.dxos.workers.dev/ice' }],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export const Default = {
|
|
150
|
+
decorators: [withClientProvider({ config }), withTheme],
|
|
151
|
+
};
|
package/src/halo/index.ts
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { Credential } from '@dxos/client/halo';
|
|
6
|
+
import { useMulticastObservable } from '@dxos/react-hooks';
|
|
7
|
+
|
|
8
|
+
import { useClient } from '../client';
|
|
9
|
+
|
|
10
|
+
export const useCredentials = (): Credential[] => {
|
|
11
|
+
const client = useClient();
|
|
12
|
+
return useMulticastObservable(client.halo.credentials);
|
|
13
|
+
};
|