livekit-client 2.18.10 → 2.19.1
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/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +749 -438
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.pt.worker.js.map +1 -1
- package/dist/livekit-client.pt.worker.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +0 -3
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +4 -2
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +5 -13
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +5 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/rpc/client/RpcClientManager.d.ts +39 -0
- package/dist/src/room/rpc/client/RpcClientManager.d.ts.map +1 -0
- package/dist/src/room/rpc/client/events.d.ts +8 -0
- package/dist/src/room/rpc/client/events.d.ts.map +1 -0
- package/dist/src/room/rpc/index.d.ts +6 -0
- package/dist/src/room/rpc/index.d.ts.map +1 -0
- package/dist/src/room/rpc/server/RpcServerManager.d.ts +44 -0
- package/dist/src/room/rpc/server/RpcServerManager.d.ts.map +1 -0
- package/dist/src/room/rpc/server/events.d.ts +8 -0
- package/dist/src/room/rpc/server/events.d.ts.map +1 -0
- package/dist/src/room/{rpc.d.ts → rpc/utils.d.ts} +34 -4
- package/dist/src/room/rpc/utils.d.ts.map +1 -0
- package/dist/src/room/utils.d.ts +1 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/version.d.ts +8 -0
- package/dist/src/version.d.ts.map +1 -1
- package/dist/ts4.2/room/RTCEngine.d.ts +0 -3
- package/dist/ts4.2/room/Room.d.ts +4 -2
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +5 -13
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +5 -1
- package/dist/ts4.2/room/rpc/client/RpcClientManager.d.ts +43 -0
- package/dist/ts4.2/room/rpc/client/events.d.ts +8 -0
- package/dist/ts4.2/room/rpc/index.d.ts +7 -0
- package/dist/ts4.2/room/rpc/server/RpcServerManager.d.ts +44 -0
- package/dist/ts4.2/room/rpc/server/events.d.ts +8 -0
- package/dist/ts4.2/room/{rpc.d.ts → rpc/utils.d.ts} +34 -4
- package/dist/ts4.2/room/utils.d.ts +1 -0
- package/dist/ts4.2/version.d.ts +8 -0
- package/package.json +4 -2
- package/src/api/SignalClient.ts +1 -0
- package/src/room/RTCEngine.ts +4 -30
- package/src/room/Room.test.ts +99 -1
- package/src/room/Room.ts +107 -88
- package/src/room/participant/LocalParticipant.ts +16 -180
- package/src/room/participant/RemoteParticipant.ts +9 -0
- package/src/room/rpc/client/RpcClientManager.test.ts +430 -0
- package/src/room/rpc/client/RpcClientManager.ts +269 -0
- package/src/room/rpc/client/events.ts +9 -0
- package/src/room/rpc/index.ts +14 -0
- package/src/room/rpc/server/RpcServerManager.test.ts +471 -0
- package/src/room/rpc/server/RpcServerManager.ts +293 -0
- package/src/room/rpc/server/events.ts +9 -0
- package/src/room/{rpc.ts → rpc/utils.ts} +49 -8
- package/src/room/utils.ts +7 -1
- package/src/version.ts +10 -0
- package/dist/src/room/rpc.d.ts.map +0 -1
- package/src/room/rpc.test.ts +0 -301
|
@@ -8,7 +8,7 @@ import LocalDataTrack from '../data-track/LocalDataTrack';
|
|
|
8
8
|
import type OutgoingDataTrackManager from '../data-track/outgoing/OutgoingDataTrackManager';
|
|
9
9
|
import type { DataTrackOptions } from '../data-track/outgoing/types';
|
|
10
10
|
import type { PerformRpcParams, RpcInvocationData } from '../rpc';
|
|
11
|
-
import { RpcError } from '../rpc';
|
|
11
|
+
import { RpcClientManager, RpcError, RpcServerManager } from '../rpc';
|
|
12
12
|
import LocalTrack from '../track/LocalTrack';
|
|
13
13
|
import LocalTrackPublication from '../track/LocalTrackPublication';
|
|
14
14
|
import { Track } from '../track/Track';
|
|
@@ -40,15 +40,14 @@ export default class LocalParticipant extends Participant {
|
|
|
40
40
|
private signalConnectedFuture?;
|
|
41
41
|
private activeAgentFuture?;
|
|
42
42
|
private firstActiveAgent?;
|
|
43
|
-
private rpcHandlers;
|
|
44
43
|
private roomOutgoingDataStreamManager;
|
|
45
44
|
private roomOutgoingDataTrackManager;
|
|
45
|
+
private rpcClientManager;
|
|
46
|
+
private rpcServerManager;
|
|
46
47
|
private pendingSignalRequests;
|
|
47
48
|
private enabledPublishVideoCodecs;
|
|
48
|
-
private pendingAcks;
|
|
49
|
-
private pendingResponses;
|
|
50
49
|
/** @internal */
|
|
51
|
-
constructor(sid: string, identity: string, engine: RTCEngine, options: InternalRoomOptions,
|
|
50
|
+
constructor(sid: string, identity: string, engine: RTCEngine, options: InternalRoomOptions, roomOutgoingDataStreamManager: OutgoingDataStreamManager, roomOutgoingDataTrackManager: OutgoingDataTrackManager, rpcClientManager: RpcClientManager, rpcServerManager: RpcServerManager);
|
|
52
51
|
get lastCameraError(): Error | undefined;
|
|
53
52
|
get lastMicrophoneError(): Error | undefined;
|
|
54
53
|
get isE2EEEnabled(): boolean;
|
|
@@ -63,7 +62,6 @@ export default class LocalParticipant extends Participant {
|
|
|
63
62
|
private handleClosing;
|
|
64
63
|
private handleSignalConnected;
|
|
65
64
|
private handleSignalRequestResponse;
|
|
66
|
-
private handleDataPacket;
|
|
67
65
|
/**
|
|
68
66
|
* Sets and updates the metadata of the local participant.
|
|
69
67
|
* Note: this requires `canUpdateOwnMetadata` permission.
|
|
@@ -222,7 +220,7 @@ export default class LocalParticipant extends Participant {
|
|
|
222
220
|
* @returns A promise that resolves with the response payload or rejects with an error.
|
|
223
221
|
* @throws Error on failure. Details in `message`.
|
|
224
222
|
*/
|
|
225
|
-
performRpc(
|
|
223
|
+
performRpc(params: PerformRpcParams): TypedPromise<string, RpcError>;
|
|
226
224
|
/**
|
|
227
225
|
* @deprecated use `room.registerRpcMethod` instead
|
|
228
226
|
*/
|
|
@@ -249,12 +247,6 @@ export default class LocalParticipant extends Participant {
|
|
|
249
247
|
* participant/track. Any omitted participants will not receive any permissions.
|
|
250
248
|
*/
|
|
251
249
|
setTrackSubscriptionPermissions(allParticipantsAllowed: boolean, participantTrackPermissions?: ParticipantTrackPermission[]): void;
|
|
252
|
-
private handleIncomingRpcAck;
|
|
253
|
-
private handleIncomingRpcResponse;
|
|
254
|
-
/** @internal */
|
|
255
|
-
private publishRpcRequest;
|
|
256
|
-
/** @internal */
|
|
257
|
-
handleParticipantDisconnected(participantIdentity: string): void;
|
|
258
250
|
/** @internal */
|
|
259
251
|
setEnabledPublishCodecs(codecs: Codec[]): void;
|
|
260
252
|
/** @internal */
|
|
@@ -22,6 +22,10 @@ export default class RemoteParticipant extends Participant {
|
|
|
22
22
|
* const track = await remoteParticipant.dataTracks.getDeferred("data track name"); */
|
|
23
23
|
dataTracks: DeferrableMap<RemoteDataTrack['info']['name'], RemoteDataTrack>;
|
|
24
24
|
signalClient: SignalClient;
|
|
25
|
+
/** A version number indicating the set of features that the report participant's client supports.
|
|
26
|
+
* @internal
|
|
27
|
+
**/
|
|
28
|
+
clientProtocol: number;
|
|
25
29
|
private volumeMap;
|
|
26
30
|
private audioOutput?;
|
|
27
31
|
/** @internal */
|
|
@@ -31,7 +35,7 @@ export default class RemoteParticipant extends Participant {
|
|
|
31
35
|
remoteParticipant: string;
|
|
32
36
|
};
|
|
33
37
|
/** @internal */
|
|
34
|
-
constructor(signalClient: SignalClient, sid: string, identity?: string, name?: string, metadata?: string, attributes?: Record<string, string>, loggerOptions?: LoggerOptions, kind?: ParticipantKind, remoteDataTracks?: Array<RemoteDataTrack
|
|
38
|
+
constructor(signalClient: SignalClient, sid: string, identity?: string, name?: string, metadata?: string, attributes?: Record<string, string>, loggerOptions?: LoggerOptions, kind?: ParticipantKind, remoteDataTracks?: Array<RemoteDataTrack>, clientProtocol?: number);
|
|
35
39
|
protected addTrackPublication(publication: RemoteTrackPublication): void;
|
|
36
40
|
getTrackPublication(source: Track.Source): RemoteTrackPublication | undefined;
|
|
37
41
|
getTrackPublicationByName(name: string): RemoteTrackPublication | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type TypedEmitter from 'typed-emitter';
|
|
2
|
+
import type { StructuredLogger } from '../../../logger';
|
|
3
|
+
import type { TextStreamReader } from '../../data-stream/incoming/StreamReader';
|
|
4
|
+
import type OutgoingDataStreamManager from '../../data-stream/outgoing/OutgoingDataStreamManager';
|
|
5
|
+
import type Participant from '../../participant/Participant';
|
|
6
|
+
import type { PerformRpcParams } from '../utils';
|
|
7
|
+
import { RpcError } from '../utils';
|
|
8
|
+
import type { RpcClientManagerCallbacks } from './events';
|
|
9
|
+
declare const RpcClientManager_base: new () => TypedEmitter<RpcClientManagerCallbacks>;
|
|
10
|
+
/**
|
|
11
|
+
* Manages the client (caller) side of RPC: sending requests, tracking pending
|
|
12
|
+
* ack/response state, and handling incoming ack/response packets.
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export default class RpcClientManager extends RpcClientManager_base {
|
|
16
|
+
private log;
|
|
17
|
+
private outgoingDataStreamManager;
|
|
18
|
+
private getRemoteParticipantClientProtocol;
|
|
19
|
+
private getServerVersion;
|
|
20
|
+
private pendingAcks;
|
|
21
|
+
private pendingResponses;
|
|
22
|
+
constructor(log: StructuredLogger, outgoingDataStreamManager: OutgoingDataStreamManager, getRemoteParticipantClientProtocol: (identity: Participant['identity']) => number, getServerVersion: () => string | undefined);
|
|
23
|
+
performRpc({ destinationIdentity, method, payload, responseTimeout: responseTimeoutMs, }: PerformRpcParams): Promise<[
|
|
24
|
+
id: string,
|
|
25
|
+
completionPromise: Promise<string>
|
|
26
|
+
]>;
|
|
27
|
+
private publishRpcRequest;
|
|
28
|
+
/**
|
|
29
|
+
* Handle an incoming data stream containing an RPC response payload.
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
handleIncomingDataStream(reader: TextStreamReader, senderIdentity: Participant['identity'], attributes: Record<string, string>): Promise<void>;
|
|
33
|
+
/** @internal */
|
|
34
|
+
handleIncomingRpcResponseSuccess(requestId: string, payload: string): void;
|
|
35
|
+
/** @internal */
|
|
36
|
+
handleIncomingRpcResponseFailure(requestId: string, error: RpcError): void;
|
|
37
|
+
/** @internal */
|
|
38
|
+
handleIncomingRpcAck(requestId: string): void;
|
|
39
|
+
/** @internal */
|
|
40
|
+
handleParticipantDisconnected(participantIdentity: string): void;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=RpcClientManager.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as RpcClientManager } from './client/RpcClientManager';
|
|
2
|
+
export type { RpcClientManagerCallbacks } from './client/events';
|
|
3
|
+
export { default as RpcServerManager } from './server/RpcServerManager';
|
|
4
|
+
export type { RpcServerManagerCallbacks } from './server/events';
|
|
5
|
+
export type { PerformRpcParams, RpcInvocationData } from './utils';
|
|
6
|
+
export { RPC_REQUEST_DATA_STREAM_TOPIC, RPC_RESPONSE_DATA_STREAM_TOPIC, RpcRequestAttrs, RpcError, byteLength, truncateBytes } from './utils';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { RpcRequest } from '@livekit/protocol';
|
|
2
|
+
import type TypedEmitter from 'typed-emitter';
|
|
3
|
+
import type { StructuredLogger } from '../../../logger';
|
|
4
|
+
import type { TextStreamReader } from '../../data-stream/incoming/StreamReader';
|
|
5
|
+
import type OutgoingDataStreamManager from '../../data-stream/outgoing/OutgoingDataStreamManager';
|
|
6
|
+
import type Participant from '../../participant/Participant';
|
|
7
|
+
import type { RpcInvocationData } from '../utils';
|
|
8
|
+
import type { RpcServerManagerCallbacks } from './events';
|
|
9
|
+
declare const RpcServerManager_base: new () => TypedEmitter<RpcServerManagerCallbacks>;
|
|
10
|
+
/**
|
|
11
|
+
* Manages the server (handler) side of RPC: processing incoming requests,
|
|
12
|
+
* managing registered method handlers, and sending responses.
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export default class RpcServerManager extends RpcServerManager_base {
|
|
16
|
+
private log;
|
|
17
|
+
private outgoingDataStreamManager;
|
|
18
|
+
private getRemoteParticipantClientProtocol;
|
|
19
|
+
private rpcHandlers;
|
|
20
|
+
constructor(log: StructuredLogger, outgoingDataStreamManager: OutgoingDataStreamManager, getRemoteParticipantClientProtocol: (identity: Participant['identity']) => number);
|
|
21
|
+
registerRpcMethod(method: string, handler: (data: RpcInvocationData) => Promise<string>): void;
|
|
22
|
+
unregisterRpcMethod(method: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Handle an incoming RPCRequest message containing a payload.
|
|
25
|
+
* This handles "version 1" of rpc requests.
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
handleIncomingRpcRequest(callerIdentity: string, rpcRequest: RpcRequest): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Handle an incoming data stream containing a RPC request payload.
|
|
31
|
+
* This handles "version 2" of rpc requests.
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
handleIncomingDataStream(reader: TextStreamReader, callerIdentity: Participant['identity'], dataStreamAttrs: Record<string, string>): Promise<void>;
|
|
35
|
+
private publishRpcAck;
|
|
36
|
+
private publishRpcResponsePacket;
|
|
37
|
+
/**
|
|
38
|
+
* Send a successful RPC response payload, choosing the transport based on
|
|
39
|
+
* the caller's client protocol version.
|
|
40
|
+
*/
|
|
41
|
+
private publishRpcResponse;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=RpcServerManager.d.ts.map
|
|
@@ -49,6 +49,7 @@ export declare class RpcError extends Error {
|
|
|
49
49
|
static MAX_DATA_BYTES: number;
|
|
50
50
|
code: number;
|
|
51
51
|
data?: string;
|
|
52
|
+
cause?: unknown;
|
|
52
53
|
/**
|
|
53
54
|
* Creates an error object with the given code and message, plus an optional data payload.
|
|
54
55
|
*
|
|
@@ -56,7 +57,9 @@ export declare class RpcError extends Error {
|
|
|
56
57
|
*
|
|
57
58
|
* Error codes 1001-1999 are reserved for built-in errors (see RpcError.ErrorCode for their meanings).
|
|
58
59
|
*/
|
|
59
|
-
constructor(code: number, message: string, data?: string
|
|
60
|
+
constructor(code: number, message: string, data?: string, options?: {
|
|
61
|
+
cause?: unknown;
|
|
62
|
+
});
|
|
60
63
|
/**
|
|
61
64
|
* @internal
|
|
62
65
|
*/
|
|
@@ -87,9 +90,36 @@ export declare class RpcError extends Error {
|
|
|
87
90
|
*
|
|
88
91
|
* @internal
|
|
89
92
|
*/
|
|
90
|
-
static builtIn(key: keyof typeof RpcError.ErrorCode, data?: string
|
|
93
|
+
static builtIn(key: keyof typeof RpcError.ErrorCode, data?: string, options?: {
|
|
94
|
+
cause?: unknown;
|
|
95
|
+
}): RpcError;
|
|
91
96
|
}
|
|
92
|
-
export declare const
|
|
97
|
+
export declare const MAX_V1_PAYLOAD_BYTES = 15360;
|
|
98
|
+
/**
|
|
99
|
+
* Topic used for v2 RPC request data streams.
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
102
|
+
export declare const RPC_REQUEST_DATA_STREAM_TOPIC = "lk.rpc_request";
|
|
103
|
+
/**
|
|
104
|
+
* Topic used for v2 RPC response data streams.
|
|
105
|
+
* @internal
|
|
106
|
+
*/
|
|
107
|
+
export declare const RPC_RESPONSE_DATA_STREAM_TOPIC = "lk.rpc_response";
|
|
108
|
+
/** @internal */
|
|
109
|
+
export declare enum RpcRequestAttrs {
|
|
110
|
+
RPC_REQUEST_ID = "lk.rpc_request_id",
|
|
111
|
+
RPC_REQUEST_METHOD = "lk.rpc_request_method",
|
|
112
|
+
RPC_REQUEST_RESPONSE_TIMEOUT_MS = "lk.rpc_request_response_timeout_ms",
|
|
113
|
+
RPC_REQUEST_VERSION = "lk.rpc_request_version"
|
|
114
|
+
}
|
|
115
|
+
/** Initial version of rpc which uses RpcRequest / RpcResponse messages.
|
|
116
|
+
* @internal
|
|
117
|
+
**/
|
|
118
|
+
export declare const RPC_VERSION_V1 = 1;
|
|
119
|
+
/** Rpc version backed by data streams instead of RpcRequest / RpcResponse.
|
|
120
|
+
* @internal
|
|
121
|
+
**/
|
|
122
|
+
export declare const RPC_VERSION_V2 = 2;
|
|
93
123
|
/**
|
|
94
124
|
* @internal
|
|
95
125
|
*/
|
|
@@ -98,4 +128,4 @@ export declare function byteLength(str: string): number;
|
|
|
98
128
|
* @internal
|
|
99
129
|
*/
|
|
100
130
|
export declare function truncateBytes(str: string, maxBytes: number): string;
|
|
101
|
-
//# sourceMappingURL=
|
|
131
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -147,4 +147,5 @@ export declare function isRemoteParticipant(p: Participant): p is RemoteParticip
|
|
|
147
147
|
export declare function splitUtf8(s: string, n: number): NonSharedUint8Array[];
|
|
148
148
|
export declare function extractMaxAgeFromRequestHeaders(headers: Headers): number | undefined;
|
|
149
149
|
export declare function isCompressionStreamSupported(): boolean;
|
|
150
|
+
export declare function isPublisherOfferWithJoinSupported(): boolean;
|
|
150
151
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/ts4.2/version.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
export declare const version: string;
|
|
2
2
|
export declare const protocolVersion = 17;
|
|
3
|
+
/** Initial client protocol. */
|
|
4
|
+
export declare const CLIENT_PROTOCOL_DEFAULT = 0;
|
|
5
|
+
/** Replaces RPC v1 protocol with a v2 data streams based one to support unlimited request /
|
|
6
|
+
* response payload length. */
|
|
7
|
+
export declare const CLIENT_PROTOCOL_DATA_STREAM_RPC = 1;
|
|
8
|
+
/** The client protocol version indicates what level of support that the client has for
|
|
9
|
+
* client <-> client api interactions. */
|
|
10
|
+
export declare const clientProtocol = 1;
|
|
3
11
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livekit-client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.1",
|
|
4
4
|
"description": "JavaScript/TypeScript client SDK for LiveKit",
|
|
5
5
|
"main": "./dist/livekit-client.umd.js",
|
|
6
6
|
"unpkg": "./dist/livekit-client.umd.js",
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
"happy-dom": "^20.0.0",
|
|
92
92
|
"jsdom": "^26.1.0",
|
|
93
93
|
"prettier": "^3.4.2",
|
|
94
|
+
"publint": "^0.3.21",
|
|
94
95
|
"rollup": "4.60.2",
|
|
95
96
|
"rollup-plugin-delete": "^2.1.0",
|
|
96
97
|
"rollup-plugin-typescript2": "0.37.0",
|
|
@@ -118,7 +119,8 @@
|
|
|
118
119
|
"format": "prettier --write src examples/**/*.ts",
|
|
119
120
|
"format:check": "prettier --check src examples/**/*.ts",
|
|
120
121
|
"throws:check": "pnpm --package=@livekit/throws-transformer dlx throws-check 'src/!(*.test).ts' 'src/**/!(*.test).ts'",
|
|
121
|
-
"
|
|
122
|
+
"type:check": "tsc --noEmit",
|
|
123
|
+
"ci:publish": "pnpm type:check && pnpm build && pnpm compat && changeset publish",
|
|
122
124
|
"downlevel-dts": "downlevel-dts ./dist/src ./dist/ts4.2 --to=4.2",
|
|
123
125
|
"compat": "eslint --config ./eslint.config.dist.mjs --no-inline-config ./dist/livekit-client.esm.mjs",
|
|
124
126
|
"size-limit": "size-limit"
|
package/src/api/SignalClient.ts
CHANGED
|
@@ -1157,6 +1157,7 @@ function createConnectionParams(
|
|
|
1157
1157
|
params.set('sdk', isReactNative() ? 'reactnative' : 'js');
|
|
1158
1158
|
params.set('version', info.version!);
|
|
1159
1159
|
params.set('protocol', info.protocol!.toString());
|
|
1160
|
+
params.set('client_protocol', info.clientProtocol!.toString());
|
|
1160
1161
|
if (info.deviceModel) {
|
|
1161
1162
|
params.set('device_model', info.deviceModel);
|
|
1162
1163
|
}
|
package/src/room/RTCEngine.ts
CHANGED
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
Room as RoomModel,
|
|
27
27
|
RoomMovedResponse,
|
|
28
28
|
RpcAck,
|
|
29
|
-
RpcResponse,
|
|
30
29
|
ServerInfo,
|
|
31
30
|
SessionDescription,
|
|
32
31
|
SignalTarget,
|
|
@@ -79,7 +78,6 @@ import {
|
|
|
79
78
|
UnexpectedConnectionState,
|
|
80
79
|
} from './errors';
|
|
81
80
|
import { EngineEvent } from './events';
|
|
82
|
-
import { RpcError } from './rpc';
|
|
83
81
|
import CriticalTimers from './timers';
|
|
84
82
|
import type LocalTrack from './track/LocalTrack';
|
|
85
83
|
import type LocalTrackPublication from './track/LocalTrackPublication';
|
|
@@ -92,7 +90,7 @@ import { getTrackPublicationInfo } from './track/utils';
|
|
|
92
90
|
import type { LoggerOptions } from './types';
|
|
93
91
|
import {
|
|
94
92
|
Future,
|
|
95
|
-
|
|
93
|
+
isPublisherOfferWithJoinSupported,
|
|
96
94
|
isVideoCodec,
|
|
97
95
|
isVideoTrack,
|
|
98
96
|
isWeb,
|
|
@@ -124,7 +122,7 @@ enum PCState {
|
|
|
124
122
|
export enum DataChannelKind {
|
|
125
123
|
RELIABLE = DataPacket_Kind.RELIABLE,
|
|
126
124
|
LOSSY = DataPacket_Kind.LOSSY,
|
|
127
|
-
DATA_TRACK_LOSSY,
|
|
125
|
+
DATA_TRACK_LOSSY = 2,
|
|
128
126
|
}
|
|
129
127
|
|
|
130
128
|
/** @internal */
|
|
@@ -327,7 +325,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
327
325
|
|
|
328
326
|
this.setupSignalClientCallbacks();
|
|
329
327
|
let offerProto: SessionDescription | undefined;
|
|
330
|
-
if (!useV0Path &&
|
|
328
|
+
if (!useV0Path && isPublisherOfferWithJoinSupported()) {
|
|
331
329
|
if (!this.pcManager) {
|
|
332
330
|
await this.configure();
|
|
333
331
|
this.createDataChannels();
|
|
@@ -360,7 +358,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
360
358
|
this.participantSid = joinResponse.participant?.sid;
|
|
361
359
|
|
|
362
360
|
this.subscriberPrimary = joinResponse.subscriberPrimary;
|
|
363
|
-
if (!useV0Path &&
|
|
361
|
+
if (!useV0Path && isPublisherOfferWithJoinSupported()) {
|
|
364
362
|
this.pcManager?.updateConfiguration(this.makeRTCConfiguration(joinResponse));
|
|
365
363
|
} else {
|
|
366
364
|
if (!this.pcManager) {
|
|
@@ -1464,30 +1462,6 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1464
1462
|
});
|
|
1465
1463
|
};
|
|
1466
1464
|
|
|
1467
|
-
/** @internal */
|
|
1468
|
-
async publishRpcResponse(
|
|
1469
|
-
destinationIdentity: string,
|
|
1470
|
-
requestId: string,
|
|
1471
|
-
payload: string | null,
|
|
1472
|
-
error: RpcError | null,
|
|
1473
|
-
) {
|
|
1474
|
-
const packet = new DataPacket({
|
|
1475
|
-
destinationIdentities: [destinationIdentity],
|
|
1476
|
-
kind: DataPacket_Kind.RELIABLE,
|
|
1477
|
-
value: {
|
|
1478
|
-
case: 'rpcResponse',
|
|
1479
|
-
value: new RpcResponse({
|
|
1480
|
-
requestId,
|
|
1481
|
-
value: error
|
|
1482
|
-
? { case: 'error', value: error.toProto() }
|
|
1483
|
-
: { case: 'payload', value: payload ?? '' },
|
|
1484
|
-
}),
|
|
1485
|
-
},
|
|
1486
|
-
});
|
|
1487
|
-
|
|
1488
|
-
await this.sendDataPacket(packet, DataChannelKind.RELIABLE);
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
1465
|
/** @internal */
|
|
1492
1466
|
async publishRpcAck(destinationIdentity: string, requestId: string) {
|
|
1493
1467
|
const packet = new DataPacket({
|
package/src/room/Room.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ClientInfo_Capability, JoinResponse } from '@livekit/protocol';
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
3
|
import Room from './Room';
|
|
4
4
|
import { roomConnectOptionDefaults, roomOptionDefaults } from './defaults';
|
|
5
5
|
import { RoomEvent } from './events';
|
|
@@ -89,3 +89,101 @@ describe('Room signaling options', () => {
|
|
|
89
89
|
);
|
|
90
90
|
});
|
|
91
91
|
});
|
|
92
|
+
|
|
93
|
+
describe('Room lifecycle', () => {
|
|
94
|
+
afterEach(() => {
|
|
95
|
+
vi.restoreAllMocks();
|
|
96
|
+
// Tear down the mocked mediaDevices so other tests see the env they
|
|
97
|
+
// expected (happy-dom does not provide navigator.mediaDevices by default).
|
|
98
|
+
if ((navigator as { mediaDevices?: unknown }).mediaDevices) {
|
|
99
|
+
Object.defineProperty(navigator, 'mediaDevices', {
|
|
100
|
+
configurable: true,
|
|
101
|
+
value: undefined,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('wraps the constructor-registered devicechange listener in a WeakRef so the Room is GC-eligible (#1940)', async () => {
|
|
107
|
+
// happy-dom does not provide navigator.mediaDevices. Install a minimal
|
|
108
|
+
// EventTarget stand-in so the constructor takes the listener-registration
|
|
109
|
+
// branch and we can observe the registered listener.
|
|
110
|
+
const mediaDevices = new EventTarget() as EventTarget & {
|
|
111
|
+
addEventListener: EventTarget['addEventListener'];
|
|
112
|
+
removeEventListener: EventTarget['removeEventListener'];
|
|
113
|
+
};
|
|
114
|
+
Object.defineProperty(navigator, 'mediaDevices', {
|
|
115
|
+
configurable: true,
|
|
116
|
+
value: mediaDevices,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const addSpy = vi.spyOn(mediaDevices, 'addEventListener');
|
|
120
|
+
const derefSpy = vi.spyOn(WeakRef.prototype, 'deref');
|
|
121
|
+
const cleanupRegistrySpy = Room.cleanupRegistry
|
|
122
|
+
? vi.spyOn(Room.cleanupRegistry, 'register')
|
|
123
|
+
: undefined;
|
|
124
|
+
|
|
125
|
+
const room = new Room();
|
|
126
|
+
const handleDeviceChangeSpy = vi.spyOn(
|
|
127
|
+
room as unknown as { handleDeviceChange: (ev: Event) => void },
|
|
128
|
+
'handleDeviceChange',
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Constructor must register exactly one devicechange listener with AbortSignal teardown.
|
|
132
|
+
const deviceChangeAdds = addSpy.mock.calls.filter(([type]) => type === 'devicechange');
|
|
133
|
+
expect(deviceChangeAdds).toHaveLength(1);
|
|
134
|
+
const listener = deviceChangeAdds[0][1] as EventListener;
|
|
135
|
+
const addOptions = deviceChangeAdds[0][2] as AddEventListenerOptions | undefined;
|
|
136
|
+
expect(addOptions?.signal).toBeInstanceOf(AbortSignal);
|
|
137
|
+
|
|
138
|
+
// FinalizationRegistry must be registered with the Room as the target so the
|
|
139
|
+
// cleanup callback fires when the user drops their Room reference.
|
|
140
|
+
if (Room.cleanupRegistry) {
|
|
141
|
+
expect(cleanupRegistrySpy).toHaveBeenCalledWith(room, expect.any(Function));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// While the WeakRef still derefs to the Room, the listener forwards to handleDeviceChange.
|
|
145
|
+
listener.call(null, new Event('devicechange'));
|
|
146
|
+
expect(handleDeviceChangeSpy).toHaveBeenCalledTimes(1);
|
|
147
|
+
|
|
148
|
+
// Simulate the Room being GC'd by forcing deref to return undefined; the
|
|
149
|
+
// listener must short-circuit instead of calling handleDeviceChange.
|
|
150
|
+
derefSpy.mockReturnValue(undefined);
|
|
151
|
+
listener.call(null, new Event('devicechange'));
|
|
152
|
+
expect(handleDeviceChangeSpy).toHaveBeenCalledTimes(1);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('falls back to a direct devicechange listener when WeakRef/FinalizationRegistry are unavailable (#1944)', async () => {
|
|
156
|
+
const mediaDevices = new EventTarget() as EventTarget & {
|
|
157
|
+
addEventListener: EventTarget['addEventListener'];
|
|
158
|
+
removeEventListener: EventTarget['removeEventListener'];
|
|
159
|
+
};
|
|
160
|
+
Object.defineProperty(navigator, 'mediaDevices', {
|
|
161
|
+
configurable: true,
|
|
162
|
+
value: mediaDevices,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Simulate a legacy browser by stubbing out cleanupRegistry.
|
|
166
|
+
const originalRegistry = Room.cleanupRegistry;
|
|
167
|
+
Object.defineProperty(Room, 'cleanupRegistry', {
|
|
168
|
+
configurable: true,
|
|
169
|
+
value: false,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const addSpy = vi.spyOn(mediaDevices, 'addEventListener');
|
|
174
|
+
const room = new Room();
|
|
175
|
+
const handleDeviceChange = (room as unknown as { handleDeviceChange: () => void })
|
|
176
|
+
.handleDeviceChange;
|
|
177
|
+
|
|
178
|
+
const deviceChangeAdds = addSpy.mock.calls.filter(([type]) => type === 'devicechange');
|
|
179
|
+
expect(deviceChangeAdds).toHaveLength(1);
|
|
180
|
+
// The registered listener is the bare handleDeviceChange method (no WeakRef closure).
|
|
181
|
+
expect(deviceChangeAdds[0][1]).toBe(handleDeviceChange);
|
|
182
|
+
} finally {
|
|
183
|
+
Object.defineProperty(Room, 'cleanupRegistry', {
|
|
184
|
+
configurable: true,
|
|
185
|
+
value: originalRegistry,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|