@dxos/react-client 0.8.4-main.1da679c → 0.8.4-main.21d9917
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/dist/lib/browser/chunk-5UKA2BMA.mjs +179 -0
- package/dist/lib/browser/chunk-5UKA2BMA.mjs.map +7 -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-PFF63FPR.mjs +16 -0
- package/dist/lib/browser/chunk-PFF63FPR.mjs.map +7 -0
- package/dist/lib/browser/chunk-TSF6Y5VB.mjs +8 -0
- package/dist/lib/browser/chunk-TSF6Y5VB.mjs.map +7 -0
- package/dist/lib/browser/devtools/index.mjs +2 -1
- package/dist/lib/browser/devtools/index.mjs.map +1 -1
- package/dist/lib/browser/echo/index.mjs +46 -93
- package/dist/lib/browser/echo/index.mjs.map +4 -4
- package/dist/lib/browser/halo/index.mjs +8 -21
- package/dist/lib/browser/halo/index.mjs.map +2 -2
- package/dist/lib/browser/index.mjs +114 -12
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/invitations/index.mjs +1 -1
- package/dist/lib/browser/mesh/index.mjs +2 -1
- package/dist/lib/browser/mesh/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +91 -83
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/chunk-373DPHEQ.mjs +10 -0
- package/dist/lib/node-esm/chunk-373DPHEQ.mjs.map +7 -0
- 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-NETTSER2.mjs +17 -0
- package/dist/lib/node-esm/chunk-NETTSER2.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-T3QTQHBD.mjs +180 -0
- package/dist/lib/node-esm/chunk-T3QTQHBD.mjs.map +7 -0
- package/dist/lib/node-esm/devtools/index.mjs +2 -1
- package/dist/lib/node-esm/devtools/index.mjs.map +1 -1
- package/dist/lib/node-esm/echo/index.mjs +46 -93
- package/dist/lib/node-esm/echo/index.mjs.map +4 -4
- package/dist/lib/node-esm/halo/index.mjs +8 -21
- package/dist/lib/node-esm/halo/index.mjs.map +2 -2
- package/dist/lib/node-esm/index.mjs +114 -12
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/invitations/index.mjs +1 -1
- package/dist/lib/node-esm/mesh/index.mjs +2 -1
- package/dist/lib/node-esm/mesh/index.mjs.map +1 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +91 -83
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/client/ClientProvider.d.ts +0 -10
- package/dist/types/src/client/ClientProvider.d.ts.map +1 -1
- package/dist/types/src/echo/index.d.ts +2 -2
- package/dist/types/src/echo/index.d.ts.map +1 -1
- package/dist/types/src/echo/useMembers.d.ts +2 -2
- package/dist/types/src/echo/useMembers.d.ts.map +1 -1
- package/dist/types/src/echo/useQueue.d.ts +2 -2
- package/dist/types/src/echo/useQueue.d.ts.map +1 -1
- package/dist/types/src/echo/useSpaceInvitations.d.ts +3 -2
- package/dist/types/src/echo/useSpaceInvitations.d.ts.map +1 -1
- package/dist/types/src/echo/useSpaceProperties.d.ts +13 -0
- package/dist/types/src/echo/useSpaceProperties.d.ts.map +1 -0
- package/dist/types/src/echo/useSpaces.d.ts +17 -8
- 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 +1 -1
- 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 +6 -5
- package/dist/types/src/testing/ClientRepeater.d.ts.map +1 -1
- package/dist/types/src/testing/ClientRepeater.stories.d.ts +0 -1
- package/dist/types/src/testing/ClientRepeater.stories.d.ts.map +1 -1
- package/dist/types/src/testing/context.d.ts +4 -2
- package/dist/types/src/testing/context.d.ts.map +1 -1
- package/dist/types/src/testing/withClientProvider.d.ts +5 -9
- package/dist/types/src/testing/withClientProvider.d.ts.map +1 -1
- package/dist/types/src/testing/withClientProvider.stories.d.ts +1 -1
- 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 +35 -32
- package/src/client/ClientProvider.tsx +12 -26
- package/src/echo/index.ts +2 -2
- package/src/echo/useMembers.ts +5 -3
- package/src/echo/useQueue.ts +2 -2
- package/src/echo/useSpaceInvitations.ts +9 -4
- package/src/echo/useSpaceProperties.ts +28 -0
- package/src/echo/useSpaces.test.tsx +1 -1
- package/src/echo/useSpaces.ts +33 -12
- package/src/echo/useSubscription.ts +1 -1
- package/src/echo/useSyncState.ts +2 -2
- package/src/halo/Passkey.stories.tsx +11 -14
- package/src/invitations/useInvitationStatus.ts +9 -8
- package/src/playwright/playwright.config.ts +1 -1
- package/src/testing/ClientRepeater.stories.tsx +9 -6
- package/src/testing/ClientRepeater.tsx +12 -15
- package/src/testing/context.tsx +4 -3
- package/src/testing/withClientProvider.stories.tsx +12 -9
- package/src/testing/withClientProvider.tsx +75 -59
- package/tsconfig.json +3 -6
- package/vitest.config.ts +12 -16
- package/dist/lib/browser/chunk-BXOLJYAY.mjs.map +0 -7
- package/dist/lib/browser/chunk-TA2HGRAF.mjs +0 -309
- package/dist/lib/browser/chunk-TA2HGRAF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-256IHY6F.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ZJMNF7GC.mjs +0 -311
- package/dist/lib/node-esm/chunk-ZJMNF7GC.mjs.map +0 -7
- package/dist/types/src/echo/useQuery.d.ts +0 -11
- package/dist/types/src/echo/useQuery.d.ts.map +0 -1
- package/dist/types/src/echo/useQuery.test.d.ts +0 -2
- package/dist/types/src/echo/useQuery.test.d.ts.map +0 -1
- package/dist/types/src/echo/useSchema.d.ts +0 -8
- package/dist/types/src/echo/useSchema.d.ts.map +0 -1
- package/src/echo/useQuery.test.tsx +0 -129
- package/src/echo/useQuery.ts +0 -64
- package/src/echo/useSchema.ts +0 -54
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { type FC,
|
|
5
|
+
import React, { type FC, useRef, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { Client
|
|
7
|
+
import { Client } from '@dxos/client';
|
|
8
8
|
import { TestBuilder, performInvitation } from '@dxos/client/testing';
|
|
9
|
-
import { type
|
|
10
|
-
import { registerSignalsRuntime } from '@dxos/echo-signals/react';
|
|
9
|
+
import { type Type } from '@dxos/echo';
|
|
11
10
|
import { faker } from '@dxos/random';
|
|
12
11
|
import { useAsyncEffect } from '@dxos/react-hooks';
|
|
13
12
|
|
|
14
13
|
import { ClientProvider } from '../client';
|
|
14
|
+
import { type SpaceId } from '../echo';
|
|
15
15
|
|
|
16
16
|
import { type WithClientProviderProps } from './withClientProvider';
|
|
17
17
|
|
|
18
|
-
export type ClientRepeatedComponentProps = { id: number; count: number;
|
|
18
|
+
export type ClientRepeatedComponentProps = { id: number; count: number; spaceId?: SpaceId };
|
|
19
19
|
|
|
20
20
|
export type ClientRepeaterControlsProps = { clients: Client[] };
|
|
21
21
|
|
|
@@ -26,9 +26,9 @@ export type ClientRepeaterProps<P extends ClientRepeatedComponentProps> = {
|
|
|
26
26
|
controls?: FC<ClientRepeaterControlsProps>;
|
|
27
27
|
count?: number;
|
|
28
28
|
clients?: Client[];
|
|
29
|
-
types?:
|
|
29
|
+
types?: Type.Obj.Any[];
|
|
30
30
|
args?: Omit<P, 'id' | 'count'>;
|
|
31
|
-
} & Pick<WithClientProviderProps, 'createIdentity' | 'createSpace' | '
|
|
31
|
+
} & Pick<WithClientProviderProps, 'createIdentity' | 'createSpace' | 'onCreateSpace'>;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Utility component for Storybook stories which sets up clients for n peers.
|
|
@@ -48,14 +48,11 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
48
48
|
types,
|
|
49
49
|
createIdentity,
|
|
50
50
|
createSpace,
|
|
51
|
-
|
|
51
|
+
onCreateSpace,
|
|
52
52
|
} = props;
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
registerSignalsRuntime();
|
|
55
|
-
}, []);
|
|
56
53
|
|
|
57
54
|
const [clients, setClients] = useState(props.clients ?? []);
|
|
58
|
-
const [
|
|
55
|
+
const [spaceId, setSpaceId] = useState<SpaceId>();
|
|
59
56
|
|
|
60
57
|
const testBuilder = useRef(new TestBuilder());
|
|
61
58
|
useAsyncEffect(async () => {
|
|
@@ -71,8 +68,8 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
71
68
|
if (createSpace) {
|
|
72
69
|
const client = clients[0];
|
|
73
70
|
const space = await client.spaces.create({ name: faker.commerce.productName() });
|
|
74
|
-
|
|
75
|
-
await
|
|
71
|
+
setSpaceId(space.id);
|
|
72
|
+
await onCreateSpace?.({ client, space }, {});
|
|
76
73
|
await Promise.all(clients.slice(1).flatMap((client) => performInvitation({ host: space, guest: client.spaces })));
|
|
77
74
|
}
|
|
78
75
|
|
|
@@ -88,7 +85,7 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
88
85
|
{Controls && <Controls clients={clients} />}
|
|
89
86
|
{clients.map((client, index) => (
|
|
90
87
|
<ClientProvider key={index} client={client}>
|
|
91
|
-
<Component id={index} count={clients.length}
|
|
88
|
+
<Component id={index} count={clients.length} spaceId={spaceId} {...{ ...props.args }} />
|
|
92
89
|
</ClientProvider>
|
|
93
90
|
))}
|
|
94
91
|
</>
|
package/src/testing/context.tsx
CHANGED
|
@@ -4,16 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
import { createContext, useContext } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type Space } from '@dxos/client/echo';
|
|
7
|
+
import { type Space, type SpaceId } from '@dxos/client/echo';
|
|
8
8
|
import { raise } from '@dxos/debug';
|
|
9
9
|
|
|
10
10
|
export type ClientStory = {
|
|
11
|
+
index?: number;
|
|
12
|
+
spaceId?: SpaceId;
|
|
11
13
|
space?: Space;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
// TODO(wittjosiah): Add to multi-client as well.
|
|
15
16
|
export const ClientStory = createContext<ClientStory | undefined>(undefined);
|
|
16
17
|
|
|
17
|
-
export const
|
|
18
|
+
export const useClientStory = (): ClientStory => {
|
|
18
19
|
return useContext(ClientStory) ?? raise(new Error('Missing ClientStory'));
|
|
19
20
|
};
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React from 'react';
|
|
9
7
|
|
|
10
8
|
import { log } from '@dxos/log';
|
|
9
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
10
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
12
|
-
import { render
|
|
11
|
+
import { render } from '@dxos/storybook-utils';
|
|
13
12
|
|
|
14
13
|
import { useClient } from '../client';
|
|
15
14
|
import { type Space, useSpaces } from '../echo';
|
|
@@ -29,7 +28,7 @@ const DefaultStory = () => {
|
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
return (
|
|
32
|
-
<div className='flex flex-col
|
|
31
|
+
<div className='flex flex-col divide-y divide-separator border border-separator'>
|
|
33
32
|
<SyntaxHighlighter language='json'>{JSON.stringify(client.toJSON(), null, 2)}</SyntaxHighlighter>
|
|
34
33
|
{spaces.map((space) => (
|
|
35
34
|
<SpaceInfo key={space.id} space={space} />
|
|
@@ -41,6 +40,7 @@ const DefaultStory = () => {
|
|
|
41
40
|
const meta = {
|
|
42
41
|
title: 'sdk/react-client/withClientProvider',
|
|
43
42
|
render: render(DefaultStory),
|
|
43
|
+
decorators: [withTheme],
|
|
44
44
|
} satisfies Meta<typeof DefaultStory>;
|
|
45
45
|
|
|
46
46
|
export default meta;
|
|
@@ -53,19 +53,22 @@ const clientProps: WithClientProviderProps = {
|
|
|
53
53
|
onInitialized: async (client) => {
|
|
54
54
|
log.info('onInitialized', { client });
|
|
55
55
|
},
|
|
56
|
-
|
|
57
|
-
log.info('
|
|
56
|
+
onCreateSpace: async ({ client, space }) => {
|
|
57
|
+
log.info('onCreateSpace', { client, space });
|
|
58
58
|
},
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
export const Default: Story = {
|
|
62
|
-
decorators: [withClientProvider(clientProps)
|
|
62
|
+
decorators: [withClientProvider(clientProps)],
|
|
63
|
+
parameters: {
|
|
64
|
+
layout: 'centered',
|
|
65
|
+
},
|
|
63
66
|
};
|
|
64
67
|
|
|
65
68
|
export const Multiple: Story = {
|
|
66
69
|
decorators: [
|
|
67
|
-
withMultiClientProvider({ ...clientProps, numClients: 3 }),
|
|
68
70
|
withTheme,
|
|
69
|
-
withLayout({ classNames:
|
|
71
|
+
withLayout({ classNames: 'grid grid-cols-3' }),
|
|
72
|
+
withMultiClientProvider({ ...clientProps, numClients: 3 }),
|
|
70
73
|
],
|
|
71
74
|
};
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Decorator, type StoryContext } from '@storybook/react';
|
|
6
|
-
import React, { type PropsWithChildren,
|
|
6
|
+
import React, { type PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
7
|
import { type FallbackProps, ErrorBoundary as NaturalErrorBoundary } from 'react-error-boundary';
|
|
8
8
|
|
|
9
9
|
import { Trigger } from '@dxos/async';
|
|
10
10
|
import { type Client } from '@dxos/client';
|
|
11
|
-
import { type Space } from '@dxos/client/echo';
|
|
11
|
+
import { type Space, type SpaceId } from '@dxos/client/echo';
|
|
12
12
|
import { TestBuilder, performInvitation } from '@dxos/client/testing';
|
|
13
13
|
import { log } from '@dxos/log';
|
|
14
14
|
import { type MaybePromise } from '@dxos/util';
|
|
@@ -21,9 +21,9 @@ type InitializeProps = {
|
|
|
21
21
|
createIdentity?: boolean;
|
|
22
22
|
createSpace?: boolean;
|
|
23
23
|
onInitialized?: (client: Client) => MaybePromise<void>;
|
|
24
|
-
|
|
24
|
+
onCreateIdentity?: (props: { client: Client }, context: StoryContext | any) => MaybePromise<void>;
|
|
25
25
|
// NOTE: context must be untyped until ClientRepeater is removed.
|
|
26
|
-
|
|
26
|
+
onCreateSpace?: (props: { client: Client; space: Space }, context: StoryContext | any) => MaybePromise<void>;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -31,7 +31,7 @@ type InitializeProps = {
|
|
|
31
31
|
*/
|
|
32
32
|
const initializeClient = async (
|
|
33
33
|
client: Client,
|
|
34
|
-
{ createIdentity, createSpace,
|
|
34
|
+
{ createIdentity, createSpace, onCreateIdentity, onCreateSpace, onInitialized }: InitializeProps,
|
|
35
35
|
context: StoryContext,
|
|
36
36
|
): Promise<ClientStory> => {
|
|
37
37
|
await onInitialized?.(client);
|
|
@@ -39,17 +39,20 @@ const initializeClient = async (
|
|
|
39
39
|
if (createIdentity || createSpace) {
|
|
40
40
|
if (!client.halo.identity.get()) {
|
|
41
41
|
await client.halo.createIdentity();
|
|
42
|
-
await
|
|
42
|
+
await client.spaces.waitUntilReady();
|
|
43
|
+
await client.spaces.default.waitUntilReady();
|
|
44
|
+
await onCreateIdentity?.({ client }, context);
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
let space: Space | undefined;
|
|
47
49
|
if (createSpace) {
|
|
48
50
|
space = await client.spaces.create({ name: 'Test Space' });
|
|
49
|
-
await
|
|
51
|
+
await space.waitUntilReady(); // Is this required?
|
|
52
|
+
await onCreateSpace?.({ client, space }, context);
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
return { space };
|
|
55
|
+
return { space, spaceId: space?.id };
|
|
53
56
|
};
|
|
54
57
|
|
|
55
58
|
export type WithClientProviderProps = InitializeProps & Omit<ClientProviderProps, 'onInitialized'>;
|
|
@@ -60,8 +63,8 @@ export type WithClientProviderProps = InitializeProps & Omit<ClientProviderProps
|
|
|
60
63
|
export const withClientProvider = ({
|
|
61
64
|
createIdentity,
|
|
62
65
|
createSpace,
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
onCreateSpace,
|
|
67
|
+
onCreateIdentity,
|
|
65
68
|
onInitialized,
|
|
66
69
|
...props
|
|
67
70
|
}: WithClientProviderProps = {}): Decorator => {
|
|
@@ -73,8 +76,8 @@ export const withClientProvider = ({
|
|
|
73
76
|
{
|
|
74
77
|
createIdentity,
|
|
75
78
|
createSpace,
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
onCreateSpace,
|
|
80
|
+
onCreateIdentity,
|
|
78
81
|
onInitialized,
|
|
79
82
|
},
|
|
80
83
|
context,
|
|
@@ -101,75 +104,87 @@ export const withClientProvider = ({
|
|
|
101
104
|
export type WithMultiClientProviderProps = InitializeProps &
|
|
102
105
|
Omit<ClientProviderProps, 'onInitialized'> & { numClients?: number };
|
|
103
106
|
|
|
104
|
-
const MultiClientContext = createContext<{ id: number }>({ id: 0 });
|
|
105
|
-
|
|
106
|
-
export const useMultiClient = () => useContext(MultiClientContext);
|
|
107
|
-
|
|
108
107
|
/**
|
|
109
108
|
* Decorator that creates a scaffold for multiple clients.
|
|
110
109
|
* Orchestrates invitations between a randomly selected host and the remaining clients.
|
|
111
|
-
* NOTE: Should come before withLayout.
|
|
112
110
|
*/
|
|
113
111
|
export const withMultiClientProvider = ({
|
|
114
112
|
numClients = 2,
|
|
115
113
|
createIdentity,
|
|
116
114
|
createSpace,
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
onCreateSpace,
|
|
116
|
+
onCreateIdentity,
|
|
119
117
|
onInitialized,
|
|
120
118
|
...props
|
|
121
119
|
}: WithMultiClientProviderProps): Decorator => {
|
|
122
120
|
return (Story, context) => {
|
|
123
|
-
const builder = useRef(new TestBuilder());
|
|
124
|
-
const hostRef = useRef<Client>();
|
|
125
121
|
const spaceReady = useRef(new Trigger<Space | undefined>());
|
|
122
|
+
const [spaceId, setSpaceId] = useState<SpaceId>();
|
|
123
|
+
|
|
124
|
+
const clients = useMemo(() => {
|
|
125
|
+
const buidler = new TestBuilder();
|
|
126
|
+
return Array.from({ length: numClients }).map(() => {
|
|
127
|
+
return {
|
|
128
|
+
services: buidler.createLocalClientServices(),
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
}, [numClients]);
|
|
126
132
|
|
|
127
133
|
// Handle invitations.
|
|
128
134
|
// NOTE: The zeroth client isn't necessarily the first to be initialized.
|
|
129
|
-
const handleInitialized = async (client: Client, index: number) => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
const handleInitialized = useCallback(async (client: Client, index: number) => {
|
|
136
|
+
if (index === 0) {
|
|
137
|
+
// Initialize host.
|
|
138
|
+
const { space } = await initializeClient(
|
|
139
|
+
client,
|
|
140
|
+
{
|
|
141
|
+
createIdentity,
|
|
142
|
+
createSpace,
|
|
143
|
+
onCreateSpace,
|
|
144
|
+
onCreateIdentity,
|
|
145
|
+
onInitialized,
|
|
146
|
+
},
|
|
147
|
+
context,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
log.info('inviting', { index, spaceId: space?.id });
|
|
151
|
+
setSpaceId(space?.id);
|
|
152
|
+
spaceReady.current.wake(space);
|
|
153
|
+
} else {
|
|
154
|
+
// Initialize guest.
|
|
155
|
+
await initializeClient(client, { createIdentity, onInitialized }, context);
|
|
156
|
+
|
|
157
|
+
log.info('waiting', { index });
|
|
158
|
+
const space = await spaceReady.current.wait();
|
|
159
|
+
if (space) {
|
|
160
|
+
log.info('joining', { index, spaceId: space.id });
|
|
161
|
+
await Promise.all(
|
|
162
|
+
performInvitation({
|
|
163
|
+
host: space,
|
|
164
|
+
guest: client.spaces,
|
|
165
|
+
}),
|
|
144
166
|
);
|
|
145
|
-
|
|
146
|
-
spaceReady.current.wake(space);
|
|
147
|
-
log.info('inviting', { index });
|
|
148
|
-
} else {
|
|
149
|
-
await initializeClient(client, { createIdentity, onInitialized }, context);
|
|
150
|
-
const space = await spaceReady.current.wait();
|
|
151
|
-
if (space) {
|
|
152
|
-
log.info('joining', { index });
|
|
153
|
-
await Promise.all(performInvitation({ host: space, guest: client.spaces }));
|
|
154
|
-
}
|
|
155
167
|
}
|
|
156
168
|
}
|
|
157
|
-
};
|
|
169
|
+
}, []);
|
|
158
170
|
|
|
159
171
|
return (
|
|
160
|
-
<
|
|
161
|
-
{
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
<div className='absolute inset-0 grid grid-flow-col auto-cols-fr gap-4 overflow-hidden'>
|
|
173
|
+
{clients.map((client, index) => (
|
|
174
|
+
<ClientProvider
|
|
175
|
+
key={index}
|
|
176
|
+
services={client.services}
|
|
177
|
+
onInitialized={(client) => handleInitialized(client, index)}
|
|
178
|
+
{...props}
|
|
179
|
+
>
|
|
180
|
+
<ClientStory.Provider value={{ index, spaceId }}>
|
|
181
|
+
<ErrorBoundary>
|
|
182
|
+
<Story />
|
|
183
|
+
</ErrorBoundary>
|
|
184
|
+
</ClientStory.Provider>
|
|
185
|
+
</ClientProvider>
|
|
171
186
|
))}
|
|
172
|
-
</
|
|
187
|
+
</div>
|
|
173
188
|
);
|
|
174
189
|
};
|
|
175
190
|
};
|
|
@@ -195,6 +210,7 @@ const ErrorBoundary = ({ children }: PropsWithChildren) => {
|
|
|
195
210
|
const ErrorFallback = ({ error }: FallbackProps) => {
|
|
196
211
|
const { name, message, stack } =
|
|
197
212
|
error instanceof Error ? error : { name: 'Error', message: String(error), stack: undefined };
|
|
213
|
+
|
|
198
214
|
return (
|
|
199
215
|
<div role='alert' className='flex flex-col p-4 gap-4 overflow-auto'>
|
|
200
216
|
<h1 className='text-xl text-red-500'>{name}</h1>
|
package/tsconfig.json
CHANGED
|
@@ -45,16 +45,13 @@
|
|
|
45
45
|
"path": "../../core/echo/echo"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
|
-
"path": "../../core/echo/echo-
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"path": "../../core/echo/echo-signals"
|
|
48
|
+
"path": "../../core/echo/echo-react"
|
|
52
49
|
},
|
|
53
50
|
{
|
|
54
51
|
"path": "../../core/protocols"
|
|
55
52
|
},
|
|
56
53
|
{
|
|
57
|
-
"path": "../../ui/primitives/react-hooks"
|
|
54
|
+
"path": "../../ui/react-primitives/react-hooks"
|
|
58
55
|
},
|
|
59
56
|
{
|
|
60
57
|
"path": "../../ui/react-ui"
|
|
@@ -63,7 +60,7 @@
|
|
|
63
60
|
"path": "../../ui/react-ui-syntax-highlighter"
|
|
64
61
|
},
|
|
65
62
|
{
|
|
66
|
-
"path": "../../ui/
|
|
63
|
+
"path": "../../ui/ui-theme"
|
|
67
64
|
},
|
|
68
65
|
{
|
|
69
66
|
"path": "../client"
|
package/vitest.config.ts
CHANGED
|
@@ -2,21 +2,17 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
+
import { createConfig } from '../../../vitest.base.config';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// TODO(dmaretskyi): Enabled because client tests were flaky. Remove when that's not the case.
|
|
19
|
-
retry: 2,
|
|
20
|
-
},
|
|
21
|
-
}),
|
|
22
|
-
);
|
|
10
|
+
export default createConfig({
|
|
11
|
+
dirname: typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)),
|
|
12
|
+
node: {
|
|
13
|
+
environment: 'happy-dom',
|
|
14
|
+
// TODO(dmaretskyi): Enabled because client tests were flaky. Remove when that's not the case.
|
|
15
|
+
retry: 2,
|
|
16
|
+
},
|
|
17
|
+
storybook: true,
|
|
18
|
+
});
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/invitations/index.ts", "../../../src/invitations/useInvitationStatus.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2020 DXOS.org\n//\n\n// NOTE: Export * fails here.\nexport {\n AuthenticatingInvitationObservable,\n CancellableInvitationObservable,\n InvitationEncoder,\n Invitation,\n type Invitations,\n InvitationsProxy,\n} from '@dxos/client/invitations';\n\nexport * from './useInvitationStatus';\n", "//\n// Copyright 2022 DXOS.org\n//\n\nimport { type Reducer, useCallback, useEffect, useMemo, useReducer } from 'react';\n\nimport { type PublicKey } from '@dxos/client';\nimport {\n type AuthenticatingInvitationObservable,\n type CancellableInvitationObservable,\n Invitation,\n InvitationEncoder,\n} from '@dxos/client/invitations';\nimport { log } from '@dxos/log';\n\nexport type InvitationResult = {\n spaceKey: PublicKey | null;\n identityKey: PublicKey | null;\n swarmKey: PublicKey | null;\n target: string | null;\n};\n\ninterface InvitationReducerState {\n status: Invitation.State; // TODO(burdon): Rename state.\n haltedAt?: Invitation.State;\n result: InvitationResult;\n error?: number;\n id?: string;\n multiUse?: boolean;\n invitationCode?: string;\n authCode?: string;\n authMethod?: Invitation.AuthMethod;\n type?: Invitation.Type;\n}\n\ntype InvitationPayload = { invitation: Invitation };\n\nexport type InvitationAction =\n | ({\n status:\n | Invitation.State.INIT\n | Invitation.State.CONNECTED\n | Invitation.State.READY_FOR_AUTHENTICATION\n | Invitation.State.AUTHENTICATING;\n } & InvitationPayload)\n | ({\n status: Invitation.State.CONNECTING;\n observable: CancellableInvitationObservable;\n } & InvitationPayload)\n | ({\n status: Invitation.State.SUCCESS;\n result: InvitationResult;\n } & InvitationPayload)\n | ({\n status: Invitation.State.CANCELLED | Invitation.State.TIMEOUT;\n haltedAt: Invitation.State;\n } & InvitationPayload)\n | {\n status: Invitation.State.ERROR;\n error?: Error;\n haltedAt: Invitation.State;\n };\n\nexport type InvitationStatus = {\n id?: string;\n invitationCode?: string;\n authCode?: string;\n authMethod?: Invitation.AuthMethod;\n type?: Invitation.Type;\n status: Invitation.State;\n haltedAt?: Invitation.State;\n multiUse?: boolean;\n shareable?: boolean;\n result: InvitationResult;\n error?: number;\n cancel(): void;\n // TODO(wittjosiah): Remove?\n connect(observable: CancellableInvitationObservable): void;\n authenticate(authCode: string): Promise<void>;\n};\n\n// Without private key, the invitation code cannot be created.\n// These invitations are only available to be accepted but not shared.\nconst isShareableInvitation = (invitation: Invitation) =>\n invitation.authMethod !== Invitation.AuthMethod.KNOWN_PUBLIC_KEY || invitation.guestKeypair?.privateKey;\n\nexport const useInvitationStatus = (observable?: CancellableInvitationObservable): InvitationStatus => {\n const [state, dispatch] = useReducer<Reducer<InvitationReducerState, InvitationAction>, null>(\n (prev, action) => {\n log('useInvitationStatus', { action });\n const invitationProps =\n 'invitation' in action\n ? {\n id: action.invitation.invitationId,\n multiUse: action.invitation.multiUse,\n invitationCode: InvitationEncoder.encode(action.invitation),\n authCode: action.invitation.authCode,\n authMethod: action.invitation.authMethod,\n shareable: isShareableInvitation(action.invitation),\n type: action.invitation.type,\n }\n : {};\n return {\n ...prev,\n ...invitationProps,\n // TODO(burdon): State.\n status: action.status,\n // `invitationObservable`, `secret`, and `result` is persisted between the status-actions that set them.\n result: action.status === Invitation.State.SUCCESS ? action.result : prev.result,\n // `error` gets set each time we enter the error state\n ...(action.status === Invitation.State.ERROR && { error: action.error }),\n // `haltedAt` gets set on only the first error/cancelled/timeout action and reset on any others.\n ...((action.status === Invitation.State.ERROR ||\n action.status === Invitation.State.CANCELLED ||\n action.status === Invitation.State.TIMEOUT) && {\n haltedAt: typeof prev.haltedAt === 'undefined' ? action.haltedAt : prev.haltedAt,\n }),\n } as InvitationReducerState;\n },\n null,\n (_arg: null) => {\n const invitation = observable?.get();\n return {\n status: Invitation.State.INIT,\n result: { spaceKey: null, identityKey: null, swarmKey: null, target: null },\n id: invitation?.invitationId,\n multiUse: invitation?.multiUse,\n invitationCode: invitation ? InvitationEncoder.encode(invitation) : undefined,\n authCode: invitation?.authCode,\n authMethod: invitation?.authMethod,\n shareable: invitation ? isShareableInvitation(invitation) : false,\n type: invitation?.type,\n };\n },\n );\n\n // Handle unmount.\n\n useEffect(() => {\n const update = (invitation: Invitation) => {\n switch (invitation.state) {\n case Invitation.State.INIT:\n case Invitation.State.CONNECTED:\n case Invitation.State.READY_FOR_AUTHENTICATION:\n case Invitation.State.AUTHENTICATING: {\n dispatch({\n invitation,\n status: invitation.state,\n });\n break;\n }\n\n case Invitation.State.SUCCESS: {\n dispatch({\n invitation,\n status: invitation.state,\n result: {\n spaceKey: invitation.spaceKey || null,\n identityKey: invitation.identityKey || null,\n swarmKey: invitation.swarmKey || null,\n target: invitation.target || null,\n },\n });\n break;\n }\n\n case Invitation.State.CANCELLED:\n case Invitation.State.TIMEOUT: {\n dispatch({ invitation, status: invitation.state, haltedAt: state.status });\n break;\n }\n }\n };\n\n const subscription = observable?.subscribe(update, (err: Error) => {\n dispatch({ status: Invitation.State.ERROR, error: err, haltedAt: state.status });\n });\n\n const currentState = observable?.get();\n if (currentState) {\n update(currentState);\n }\n\n return () => subscription?.unsubscribe();\n }, [observable, state.status]);\n\n // Return memoized callbacks & values.\n\n const connect = useCallback((observable: CancellableInvitationObservable) => {\n dispatch({ invitation: observable.get(), status: Invitation.State.CONNECTING, observable });\n }, []);\n\n const authenticate = useCallback(\n (authCode: string) => {\n log('authenticating...', { authCode });\n return (observable as AuthenticatingInvitationObservable).authenticate(authCode);\n },\n [observable],\n );\n\n const cancel = useCallback(async () => observable?.cancel(), [observable]);\n\n return useMemo(() => {\n return {\n ...state,\n cancel,\n connect,\n authenticate,\n };\n }, [state, cancel, connect, authenticate]);\n};\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["AuthenticatingInvitationObservable", "CancellableInvitationObservable", "InvitationEncoder", "Invitation", "InvitationsProxy", "useCallback", "useEffect", "useMemo", "useReducer", "Invitation", "InvitationEncoder", "log", "isShareableInvitation", "invitation", "authMethod", "AuthMethod", "KNOWN_PUBLIC_KEY", "guestKeypair", "privateKey", "useInvitationStatus", "observable", "state", "dispatch", "prev", "action", "invitationProps", "id", "invitationId", "multiUse", "invitationCode", "encode", "authCode", "shareable", "type", "status", "result", "State", "SUCCESS", "ERROR", "error", "CANCELLED", "TIMEOUT", "haltedAt", "_arg", "get", "INIT", "spaceKey", "identityKey", "swarmKey", "target", "undefined", "update", "CONNECTED", "READY_FOR_AUTHENTICATION", "AUTHENTICATING", "subscription", "subscribe", "err", "currentState", "unsubscribe", "connect", "CONNECTING", "authenticate", "cancel"]
|
|
7
|
-
}
|