@dxos/react-client 0.7.5-labs.a279d8c → 0.7.5-labs.a8b535d

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 (77) hide show
  1. package/dist/lib/browser/{chunk-Z6OJATVN.mjs → chunk-72QD3PXL.mjs} +15 -13
  2. package/dist/lib/browser/{chunk-Z6OJATVN.mjs.map → chunk-72QD3PXL.mjs.map} +3 -3
  3. package/dist/lib/browser/devtools/index.mjs +1 -1
  4. package/dist/lib/browser/echo/index.mjs +44 -9
  5. package/dist/lib/browser/echo/index.mjs.map +4 -4
  6. package/dist/lib/browser/halo/index.mjs +15 -7
  7. package/dist/lib/browser/halo/index.mjs.map +4 -4
  8. package/dist/lib/browser/index.mjs +1 -1
  9. package/dist/lib/browser/mesh/index.mjs +1 -1
  10. package/dist/lib/browser/meta.json +1 -1
  11. package/dist/lib/browser/testing/index.mjs +1 -1
  12. package/dist/lib/node/{chunk-DITMVTAF.cjs → chunk-5RUIFUEC.cjs} +17 -15
  13. package/dist/lib/node/{chunk-DITMVTAF.cjs.map → chunk-5RUIFUEC.cjs.map} +3 -3
  14. package/dist/lib/node/devtools/index.cjs +2 -2
  15. package/dist/lib/node/echo/index.cjs +47 -13
  16. package/dist/lib/node/echo/index.cjs.map +4 -4
  17. package/dist/lib/node/halo/index.cjs +16 -9
  18. package/dist/lib/node/halo/index.cjs.map +4 -4
  19. package/dist/lib/node/index.cjs +12 -12
  20. package/dist/lib/node/mesh/index.cjs +2 -2
  21. package/dist/lib/node/meta.json +1 -1
  22. package/dist/lib/node/testing/index.cjs +4 -4
  23. package/dist/lib/node-esm/{chunk-6QHTUQ4Z.mjs → chunk-NBTZZNQ5.mjs} +15 -13
  24. package/dist/lib/node-esm/{chunk-6QHTUQ4Z.mjs.map → chunk-NBTZZNQ5.mjs.map} +3 -3
  25. package/dist/lib/node-esm/devtools/index.mjs +1 -1
  26. package/dist/lib/node-esm/echo/index.mjs +44 -9
  27. package/dist/lib/node-esm/echo/index.mjs.map +4 -4
  28. package/dist/lib/node-esm/halo/index.mjs +15 -7
  29. package/dist/lib/node-esm/halo/index.mjs.map +4 -4
  30. package/dist/lib/node-esm/index.mjs +1 -1
  31. package/dist/lib/node-esm/mesh/index.mjs +1 -1
  32. package/dist/lib/node-esm/meta.json +1 -1
  33. package/dist/lib/node-esm/testing/index.mjs +1 -1
  34. package/dist/types/src/client/AgentHostingProvider.d.ts +1 -1
  35. package/dist/types/src/client/AgentHostingProvider.d.ts.map +1 -1
  36. package/dist/types/src/client/ClientProvider.d.ts.map +1 -1
  37. package/dist/types/src/client/useClient.d.ts +1 -1
  38. package/dist/types/src/client/useClient.d.ts.map +1 -1
  39. package/dist/types/src/client/useConfig.d.ts +1 -1
  40. package/dist/types/src/client/useConfig.d.ts.map +1 -1
  41. package/dist/types/src/echo/index.d.ts +1 -0
  42. package/dist/types/src/echo/index.d.ts.map +1 -1
  43. package/dist/types/src/echo/useQuery.d.ts +1 -1
  44. package/dist/types/src/echo/useQuery.d.ts.map +1 -1
  45. package/dist/types/src/echo/useSchema.d.ts +7 -0
  46. package/dist/types/src/echo/useSchema.d.ts.map +1 -0
  47. package/dist/types/src/halo/Passkey.stories.d.ts +10 -0
  48. package/dist/types/src/halo/Passkey.stories.d.ts.map +1 -0
  49. package/dist/types/src/halo/index.d.ts +1 -0
  50. package/dist/types/src/halo/index.d.ts.map +1 -1
  51. package/dist/types/src/halo/useCredentials.d.ts +3 -0
  52. package/dist/types/src/halo/useCredentials.d.ts.map +1 -0
  53. package/dist/types/src/playwright/{playwright.config.d.ts → playwright.config.d.cts} +1 -1
  54. package/dist/types/src/playwright/playwright.config.d.cts.map +1 -0
  55. package/dist/types/src/testing/ClientRepeater.d.ts +2 -2
  56. package/dist/types/src/testing/ClientRepeater.d.ts.map +1 -1
  57. package/dist/types/src/testing/ClientRepeater.stories.d.ts +3 -4
  58. package/dist/types/src/testing/ClientRepeater.stories.d.ts.map +1 -1
  59. package/dist/types/src/testing/util.d.ts +2 -3
  60. package/dist/types/src/testing/util.d.ts.map +1 -1
  61. package/dist/types/src/testing/withClientProvider.stories.d.ts +3 -4
  62. package/dist/types/src/testing/withClientProvider.stories.d.ts.map +1 -1
  63. package/dist/types/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +24 -23
  65. package/project.json +1 -0
  66. package/src/client/ClientProvider.test.tsx +3 -2
  67. package/src/client/ClientProvider.tsx +11 -8
  68. package/src/echo/index.ts +1 -0
  69. package/src/echo/useQuery.ts +8 -7
  70. package/src/echo/useSchema.ts +39 -0
  71. package/src/halo/Passkey.stories.tsx +153 -0
  72. package/src/halo/index.ts +1 -0
  73. package/src/halo/useCredentials.ts +13 -0
  74. package/tsconfig.json +1 -7
  75. package/dist/types/src/playwright/playwright.config.d.ts.map +0 -1
  76. /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
  77. /package/src/playwright/{playwright.config.ts → playwright.config.cts} +0 -0
