@meshagent/meshagent-react 0.38.1 → 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.
Files changed (39) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +78 -41
  3. package/dist/cjs/client-toolkits.d.ts +2 -2
  4. package/dist/cjs/client-toolkits.js +143 -10
  5. package/dist/cjs/document-connection-scope.d.ts +7 -12
  6. package/dist/cjs/document-connection-scope.js +88 -82
  7. package/dist/cjs/index.d.ts +3 -2
  8. package/dist/cjs/index.js +3 -2
  9. package/dist/cjs/livekit-client.d.ts +23 -0
  10. package/dist/cjs/livekit-client.js +66 -0
  11. package/dist/cjs/livekit-protocol.d.ts +21 -0
  12. package/dist/cjs/livekit-protocol.js +97 -0
  13. package/dist/cjs/room-connection-scope.d.ts +16 -16
  14. package/dist/cjs/room-connection-scope.js +207 -90
  15. package/dist/cjs/room-participants.d.ts +2 -0
  16. package/dist/cjs/room-participants.js +46 -0
  17. package/dist/esm/client-toolkits.d.ts +2 -2
  18. package/dist/esm/client-toolkits.js +145 -12
  19. package/dist/esm/document-connection-scope.d.ts +7 -12
  20. package/dist/esm/document-connection-scope.js +88 -81
  21. package/dist/esm/index.d.ts +3 -2
  22. package/dist/esm/index.js +3 -2
  23. package/dist/esm/livekit-client.d.ts +23 -0
  24. package/dist/esm/livekit-client.js +61 -0
  25. package/dist/esm/livekit-protocol.d.ts +21 -0
  26. package/dist/esm/livekit-protocol.js +60 -0
  27. package/dist/esm/room-connection-scope.d.ts +16 -16
  28. package/dist/esm/room-connection-scope.js +205 -89
  29. package/dist/esm/room-participants.d.ts +2 -0
  30. package/dist/esm/room-participants.js +43 -0
  31. package/package.json +2 -2
  32. package/dist/cjs/chat.d.ts +0 -33
  33. package/dist/cjs/chat.js +0 -207
  34. package/dist/cjs/file-upload.d.ts +0 -43
  35. package/dist/cjs/file-upload.js +0 -168
  36. package/dist/esm/chat.d.ts +0 -33
  37. package/dist/esm/chat.js +0 -201
  38. package/dist/esm/file-upload.d.ts +0 -43
  39. package/dist/esm/file-upload.js +0 -163
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
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
+
10
+ ## [0.38.2]
11
+ - Stability
12
+
1
13
  ## [0.38.1]
2
14
  - Updated the TypeScript HTML schema example to use `SimpleValue.string` for value properties.
3
15
  - Removed the JavaScript and TypeScript schema registry example scripts as part of the example refresh.
@@ -6,7 +18,11 @@
6
18
  - Stability
7
19
 
8
20
  ## [0.37.2]
9
- - Stability
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.
10
26
 
11
27
  ## [0.37.1]
