@meshagent/meshagent-react 0.38.2 → 0.38.3
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/CHANGELOG.md +14 -1
- package/README.md +78 -41
- package/dist/cjs/client-toolkits.d.ts +2 -2
- package/dist/cjs/client-toolkits.js +143 -10
- package/dist/cjs/document-connection-scope.d.ts +7 -12
- package/dist/cjs/document-connection-scope.js +88 -82
- package/dist/cjs/index.d.ts +3 -2
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/livekit-client.d.ts +23 -0
- package/dist/cjs/livekit-client.js +66 -0
- package/dist/cjs/livekit-protocol.d.ts +21 -0
- package/dist/cjs/livekit-protocol.js +97 -0
- package/dist/cjs/room-connection-scope.d.ts +16 -16
- package/dist/cjs/room-connection-scope.js +207 -90
- package/dist/cjs/room-participants.d.ts +2 -0
- package/dist/cjs/room-participants.js +46 -0
- package/dist/esm/client-toolkits.d.ts +2 -2
- package/dist/esm/client-toolkits.js +145 -12
- package/dist/esm/document-connection-scope.d.ts +7 -12
- package/dist/esm/document-connection-scope.js +88 -81
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -2
- package/dist/esm/livekit-client.d.ts +23 -0
- package/dist/esm/livekit-client.js +61 -0
- package/dist/esm/livekit-protocol.d.ts +21 -0
- package/dist/esm/livekit-protocol.js +60 -0
- package/dist/esm/room-connection-scope.d.ts +16 -16
- package/dist/esm/room-connection-scope.js +205 -89
- package/dist/esm/room-participants.d.ts +2 -0
- package/dist/esm/room-participants.js +43 -0
- package/package.json +2 -2
- package/dist/cjs/chat.d.ts +0 -33
- package/dist/cjs/chat.js +0 -207
- package/dist/cjs/file-upload.d.ts +0 -43
- package/dist/cjs/file-upload.js +0 -168
- package/dist/esm/chat.d.ts +0 -33
- package/dist/esm/chat.js +0 -201
- package/dist/esm/file-upload.d.ts +0 -43
- package/dist/esm/file-upload.js +0 -163
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## [0.38.3]
|
|
2
|
+
- Breaking: TypeScript container image summaries now use `references`/`preferredRef` and metadata fields (timestamps/media type), and `inspectImage` returns manifests/layers/content size; `tags`/`size` are removed.
|
|
3
|
+
- `getUsage` now supports filters for users, room, provider, model, and usage type.
|
|
4
|
+
- Room connection and errors improved: `RoomServerException` now carries `statusCode` and `retryable`, and `RoomClient` can route OAuth/secret requests via handler options.
|
|
5
|
+
- React hooks add robust connection retry/backoff, new authorization helpers, and optional secret/OAuth handlers; document connection now supports schema/initial JSON and improved cleanup.
|
|
6
|
+
- New Livekit support (client + protocol channel) and room participant hook; hosted toolkits are now shared per room to avoid duplicate starts.
|
|
7
|
+
- Breaking: `meshagent-react` no longer exports the legacy chat and file-upload modules.
|
|
8
|
+
- Tailwind chat UI is rebuilt with multi-thread support, new thread creation via agent tools, and file attachment utilities, with new exports for thread and conversation helpers.
|
|
9
|
+
|
|
1
10
|
## [0.38.2]
|
|
2
11
|
- Stability
|
|
3
12
|
|
|
@@ -9,7 +18,11 @@
|
|
|
9
18
|
- Stability
|
|
10
19
|
|
|
11
20
|
## [0.37.2]
|
|
12
|
-
-
|
|
21
|
+
- Reintroduced `staticAuthorization` for parity with the Flutter room utilities.
|
|
22
|
+
- `useRoomConnection` now retries retryable startup failures, exposes a `retrying` state, and supports `onReady` plus custom client factories.
|
|
23
|
+
- `useDocumentConnection` now supports `schema` and `initialJson`, reacts to room/path changes, and safely closes documents that finish opening after unmount.
|
|
24
|
+
- `useRoomParticipants` now cleans up listeners correctly and updates cleanly when the room instance changes.
|
|
25
|
+
- Added `livekit-client.ts` and `livekit-protocol.ts`, including a `room.livekit` helper and a typed LiveKit data-channel protocol adapter.
|
|
13
26
|
|
|
14
27
|
## [0.37.1]
|
|
15
28
|
- Added `useAuth` to the React auth package to handle OAuth redirect/callback, token refresh, and profile loading without React Query, plus a `useEnsureLogin` compatibility wrapper.
|
package/README.md
CHANGED
|
@@ -1,43 +1,80 @@
|
|
|
1
1
|
# [Meshagent](https://www.meshagent.com)
|
|
2
2
|
|
|
3
|
-
## MeshAgent
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
3
|
+
## MeshAgent React
|
|
4
|
+
|
|
5
|
+
`@meshagent/meshagent-react` provides React hooks for the room and document utilities built on top of `@meshagent/meshagent`.
|
|
6
|
+
|
|
7
|
+
The package is the React counterpart to the Flutter utility widgets in `meshagent-flutter`.
|
|
8
|
+
|
|
9
|
+
## Hooks
|
|
10
|
+
|
|
11
|
+
- `useRoomConnection(...)`: authorize, connect, retry retryable room startup failures, and dispose a `RoomClient`
|
|
12
|
+
- `useDocumentConnection(...)`: open a room document, retry failed opens, and close it on unmount
|
|
13
|
+
- `useRoomParticipants(...)`: track the current remote participants for a room
|
|
14
|
+
- `useClientToolkits(...)`: host client-side toolkits for the lifetime of a component
|
|
15
|
+
- `useRoomIndicators(...)`: listen for typing and thinking indicators on a chat path
|
|
16
|
+
- `LivekitClient` / `room.livekit`: fetch LiveKit connection info from the room toolkit
|
|
17
|
+
- `LivekitProtocolChannel`: bridge MeshAgent protocol traffic over LiveKit data messages
|
|
18
|
+
|
|
19
|
+
## Example
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import {
|
|
23
|
+
staticAuthorization,
|
|
24
|
+
useDocumentConnection,
|
|
25
|
+
useRoomConnection,
|
|
26
|
+
} from "@meshagent/meshagent-react";
|
|
27
|
+
import type { RoomClient } from "@meshagent/meshagent";
|
|
28
|
+
|
|
29
|
+
export function RoomScreen(props: {
|
|
30
|
+
projectId: string;
|
|
31
|
+
roomName: string;
|
|
32
|
+
url: string;
|
|
33
|
+
jwt: string;
|
|
34
|
+
}) {
|
|
35
|
+
const connection = useRoomConnection({
|
|
36
|
+
authorization: staticAuthorization({
|
|
37
|
+
projectId: props.projectId,
|
|
38
|
+
roomName: props.roomName,
|
|
39
|
+
url: props.url,
|
|
40
|
+
jwt: props.jwt,
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (connection.state === "authorizing" || connection.state === "connecting" || connection.state === "retrying") {
|
|
45
|
+
return <div>Connecting...</div>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!connection.client || connection.state === "done") {
|
|
49
|
+
return <div>Connection failed: {String(connection.error)}</div>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return <ConnectedRoom room={connection.client} />;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function ConnectedRoom({ room }: { room: RoomClient }) {
|
|
56
|
+
const documentConnection = useDocumentConnection({
|
|
57
|
+
room,
|
|
58
|
+
path: "/notes/thread.thread",
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (documentConnection.loading) {
|
|
62
|
+
return <div>Loading document...</div>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (documentConnection.error || !documentConnection.document) {
|
|
66
|
+
return <div>Document error: {String(documentConnection.error)}</div>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return <div>Connected to document {documentConnection.document.id}</div>;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Authorization Helpers
|
|
74
|
+
|
|
75
|
+
- `developmentAuthorization(...)`: generate a participant JWT locally for development
|
|
76
|
+
- `staticAuthorization(...)`: reuse a JWT and room URL that your backend already issued
|
|
77
|
+
|
|
78
|
+
## Documentation
|
|
79
|
+
|
|
80
|
+
- [docs.meshagent.com](https://docs.meshagent.com/)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { RoomClient, Toolkit } from
|
|
1
|
+
import { RoomClient, Toolkit } from "@meshagent/meshagent";
|
|
2
2
|
interface ClientToolkitsProps {
|
|
3
3
|
room: RoomClient;
|
|
4
4
|
toolkits: Toolkit[];
|
|
5
5
|
public?: boolean;
|
|
6
6
|
}
|
|
7
|
-
export declare const useClientToolkits: ({ room, toolkits, public: isPublic }: ClientToolkitsProps) => void;
|
|
7
|
+
export declare const useClientToolkits: ({ room, toolkits, public: isPublic, }: ClientToolkitsProps) => void;
|
|
8
8
|
export {};
|
|
@@ -3,33 +3,166 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.useClientToolkits = void 0;
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
const meshagent_1 = require("@meshagent/meshagent");
|
|
6
|
-
const
|
|
6
|
+
const sharedHostedToolkits = new WeakMap();
|
|
7
|
+
function getToolkitDefinition(toolkit, isPublic) {
|
|
8
|
+
return JSON.stringify({
|
|
9
|
+
public: isPublic,
|
|
10
|
+
name: toolkit.name,
|
|
11
|
+
title: toolkit.title,
|
|
12
|
+
description: toolkit.description,
|
|
13
|
+
thumbnailUrl: toolkit.thumbnailUrl ?? null,
|
|
14
|
+
rules: [...toolkit.rules],
|
|
15
|
+
tools: toolkit.tools.map((tool) => ({
|
|
16
|
+
name: tool.name,
|
|
17
|
+
title: tool.title,
|
|
18
|
+
description: tool.description,
|
|
19
|
+
inputSpec: tool.inputSpec?.toJson() ?? null,
|
|
20
|
+
outputSpec: tool.outputSpec?.toJson() ?? null,
|
|
21
|
+
thumbnailUrl: tool.thumbnailUrl ?? null,
|
|
22
|
+
})),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function getOrCreateSharedRoomToolkits(room) {
|
|
26
|
+
const existing = sharedHostedToolkits.get(room);
|
|
27
|
+
if (existing !== undefined) {
|
|
28
|
+
return existing;
|
|
29
|
+
}
|
|
30
|
+
const created = new Map();
|
|
31
|
+
sharedHostedToolkits.set(room, created);
|
|
32
|
+
return created;
|
|
33
|
+
}
|
|
34
|
+
async function releaseHostedToolkitLease(room, toolkitName, entry) {
|
|
35
|
+
const roomToolkits = sharedHostedToolkits.get(room);
|
|
36
|
+
if (roomToolkits?.get(toolkitName) !== entry || entry.refs === 0) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
entry.refs -= 1;
|
|
40
|
+
if (entry.refs > 0) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (entry.stopPromise !== null) {
|
|
44
|
+
await entry.stopPromise;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
entry.stopPromise = (async () => {
|
|
48
|
+
try {
|
|
49
|
+
const hostedToolkit = await entry.startedToolkit;
|
|
50
|
+
await hostedToolkit.stop();
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
const latestRoomToolkits = sharedHostedToolkits.get(room);
|
|
54
|
+
if (latestRoomToolkits?.get(toolkitName) === entry) {
|
|
55
|
+
latestRoomToolkits.delete(toolkitName);
|
|
56
|
+
if (latestRoomToolkits.size === 0) {
|
|
57
|
+
sharedHostedToolkits.delete(room);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
entry.stopPromise = null;
|
|
61
|
+
}
|
|
62
|
+
})();
|
|
63
|
+
await entry.stopPromise;
|
|
64
|
+
}
|
|
65
|
+
async function acquireHostedToolkitLease({ room, toolkit, public_: isPublic, }) {
|
|
66
|
+
const definition = getToolkitDefinition(toolkit, isPublic);
|
|
67
|
+
while (true) {
|
|
68
|
+
const roomToolkits = getOrCreateSharedRoomToolkits(room);
|
|
69
|
+
const existing = roomToolkits.get(toolkit.name);
|
|
70
|
+
if (existing === undefined) {
|
|
71
|
+
const entry = {
|
|
72
|
+
definition,
|
|
73
|
+
refs: 1,
|
|
74
|
+
startedToolkit: (0, meshagent_1.startHostedToolkit)({
|
|
75
|
+
room,
|
|
76
|
+
toolkit,
|
|
77
|
+
public_: isPublic,
|
|
78
|
+
}),
|
|
79
|
+
stopPromise: null,
|
|
80
|
+
};
|
|
81
|
+
roomToolkits.set(toolkit.name, entry);
|
|
82
|
+
try {
|
|
83
|
+
await entry.startedToolkit;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (roomToolkits.get(toolkit.name) === entry) {
|
|
87
|
+
roomToolkits.delete(toolkit.name);
|
|
88
|
+
if (roomToolkits.size === 0) {
|
|
89
|
+
sharedHostedToolkits.delete(room);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
release: () => releaseHostedToolkitLease(room, toolkit.name, entry),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (existing.stopPromise !== null) {
|
|
99
|
+
await existing.stopPromise;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (existing.definition !== definition) {
|
|
103
|
+
throw new Error(`toolkit '${toolkit.name}' is already hosted for this room with a different definition`);
|
|
104
|
+
}
|
|
105
|
+
existing.refs += 1;
|
|
106
|
+
try {
|
|
107
|
+
await existing.startedToolkit;
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
existing.refs -= 1;
|
|
111
|
+
if (existing.refs === 0 && roomToolkits.get(toolkit.name) === existing) {
|
|
112
|
+
roomToolkits.delete(toolkit.name);
|
|
113
|
+
if (roomToolkits.size === 0) {
|
|
114
|
+
sharedHostedToolkits.delete(room);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
release: () => releaseHostedToolkitLease(room, toolkit.name, existing),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const useClientToolkits = ({ room, toolkits, public: isPublic = false, }) => {
|
|
125
|
+
const cleanupRef = (0, react_1.useRef)(Promise.resolve());
|
|
7
126
|
(0, react_1.useEffect)(() => {
|
|
8
127
|
let disposed = false;
|
|
9
|
-
const
|
|
10
|
-
|
|
128
|
+
const toolkitLeases = [];
|
|
129
|
+
const waitForPreviousCleanup = cleanupRef.current;
|
|
130
|
+
const startPromise = (async () => {
|
|
131
|
+
await waitForPreviousCleanup;
|
|
11
132
|
try {
|
|
12
133
|
for (const toolkit of toolkits) {
|
|
13
|
-
|
|
134
|
+
if (disposed) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const lease = await acquireHostedToolkitLease({
|
|
14
138
|
room,
|
|
15
139
|
toolkit,
|
|
16
140
|
public_: isPublic,
|
|
17
141
|
});
|
|
18
142
|
if (disposed) {
|
|
19
|
-
await
|
|
20
|
-
|
|
143
|
+
await lease.release();
|
|
144
|
+
return;
|
|
21
145
|
}
|
|
22
|
-
|
|
146
|
+
toolkitLeases.push(lease);
|
|
23
147
|
}
|
|
24
148
|
}
|
|
25
149
|
catch (error) {
|
|
26
|
-
|
|
27
|
-
|
|
150
|
+
const leasesToRelease = toolkitLeases.splice(0);
|
|
151
|
+
await Promise.all(leasesToRelease.map((lease) => lease.release()));
|
|
152
|
+
if (!disposed) {
|
|
153
|
+
console.error("unable to start client toolkits", error);
|
|
154
|
+
}
|
|
28
155
|
}
|
|
29
156
|
})();
|
|
30
157
|
return () => {
|
|
31
158
|
disposed = true;
|
|
32
|
-
|
|
159
|
+
cleanupRef.current = (async () => {
|
|
160
|
+
await startPromise;
|
|
161
|
+
const leasesToRelease = toolkitLeases.splice(0);
|
|
162
|
+
await Promise.all(leasesToRelease.map((lease) => lease.release()));
|
|
163
|
+
})().catch((error) => {
|
|
164
|
+
console.error("unable to stop client toolkits", error);
|
|
165
|
+
});
|
|
33
166
|
};
|
|
34
167
|
}, [room, toolkits, isPublic]);
|
|
35
168
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { MeshDocument,
|
|
1
|
+
import { MeshDocument, MeshSchema, RoomClient } from '@meshagent/meshagent';
|
|
2
2
|
export interface UseDocumentConnectionProps {
|
|
3
3
|
room: RoomClient;
|
|
4
4
|
path: string;
|
|
5
|
+
schema?: MeshSchema;
|
|
6
|
+
initialJson?: Record<string, unknown>;
|
|
5
7
|
onConnected?: (document: MeshDocument) => void;
|
|
6
8
|
onError?: (error: unknown) => void;
|
|
7
9
|
}
|
|
@@ -13,18 +15,11 @@ export interface UseDocumentConnectionResult {
|
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Connects to a Mesh document inside an existing RoomClient and keeps it in sync.
|
|
16
|
-
*
|
|
17
|
-
* The function retries with an exponential back‑off (capped at 60 s) until the
|
|
18
|
-
* document becomes available or the component unmounts.
|
|
19
|
-
*
|
|
20
|
-
* @param room An already‑connected RoomClient.
|
|
21
|
-
* @param path Path to the document inside the room.
|
|
22
18
|
*/
|
|
23
|
-
export declare function useDocumentConnection({ room, path, onConnected, onError }: UseDocumentConnectionProps): UseDocumentConnectionResult;
|
|
24
|
-
type
|
|
25
|
-
export declare function useDocumentChanged({ document, onChanged }: {
|
|
19
|
+
export declare function useDocumentConnection({ room, path, schema, initialJson, onConnected, onError, }: UseDocumentConnectionProps): UseDocumentConnectionResult;
|
|
20
|
+
type OnChangedHandler = (document: MeshDocument) => void;
|
|
21
|
+
export declare function useDocumentChanged({ document, onChanged, }: {
|
|
26
22
|
document: MeshDocument | null;
|
|
27
|
-
onChanged:
|
|
23
|
+
onChanged: OnChangedHandler;
|
|
28
24
|
}): void;
|
|
29
|
-
export declare function useRoomParticipants(room: RoomClient | null): Iterable<RemoteParticipant>;
|
|
30
25
|
export {};
|
|
@@ -2,111 +2,117 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useDocumentConnection = useDocumentConnection;
|
|
4
4
|
exports.useDocumentChanged = useDocumentChanged;
|
|
5
|
-
exports.useRoomParticipants = useRoomParticipants;
|
|
6
5
|
const react_1 = require("react");
|
|
6
|
+
function getRetryDelayMs(retryCount) {
|
|
7
|
+
return Math.min(60000, 500 * 2 ** retryCount);
|
|
8
|
+
}
|
|
9
|
+
async function closeDocument(room, path) {
|
|
10
|
+
try {
|
|
11
|
+
await room.sync.close(path);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
}
|
|
15
|
+
}
|
|
7
16
|
/**
|
|
8
17
|
* Connects to a Mesh document inside an existing RoomClient and keeps it in sync.
|
|
9
|
-
*
|
|
10
|
-
* The function retries with an exponential back‑off (capped at 60 s) until the
|
|
11
|
-
* document becomes available or the component unmounts.
|
|
12
|
-
*
|
|
13
|
-
* @param room An already‑connected RoomClient.
|
|
14
|
-
* @param path Path to the document inside the room.
|
|
15
18
|
*/
|
|
16
|
-
function useDocumentConnection({ room, path, onConnected, onError }) {
|
|
17
|
-
const [schemaFileExists, setSchemaFileExists] = (0, react_1.useState)(null);
|
|
19
|
+
function useDocumentConnection({ room, path, schema, initialJson, onConnected, onError, }) {
|
|
20
|
+
const [schemaFileExists, setSchemaFileExists] = (0, react_1.useState)(schema != null);
|
|
18
21
|
const [document, setDocument] = (0, react_1.useState)(null);
|
|
19
22
|
const [error, setError] = (0, react_1.useState)(null);
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const onConnectedRef = (0, react_1.useRef)(onConnected);
|
|
24
|
+
const onErrorRef = (0, react_1.useRef)(onError);
|
|
25
|
+
(0, react_1.useEffect)(() => {
|
|
26
|
+
onConnectedRef.current = onConnected;
|
|
27
|
+
}, [onConnected]);
|
|
28
|
+
(0, react_1.useEffect)(() => {
|
|
29
|
+
onErrorRef.current = onError;
|
|
30
|
+
}, [onError]);
|
|
25
31
|
(0, react_1.useEffect)(() => {
|
|
26
32
|
let cancelled = false;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
setSchemaFileExists(false);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
const doc = await room.sync.open(path);
|
|
38
|
-
if (cancelled)
|
|
39
|
-
return;
|
|
40
|
-
openedRef.current = true;
|
|
41
|
-
// sleep for 100 ms to ensure the document is ready
|
|
42
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
43
|
-
setDocument(doc);
|
|
44
|
-
setError(null);
|
|
45
|
-
if (onConnected) {
|
|
46
|
-
onConnected(doc);
|
|
47
|
-
}
|
|
33
|
+
let retryTimeout = null;
|
|
34
|
+
let opened = false;
|
|
35
|
+
let nextRetryCount = 0;
|
|
36
|
+
const clearRetryTimeout = () => {
|
|
37
|
+
if (retryTimeout != null) {
|
|
38
|
+
clearTimeout(retryTimeout);
|
|
39
|
+
retryTimeout = null;
|
|
48
40
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
};
|
|
42
|
+
const waitForRetry = (delayMs) => new Promise((resolve) => {
|
|
43
|
+
retryTimeout = setTimeout(() => {
|
|
44
|
+
retryTimeout = null;
|
|
45
|
+
resolve();
|
|
46
|
+
}, delayMs);
|
|
47
|
+
});
|
|
48
|
+
setDocument(null);
|
|
49
|
+
setError(null);
|
|
50
|
+
setSchemaFileExists(schema != null);
|
|
51
|
+
void (async () => {
|
|
52
|
+
while (!cancelled) {
|
|
53
|
+
try {
|
|
54
|
+
if (schema == null) {
|
|
55
|
+
const pathExtension = path.split('.').pop()?.toLowerCase();
|
|
56
|
+
const schemaFile = `.schemas/${pathExtension}.json`;
|
|
57
|
+
const nextSchemaExists = await room.storage.exists(schemaFile);
|
|
58
|
+
if (cancelled) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
setSchemaFileExists(nextSchemaExists);
|
|
62
|
+
if (!nextSchemaExists) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const nextDocument = await room.sync.open(path, { initialJson, schema });
|
|
67
|
+
if (cancelled) {
|
|
68
|
+
await closeDocument(room, path);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
opened = true;
|
|
72
|
+
nextRetryCount = 0;
|
|
73
|
+
setDocument(nextDocument);
|
|
74
|
+
setError(null);
|
|
75
|
+
onConnectedRef.current?.(nextDocument);
|
|
52
76
|
return;
|
|
53
|
-
setError(err);
|
|
54
|
-
if (onError) {
|
|
55
|
-
onError(err);
|
|
56
77
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
78
|
+
catch (nextError) {
|
|
79
|
+
if (cancelled) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
setDocument(null);
|
|
83
|
+
setError(nextError);
|
|
84
|
+
onErrorRef.current?.(nextError);
|
|
85
|
+
await waitForRetry(getRetryDelayMs(nextRetryCount));
|
|
86
|
+
nextRetryCount += 1;
|
|
87
|
+
}
|
|
61
88
|
}
|
|
62
|
-
};
|
|
63
|
-
openDocument();
|
|
89
|
+
})();
|
|
64
90
|
return () => {
|
|
65
91
|
cancelled = true;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
if (openedRef.current) {
|
|
71
|
-
room.sync.close(path);
|
|
92
|
+
clearRetryTimeout();
|
|
93
|
+
if (opened) {
|
|
94
|
+
void closeDocument(room, path);
|
|
72
95
|
}
|
|
73
|
-
setDocument(null);
|
|
74
|
-
retryCountRef.current = 0;
|
|
75
|
-
openedRef.current = false;
|
|
76
96
|
};
|
|
77
|
-
}, [path]);
|
|
97
|
+
}, [initialJson, path, room, schema]);
|
|
78
98
|
return {
|
|
79
99
|
document,
|
|
80
100
|
error,
|
|
81
|
-
loading: document
|
|
82
|
-
schemaFileExists
|
|
101
|
+
loading: document == null && error == null,
|
|
102
|
+
schemaFileExists,
|
|
83
103
|
};
|
|
84
104
|
}
|
|
85
|
-
function useDocumentChanged({ document, onChanged }) {
|
|
105
|
+
function useDocumentChanged({ document, onChanged, }) {
|
|
106
|
+
const onChangedRef = (0, react_1.useRef)(onChanged);
|
|
86
107
|
(0, react_1.useEffect)(() => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
onChanged(document);
|
|
90
|
-
return () => s.unsubscribe();
|
|
91
|
-
}
|
|
92
|
-
}, [document]);
|
|
93
|
-
}
|
|
94
|
-
function useRoomParticipants(room) {
|
|
95
|
-
const [participants, setParticipants] = (0, react_1.useState)(() => []);
|
|
108
|
+
onChangedRef.current = onChanged;
|
|
109
|
+
}, [onChanged]);
|
|
96
110
|
(0, react_1.useEffect)(() => {
|
|
97
|
-
if (
|
|
111
|
+
if (document == null) {
|
|
98
112
|
return;
|
|
99
113
|
}
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
updateParticipants();
|
|
105
|
-
return () => {
|
|
106
|
-
room.messaging.off('participant_added', updateParticipants);
|
|
107
|
-
room.messaging.off('participant_removed', updateParticipants);
|
|
108
|
-
room.messaging.on('messaging_enabled', updateParticipants);
|
|
109
|
-
};
|
|
110
|
-
}, [room]);
|
|
111
|
-
return participants;
|
|
114
|
+
const subscription = document.listen(() => onChangedRef.current(document));
|
|
115
|
+
onChangedRef.current(document);
|
|
116
|
+
return () => subscription.unsubscribe();
|
|
117
|
+
}, [document]);
|
|
112
118
|
}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export * from './chat';
|
|
2
1
|
export * from './client-toolkits';
|
|
3
2
|
export * from './document-connection-scope';
|
|
4
|
-
export * from './
|
|
3
|
+
export * from './livekit-client';
|
|
4
|
+
export * from './livekit-protocol';
|
|
5
5
|
export * from './room-connection-scope';
|
|
6
|
+
export * from './room-participants';
|
|
6
7
|
export * from './subscribe-async-gen';
|
package/dist/cjs/index.js
CHANGED
|
@@ -14,9 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./chat"), exports);
|
|
18
17
|
__exportStar(require("./client-toolkits"), exports);
|
|
19
18
|
__exportStar(require("./document-connection-scope"), exports);
|
|
20
|
-
__exportStar(require("./
|
|
19
|
+
__exportStar(require("./livekit-client"), exports);
|
|
20
|
+
__exportStar(require("./livekit-protocol"), exports);
|
|
21
21
|
__exportStar(require("./room-connection-scope"), exports);
|
|
22
|
+
__exportStar(require("./room-participants"), exports);
|
|
22
23
|
__exportStar(require("./subscribe-async-gen"), exports);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { RoomClient } from '@meshagent/meshagent';
|
|
2
|
+
export declare class LivekitConnectionInfo {
|
|
3
|
+
readonly url: string;
|
|
4
|
+
readonly token: string;
|
|
5
|
+
constructor({ url, token }: {
|
|
6
|
+
url: string;
|
|
7
|
+
token: string;
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export declare class LivekitClient {
|
|
11
|
+
readonly room: RoomClient;
|
|
12
|
+
constructor({ room }: {
|
|
13
|
+
room: RoomClient;
|
|
14
|
+
});
|
|
15
|
+
getConnectionInfo({ breakoutRoom, }?: {
|
|
16
|
+
breakoutRoom?: string;
|
|
17
|
+
}): Promise<LivekitConnectionInfo>;
|
|
18
|
+
}
|
|
19
|
+
declare module '@meshagent/meshagent' {
|
|
20
|
+
interface RoomClient {
|
|
21
|
+
readonly livekit: LivekitClient;
|
|
22
|
+
}
|
|
23
|
+
}
|