@meshagent/meshagent-react 0.38.2 → 0.38.4
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 +17 -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
|
@@ -1,32 +1,165 @@
|
|
|
1
|
-
import { useEffect } from
|
|
2
|
-
import { startHostedToolkit } from
|
|
3
|
-
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { startHostedToolkit } from "@meshagent/meshagent";
|
|
3
|
+
const sharedHostedToolkits = new WeakMap();
|
|
4
|
+
function getToolkitDefinition(toolkit, isPublic) {
|
|
5
|
+
return JSON.stringify({
|
|
6
|
+
public: isPublic,
|
|
7
|
+
name: toolkit.name,
|
|
8
|
+
title: toolkit.title,
|
|
9
|
+
description: toolkit.description,
|
|
10
|
+
thumbnailUrl: toolkit.thumbnailUrl ?? null,
|
|
11
|
+
rules: [...toolkit.rules],
|
|
12
|
+
tools: toolkit.tools.map((tool) => ({
|
|
13
|
+
name: tool.name,
|
|
14
|
+
title: tool.title,
|
|
15
|
+
description: tool.description,
|
|
16
|
+
inputSpec: tool.inputSpec?.toJson() ?? null,
|
|
17
|
+
outputSpec: tool.outputSpec?.toJson() ?? null,
|
|
18
|
+
thumbnailUrl: tool.thumbnailUrl ?? null,
|
|
19
|
+
})),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function getOrCreateSharedRoomToolkits(room) {
|
|
23
|
+
const existing = sharedHostedToolkits.get(room);
|
|
24
|
+
if (existing !== undefined) {
|
|
25
|
+
return existing;
|
|
26
|
+
}
|
|
27
|
+
const created = new Map();
|
|
28
|
+
sharedHostedToolkits.set(room, created);
|
|
29
|
+
return created;
|
|
30
|
+
}
|
|
31
|
+
async function releaseHostedToolkitLease(room, toolkitName, entry) {
|
|
32
|
+
const roomToolkits = sharedHostedToolkits.get(room);
|
|
33
|
+
if (roomToolkits?.get(toolkitName) !== entry || entry.refs === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
entry.refs -= 1;
|
|
37
|
+
if (entry.refs > 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (entry.stopPromise !== null) {
|
|
41
|
+
await entry.stopPromise;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
entry.stopPromise = (async () => {
|
|
45
|
+
try {
|
|
46
|
+
const hostedToolkit = await entry.startedToolkit;
|
|
47
|
+
await hostedToolkit.stop();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
const latestRoomToolkits = sharedHostedToolkits.get(room);
|
|
51
|
+
if (latestRoomToolkits?.get(toolkitName) === entry) {
|
|
52
|
+
latestRoomToolkits.delete(toolkitName);
|
|
53
|
+
if (latestRoomToolkits.size === 0) {
|
|
54
|
+
sharedHostedToolkits.delete(room);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
entry.stopPromise = null;
|
|
58
|
+
}
|
|
59
|
+
})();
|
|
60
|
+
await entry.stopPromise;
|
|
61
|
+
}
|
|
62
|
+
async function acquireHostedToolkitLease({ room, toolkit, public_: isPublic, }) {
|
|
63
|
+
const definition = getToolkitDefinition(toolkit, isPublic);
|
|
64
|
+
while (true) {
|
|
65
|
+
const roomToolkits = getOrCreateSharedRoomToolkits(room);
|
|
66
|
+
const existing = roomToolkits.get(toolkit.name);
|
|
67
|
+
if (existing === undefined) {
|
|
68
|
+
const entry = {
|
|
69
|
+
definition,
|
|
70
|
+
refs: 1,
|
|
71
|
+
startedToolkit: startHostedToolkit({
|
|
72
|
+
room,
|
|
73
|
+
toolkit,
|
|
74
|
+
public_: isPublic,
|
|
75
|
+
}),
|
|
76
|
+
stopPromise: null,
|
|
77
|
+
};
|
|
78
|
+
roomToolkits.set(toolkit.name, entry);
|
|
79
|
+
try {
|
|
80
|
+
await entry.startedToolkit;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (roomToolkits.get(toolkit.name) === entry) {
|
|
84
|
+
roomToolkits.delete(toolkit.name);
|
|
85
|
+
if (roomToolkits.size === 0) {
|
|
86
|
+
sharedHostedToolkits.delete(room);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
release: () => releaseHostedToolkitLease(room, toolkit.name, entry),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (existing.stopPromise !== null) {
|
|
96
|
+
await existing.stopPromise;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (existing.definition !== definition) {
|
|
100
|
+
throw new Error(`toolkit '${toolkit.name}' is already hosted for this room with a different definition`);
|
|
101
|
+
}
|
|
102
|
+
existing.refs += 1;
|
|
103
|
+
try {
|
|
104
|
+
await existing.startedToolkit;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
existing.refs -= 1;
|
|
108
|
+
if (existing.refs === 0 && roomToolkits.get(toolkit.name) === existing) {
|
|
109
|
+
roomToolkits.delete(toolkit.name);
|
|
110
|
+
if (roomToolkits.size === 0) {
|
|
111
|
+
sharedHostedToolkits.delete(room);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
release: () => releaseHostedToolkitLease(room, toolkit.name, existing),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export const useClientToolkits = ({ room, toolkits, public: isPublic = false, }) => {
|
|
122
|
+
const cleanupRef = useRef(Promise.resolve());
|
|
4
123
|
useEffect(() => {
|
|
5
124
|
let disposed = false;
|
|
6
|
-
const
|
|
7
|
-
|
|
125
|
+
const toolkitLeases = [];
|
|
126
|
+
const waitForPreviousCleanup = cleanupRef.current;
|
|
127
|
+
const startPromise = (async () => {
|
|
128
|
+
await waitForPreviousCleanup;
|
|
8
129
|
try {
|
|
9
130
|
for (const toolkit of toolkits) {
|
|
10
|
-
|
|
131
|
+
if (disposed) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const lease = await acquireHostedToolkitLease({
|
|
11
135
|
room,
|
|
12
136
|
toolkit,
|
|
13
137
|
public_: isPublic,
|
|
14
138
|
});
|
|
15
139
|
if (disposed) {
|
|
16
|
-
await
|
|
17
|
-
|
|
140
|
+
await lease.release();
|
|
141
|
+
return;
|
|
18
142
|
}
|
|
19
|
-
|
|
143
|
+
toolkitLeases.push(lease);
|
|
20
144
|
}
|
|
21
145
|
}
|
|
22
146
|
catch (error) {
|
|
23
|
-
|
|
24
|
-
|
|
147
|
+
const leasesToRelease = toolkitLeases.splice(0);
|
|
148
|
+
await Promise.all(leasesToRelease.map((lease) => lease.release()));
|
|
149
|
+
if (!disposed) {
|
|
150
|
+
console.error("unable to start client toolkits", error);
|
|
151
|
+
}
|
|
25
152
|
}
|
|
26
153
|
})();
|
|
27
154
|
return () => {
|
|
28
155
|
disposed = true;
|
|
29
|
-
|
|
156
|
+
cleanupRef.current = (async () => {
|
|
157
|
+
await startPromise;
|
|
158
|
+
const leasesToRelease = toolkitLeases.splice(0);
|
|
159
|
+
await Promise.all(leasesToRelease.map((lease) => lease.release()));
|
|
160
|
+
})().catch((error) => {
|
|
161
|
+
console.error("unable to stop client toolkits", error);
|
|
162
|
+
});
|
|
30
163
|
};
|
|
31
164
|
}, [room, toolkits, isPublic]);
|
|
32
165
|
};
|
|
@@ -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 {};
|
|
@@ -1,107 +1,114 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
function getRetryDelayMs(retryCount) {
|
|
3
|
+
return Math.min(60000, 500 * 2 ** retryCount);
|
|
4
|
+
}
|
|
5
|
+
async function closeDocument(room, path) {
|
|
6
|
+
try {
|
|
7
|
+
await room.sync.close(path);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
}
|
|
11
|
+
}
|
|
2
12
|
/**
|
|
3
13
|
* Connects to a Mesh document inside an existing RoomClient and keeps it in sync.
|
|
4
|
-
*
|
|
5
|
-
* The function retries with an exponential back‑off (capped at 60 s) until the
|
|
6
|
-
* document becomes available or the component unmounts.
|
|
7
|
-
*
|
|
8
|
-
* @param room An already‑connected RoomClient.
|
|
9
|
-
* @param path Path to the document inside the room.
|
|
10
14
|
*/
|
|
11
|
-
export function useDocumentConnection({ room, path, onConnected, onError }) {
|
|
12
|
-
const [schemaFileExists, setSchemaFileExists] = useState(null);
|
|
15
|
+
export function useDocumentConnection({ room, path, schema, initialJson, onConnected, onError, }) {
|
|
16
|
+
const [schemaFileExists, setSchemaFileExists] = useState(schema != null);
|
|
13
17
|
const [document, setDocument] = useState(null);
|
|
14
18
|
const [error, setError] = useState(null);
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
const onConnectedRef = useRef(onConnected);
|
|
20
|
+
const onErrorRef = useRef(onError);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
onConnectedRef.current = onConnected;
|
|
23
|
+
}, [onConnected]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
onErrorRef.current = onError;
|
|
26
|
+
}, [onError]);
|
|
20
27
|
useEffect(() => {
|
|
21
28
|
let cancelled = false;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
setSchemaFileExists(false);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
const doc = await room.sync.open(path);
|
|
33
|
-
if (cancelled)
|
|
34
|
-
return;
|
|
35
|
-
openedRef.current = true;
|
|
36
|
-
// sleep for 100 ms to ensure the document is ready
|
|
37
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
38
|
-
setDocument(doc);
|
|
39
|
-
setError(null);
|
|
40
|
-
if (onConnected) {
|
|
41
|
-
onConnected(doc);
|
|
42
|
-
}
|
|
29
|
+
let retryTimeout = null;
|
|
30
|
+
let opened = false;
|
|
31
|
+
let nextRetryCount = 0;
|
|
32
|
+
const clearRetryTimeout = () => {
|
|
33
|
+
if (retryTimeout != null) {
|
|
34
|
+
clearTimeout(retryTimeout);
|
|
35
|
+
retryTimeout = null;
|
|
43
36
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
};
|
|
38
|
+
const waitForRetry = (delayMs) => new Promise((resolve) => {
|
|
39
|
+
retryTimeout = setTimeout(() => {
|
|
40
|
+
retryTimeout = null;
|
|
41
|
+
resolve();
|
|
42
|
+
}, delayMs);
|
|
43
|
+
});
|
|
44
|
+
setDocument(null);
|
|
45
|
+
setError(null);
|
|
46
|
+
setSchemaFileExists(schema != null);
|
|
47
|
+
void (async () => {
|
|
48
|
+
while (!cancelled) {
|
|
49
|
+
try {
|
|
50
|
+
if (schema == null) {
|
|
51
|
+
const pathExtension = path.split('.').pop()?.toLowerCase();
|
|
52
|
+
const schemaFile = `.schemas/${pathExtension}.json`;
|
|
53
|
+
const nextSchemaExists = await room.storage.exists(schemaFile);
|
|
54
|
+
if (cancelled) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
setSchemaFileExists(nextSchemaExists);
|
|
58
|
+
if (!nextSchemaExists) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const nextDocument = await room.sync.open(path, { initialJson, schema });
|
|
63
|
+
if (cancelled) {
|
|
64
|
+
await closeDocument(room, path);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
opened = true;
|
|
68
|
+
nextRetryCount = 0;
|
|
69
|
+
setDocument(nextDocument);
|
|
70
|
+
setError(null);
|
|
71
|
+
onConnectedRef.current?.(nextDocument);
|
|
47
72
|
return;
|
|
48
|
-
setError(err);
|
|
49
|
-
if (onError) {
|
|
50
|
-
onError(err);
|
|
51
73
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
74
|
+
catch (nextError) {
|
|
75
|
+
if (cancelled) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
setDocument(null);
|
|
79
|
+
setError(nextError);
|
|
80
|
+
onErrorRef.current?.(nextError);
|
|
81
|
+
await waitForRetry(getRetryDelayMs(nextRetryCount));
|
|
82
|
+
nextRetryCount += 1;
|
|
83
|
+
}
|
|
56
84
|
}
|
|
57
|
-
};
|
|
58
|
-
openDocument();
|
|
85
|
+
})();
|
|
59
86
|
return () => {
|
|
60
87
|
cancelled = true;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
if (openedRef.current) {
|
|
66
|
-
room.sync.close(path);
|
|
88
|
+
clearRetryTimeout();
|
|
89
|
+
if (opened) {
|
|
90
|
+
void closeDocument(room, path);
|
|
67
91
|
}
|
|
68
|
-
setDocument(null);
|
|
69
|
-
retryCountRef.current = 0;
|
|
70
|
-
openedRef.current = false;
|
|
71
92
|
};
|
|
72
|
-
}, [path]);
|
|
93
|
+
}, [initialJson, path, room, schema]);
|
|
73
94
|
return {
|
|
74
95
|
document,
|
|
75
96
|
error,
|
|
76
|
-
loading: document
|
|
77
|
-
schemaFileExists
|
|
97
|
+
loading: document == null && error == null,
|
|
98
|
+
schemaFileExists,
|
|
78
99
|
};
|
|
79
100
|
}
|
|
80
|
-
export function useDocumentChanged({ document, onChanged }) {
|
|
101
|
+
export function useDocumentChanged({ document, onChanged, }) {
|
|
102
|
+
const onChangedRef = useRef(onChanged);
|
|
81
103
|
useEffect(() => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
onChanged(document);
|
|
85
|
-
return () => s.unsubscribe();
|
|
86
|
-
}
|
|
87
|
-
}, [document]);
|
|
88
|
-
}
|
|
89
|
-
export function useRoomParticipants(room) {
|
|
90
|
-
const [participants, setParticipants] = useState(() => []);
|
|
104
|
+
onChangedRef.current = onChanged;
|
|
105
|
+
}, [onChanged]);
|
|
91
106
|
useEffect(() => {
|
|
92
|
-
if (
|
|
107
|
+
if (document == null) {
|
|
93
108
|
return;
|
|
94
109
|
}
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
updateParticipants();
|
|
100
|
-
return () => {
|
|
101
|
-
room.messaging.off('participant_added', updateParticipants);
|
|
102
|
-
room.messaging.off('participant_removed', updateParticipants);
|
|
103
|
-
room.messaging.on('messaging_enabled', updateParticipants);
|
|
104
|
-
};
|
|
105
|
-
}, [room]);
|
|
106
|
-
return participants;
|
|
110
|
+
const subscription = document.listen(() => onChangedRef.current(document));
|
|
111
|
+
onChangedRef.current(document);
|
|
112
|
+
return () => subscription.unsubscribe();
|
|
113
|
+
}, [document]);
|
|
107
114
|
}
|
package/dist/esm/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/esm/index.js
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';
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { JsonContent, RoomClient, RoomServerException, } from '@meshagent/meshagent';
|
|
2
|
+
export class LivekitConnectionInfo {
|
|
3
|
+
constructor({ url, token }) {
|
|
4
|
+
Object.defineProperty(this, "url", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: void 0
|
|
9
|
+
});
|
|
10
|
+
Object.defineProperty(this, "token", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
writable: true,
|
|
14
|
+
value: void 0
|
|
15
|
+
});
|
|
16
|
+
this.url = url;
|
|
17
|
+
this.token = token;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class LivekitClient {
|
|
21
|
+
constructor({ room }) {
|
|
22
|
+
Object.defineProperty(this, "room", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: void 0
|
|
27
|
+
});
|
|
28
|
+
this.room = room;
|
|
29
|
+
}
|
|
30
|
+
async getConnectionInfo({ breakoutRoom, } = {}) {
|
|
31
|
+
const response = await this.room.invoke({
|
|
32
|
+
toolkit: 'livekit',
|
|
33
|
+
tool: 'connect',
|
|
34
|
+
input: { breakout_room: breakoutRoom ?? null },
|
|
35
|
+
});
|
|
36
|
+
if (!(response instanceof JsonContent)) {
|
|
37
|
+
throw new RoomServerException('unexpected return type from livekit.connect');
|
|
38
|
+
}
|
|
39
|
+
const responseJson = response.json;
|
|
40
|
+
if (typeof responseJson !== 'object'
|
|
41
|
+
|| responseJson == null
|
|
42
|
+
|| Array.isArray(responseJson)) {
|
|
43
|
+
throw new RoomServerException('unexpected return type from livekit.connect');
|
|
44
|
+
}
|
|
45
|
+
const token = responseJson.token;
|
|
46
|
+
const url = responseJson.url;
|
|
47
|
+
if (typeof token !== 'string' || typeof url !== 'string') {
|
|
48
|
+
throw new RoomServerException('unexpected return type from livekit.connect');
|
|
49
|
+
}
|
|
50
|
+
return new LivekitConnectionInfo({ token, url });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!Object.getOwnPropertyDescriptor(RoomClient.prototype, 'livekit')) {
|
|
54
|
+
Object.defineProperty(RoomClient.prototype, 'livekit', {
|
|
55
|
+
configurable: true,
|
|
56
|
+
enumerable: false,
|
|
57
|
+
get() {
|
|
58
|
+
return new LivekitClient({ room: this });
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ProtocolChannel } from '@meshagent/meshagent';
|
|
2
|
+
import * as livekit from 'livekit-client';
|
|
3
|
+
export declare class LivekitProtocolChannel implements ProtocolChannel {
|
|
4
|
+
private readonly room;
|
|
5
|
+
private readonly remote;
|
|
6
|
+
private readonly topic;
|
|
7
|
+
private onDataReceived?;
|
|
8
|
+
private readonly handleDataReceivedBound;
|
|
9
|
+
constructor({ room, remote, topic, }: {
|
|
10
|
+
room: livekit.Room;
|
|
11
|
+
remote: livekit.RemoteParticipant;
|
|
12
|
+
topic: string;
|
|
13
|
+
});
|
|
14
|
+
start(onDataReceived: (data: Uint8Array) => void, _params: {
|
|
15
|
+
onDone?: () => void;
|
|
16
|
+
onError?: (error: unknown) => void;
|
|
17
|
+
}): void;
|
|
18
|
+
sendData(data: Uint8Array): Promise<void>;
|
|
19
|
+
dispose(): void;
|
|
20
|
+
private handleDataReceived;
|
|
21
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as livekit from 'livekit-client';
|
|
2
|
+
export class LivekitProtocolChannel {
|
|
3
|
+
constructor({ room, remote, topic, }) {
|
|
4
|
+
Object.defineProperty(this, "room", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: void 0
|
|
9
|
+
});
|
|
10
|
+
Object.defineProperty(this, "remote", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
writable: true,
|
|
14
|
+
value: void 0
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(this, "topic", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: void 0
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(this, "onDataReceived", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: void 0
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "handleDataReceivedBound", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: void 0
|
|
33
|
+
});
|
|
34
|
+
this.room = room;
|
|
35
|
+
this.remote = remote;
|
|
36
|
+
this.topic = topic;
|
|
37
|
+
this.handleDataReceivedBound = this.handleDataReceived.bind(this);
|
|
38
|
+
}
|
|
39
|
+
start(onDataReceived, _params) {
|
|
40
|
+
this.onDataReceived = onDataReceived;
|
|
41
|
+
this.room.on(livekit.RoomEvent.DataReceived, this.handleDataReceivedBound);
|
|
42
|
+
}
|
|
43
|
+
async sendData(data) {
|
|
44
|
+
await this.room.localParticipant.publishData(data, {
|
|
45
|
+
reliable: true,
|
|
46
|
+
topic: this.topic,
|
|
47
|
+
destinationIdentities: [this.remote.identity],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
dispose() {
|
|
51
|
+
this.room.off(livekit.RoomEvent.DataReceived, this.handleDataReceivedBound);
|
|
52
|
+
this.onDataReceived = undefined;
|
|
53
|
+
}
|
|
54
|
+
handleDataReceived(payload, participant, _kind, topic, _encryptionType) {
|
|
55
|
+
const identityMatches = participant?.identity === this.remote.identity;
|
|
56
|
+
if (identityMatches && topic === this.topic) {
|
|
57
|
+
this.onDataReceived?.(payload);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { RoomClient } from '@meshagent/meshagent';
|
|
2
|
-
|
|
3
|
-
url: string;
|
|
4
|
-
jwt: string;
|
|
5
|
-
}
|
|
1
|
+
import { type OAuthTokenRequest, RoomClient, type SecretRequest } from '@meshagent/meshagent';
|
|
2
|
+
import type { RoomConnectionInfo } from '@meshagent/meshagent';
|
|
6
3
|
export declare const developmentAuthorization: ({ url, projectId, apiKeyId, participantName, roomName, secret, }: {
|
|
7
4
|
url: string;
|
|
8
5
|
projectId: string;
|
|
@@ -11,27 +8,30 @@ export declare const developmentAuthorization: ({ url, projectId, apiKeyId, part
|
|
|
11
8
|
roomName: string;
|
|
12
9
|
secret: string;
|
|
13
10
|
}) => (() => Promise<RoomConnectionInfo>);
|
|
11
|
+
export declare const staticAuthorization: ({ projectId, roomName, url, jwt, }: {
|
|
12
|
+
projectId: string;
|
|
13
|
+
roomName: string;
|
|
14
|
+
url: string;
|
|
15
|
+
jwt: string;
|
|
16
|
+
}) => (() => Promise<RoomConnectionInfo>);
|
|
14
17
|
export interface UseRoomConnectionOptions {
|
|
15
|
-
|
|
16
|
-
authorization: () => Promise<
|
|
17
|
-
url: string;
|
|
18
|
-
jwt: string;
|
|
19
|
-
}>;
|
|
20
|
-
/** Enable the optional messaging layer (default = `true`). */
|
|
18
|
+
reconnectKey?: string;
|
|
19
|
+
authorization: () => Promise<RoomConnectionInfo>;
|
|
21
20
|
enableMessaging?: boolean;
|
|
21
|
+
onReady?: (room: RoomClient) => void;
|
|
22
|
+
oauthTokenRequestHandler?: (room: RoomClient, request: OAuthTokenRequest) => Promise<void> | void;
|
|
23
|
+
secretRequestHandler?: (room: RoomClient, request: SecretRequest) => Promise<void> | void;
|
|
24
|
+
roomClientFactory?: (connectionInfo: RoomConnectionInfo) => RoomClient;
|
|
22
25
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Shape of the object returned by the hook.
|
|
25
|
-
*/
|
|
26
26
|
export interface UseRoomConnectionResult {
|
|
27
27
|
client: RoomClient | null;
|
|
28
|
-
state: 'authorizing' | 'connecting' | 'ready' | 'done';
|
|
28
|
+
state: 'authorizing' | 'connecting' | 'retrying' | 'ready' | 'done';
|
|
29
29
|
ready: boolean;
|
|
30
30
|
done: boolean;
|
|
31
31
|
error: unknown;
|
|
32
32
|
dispose: () => void;
|
|
33
33
|
}
|
|
34
|
-
export declare function useRoomConnection(
|
|
34
|
+
export declare function useRoomConnection({ reconnectKey, authorization, enableMessaging, onReady, oauthTokenRequestHandler, secretRequestHandler, roomClientFactory, }: UseRoomConnectionOptions): UseRoomConnectionResult;
|
|
35
35
|
export interface UseRoomIndicatorsResult {
|
|
36
36
|
typing: boolean;
|
|
37
37
|
thinking: boolean;
|