@dxos/react-client 0.8.4-main.72ec0f3 → 0.8.4-main.74a063c4e0
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 +2 -4
- package/.storybook/preview.mts +2 -2
- package/dist/lib/browser/chunk-BVDNYEYI.mjs +179 -0
- package/dist/lib/browser/chunk-BVDNYEYI.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 +51 -114
- package/dist/lib/browser/echo/index.mjs.map +4 -4
- package/dist/lib/browser/halo/index.mjs +2 -1
- package/dist/lib/browser/halo/index.mjs.map +1 -1
- package/dist/lib/browser/index.mjs +114 -12
- package/dist/lib/browser/index.mjs.map +4 -4
- 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 +85 -114
- 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-NETTSER2.mjs +17 -0
- package/dist/lib/node-esm/chunk-NETTSER2.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-VSA674XR.mjs +180 -0
- package/dist/lib/node-esm/chunk-VSA674XR.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 +51 -114
- package/dist/lib/node-esm/echo/index.mjs.map +4 -4
- package/dist/lib/node-esm/halo/index.mjs +2 -1
- package/dist/lib/node-esm/halo/index.mjs.map +1 -1
- package/dist/lib/node-esm/index.mjs +114 -12
- package/dist/lib/node-esm/index.mjs.map +4 -4
- 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 +85 -114
- 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 +3 -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 +18 -9
- package/dist/types/src/echo/useSpaces.d.ts.map +1 -1
- package/dist/types/src/echo/useSyncState.d.ts.map +1 -1
- package/dist/types/src/halo/Passkey.stories.d.ts.map +1 -1
- package/dist/types/src/testing/ClientRepeater.d.ts +5 -4
- package/dist/types/src/testing/ClientRepeater.d.ts.map +1 -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/util.d.ts.map +1 -1
- package/dist/types/src/testing/withClientProvider.d.ts +0 -3
- package/dist/types/src/testing/withClientProvider.d.ts.map +1 -1
- package/dist/types/src/testing/withClientProvider.stories.d.ts +2 -1
- package/dist/types/src/testing/withClientProvider.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/docs/README.md +38 -38
- package/moon.yml +2 -5
- package/package.json +35 -32
- package/src/client/ClientProvider.test.tsx +0 -1
- package/src/client/ClientProvider.tsx +12 -27
- package/src/client/useConfig.test.tsx +0 -1
- package/src/echo/index.ts +2 -2
- package/src/echo/useMembers.ts +5 -3
- package/src/echo/useQueue.ts +3 -2
- package/src/echo/useSpaceInvitations.ts +9 -5
- package/src/echo/useSpaceProperties.ts +28 -0
- package/src/echo/useSpaces.test.tsx +7 -14
- package/src/echo/useSpaces.ts +33 -27
- package/src/echo/useSyncState.ts +4 -3
- package/src/halo/Passkey.stories.tsx +4 -5
- package/src/halo/useDevices.test.tsx +0 -1
- package/src/testing/ClientRepeater.stories.tsx +10 -6
- package/src/testing/ClientRepeater.tsx +11 -15
- package/src/testing/context.tsx +4 -3
- package/src/testing/util.tsx +0 -1
- package/src/testing/withClientProvider.stories.tsx +12 -7
- package/src/testing/withClientProvider.tsx +62 -82
- package/tsconfig.json +3 -3
- package/typedoc.json +1 -1
- package/vitest.config.ts +1 -3
- 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-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/dist/types/src/playwright/playwright.config.d.ts +0 -3
- package/dist/types/src/playwright/playwright.config.d.ts.map +0 -1
- package/dist/types/src/playwright/smoke.spec.d.ts +0 -2
- package/dist/types/src/playwright/smoke.spec.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
- package/src/playwright/playwright.config.ts +0 -17
- package/src/playwright/smoke.spec.ts +0 -24
|
@@ -9,12 +9,11 @@ import { Config, PublicKey } from '@dxos/client';
|
|
|
9
9
|
import { invariant } from '@dxos/invariant';
|
|
10
10
|
import { log } from '@dxos/log';
|
|
11
11
|
import { Button } from '@dxos/react-ui';
|
|
12
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
13
12
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
13
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
14
14
|
|
|
15
15
|
import { useClient } from '../client';
|
|
16
16
|
import { withClientProvider } from '../testing';
|
|
17
|
-
|
|
18
17
|
import { useCredentials } from './useCredentials';
|
|
19
18
|
import { useIdentity } from './useIdentity';
|
|
20
19
|
|
|
@@ -105,7 +104,7 @@ const Test = () => {
|
|
|
105
104
|
|
|
106
105
|
return (
|
|
107
106
|
<>
|
|
108
|
-
<div className='
|
|
107
|
+
<div className='mb-4 flex gap-2'>
|
|
109
108
|
<Button disabled={!!identity} onClick={handleCreateIdentity}>
|
|
110
109
|
Create Identity
|
|
111
110
|
</Button>
|
|
@@ -116,7 +115,7 @@ const Test = () => {
|
|
|
116
115
|
Authenticate with Passkey
|
|
117
116
|
</Button>
|
|
118
117
|
</div>
|
|
119
|
-
<div className='flex flex-col min-w-[28rem] divide-y divide-separator border border-separator rounded'>
|
|
118
|
+
<div className='flex flex-col min-w-[28rem] divide-y divide-separator border border-separator rounded-sm'>
|
|
120
119
|
<SyntaxHighlighter language='json'>
|
|
121
120
|
{JSON.stringify({ identity, credentials: credentials.length }, null, 2)}
|
|
122
121
|
</SyntaxHighlighter>
|
|
@@ -148,7 +147,7 @@ const config = new Config({
|
|
|
148
147
|
const meta = {
|
|
149
148
|
title: 'sdk/react-client/Passkeys',
|
|
150
149
|
render: Test,
|
|
151
|
-
decorators: [withClientProvider({ config }), withTheme],
|
|
150
|
+
decorators: [withClientProvider({ config }), withTheme()],
|
|
152
151
|
} satisfies Meta;
|
|
153
152
|
|
|
154
153
|
export default meta;
|
|
@@ -6,7 +6,6 @@ import { renderHook } from '@testing-library/react';
|
|
|
6
6
|
import { describe, expect, test } from 'vitest';
|
|
7
7
|
|
|
8
8
|
import { createClient, createClientContextProvider } from '../testing/util';
|
|
9
|
-
|
|
10
9
|
import { useDevices } from './useDevices';
|
|
11
10
|
|
|
12
11
|
describe('useDevices', () => {
|
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
|
+
import { Obj } from '@dxos/echo';
|
|
8
9
|
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/react-ui/testing';
|
|
11
12
|
|
|
12
13
|
import { useClient } from '../client';
|
|
13
14
|
import { useSpace } from '../echo';
|
|
14
|
-
|
|
15
15
|
import { type ClientRepeatedComponentProps, ClientRepeater } from './ClientRepeater';
|
|
16
16
|
|
|
17
17
|
const meta = {
|
|
18
18
|
title: 'sdk/react-client/ClientRepeater',
|
|
19
|
-
decorators: [withTheme],
|
|
19
|
+
decorators: [withTheme()],
|
|
20
20
|
} satisfies Meta<typeof Input>;
|
|
21
21
|
|
|
22
22
|
export default meta;
|
|
@@ -32,9 +32,9 @@ const ClientStory = () => {
|
|
|
32
32
|
return <JsonPanel value={client.toJSON()} />;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
const ClientSpace = ({
|
|
35
|
+
const ClientSpace = ({ spaceId }: ClientRepeatedComponentProps) => {
|
|
36
36
|
const client = useClient();
|
|
37
|
-
const space = useSpace(
|
|
37
|
+
const space = useSpace(spaceId);
|
|
38
38
|
if (!space?.isOpen) {
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
@@ -45,7 +45,11 @@ const ClientSpace = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
|
45
45
|
<Input.TextInput
|
|
46
46
|
placeholder='Name'
|
|
47
47
|
value={space.properties.name}
|
|
48
|
-
onChange={(event) =>
|
|
48
|
+
onChange={(event) =>
|
|
49
|
+
Obj.change(space.properties, (obj) => {
|
|
50
|
+
obj.name = event.target.value;
|
|
51
|
+
})
|
|
52
|
+
}
|
|
49
53
|
/>
|
|
50
54
|
</Input.Root>
|
|
51
55
|
<JsonPanel value={client.toJSON()} />
|
|
@@ -2,20 +2,19 @@
|
|
|
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 {
|
|
11
|
-
import { faker } from '@dxos/random';
|
|
9
|
+
import { type Type } from '@dxos/echo';
|
|
10
|
+
import { random } from '@dxos/random';
|
|
12
11
|
import { useAsyncEffect } from '@dxos/react-hooks';
|
|
13
12
|
|
|
14
13
|
import { ClientProvider } from '../client';
|
|
15
|
-
|
|
14
|
+
import { type SpaceId } from '../echo';
|
|
16
15
|
import { type WithClientProviderProps } from './withClientProvider';
|
|
17
16
|
|
|
18
|
-
export type ClientRepeatedComponentProps = { id: number; count: number;
|
|
17
|
+
export type ClientRepeatedComponentProps = { id: number; count: number; spaceId?: SpaceId };
|
|
19
18
|
|
|
20
19
|
export type ClientRepeaterControlsProps = { clients: Client[] };
|
|
21
20
|
|
|
@@ -26,7 +25,7 @@ export type ClientRepeaterProps<P extends ClientRepeatedComponentProps> = {
|
|
|
26
25
|
controls?: FC<ClientRepeaterControlsProps>;
|
|
27
26
|
count?: number;
|
|
28
27
|
clients?: Client[];
|
|
29
|
-
types?:
|
|
28
|
+
types?: Type.AnyObj[];
|
|
30
29
|
args?: Omit<P, 'id' | 'count'>;
|
|
31
30
|
} & Pick<WithClientProviderProps, 'createIdentity' | 'createSpace' | 'onCreateSpace'>;
|
|
32
31
|
|
|
@@ -50,12 +49,9 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
50
49
|
createSpace,
|
|
51
50
|
onCreateSpace,
|
|
52
51
|
} = props;
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
registerSignalsRuntime();
|
|
55
|
-
}, []);
|
|
56
52
|
|
|
57
53
|
const [clients, setClients] = useState(props.clients ?? []);
|
|
58
|
-
const [
|
|
54
|
+
const [spaceId, setSpaceId] = useState<SpaceId>();
|
|
59
55
|
|
|
60
56
|
const testBuilder = useRef(new TestBuilder());
|
|
61
57
|
useAsyncEffect(async () => {
|
|
@@ -70,8 +66,8 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
70
66
|
|
|
71
67
|
if (createSpace) {
|
|
72
68
|
const client = clients[0];
|
|
73
|
-
const space = await client.spaces.create({ name:
|
|
74
|
-
|
|
69
|
+
const space = await client.spaces.create({ name: random.commerce.productName() });
|
|
70
|
+
setSpaceId(space.id);
|
|
75
71
|
await onCreateSpace?.({ client, space }, {});
|
|
76
72
|
await Promise.all(clients.slice(1).flatMap((client) => performInvitation({ host: space, guest: client.spaces })));
|
|
77
73
|
}
|
|
@@ -88,7 +84,7 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
88
84
|
{Controls && <Controls clients={clients} />}
|
|
89
85
|
{clients.map((client, index) => (
|
|
90
86
|
<ClientProvider key={index} client={client}>
|
|
91
|
-
<Component id={index} count={clients.length}
|
|
87
|
+
<Component id={index} count={clients.length} spaceId={spaceId} {...{ ...props.args }} />
|
|
92
88
|
</ClientProvider>
|
|
93
89
|
))}
|
|
94
90
|
</>
|
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
|
};
|
package/src/testing/util.tsx
CHANGED
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
6
|
import React from 'react';
|
|
7
|
+
import { expect, within } from 'storybook/test';
|
|
7
8
|
|
|
8
9
|
import { log } from '@dxos/log';
|
|
9
|
-
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
10
10
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
11
|
-
import {
|
|
11
|
+
import { withLayout, withTheme, Loading } from '@dxos/react-ui/testing';
|
|
12
12
|
|
|
13
13
|
import { useClient } from '../client';
|
|
14
14
|
import { type Space, useSpaces } from '../echo';
|
|
15
|
-
|
|
16
15
|
import { type WithClientProviderProps, withClientProvider, withMultiClientProvider } from './withClientProvider';
|
|
17
16
|
|
|
18
17
|
const SpaceInfo = ({ space }: { space: Space }) => {
|
|
@@ -24,7 +23,7 @@ const DefaultStory = () => {
|
|
|
24
23
|
const client = useClient();
|
|
25
24
|
const spaces = useSpaces();
|
|
26
25
|
if (!client) {
|
|
27
|
-
return
|
|
26
|
+
return <Loading data={{ client: !!client }} />;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
return (
|
|
@@ -39,8 +38,9 @@ const DefaultStory = () => {
|
|
|
39
38
|
|
|
40
39
|
const meta = {
|
|
41
40
|
title: 'sdk/react-client/withClientProvider',
|
|
42
|
-
render:
|
|
43
|
-
decorators: [withTheme],
|
|
41
|
+
render: DefaultStory,
|
|
42
|
+
decorators: [withTheme()],
|
|
43
|
+
tags: ['test'],
|
|
44
44
|
} satisfies Meta<typeof DefaultStory>;
|
|
45
45
|
|
|
46
46
|
export default meta;
|
|
@@ -63,11 +63,16 @@ export const Default: Story = {
|
|
|
63
63
|
parameters: {
|
|
64
64
|
layout: 'centered',
|
|
65
65
|
},
|
|
66
|
+
play: async ({ canvasElement }) => {
|
|
67
|
+
const canvas = within(canvasElement);
|
|
68
|
+
const initializedText = await canvas.findByText(/"initialized": true/i, {}, { timeout: 30_000 });
|
|
69
|
+
await expect(initializedText).toBeVisible();
|
|
70
|
+
},
|
|
66
71
|
};
|
|
67
72
|
|
|
68
73
|
export const Multiple: Story = {
|
|
69
74
|
decorators: [
|
|
70
|
-
withTheme,
|
|
75
|
+
withTheme(),
|
|
71
76
|
withLayout({ classNames: 'grid grid-cols-3' }),
|
|
72
77
|
withMultiClientProvider({ ...clientProps, numClients: 3 }),
|
|
73
78
|
],
|
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Decorator, type StoryContext } from '@storybook/react';
|
|
6
|
-
import React, {
|
|
7
|
-
import { type FallbackProps, ErrorBoundary as NaturalErrorBoundary } from 'react-error-boundary';
|
|
6
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
8
7
|
|
|
9
8
|
import { Trigger } from '@dxos/async';
|
|
10
9
|
import { type Client } from '@dxos/client';
|
|
11
|
-
import { type Space } from '@dxos/client/echo';
|
|
10
|
+
import { type Space, type SpaceId } from '@dxos/client/echo';
|
|
12
11
|
import { TestBuilder, performInvitation } from '@dxos/client/testing';
|
|
13
12
|
import { log } from '@dxos/log';
|
|
13
|
+
import { ErrorBoundary } from '@dxos/react-ui';
|
|
14
14
|
import { type MaybePromise } from '@dxos/util';
|
|
15
15
|
|
|
16
16
|
import { ClientProvider, type ClientProviderProps } from '../client';
|
|
17
|
-
|
|
18
17
|
import { ClientStory } from './context';
|
|
19
18
|
|
|
20
19
|
type InitializeProps = {
|
|
@@ -39,8 +38,6 @@ const initializeClient = async (
|
|
|
39
38
|
if (createIdentity || createSpace) {
|
|
40
39
|
if (!client.halo.identity.get()) {
|
|
41
40
|
await client.halo.createIdentity();
|
|
42
|
-
await client.spaces.waitUntilReady();
|
|
43
|
-
await client.spaces.default.waitUntilReady();
|
|
44
41
|
await onCreateIdentity?.({ client }, context);
|
|
45
42
|
}
|
|
46
43
|
}
|
|
@@ -52,7 +49,7 @@ const initializeClient = async (
|
|
|
52
49
|
await onCreateSpace?.({ client, space }, context);
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
return { space };
|
|
52
|
+
return { space, spaceId: space?.id };
|
|
56
53
|
};
|
|
57
54
|
|
|
58
55
|
export type WithClientProviderProps = InitializeProps & Omit<ClientProviderProps, 'onInitialized'>;
|
|
@@ -87,7 +84,7 @@ export const withClientProvider = ({
|
|
|
87
84
|
};
|
|
88
85
|
|
|
89
86
|
return (
|
|
90
|
-
<ErrorBoundary>
|
|
87
|
+
<ErrorBoundary name='client-provider'>
|
|
91
88
|
<ClientProvider onInitialized={handleInitialized} {...props}>
|
|
92
89
|
<ClientStory.Provider value={data}>
|
|
93
90
|
<Story />
|
|
@@ -104,10 +101,6 @@ export const withClientProvider = ({
|
|
|
104
101
|
export type WithMultiClientProviderProps = InitializeProps &
|
|
105
102
|
Omit<ClientProviderProps, 'onInitialized'> & { numClients?: number };
|
|
106
103
|
|
|
107
|
-
const MultiClientContext = createContext<{ id: number }>({ id: 0 });
|
|
108
|
-
|
|
109
|
-
export const useMultiClient = () => useContext(MultiClientContext);
|
|
110
|
-
|
|
111
104
|
/**
|
|
112
105
|
* Decorator that creates a scaffold for multiple clients.
|
|
113
106
|
* Orchestrates invitations between a randomly selected host and the remaining clients.
|
|
@@ -122,86 +115,73 @@ export const withMultiClientProvider = ({
|
|
|
122
115
|
...props
|
|
123
116
|
}: WithMultiClientProviderProps): Decorator => {
|
|
124
117
|
return (Story, context) => {
|
|
125
|
-
const builder = useRef(new TestBuilder());
|
|
126
|
-
const hostRef = useRef<Client>(null);
|
|
127
118
|
const spaceReady = useRef(new Trigger<Space | undefined>());
|
|
119
|
+
const [spaceId, setSpaceId] = useState<SpaceId>();
|
|
120
|
+
|
|
121
|
+
const clients = useMemo(() => {
|
|
122
|
+
const buidler = new TestBuilder();
|
|
123
|
+
return Array.from({ length: numClients }).map(() => {
|
|
124
|
+
return {
|
|
125
|
+
services: buidler.createLocalClientServices(),
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}, [numClients]);
|
|
128
129
|
|
|
129
130
|
// Handle invitations.
|
|
130
131
|
// NOTE: The zeroth client isn't necessarily the first to be initialized.
|
|
131
|
-
const handleInitialized = async (client: Client, index: number) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
132
|
+
const handleInitialized = useCallback(async (client: Client, index: number) => {
|
|
133
|
+
if (index === 0) {
|
|
134
|
+
// Initialize host.
|
|
135
|
+
const { space } = await initializeClient(
|
|
136
|
+
client,
|
|
137
|
+
{
|
|
138
|
+
createIdentity,
|
|
139
|
+
createSpace,
|
|
140
|
+
onCreateSpace,
|
|
141
|
+
onCreateIdentity,
|
|
142
|
+
onInitialized,
|
|
143
|
+
},
|
|
144
|
+
context,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
log.info('inviting', { index, spaceId: space?.id });
|
|
148
|
+
setSpaceId(space?.id);
|
|
149
|
+
spaceReady.current.wake(space);
|
|
150
|
+
} else {
|
|
151
|
+
// Initialize guest.
|
|
152
|
+
await initializeClient(client, { createIdentity, onInitialized }, context);
|
|
153
|
+
|
|
154
|
+
log.info('waiting', { index });
|
|
155
|
+
const space = await spaceReady.current.wait();
|
|
156
|
+
if (space) {
|
|
157
|
+
log.info('joining', { index, spaceId: space.id });
|
|
158
|
+
await Promise.all(
|
|
159
|
+
performInvitation({
|
|
160
|
+
host: space,
|
|
161
|
+
guest: client.spaces,
|
|
162
|
+
}),
|
|
146
163
|
);
|
|
147
|
-
|
|
148
|
-
spaceReady.current.wake(space);
|
|
149
|
-
log.info('inviting', { index });
|
|
150
|
-
} else {
|
|
151
|
-
await initializeClient(client, { createIdentity, onInitialized }, context);
|
|
152
|
-
const space = await spaceReady.current.wait();
|
|
153
|
-
if (space) {
|
|
154
|
-
log.info('joining', { index });
|
|
155
|
-
await Promise.all(performInvitation({ host: space, guest: client.spaces }));
|
|
156
|
-
}
|
|
157
164
|
}
|
|
158
165
|
}
|
|
159
|
-
};
|
|
166
|
+
}, []);
|
|
160
167
|
|
|
161
168
|
return (
|
|
162
|
-
<
|
|
163
|
-
{
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
<div className='absolute inset-0 grid grid-flow-col auto-cols-fr gap-4 overflow-hidden'>
|
|
170
|
+
{clients.map((client, index) => (
|
|
171
|
+
<ClientProvider
|
|
172
|
+
key={index}
|
|
173
|
+
services={client.services}
|
|
174
|
+
onInitialized={(client) => handleInitialized(client, index)}
|
|
175
|
+
{...props}
|
|
176
|
+
>
|
|
177
|
+
<ClientStory.Provider value={{ index, spaceId }}>
|
|
178
|
+
<ErrorBoundary name='client-provider'>
|
|
179
|
+
<Story />
|
|
180
|
+
</ErrorBoundary>
|
|
181
|
+
</ClientStory.Provider>
|
|
182
|
+
</ClientProvider>
|
|
173
183
|
))}
|
|
174
|
-
</
|
|
184
|
+
</div>
|
|
175
185
|
);
|
|
176
186
|
};
|
|
177
187
|
};
|
|
178
|
-
|
|
179
|
-
const ErrorBoundary = ({ children }: PropsWithChildren) => {
|
|
180
|
-
const [error, setError] = useState<Error>();
|
|
181
|
-
useEffect(() => {
|
|
182
|
-
const handleError = (event: PromiseRejectionEvent) => {
|
|
183
|
-
setError(event.reason);
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
window.addEventListener('unhandledrejection', handleError);
|
|
187
|
-
return () => window.removeEventListener('unhandledrejection', handleError);
|
|
188
|
-
}, []);
|
|
189
|
-
|
|
190
|
-
if (error) {
|
|
191
|
-
return <ErrorFallback error={error} resetErrorBoundary={() => setError(undefined)} />;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return <NaturalErrorBoundary FallbackComponent={ErrorFallback}>{children}</NaturalErrorBoundary>;
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const ErrorFallback = ({ error }: FallbackProps) => {
|
|
198
|
-
const { name, message, stack } =
|
|
199
|
-
error instanceof Error ? error : { name: 'Error', message: String(error), stack: undefined };
|
|
200
|
-
return (
|
|
201
|
-
<div role='alert' className='flex flex-col p-4 gap-4 overflow-auto'>
|
|
202
|
-
<h1 className='text-xl text-red-500'>{name}</h1>
|
|
203
|
-
<div className='text-lg'>{message}</div>
|
|
204
|
-
{stack && <pre className='whitespace-pre-wrap text-sm text-subdued'>{stack}</pre>}
|
|
205
|
-
</div>
|
|
206
|
-
);
|
|
207
|
-
};
|
package/tsconfig.json
CHANGED
|
@@ -45,13 +45,13 @@
|
|
|
45
45
|
"path": "../../core/echo/echo"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
|
-
"path": "../../core/echo/echo-
|
|
48
|
+
"path": "../../core/echo/echo-react"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
"path": "../../core/protocols"
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
|
-
"path": "../../ui/primitives/react-hooks"
|
|
54
|
+
"path": "../../ui/react-primitives/react-hooks"
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
"path": "../../ui/react-ui"
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"path": "../../ui/react-ui-syntax-highlighter"
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
|
-
"path": "../../ui/
|
|
63
|
+
"path": "../../ui/ui-theme"
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
"path": "../client"
|
package/typedoc.json
CHANGED
package/vitest.config.ts
CHANGED
|
@@ -10,9 +10,7 @@ import { createConfig } from '../../../vitest.base.config';
|
|
|
10
10
|
export default createConfig({
|
|
11
11
|
dirname: typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)),
|
|
12
12
|
node: {
|
|
13
|
-
environment: '
|
|
14
|
-
// TODO(dmaretskyi): Enabled because client tests were flaky. Remove when that's not the case.
|
|
15
|
-
retry: 2,
|
|
13
|
+
environment: 'happy-dom',
|
|
16
14
|
},
|
|
17
15
|
storybook: true,
|
|
18
16
|
});
|