12
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 Typescript SDK
4
-
5
- See an example: https://github.com/meshagent/meshagent-tailwind/tree/main/example
6
-
7
- MeshAgent is your platform to create, deploy, and manage AI agents collaboratively and at scale, securely and in real time.
8
-
9
- MeshAgent removes the infrastructure headaches of building and shipping AI Agents. It spins up secure, real-time "Rooms" that connect humans, agents, and shared context -- letting you launch, share, and refine agents in hours instead of weeks.
10
-
11
- ---
12
-
13
- **Documentation**: [docs.meshagent.com](https://docs.meshagent.com/)
14
-
15
- **Website**: [www.meshagent.com](https://www.meshagent.com/)
16
-
17
- **MeshAgent Studio**: [studio.meshagent.com](https://studio.meshagent.com/)
18
-
19
- ---
20
-
21
- ## Why MeshAgent?
22
-
23
- - **Real-time multi-user "Rooms" for true human-AI teamwork**: Secure workspaces, "Rooms", automatically spin-up when someone joins, tracking presence, chat, documents, and agent activity, and disappear when everyone leaves -- so people and agents stay in sync.
24
-
25
- - **Accelerated feedback loop and shortened development cycle**: Invite a colleague or stakeholder into the MeshAgent Studio Room so they can see, hear, and interact with your agent immediately -- letting you demo, gather feedback, and iterate on the agent on the same day.
26
-
27
- - **Shareable links that showcase your agent**: After you deploy an agent, MeshAgent provides you a URL so colleagues or customers can jump straight into the Room, try the agent, and give feedback -- no code checkout, IDE, or GPU required.
28
-
29
- - **Reusable and extensibly agent tools**: MeshAgent ships prebuilt, reusable toolkits for common Room interactions (e.g., file and document handling, media generation, dynamic UI interactions) and high-demand tasks (e.g., web search or document-to-Markdown conversion). Support for custom tools and the Model Context Protocol (MCP) enables you to seamlessly integrate any MCP-compatible service into a Room without changes to your applications. Tools are centrally managed like agents, allowing you to deploy them once and reuse them effortlessly.
30
-
31
- - **Built-in Observability**: Streaming logs and traces give you real-time insight into every message, agent action, and error.
32
-
33
- - **Secure and elastic by default**: Project-scoped keys, on-demand provisioning, and auto-scaling infrastructure keep Rooms private, secure, and ready for your usage needs.
34
-
35
- - **Support across languages**: The same feature set (Rooms, Agents, Tools, Messaging, Queues, Storage, Documents, etc.) is exposed through official Python, Javascript, Typescript, and Dart SDKs, so every team member can work in their preferred language.
36
-
37
- ## Getting Setup
38
-
39
- Follow our [Getting Started Documentation](https://docs.meshagent.com/introduction/get_started) to setup your MeshAgent account, create your first project, and start building agents!
40
-
41
- ## Next Steps and Examples
42
-
43
- To see examples of agents in action and to start building your own agents check out the MeshAgent docs at [docs.meshagent.com](https://docs.meshagent.com/)
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 '@meshagent/meshagent';
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 useClientToolkits = ({ room, toolkits, public: isPublic = false }) => {
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 startedToolkits = [];
10
- void (async () => {
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
- const hostedToolkit = await (0, meshagent_1.startHostedToolkit)({
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 hostedToolkit.stop();
20
- continue;
143
+ await lease.release();
144
+ return;
21
145
  }
22
- startedToolkits.push(hostedToolkit);
146
+ toolkitLeases.push(lease);
23
147
  }
24
148
  }
25
149
  catch (error) {
26
- await Promise.all(startedToolkits.map((toolkit) => toolkit.stop()));
27
- console.error("unable to start client toolkits", error);
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
- void Promise.all(startedToolkits.map((toolkit) => toolkit.stop()));
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, RoomClient, RemoteParticipant } from '@meshagent/meshagent';
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 onChangedHandler = (document: MeshDocument) => void;
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: onChangedHandler;
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 openedRef = (0, react_1.useRef)(false);
21
- const retryCountRef = (0, react_1.useRef)(0);
22
- const timeoutRef = (0, react_1.useRef)(null);
23
- const pathExtension = path.split('.').pop()?.toLowerCase();
24
- const schemaFile = `.schemas/${pathExtension}.json`;
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
- const openDocument = async () => {
28
- try {
29
- const schemaExists = await room.storage.exists(schemaFile);
30
- if (schemaExists) {
31
- setSchemaFileExists(true);
32
- }
33
- else {
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
- catch (err) {
50
- console.error('Failed to open document:', err);
51
- if (cancelled)
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
- // Exponential back‑off: 500 ms, 1 s, 2 s, … up to 60 s.
58
- const delay = Math.min(60000, 500 * 2 ** retryCountRef.current);
59
- retryCountRef.current += 1;
60
- timeoutRef.current = setTimeout(openDocument, delay);
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
- if (timeoutRef.current !== null) {
67
- clearTimeout(timeoutRef.current);
68
- timeoutRef.current = null;
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 === null && error == null,
82
- schemaFileExists: schemaFileExists !== null ? schemaFileExists : true,
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
- if (document) {
88
- const s = document.listen(() => onChanged(document));
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 (!room || !room.messaging) {
111
+ if (document == null) {
98
112
  return;
99
113
  }
100
- const updateParticipants = () => setParticipants(room.messaging.remoteParticipants);
101
- room.messaging.on('participant_added', updateParticipants);
102
- room.messaging.on('participant_removed', updateParticipants);
103
- room.messaging.on('messaging_enabled', updateParticipants);
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
  }
@@ -1,6 +1,7 @@
1
- export * from './chat';
2
1
  export * from './client-toolkits';
3
2
  export * from './document-connection-scope';
4
- export * from './file-upload';
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("./file-upload"), exports);
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
+ }