@dxos/react-client 0.7.5-labs.5f04cf6 → 0.7.5-labs.e27f9b9

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.
Files changed (44) hide show
  1. package/dist/lib/browser/{chunk-Z6OJATVN.mjs → chunk-LVTCEDI2.mjs} +13 -11
  2. package/dist/lib/browser/{chunk-Z6OJATVN.mjs.map → chunk-LVTCEDI2.mjs.map} +2 -2
  3. package/dist/lib/browser/devtools/index.mjs +1 -1
  4. package/dist/lib/browser/echo/index.mjs +1 -1
  5. package/dist/lib/browser/halo/index.mjs +15 -7
  6. package/dist/lib/browser/halo/index.mjs.map +4 -4
  7. package/dist/lib/browser/index.mjs +1 -1
  8. package/dist/lib/browser/mesh/index.mjs +1 -1
  9. package/dist/lib/browser/meta.json +1 -1
  10. package/dist/lib/browser/testing/index.mjs +1 -1
  11. package/dist/lib/node/{chunk-DITMVTAF.cjs → chunk-Y2HFUT7P.cjs} +16 -14
  12. package/dist/lib/node/{chunk-DITMVTAF.cjs.map → chunk-Y2HFUT7P.cjs.map} +2 -2
  13. package/dist/lib/node/devtools/index.cjs +2 -2
  14. package/dist/lib/node/echo/index.cjs +3 -3
  15. package/dist/lib/node/halo/index.cjs +16 -9
  16. package/dist/lib/node/halo/index.cjs.map +4 -4
  17. package/dist/lib/node/index.cjs +12 -12
  18. package/dist/lib/node/mesh/index.cjs +2 -2
  19. package/dist/lib/node/meta.json +1 -1
  20. package/dist/lib/node/testing/index.cjs +4 -4
  21. package/dist/lib/node-esm/{chunk-6QHTUQ4Z.mjs → chunk-MXY3GJGN.mjs} +13 -11
  22. package/dist/lib/node-esm/{chunk-6QHTUQ4Z.mjs.map → chunk-MXY3GJGN.mjs.map} +2 -2
  23. package/dist/lib/node-esm/devtools/index.mjs +1 -1
  24. package/dist/lib/node-esm/echo/index.mjs +1 -1
  25. package/dist/lib/node-esm/halo/index.mjs +15 -7
  26. package/dist/lib/node-esm/halo/index.mjs.map +4 -4
  27. package/dist/lib/node-esm/index.mjs +1 -1
  28. package/dist/lib/node-esm/mesh/index.mjs +1 -1
  29. package/dist/lib/node-esm/meta.json +1 -1
  30. package/dist/lib/node-esm/testing/index.mjs +1 -1
  31. package/dist/types/src/client/ClientProvider.d.ts.map +1 -1
  32. package/dist/types/src/halo/Passkey.stories.d.ts +11 -0
  33. package/dist/types/src/halo/Passkey.stories.d.ts.map +1 -0
  34. package/dist/types/src/halo/index.d.ts +1 -0
  35. package/dist/types/src/halo/index.d.ts.map +1 -1
  36. package/dist/types/src/halo/useCredentials.d.ts +3 -0
  37. package/dist/types/src/halo/useCredentials.d.ts.map +1 -0
  38. package/dist/types/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +23 -23
  40. package/src/client/ClientProvider.test.tsx +3 -2
  41. package/src/client/ClientProvider.tsx +9 -6
  42. package/src/halo/Passkey.stories.tsx +125 -0
  43. package/src/halo/index.ts +1 -0
  44. package/src/halo/useCredentials.ts +13 -0
@@ -151,10 +151,11 @@ describe('ClientProvider', () => {
151
151
 
152
152
  await act(async () => {
153
153
  await waitForCondition({
154
- condition: () => !client.initialized && newClient.status.get() === SystemStatus.ACTIVE,
154
+ condition: () => newClient.status.get() === SystemStatus.ACTIVE,
155
155
  });
156
156
  });
157
- expect(client.initialized).toBe(false);
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
  });
@@ -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
- void client
178
- ?.destroy()
179
- .then(() => {
180
- log('destroyed');
181
- })
182
- .catch((err) => log.catch(err));
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
 
