From 48b41c9d19228a0c37f0a32cc66d16e8e7592c34 Mon Sep 17 00:00:00 2001 From: nvms Date: Tue, 22 Apr 2025 10:53:45 -0400 Subject: [PATCH] simplify metadata retrieval --- packages/mesh/src/client/client.ts | 114 +++++++++++++++++++++++- packages/mesh/src/server/mesh-server.ts | 52 +++++++++++ 2 files changed, 163 insertions(+), 3 deletions(-) diff --git a/packages/mesh/src/client/client.ts b/packages/mesh/src/client/client.ts index 8be9dd1..652b4b0 100644 --- a/packages/mesh/src/client/client.ts +++ b/packages/mesh/src/client/client.ts @@ -10,9 +10,19 @@ export { Status } from "../common/status"; export { applyPatch } from "fast-json-patch"; export type PresenceUpdate = - | { type: "join"; connectionId: string } - | { type: "leave"; connectionId: string } - | { type: "state"; connectionId: string; state: Record | null }; + | { + type: "join" | "leave"; + connectionId: string; + roomName: string; + timestamp?: number; + } + | { + type: "state"; + connectionId: string; + roomName: string; + state: any; + timestamp?: number; + }; export type PresenceUpdateCallback = ( update: PresenceUpdate @@ -766,4 +776,102 @@ export class MeshClient extends EventEmitter { return false; } } + + /** + * Gets metadata for a specific room. + * + * @param {string} roomName - The name of the room to get metadata for. + * @returns {Promise} A promise that resolves with the room metadata. + */ + async getRoomMetadata(roomName: string): Promise { + try { + const result = await this.command("mesh/get-room-metadata", { + roomName, + }); + return result.metadata; + } catch (error) { + console.error( + `[MeshClient] Failed to get metadata for room ${roomName}:`, + error + ); + return null; + } + } + + /** + * Gets metadata for a connection. + * + * @param {string} [connectionId] - The ID of the connection to get metadata for. If not provided, gets metadata for the current connection. + * @returns {Promise} A promise that resolves with the connection metadata. + */ + async getConnectionMetadata(connectionId?: string): Promise { + try { + if (connectionId) { + const result = await this.command("mesh/get-connection-metadata", { + connectionId, + }); + return result.metadata; + } else { + const result = await this.command("mesh/get-my-connection-metadata"); + return result.metadata; + } + } catch (error) { + const idText = connectionId ? ` ${connectionId}` : ""; + console.error( + `[MeshClient] Failed to get metadata for connection${idText}:`, + error + ); + return null; + } + } + + /** + * Register a callback for the connect event. + * This event is emitted when the client successfully connects to the server. + * + * @param {() => void} callback - The function to call when the client connects + * @returns {this} The client instance for chaining + */ + onConnect(callback: () => void): this { + this.on("connect", callback); + return this; + } + + /** + * Register a callback for the disconnect event. + * This event is emitted when the client disconnects from the server. + * + * @param {() => void} callback - The function to call when the client disconnects + * @returns {this} The client instance for chaining + */ + onDisconnect(callback: () => void): this { + this.on("disconnect", callback); + return this; + } + + /** + * Register a callback for the reconnect event. + * This event is emitted when the client successfully reconnects to the server + * after a disconnection. + * + * @param {() => void} callback - The function to call when the client reconnects + * @returns {this} The client instance for chaining + */ + onReconnect(callback: () => void): this { + this.on("reconnect", callback); + return this; + } + + /** + * Register a callback for the reconnect failed event. + * This event is emitted when the client fails to reconnect after reaching + * the maximum number of reconnect attempts. + * + * @param {() => void} callback - The function to call when reconnection fails + * @returns {this} The client instance for chaining + */ + onReconnectFailed(callback: () => void): this { + this.on("reconnectfailed", callback); + return this; + } } diff --git a/packages/mesh/src/server/mesh-server.ts b/packages/mesh/src/server/mesh-server.ts index 3ae582c..91d6cc0 100644 --- a/packages/mesh/src/server/mesh-server.ts +++ b/packages/mesh/src/server/mesh-server.ts @@ -576,6 +576,58 @@ export class MeshServer extends WebSocketServer { return { success: true }; } ); + + this.exposeCommand<{ connectionId: string }, { metadata: any }>( + "mesh/get-connection-metadata", + async (ctx) => { + const { connectionId } = ctx.payload; + // Try to get the local connection first + const connection = + this.connectionManager.getLocalConnection(connectionId); + + if (connection) { + // If we have the connection locally, use it to get metadata + const metadata = await this.connectionManager.getMetadata(connection); + return { metadata }; + } else { + // If the connection is not local, we need to get metadata directly from Redis + // This is a workaround since we don't have direct access to the connection + const metadata = await this.redisManager.redis.hget( + "mesh:connections", + connectionId + ); + return { metadata: metadata ? JSON.parse(metadata) : null }; + } + } + ); + + this.exposeCommand<{}, { metadata: any }>( + "mesh/get-my-connection-metadata", + async (ctx) => { + const connectionId = ctx.connection.id; + const connection = + this.connectionManager.getLocalConnection(connectionId); + if (connection) { + const metadata = await this.connectionManager.getMetadata(connection); + return { metadata }; + } else { + const metadata = await this.redisManager.redis.hget( + "mesh:connections", + connectionId + ); + return { metadata: metadata ? JSON.parse(metadata) : null }; + } + } + ); + + this.exposeCommand<{ roomName: string }, { metadata: any }>( + "mesh/get-room-metadata", + async (ctx) => { + const { roomName } = ctx.payload; + const metadata = await this.roomManager.getMetadata(roomName); + return { metadata }; + } + ); } private registerRecordCommands() {