package/src/echo/index.ts CHANGED
@@ -6,6 +6,7 @@ export * from '@dxos/client/echo';
6
6
 
7
7
  export * from './useMembers';
8
8
  export * from './useQuery';
9
+ export * from './useSchema';
9
10
  export * from './useSpaces';
10
11
  export * from './useSpaceInvitations';
11
12
  export * from './useSubscription';
@@ -5,11 +5,11 @@
5
5
  import { useMemo, useSyncExternalStore } from 'react';
6
6
 
7
7
  import {
8
- Filter,
9
8
  isSpace,
10
9
  type Echo,
11
- type ReactiveEchoObject,
12
10
  type FilterSource,
11
+ Filter,
12
+ type ReactiveEchoObject,
13
13
  type Query,
14
14
  type QueryOptions,
15
15
  type Space,
@@ -25,13 +25,14 @@ export const useQuery = <T extends ReactiveEchoObject<any>>(
25
25
  options?: QueryOptions,
26
26
  deps?: any[],
27
27
  ): T[] => {
28
- const { subscribe, getObjects } = useMemo(() => {
28
+ const { getObjects, subscribe } = useMemo(() => {
29
+ let subscribed = false;
29
30
  const query = isSpace(spaceOrEcho)
30
31
  ? spaceOrEcho.db.query(filter, options)
31
32
  : (spaceOrEcho?.query(filter, options) as Query<T> | undefined);
32
- let subscribed = false;
33
33
 
34
34
  return {
35
+ getObjects: () => (subscribed && query ? query.objects : EMPTY_ARRAY),
35
36
  subscribe: (cb: () => void) => {
36
37
  subscribed = true;
37
38
  const unsubscribe = query?.subscribe(cb) ?? noop;
@@ -40,13 +41,13 @@ export const useQuery = <T extends ReactiveEchoObject<any>>(
40
41
  subscribed = false;
41
42
  };
42
43
  },
43
- getObjects: () => (subscribed && query ? query.objects : EMPTY_ARRAY),
44
44
  };
45
45
  }, [spaceOrEcho, ...filterToDepsArray(filter), ...(deps ?? [])]);
46
46
 
47
47
  // https://beta.reactjs.org/reference/react/useSyncExternalStore
48
- // NOTE: This hook will resubscribe whenever the callback passed to the first argument changes -- make sure it is stable.
49
- return useSyncExternalStore<T[] | undefined>(subscribe, getObjects) ?? [];
48
+ // NOTE: This hook will resubscribe whenever the callback passed to the first argument changes; make sure it is stable.
49
+ const objects = useSyncExternalStore<T[] | undefined>(subscribe, getObjects);
50
+ return objects ?? [];
50
51
  };
51
52
 
52
53
  const filterToDepsArray = (filter?: FilterSource<any>) => [JSON.stringify(Filter.from(filter).toProto())];
@@ -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,153 @@
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
+ const lookupKey = PublicKey.random();
39
+ // https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialCreationOptions
40
+ const credential = await navigator.credentials
41
+ .create({
42
+ publicKey: {
43
+ challenge: new TextEncoder().encode(challenge),
44
+ rp: { id: location.hostname, name: 'Test' },
45
+ user: {
46
+ id: lookupKey.asUint8Array(),
47
+ name: identity.did,
48
+ displayName: identity.profile?.displayName ?? '',
49
+ },
50
+ pubKeyCredParams: [
51
+ { type: 'public-key', alg: -8 }, // Ed25519 (not yet supported across all browsers)
52
+ { type: 'public-key', alg: -7 }, // ES256
53
+ ],
54
+ // https://web.dev/articles/webauthn-discoverable-credentials#resident-key
55
+ authenticatorSelection: {
56
+ residentKey: 'required',
57
+ requireResidentKey: true,
58
+ },
59
+ },
60
+ })
61
+ .catch(log.error);
62
+
63
+ invariant(credential, 'Credential not available');
64
+ const recoveryKey = PublicKey.from(new Uint8Array((credential as any).response.getPublicKey()));
65
+ const algorithm = (credential as any).response.getPublicKeyAlgorithm() === -7 ? 'ES256' : 'ED25519';
66
+
67
+ invariant(client.services.services.IdentityService, 'IdentityService not available');
68
+ // TODO(wittjosiah): This needs a proper api.
69
+ await client.services.services.IdentityService.createRecoveryCredential({
70
+ recoveryKey,
71
+ algorithm,
72
+ lookupKey,
73
+ });
74
+ }, [client, identity]);
75
+
76
+ const handleAuthenticate = useCallback(async () => {
77
+ invariant(client.services.services.IdentityService, 'IdentityService not available');
78
+ const { deviceKey, controlFeedKey, challenge } =
79
+ await client.services.services.IdentityService.requestRecoveryChallenge();
80
+ const credential = await navigator.credentials
81
+ .get({
82
+ publicKey: {
83
+ challenge: Buffer.from(challenge, 'base64'),
84
+ rpId: location.hostname,
85
+ // NOTE: Don't prompt for password in storybook for test purposes.
86
+ // In practice, this should be set to 'required' for identity recovery.
87
+ userVerification: 'discouraged',
88
+ },
89
+ })
90
+ .catch(log.error);
91
+ const lookupKey = PublicKey.from(new Uint8Array((credential as any).response.userHandle));
92
+ await client.services.services.IdentityService.recoverIdentity({
93
+ external: {
94
+ lookupKey,
95
+ deviceKey,
96
+ controlFeedKey,
97
+ signature: Buffer.from((credential as any).response.signature),
98
+ clientDataJson: Buffer.from((credential as any).response.clientDataJSON),
99
+ authenticatorData: Buffer.from((credential as any).response.authenticatorData),
100
+ },
101
+ });
102
+ }, []);
103
+
104
+ return (
105
+ <>
106
+ <div className='mbe-4 flex gap-2'>
107
+ <Button disabled={!!identity} onClick={handleCreateIdentity}>
108
+ Create Identity
109
+ </Button>
110
+ <Button disabled={!identity} onClick={handleCreatePassKey}>
111
+ Create Passkey
112
+ </Button>
113
+ <Button disabled={!!identity} onClick={handleAuthenticate}>
114
+ Authenticate with Passkey
115
+ </Button>
116
+ </div>
117
+ <div className='flex flex-col min-w-[28rem] divide-y divide-separator border border-separator rounded'>
118
+ <SyntaxHighlighter language='json'>
119
+ {JSON.stringify({ identity, credentials: credentials.length }, null, 2)}
120
+ </SyntaxHighlighter>
121
+ </div>
122
+ </>
123
+ );
124
+ };
125
+
126
+ export default {
127
+ title: 'sdk/react-client/Passkeys',
128
+ render: Test,
129
+ };
130
+
131
+ const config = new Config({
132
+ runtime: {
133
+ client: {
134
+ edgeFeatures: {
135
+ agents: true,
136
+ echoReplicator: true,
137
+ feedReplicator: true,
138
+ signaling: true,
139
+ },
140
+ },
141
+ services: {
142
+ edge: {
143
+ url: 'wss://edge-main.dxos.workers.dev/',
144
+ // url: 'ws://localhost:8787',
145
+ },
146
+ iceProviders: [{ urls: 'https://edge-production.dxos.workers.dev/ice' }],
147
+ },
148
+ },
149
+ });
150
+
151
+ export const Default = {
152
+ decorators: [withClientProvider({ config }), withTheme],
153
+ };
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
+ };
package/tsconfig.json CHANGED
@@ -1,13 +1,7 @@
1
1
  {
2
2
  "extends": "../../../tsconfig.base.json",
3
3
  "compilerOptions": {
4
- "jsx": "react",
5
- "lib": [
6
- "DOM",
7
- "ESNext"
8
- ],
9
- "module": "ESNext",
10
- "outDir": "./dist/types"
4
+ "module": "ESNext"
11
5
  },
12
6
  "include": [
13
7
  "playwright",
@@ -1 +0,0 @@
1
- {"version":3,"file":"playwright.config.d.ts","sourceRoot":"","sources":["../../../../src/playwright/playwright.config.ts"],"names":[],"mappings":";AASA,wBAQG"}
File without changes