@@ -0,0 +1,125 @@
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 { Button } from '@dxos/react-ui';
12
+ import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
13
+ import { withTheme } from '@dxos/storybook-utils';
14
+
15
+ import { useCredentials } from './useCredentials';
16
+ import { useIdentity } from './useIdentity';
17
+ import { useClient } from '../client';
18
+ import { withClientProvider } from '../testing';
19
+
20
+ const getNewChallenge = () => Math.random().toString(36).substring(2);
21
+
22
+ const Test = () => {
23
+ const client = useClient();
24
+ const identity = useIdentity();
25
+ const credentials = useCredentials();
26
+
27
+ const handleCreateIdentity = useCallback(() => client.halo.createIdentity(), [client]);
28
+ // TODO(wittjosiah): Consider factoring out passkey creation to the halo api.
29
+ const handleCreatePassKey = useCallback(async () => {
30
+ invariant(identity, 'Identity not available');
31
+ const challenge = getNewChallenge();
32
+ const credential = await navigator.credentials.create({
33
+ publicKey: {
34
+ challenge: new TextEncoder().encode(challenge),
35
+ rp: { id: 'localhost', name: 'Test' },
36
+ user: {
37
+ id: new TextEncoder().encode(identity.did),
38
+ name: identity.did,
39
+ displayName: identity.profile?.displayName ?? '',
40
+ },
41
+ pubKeyCredParams: [
42
+ { type: 'public-key', alg: -8 }, // Ed25519 (not yet supported across all browsers)
43
+ { type: 'public-key', alg: -7 }, // ES256
44
+ ],
45
+ },
46
+ });
47
+
48
+ invariant(credential, 'Credential not available');
49
+ const recoveryKey = PublicKey.from(new Uint8Array((credential as any).response.getPublicKey()));
50
+ const algorithm: number = (credential as any).response.getPublicKeyAlgorithm();
51
+
52
+ invariant(client.services.services.IdentityService, 'IdentityService not available');
53
+ // TODO(wittjosiah): This needs a proper api.
54
+ await client.services.services.IdentityService.createRecoveryCredential({
55
+ recoveryKey,
56
+ algorithm,
57
+ });
58
+ }, [client, identity]);
59
+
60
+ const handleAuthenticate = useCallback(async () => {
61
+ // TODO(wittjosiah): This should come from edge.
62
+ const challenge = getNewChallenge();
63
+ const credential = await navigator.credentials.get({
64
+ publicKey: {
65
+ challenge: new TextEncoder().encode(challenge),
66
+ rpId: 'localhost',
67
+ // NOTE: Don't prompt for password in storybook for test purposes.
68
+ // In practice, this should be set to 'required' for identity recovery.
69
+ userVerification: 'discouraged',
70
+ },
71
+ });
72
+ const did = new TextDecoder().decode((credential as any).response.userHandle);
73
+ console.log({ credential, did });
74
+ // TODO(wittjosiah): Send signature to edge for verification and admission.
75
+ }, []);
76
+
77
+ return (
78
+ <>
79
+ <div className='mbe-4 flex gap-2'>
80
+ <Button disabled={!!identity} onClick={handleCreateIdentity}>
81
+ Create Identity
82
+ </Button>
83
+ <Button disabled={!identity} onClick={handleCreatePassKey}>
84
+ Create Passkey
85
+ </Button>
86
+ <Button disabled={!!identity} onClick={handleAuthenticate}>
87
+ Authenticate with Passkey
88
+ </Button>
89
+ </div>
90
+ <div className='flex flex-col min-w-[28rem] divide-y divide-separator border border-separator rounded'>
91
+ <SyntaxHighlighter language='json'>
92
+ {JSON.stringify({ identity, credentials: credentials.length }, null, 2)}
93
+ </SyntaxHighlighter>
94
+ </div>
95
+ </>
96
+ );
97
+ };
98
+
99
+ export default {
100
+ title: 'sdk/react-client/Passkeys',
101
+ render: Test,
102
+ };
103
+
104
+ const config = new Config({
105
+ runtime: {
106
+ client: {
107
+ edgeFeatures: {
108
+ agents: true,
109
+ echoReplicator: true,
110
+ feedReplicator: true,
111
+ signaling: true,
112
+ },
113
+ },
114
+ services: {
115
+ edge: {
116
+ url: 'wss://edge.dxos.workers.dev/',
117
+ },
118
+ iceProviders: [{ urls: 'https://edge.dxos.workers.dev/ice' }],
119
+ },
120
+ },
121
+ });
122
+
123
+ export const Default = {
124
+ decorators: [withClientProvider({ config }), withTheme],
125
+ };
package/src/halo/index.ts CHANGED
@@ -14,6 +14,7 @@ export {
14
14
  } from '@dxos/client/halo';
15
15
 
16
16
  export * from './useContacts';
17
+ export * from './useCredentials';
17
18
  export * from './useDevices';
18
19
  export * from './useHaloInvitations';
19
20
  export * from './useIdentity';
@@ -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
+ };