diff --git a/README.md b/README.md index 86fc538e46..2ede911154 100644 --- a/README.md +++ b/README.md @@ -320,9 +320,9 @@ room.localParticipant?.registerRpcMethod( 'greet', // method handler - will be called when the method is invoked by a RemoteParticipant - async (requestId: string, callerIdentity: string, payload: string, responseTimeout: number) => { - console.log(`Received greeting from ${callerIdentity}: ${payload}`); - return `Hello, ${callerIdentity}!`; + async (data: RpcInvocationData) => { + console.log(`Received greeting from ${data.callerIdentity}: ${data.payload}`); + return `Hello, ${data.callerIdentity}!`; } ); ``` @@ -338,7 +338,7 @@ try { const response = await room.localParticipant!.performRpc( 'recipient-identity', 'greet', - 'Hello from RPC!' + 'Hello from RPC!', ); console.log('RPC response:', response); } catch (error) { diff --git a/examples/rpc/rpc-demo.ts b/examples/rpc/rpc-demo.ts index b88155fab6..fef100633d 100644 --- a/examples/rpc/rpc-demo.ts +++ b/examples/rpc/rpc-demo.ts @@ -1,4 +1,4 @@ -import { Room, type RoomConnectOptions, RoomEvent, RpcError } from '../../src/index'; +import { Room, type RoomConnectOptions, RoomEvent, RpcError, RpcInvocationData } from '../../src/index'; let startTime: number; @@ -72,13 +72,8 @@ const registerReceiverMethods = async (greetersRoom: Room, mathGeniusRoom: Room) await greetersRoom.localParticipant?.registerRpcMethod( 'arrival', // eslint-disable-next-line @typescript-eslint/no-unused-vars - async ( - requestId: string, - callerIdentity: string, - payload: string, - responseTimeout: number, - ) => { - console.log(`[Greeter] Oh ${callerIdentity} arrived and said "${payload}"`); + async (data: RpcInvocationData) => { + console.log(`[Greeter] Oh ${data.callerIdentity} arrived and said "${data.payload}"`); await new Promise((resolve) => setTimeout(resolve, 2000)); return 'Welcome and have a wonderful day!'; }, @@ -86,17 +81,12 @@ const registerReceiverMethods = async (greetersRoom: Room, mathGeniusRoom: Room) await mathGeniusRoom.localParticipant?.registerRpcMethod( 'square-root', - async ( - requestId: string, - callerIdentity: string, - payload: string, - responseTimeout: number, - ) => { - const jsonData = JSON.parse(payload); + async (data: RpcInvocationData) => { + const jsonData = JSON.parse(data.payload); const number = jsonData.number; console.log( - `[Math Genius] I guess ${callerIdentity} wants the square root of ${number}. I've only got ${responseTimeout / 1000} seconds to respond but I think I can pull it off.`, + `[Math Genius] I guess ${data.callerIdentity} wants the square root of ${number}. I've only got ${data.responseTimeout / 1000} seconds to respond but I think I can pull it off.`, ); console.log(`[Math Genius] *doing math*…`); @@ -110,18 +100,12 @@ const registerReceiverMethods = async (greetersRoom: Room, mathGeniusRoom: Room) await mathGeniusRoom.localParticipant?.registerRpcMethod( 'divide', - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async ( - requestId: string, - callerIdentity: string, - payload: string, - responseTimeout: number, - ) => { - const jsonData = JSON.parse(payload); + async (data: RpcInvocationData) => { + const jsonData = JSON.parse(data.payload); const { numerator, denominator } = jsonData; console.log( - `[Math Genius] ${callerIdentity} wants to divide ${numerator} by ${denominator}. Let me think...`, + `[Math Genius] ${data.callerIdentity} wants to divide ${numerator} by ${denominator}. Let me think...`, ); await new Promise((resolve) => setTimeout(resolve, 2000)); diff --git a/src/index.ts b/src/index.ts index b96a089830..b00a9d3964 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,7 +39,7 @@ import { } from './room/utils'; import { getBrowser } from './utils/browserParser'; -export { RpcError } from './room/rpc'; +export { RpcError, RpcInvocationData } from './room/rpc'; export * from './connectionHelper/ConnectionCheck'; export * from './connectionHelper/checks/Checker'; diff --git a/src/room/participant/LocalParticipant.ts b/src/room/participant/LocalParticipant.ts index 5de32574e6..c4afc988f9 100644 --- a/src/room/participant/LocalParticipant.ts +++ b/src/room/participant/LocalParticipant.ts @@ -32,7 +32,7 @@ import { UnexpectedConnectionState, } from '../errors'; import { EngineEvent, ParticipantEvent, TrackEvent } from '../events'; -import { MAX_PAYLOAD_BYTES, RpcError, byteLength } from '../rpc'; +import { MAX_PAYLOAD_BYTES, RpcError, RpcInvocationData, byteLength } from '../rpc'; import LocalAudioTrack from '../track/LocalAudioTrack'; import LocalTrack from '../track/LocalTrack'; import LocalTrackPublication from '../track/LocalTrackPublication'; @@ -124,15 +124,7 @@ export default class LocalParticipant extends Participant { private enabledPublishVideoCodecs: Codec[] = []; - private rpcHandlers: Map< - string, - ( - requestId: string, - callerIdentity: string, - payload: string, - responseTimeout: number, - ) => Promise - > = new Map(); + private rpcHandlers: Map Promise> = new Map(); private pendingAcks = new Map void; participantIdentity: string }>(); @@ -1564,19 +1556,13 @@ export default class LocalParticipant extends Participant { * ```typescript * room.localParticipant?.registerRpcMethod( * 'greet', - * async (requestId: string, callerIdentity: string, payload: string, responseTimeout: number) => { - * console.log(`Received greeting from ${callerIdentity}: ${payload}`); - * return `Hello, ${callerIdentity}!`; + * async (data: RpcInvocationData) => { + * console.log(`Received greeting from ${data.callerIdentity}: ${data.payload}`); + * return `Hello, ${data.callerIdentity}!`; * } * ); * ``` * - * The handler receives the following parameters: - * - `requestId`: A unique identifier for this RPC request - * - `callerIdentity`: The identity of the RemoteParticipant who initiated the RPC call - * - `payload`: The data sent by the caller (as a string) - * - `responseTimeout`: The maximum time available to return a response (milliseconds) - * * The handler should return a Promise that resolves to a string. * If unable to respond within `responseTimeout`, the request will result in an error on the caller's side. * @@ -1586,12 +1572,7 @@ export default class LocalParticipant extends Participant { */ registerRpcMethod( method: string, - handler: ( - requestId: string, - callerIdentity: string, - payload: string, - responseTimeout: number, - ) => Promise, + handler: (data: RpcInvocationData) => Promise, ) { this.rpcHandlers.set(method, handler); } @@ -1693,7 +1674,9 @@ export default class LocalParticipant extends Participant { let responsePayload: string | null = null; try { - const response = await handler(requestId, callerIdentity, payload, responseTimeout); + const response = await handler( + new RpcInvocationData(requestId, callerIdentity, payload, responseTimeout), + ); if (byteLength(response) > MAX_PAYLOAD_BYTES) { responseError = RpcError.builtIn('RESPONSE_PAYLOAD_TOO_LARGE'); console.warn(`RPC Response payload too large for ${method}`); diff --git a/src/room/rpc.ts b/src/room/rpc.ts index 6b1c6de7c5..86ad992cba 100644 --- a/src/room/rpc.ts +++ b/src/room/rpc.ts @@ -3,6 +3,38 @@ // SPDX-License-Identifier: Apache-2.0 import { RpcError as RpcError_Proto } from '@livekit/protocol'; +/** + * Data passed to method handler for incoming RPC invocations + */ +export class RpcInvocationData { + /** + * The unique request ID. Will match at both sides of the call, useful for debugging or logging. + */ + requestId: string; + + /** + * The unique participant identity of the caller. + */ + callerIdentity: string; + + /** + * The payload of the request. User-definable format, typically JSON. + */ + payload: string; + + /** + * The maximum time the caller will wait for a response. + */ + responseTimeout: number; + + constructor(requestId: string, callerIdentity: string, payload: string, responseTimeout: number) { + this.requestId = requestId; + this.callerIdentity = callerIdentity; + this.payload = payload; + this.responseTimeout = responseTimeout; + } +} + /** * Specialized error handling for RPC methods. *