livekit-client 2.5.10 → 2.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. package/README.md +54 -0
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  4. package/dist/livekit-client.esm.mjs +431 -45
  5. package/dist/livekit-client.esm.mjs.map +1 -1
  6. package/dist/livekit-client.umd.js +1 -1
  7. package/dist/livekit-client.umd.js.map +1 -1
  8. package/dist/src/api/SignalClient.d.ts.map +1 -1
  9. package/dist/src/index.d.ts +1 -0
  10. package/dist/src/index.d.ts.map +1 -1
  11. package/dist/src/room/PCTransport.d.ts +2 -0
  12. package/dist/src/room/PCTransport.d.ts.map +1 -1
  13. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  14. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  15. package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
  16. package/dist/src/room/Room.d.ts.map +1 -1
  17. package/dist/src/room/errors.d.ts +2 -2
  18. package/dist/src/room/errors.d.ts.map +1 -1
  19. package/dist/src/room/participant/LocalParticipant.d.ts +56 -0
  20. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  21. package/dist/src/room/rpc.d.ts +96 -0
  22. package/dist/src/room/rpc.d.ts.map +1 -0
  23. package/dist/src/room/track/RemoteAudioTrack.d.ts +1 -1
  24. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
  25. package/dist/src/room/track/RemoteVideoTrack.d.ts +2 -1
  26. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  27. package/dist/src/room/track/utils.d.ts +2 -2
  28. package/dist/src/room/track/utils.d.ts.map +1 -1
  29. package/dist/ts4.2/src/index.d.ts +2 -0
  30. package/dist/ts4.2/src/room/PCTransport.d.ts +2 -0
  31. package/dist/ts4.2/src/room/errors.d.ts +2 -2
  32. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +56 -0
  33. package/dist/ts4.2/src/room/rpc.d.ts +96 -0
  34. package/dist/ts4.2/src/room/track/RemoteAudioTrack.d.ts +1 -1
  35. package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +2 -1
  36. package/dist/ts4.2/src/room/track/utils.d.ts +2 -2
  37. package/package.json +2 -2
  38. package/src/api/SignalClient.ts +19 -3
  39. package/src/index.ts +2 -0
  40. package/src/room/PCTransport.ts +42 -29
  41. package/src/room/PCTransportManager.ts +6 -1
  42. package/src/room/RTCEngine.ts +13 -3
  43. package/src/room/RegionUrlProvider.ts +3 -1
  44. package/src/room/Room.ts +9 -3
  45. package/src/room/errors.ts +2 -2
  46. package/src/room/participant/LocalParticipant.test.ts +304 -0
  47. package/src/room/participant/LocalParticipant.ts +340 -1
  48. package/src/room/rpc.ts +172 -0
  49. package/src/room/track/RemoteAudioTrack.ts +1 -1
  50. package/src/room/track/RemoteVideoTrack.ts +1 -1
  51. package/src/room/track/utils.ts +1 -6
@@ -4746,14 +4746,14 @@ const RpcResponse = /*@__PURE__*/proto3.makeMessageType("livekit.RpcResponse", (
4746
4746
  no: 3,
4747
4747
  name: "error",
4748
4748
  kind: "message",
4749
- T: RpcError,
4749
+ T: RpcError$1,
4750
4750
  oneof: "value"
4751
4751
  }]);
4752
4752
 
4753
4753
  /**
4754
4754
  * @generated from message livekit.RpcError
4755
4755
  */
4756
- const RpcError = /*@__PURE__*/proto3.makeMessageType("livekit.RpcError", () => [{
4756
+ const RpcError$1 = /*@__PURE__*/proto3.makeMessageType("livekit.RpcError", () => [{
4757
4757
  no: 1,
4758
4758
  name: "code",
4759
4759
  kind: "scalar",
@@ -11176,7 +11176,7 @@ function getOSVersion(ua) {
11176
11176
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11177
11177
  }
11178
11178
 
11179
- var version$1 = "2.5.10";
11179
+ var version$1 = "2.6.1";
11180
11180
 
11181
11181
  const version = version$1;
11182
11182
  const protocolVersion = 15;
@@ -11836,11 +11836,7 @@ function screenCaptureToDisplayMediaStreamOptions(options) {
11836
11836
  };
11837
11837
  }
11838
11838
  function mimeTypeToVideoCodecString(mimeType) {
11839
- const codec = mimeType.split('/')[1].toLowerCase();
11840
- if (!videoCodecs.includes(codec)) {
11841
- throw Error("Video codec not supported: ".concat(codec));
11842
- }
11843
- return codec;
11839
+ return mimeType.split('/')[1].toLowerCase();
11844
11840
  }
11845
11841
  function getTrackPublicationInfo(tracks) {
11846
11842
  const infos = [];
@@ -13384,11 +13380,11 @@ class SignalClient {
13384
13380
  const abortHandler = () => __awaiter(this, void 0, void 0, function* () {
13385
13381
  this.close();
13386
13382
  clearTimeout(wsTimeout);
13387
- reject(new ConnectionError('room connection has been cancelled (signal)'));
13383
+ reject(new ConnectionError('room connection has been cancelled (signal)', ConnectionErrorReason.Cancelled));
13388
13384
  });
13389
13385
  const wsTimeout = setTimeout(() => {
13390
13386
  this.close();
13391
- reject(new ConnectionError('room connection has timed out (signal)'));
13387
+ reject(new ConnectionError('room connection has timed out (signal)', ConnectionErrorReason.ServerUnreachable));
13392
13388
  }, opts.websocketTimeout);
13393
13389
  if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
13394
13390
  abortHandler();
@@ -13470,7 +13466,7 @@ class SignalClient {
13470
13466
  reject(new ConnectionError('Received leave request while trying to (re)connect', ConnectionErrorReason.LeaveRequest));
13471
13467
  } else if (!opts.reconnect) {
13472
13468
  // non-reconnect case, should receive join response first
13473
- reject(new ConnectionError("did not receive join response, got ".concat((_c = resp.message) === null || _c === void 0 ? void 0 : _c.case, " instead")));
13469
+ reject(new ConnectionError("did not receive join response, got ".concat((_c = resp.message) === null || _c === void 0 ? void 0 : _c.case, " instead"), ConnectionErrorReason.InternalError));
13474
13470
  }
13475
13471
  if (!shouldProcessMessage) {
13476
13472
  return;
@@ -13483,7 +13479,7 @@ class SignalClient {
13483
13479
  });
13484
13480
  this.ws.onclose = ev => {
13485
13481
  if (this.isEstablishingConnection) {
13486
- reject(new ConnectionError('Websocket got closed during a (re)connection attempt'));
13482
+ reject(new ConnectionError('Websocket got closed during a (re)connection attempt', ConnectionErrorReason.InternalError));
13487
13483
  }
13488
13484
  this.log.warn("websocket closed", Object.assign(Object.assign({}, this.logContext), {
13489
13485
  reason: ev.reason,
@@ -14673,6 +14669,7 @@ class PCTransport extends eventsExports.EventEmitter {
14673
14669
  var _a;
14674
14670
  super();
14675
14671
  this.log = livekitLogger;
14672
+ this.ddExtID = 0;
14676
14673
  this.pendingCandidates = [];
14677
14674
  this.restartingIce = false;
14678
14675
  this.renegotiate = false;
@@ -14894,7 +14891,7 @@ class PCTransport extends eventsExports.EventEmitter {
14894
14891
  return true;
14895
14892
  }
14896
14893
  if (isSVCCodec(trackbr.codec)) {
14897
- ensureVideoDDExtensionForSVC(media);
14894
+ this.ensureVideoDDExtensionForSVC(media, sdpParsed);
14898
14895
  }
14899
14896
  // TODO: av1 slow starting issue already fixed in chrome 124, clean this after some versions
14900
14897
  // mung sdp for av1 bitrate setting that can't apply by sendEncoding
@@ -15081,6 +15078,36 @@ class PCTransport extends eventsExports.EventEmitter {
15081
15078
  }
15082
15079
  });
15083
15080
  }
15081
+ ensureVideoDDExtensionForSVC(media, sdp) {
15082
+ var _a, _b;
15083
+ const ddFound = (_a = media.ext) === null || _a === void 0 ? void 0 : _a.some(ext => {
15084
+ if (ext.uri === ddExtensionURI) {
15085
+ return true;
15086
+ }
15087
+ return false;
15088
+ });
15089
+ if (!ddFound) {
15090
+ if (this.ddExtID === 0) {
15091
+ let maxID = 0;
15092
+ sdp.media.forEach(m => {
15093
+ var _a;
15094
+ if (m.type !== 'video') {
15095
+ return;
15096
+ }
15097
+ (_a = m.ext) === null || _a === void 0 ? void 0 : _a.forEach(ext => {
15098
+ if (ext.value > maxID) {
15099
+ maxID = ext.value;
15100
+ }
15101
+ });
15102
+ });
15103
+ this.ddExtID = maxID + 1;
15104
+ }
15105
+ (_b = media.ext) === null || _b === void 0 ? void 0 : _b.push({
15106
+ value: this.ddExtID,
15107
+ uri: ddExtensionURI
15108
+ });
15109
+ }
15110
+ }
15084
15111
  }
15085
15112
  function ensureAudioNackAndStereo(media, stereoMids, nackMids) {
15086
15113
  // found opus codec to add nack fb
@@ -15116,25 +15143,6 @@ function ensureAudioNackAndStereo(media, stereoMids, nackMids) {
15116
15143
  }
15117
15144
  }
15118
15145
  }
15119
- function ensureVideoDDExtensionForSVC(media) {
15120
- var _a, _b;
15121
- let maxID = 0;
15122
- const ddFound = (_a = media.ext) === null || _a === void 0 ? void 0 : _a.some(ext => {
15123
- if (ext.uri === ddExtensionURI) {
15124
- return true;
15125
- }
15126
- if (ext.value > maxID) {
15127
- maxID = ext.value;
15128
- }
15129
- return false;
15130
- });
15131
- if (!ddFound) {
15132
- (_b = media.ext) === null || _b === void 0 ? void 0 : _b.push({
15133
- value: maxID + 1,
15134
- uri: ddExtensionURI
15135
- });
15136
- }
15137
- }
15138
15146
  function extractStereoAndNackAudioFromOffer(offer) {
15139
15147
  var _a;
15140
15148
  const stereoMids = [];
@@ -15470,7 +15478,7 @@ class PCTransportManager {
15470
15478
  abortController === null || abortController === void 0 ? void 0 : abortController.signal.addEventListener('abort', abortHandler);
15471
15479
  const connectTimeout = CriticalTimers.setTimeout(() => {
15472
15480
  abortController === null || abortController === void 0 ? void 0 : abortController.signal.removeEventListener('abort', abortHandler);
15473
- reject(new ConnectionError('could not establish pc connection'));
15481
+ reject(new ConnectionError('could not establish pc connection', ConnectionErrorReason.InternalError));
15474
15482
  }, timeout);
15475
15483
  while (this.state !== PCTransportState.CONNECTED) {
15476
15484
  yield sleep(50); // FIXME we shouldn't rely on `sleep` in the connection paths, as it invokes `setTimeout` which can be drastically throttled by browser implementations
@@ -17001,7 +17009,7 @@ class RTCEngine extends eventsExports.EventEmitter {
17001
17009
  return new Promise((resolve, reject) => {
17002
17010
  const publicationTimeout = setTimeout(() => {
17003
17011
  delete this.pendingTrackResolvers[req.cid];
17004
- reject(new ConnectionError('publication of local track timed out, no response from server'));
17012
+ reject(new ConnectionError('publication of local track timed out, no response from server', ConnectionErrorReason.InternalError));
17005
17013
  }, 10000);
17006
17014
  this.pendingTrackResolvers[req.cid] = {
17007
17015
  resolve: info => {
@@ -17548,7 +17556,7 @@ class RTCEngine extends eventsExports.EventEmitter {
17548
17556
  } catch (e) {
17549
17557
  // TODO do we need a `failed` state here for the PC?
17550
17558
  this.pcState = PCState.Disconnected;
17551
- throw new ConnectionError("could not establish PC connection, ".concat(e.message));
17559
+ throw new ConnectionError("could not establish PC connection, ".concat(e.message), ConnectionErrorReason.InternalError);
17552
17560
  }
17553
17561
  });
17554
17562
  }
@@ -17580,7 +17588,7 @@ class RTCEngine extends eventsExports.EventEmitter {
17580
17588
  const transport = subscriber ? _this2.pcManager.subscriber : _this2.pcManager.publisher;
17581
17589
  const transportName = subscriber ? 'Subscriber' : 'Publisher';
17582
17590
  if (!transport) {
17583
- throw new ConnectionError("".concat(transportName, " connection not set"));
17591
+ throw new ConnectionError("".concat(transportName, " connection not set"), ConnectionErrorReason.InternalError);
17584
17592
  }
17585
17593
  let needNegotiation = false;
17586
17594
  if (!subscriber && !_this2.dataChannelForKind(kind, subscriber)) {
@@ -17606,7 +17614,7 @@ class RTCEngine extends eventsExports.EventEmitter {
17606
17614
  }
17607
17615
  yield sleep(50);
17608
17616
  }
17609
- throw new ConnectionError("could not establish ".concat(transportName, " connection, state: ").concat(transport.getICEConnectionState()));
17617
+ throw new ConnectionError("could not establish ".concat(transportName, " connection, state: ").concat(transport.getICEConnectionState()), ConnectionErrorReason.InternalError);
17610
17618
  }();
17611
17619
  });
17612
17620
  }
@@ -17852,7 +17860,7 @@ class RegionUrlProvider {
17852
17860
  this.lastUpdateAt = Date.now();
17853
17861
  return regionSettings;
17854
17862
  } else {
17855
- throw new ConnectionError("Could not fetch region settings: ".concat(regionSettingsResponse.statusText), regionSettingsResponse.status === 401 ? ConnectionErrorReason.NotAllowed : undefined, regionSettingsResponse.status);
17863
+ throw new ConnectionError("Could not fetch region settings: ".concat(regionSettingsResponse.statusText), regionSettingsResponse.status === 401 ? ConnectionErrorReason.NotAllowed : ConnectionErrorReason.InternalError, regionSettingsResponse.status);
17856
17864
  }
17857
17865
  });
17858
17866
  }
@@ -17865,6 +17873,120 @@ function getCloudConfigUrl(serverUrl) {
17865
17873
  return "".concat(serverUrl.protocol.replace('ws', 'http'), "//").concat(serverUrl.host, "/settings");
17866
17874
  }
17867
17875
 
17876
+ // SPDX-FileCopyrightText: 2024 LiveKit, Inc.
17877
+ //
17878
+ // SPDX-License-Identifier: Apache-2.0
17879
+ /**
17880
+ * Specialized error handling for RPC methods.
17881
+ *
17882
+ * Instances of this type, when thrown in a method handler, will have their `message`
17883
+ * serialized and sent across the wire. The sender will receive an equivalent error on the other side.
17884
+ *
17885
+ * Built-in types are included but developers may use any string, with a max length of 256 bytes.
17886
+ */
17887
+ class RpcError extends Error {
17888
+ /**
17889
+ * Creates an error object with the given code and message, plus an optional data payload.
17890
+ *
17891
+ * If thrown in an RPC method handler, the error will be sent back to the caller.
17892
+ *
17893
+ * Error codes 1001-1999 are reserved for built-in errors (see RpcError.ErrorCode for their meanings).
17894
+ */
17895
+ constructor(code, message, data) {
17896
+ super(message);
17897
+ this.code = code;
17898
+ this.message = truncateBytes(message, RpcError.MAX_MESSAGE_BYTES);
17899
+ this.data = data ? truncateBytes(data, RpcError.MAX_DATA_BYTES) : undefined;
17900
+ }
17901
+ /**
17902
+ * @internal
17903
+ */
17904
+ static fromProto(proto) {
17905
+ return new RpcError(proto.code, proto.message, proto.data);
17906
+ }
17907
+ /**
17908
+ * @internal
17909
+ */
17910
+ toProto() {
17911
+ return new RpcError$1({
17912
+ code: this.code,
17913
+ message: this.message,
17914
+ data: this.data
17915
+ });
17916
+ }
17917
+ /**
17918
+ * Creates an error object from the code, with an auto-populated message.
17919
+ *
17920
+ * @internal
17921
+ */
17922
+ static builtIn(key, data) {
17923
+ return new RpcError(RpcError.ErrorCode[key], RpcError.ErrorMessage[key], data);
17924
+ }
17925
+ }
17926
+ RpcError.MAX_MESSAGE_BYTES = 256;
17927
+ RpcError.MAX_DATA_BYTES = 15360; // 15 KB
17928
+ RpcError.ErrorCode = {
17929
+ APPLICATION_ERROR: 1500,
17930
+ CONNECTION_TIMEOUT: 1501,
17931
+ RESPONSE_TIMEOUT: 1502,
17932
+ RECIPIENT_DISCONNECTED: 1503,
17933
+ RESPONSE_PAYLOAD_TOO_LARGE: 1504,
17934
+ SEND_FAILED: 1505,
17935
+ UNSUPPORTED_METHOD: 1400,
17936
+ RECIPIENT_NOT_FOUND: 1401,
17937
+ REQUEST_PAYLOAD_TOO_LARGE: 1402,
17938
+ UNSUPPORTED_SERVER: 1403,
17939
+ UNSUPPORTED_VERSION: 1404
17940
+ };
17941
+ /**
17942
+ * @internal
17943
+ */
17944
+ RpcError.ErrorMessage = {
17945
+ APPLICATION_ERROR: 'Application error in method handler',
17946
+ CONNECTION_TIMEOUT: 'Connection timeout',
17947
+ RESPONSE_TIMEOUT: 'Response timeout',
17948
+ RECIPIENT_DISCONNECTED: 'Recipient disconnected',
17949
+ RESPONSE_PAYLOAD_TOO_LARGE: 'Response payload too large',
17950
+ SEND_FAILED: 'Failed to send',
17951
+ UNSUPPORTED_METHOD: 'Method not supported at destination',
17952
+ RECIPIENT_NOT_FOUND: 'Recipient not found',
17953
+ REQUEST_PAYLOAD_TOO_LARGE: 'Request payload too large',
17954
+ UNSUPPORTED_SERVER: 'RPC not supported by server',
17955
+ UNSUPPORTED_VERSION: 'Unsupported RPC version'
17956
+ };
17957
+ /*
17958
+ * Maximum payload size for RPC requests and responses. If a payload exceeds this size,
17959
+ * the RPC call will fail with a REQUEST_PAYLOAD_TOO_LARGE(1402) or RESPONSE_PAYLOAD_TOO_LARGE(1504) error.
17960
+ */
17961
+ const MAX_PAYLOAD_BYTES = 15360; // 15 KB
17962
+ /**
17963
+ * @internal
17964
+ */
17965
+ function byteLength(str) {
17966
+ const encoder = new TextEncoder();
17967
+ return encoder.encode(str).length;
17968
+ }
17969
+ /**
17970
+ * @internal
17971
+ */
17972
+ function truncateBytes(str, maxBytes) {
17973
+ if (byteLength(str) <= maxBytes) {
17974
+ return str;
17975
+ }
17976
+ let low = 0;
17977
+ let high = str.length;
17978
+ const encoder = new TextEncoder();
17979
+ while (low < high) {
17980
+ const mid = Math.floor((low + high + 1) / 2);
17981
+ if (encoder.encode(str.slice(0, mid)).length <= maxBytes) {
17982
+ low = mid;
17983
+ } else {
17984
+ high = mid - 1;
17985
+ }
17986
+ }
17987
+ return str.slice(0, low);
17988
+ }
17989
+
17868
17990
  class RemoteTrack extends Track {
17869
17991
  constructor(mediaTrack, sid, kind, receiver, loggerOptions) {
17870
17992
  super(mediaTrack, kind, loggerOptions);
@@ -19100,6 +19222,9 @@ class LocalParticipant extends Participant {
19100
19222
  this.allParticipantsAllowedToSubscribe = true;
19101
19223
  this.encryptionType = Encryption_Type.NONE;
19102
19224
  this.enabledPublishVideoCodecs = [];
19225
+ this.rpcHandlers = new Map();
19226
+ this.pendingAcks = new Map();
19227
+ this.pendingResponses = new Map();
19103
19228
  this.handleReconnecting = () => {
19104
19229
  if (!this.reconnectFuture) {
19105
19230
  this.reconnectFuture = new Future();
@@ -19133,6 +19258,29 @@ class LocalParticipant extends Participant {
19133
19258
  this.pendingSignalRequests.delete(requestId);
19134
19259
  }
19135
19260
  };
19261
+ this.handleDataPacket = packet => {
19262
+ switch (packet.value.case) {
19263
+ case 'rpcRequest':
19264
+ let rpcRequest = packet.value.value;
19265
+ this.handleIncomingRpcRequest(packet.participantIdentity, rpcRequest.id, rpcRequest.method, rpcRequest.payload, rpcRequest.responseTimeoutMs, rpcRequest.version);
19266
+ break;
19267
+ case 'rpcResponse':
19268
+ let rpcResponse = packet.value.value;
19269
+ let payload = null;
19270
+ let error = null;
19271
+ if (rpcResponse.value.case === 'payload') {
19272
+ payload = rpcResponse.value.value;
19273
+ } else if (rpcResponse.value.case === 'error') {
19274
+ error = RpcError.fromProto(rpcResponse.value.value);
19275
+ }
19276
+ this.handleIncomingRpcResponse(rpcResponse.requestId, payload, error);
19277
+ break;
19278
+ case 'rpcAck':
19279
+ let rpcAck = packet.value.value;
19280
+ this.handleIncomingRpcAck(rpcAck.requestId);
19281
+ break;
19282
+ }
19283
+ };
19136
19284
  this.updateTrackSubscriptionPermissions = () => {
19137
19285
  this.log.debug('updating track subscription permissions', Object.assign(Object.assign({}, this.logContext), {
19138
19286
  allParticipantsAllowed: this.allParticipantsAllowedToSubscribe,
@@ -19320,7 +19468,7 @@ class LocalParticipant extends Participant {
19320
19468
  pub.unmute();
19321
19469
  }
19322
19470
  });
19323
- this.engine.on(EngineEvent.Connected, this.handleReconnected).on(EngineEvent.SignalRestarted, this.handleReconnected).on(EngineEvent.SignalResumed, this.handleReconnected).on(EngineEvent.Restarting, this.handleReconnecting).on(EngineEvent.Resuming, this.handleReconnecting).on(EngineEvent.LocalTrackUnpublished, this.handleLocalTrackUnpublished).on(EngineEvent.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate).on(EngineEvent.Disconnected, this.handleDisconnected).on(EngineEvent.SignalRequestResponse, this.handleSignalRequestResponse);
19471
+ this.engine.on(EngineEvent.Connected, this.handleReconnected).on(EngineEvent.SignalRestarted, this.handleReconnected).on(EngineEvent.SignalResumed, this.handleReconnected).on(EngineEvent.Restarting, this.handleReconnecting).on(EngineEvent.Resuming, this.handleReconnecting).on(EngineEvent.LocalTrackUnpublished, this.handleLocalTrackUnpublished).on(EngineEvent.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate).on(EngineEvent.Disconnected, this.handleDisconnected).on(EngineEvent.SignalRequestResponse, this.handleSignalRequestResponse).on(EngineEvent.DataPacketReceived, this.handleDataPacket);
19324
19472
  }
19325
19473
  /**
19326
19474
  * Sets and updates the metadata of the local participant.
@@ -20309,6 +20457,108 @@ class LocalParticipant extends Participant {
20309
20457
  return msg;
20310
20458
  });
20311
20459
  }
20460
+ /**
20461
+ * Initiate an RPC call to a remote participant
20462
+ * @param params - Parameters for initiating the RPC call, see {@link PerformRpcParams}
20463
+ * @returns A promise that resolves with the response payload or rejects with an error.
20464
+ * @throws Error on failure. Details in `message`.
20465
+ */
20466
+ performRpc(_a) {
20467
+ return __awaiter(this, arguments, void 0, function (_ref3) {
20468
+ var _this5 = this;
20469
+ let {
20470
+ destinationIdentity,
20471
+ method,
20472
+ payload,
20473
+ responseTimeout = 10000
20474
+ } = _ref3;
20475
+ return function* () {
20476
+ const maxRoundTripLatency = 2000;
20477
+ return new Promise((resolve, reject) => __awaiter(_this5, void 0, void 0, function* () {
20478
+ var _a, _b, _c, _d;
20479
+ if (byteLength(payload) > MAX_PAYLOAD_BYTES) {
20480
+ reject(RpcError.builtIn('REQUEST_PAYLOAD_TOO_LARGE'));
20481
+ return;
20482
+ }
20483
+ if (((_b = (_a = this.engine.latestJoinResponse) === null || _a === void 0 ? void 0 : _a.serverInfo) === null || _b === void 0 ? void 0 : _b.version) && compareVersions((_d = (_c = this.engine.latestJoinResponse) === null || _c === void 0 ? void 0 : _c.serverInfo) === null || _d === void 0 ? void 0 : _d.version, '1.8.0') < 0) {
20484
+ reject(RpcError.builtIn('UNSUPPORTED_SERVER'));
20485
+ return;
20486
+ }
20487
+ const id = crypto.randomUUID();
20488
+ yield this.publishRpcRequest(destinationIdentity, id, method, payload, responseTimeout - maxRoundTripLatency);
20489
+ const ackTimeoutId = setTimeout(() => {
20490
+ this.pendingAcks.delete(id);
20491
+ reject(RpcError.builtIn('CONNECTION_TIMEOUT'));
20492
+ this.pendingResponses.delete(id);
20493
+ clearTimeout(responseTimeoutId);
20494
+ }, maxRoundTripLatency);
20495
+ this.pendingAcks.set(id, {
20496
+ resolve: () => {
20497
+ clearTimeout(ackTimeoutId);
20498
+ },
20499
+ participantIdentity: destinationIdentity
20500
+ });
20501
+ const responseTimeoutId = setTimeout(() => {
20502
+ this.pendingResponses.delete(id);
20503
+ reject(RpcError.builtIn('RESPONSE_TIMEOUT'));
20504
+ }, responseTimeout);
20505
+ this.pendingResponses.set(id, {
20506
+ resolve: (responsePayload, responseError) => {
20507
+ clearTimeout(responseTimeoutId);
20508
+ if (this.pendingAcks.has(id)) {
20509
+ console.warn('RPC response received before ack', id);
20510
+ this.pendingAcks.delete(id);
20511
+ clearTimeout(ackTimeoutId);
20512
+ }
20513
+ if (responseError) {
20514
+ reject(responseError);
20515
+ } else {
20516
+ resolve(responsePayload !== null && responsePayload !== void 0 ? responsePayload : '');
20517
+ }
20518
+ },
20519
+ participantIdentity: destinationIdentity
20520
+ });
20521
+ }));
20522
+ }();
20523
+ });
20524
+ }
20525
+ /**
20526
+ * Establishes the participant as a receiver for calls of the specified RPC method.
20527
+ * Will overwrite any existing callback for the same method.
20528
+ *
20529
+ * @param method - The name of the indicated RPC method
20530
+ * @param handler - Will be invoked when an RPC request for this method is received
20531
+ * @returns A promise that resolves when the method is successfully registered
20532
+ *
20533
+ * @example
20534
+ * ```typescript
20535
+ * room.localParticipant?.registerRpcMethod(
20536
+ * 'greet',
20537
+ * async (data: RpcInvocationData) => {
20538
+ * console.log(`Received greeting from ${data.callerIdentity}: ${data.payload}`);
20539
+ * return `Hello, ${data.callerIdentity}!`;
20540
+ * }
20541
+ * );
20542
+ * ```
20543
+ *
20544
+ * The handler should return a Promise that resolves to a string.
20545
+ * If unable to respond within `responseTimeout`, the request will result in an error on the caller's side.
20546
+ *
20547
+ * You may throw errors of type `RpcError` with a string `message` in the handler,
20548
+ * and they will be received on the caller's side with the message intact.
20549
+ * Other errors thrown in your handler will not be transmitted as-is, and will instead arrive to the caller as `1500` ("Application Error").
20550
+ */
20551
+ registerRpcMethod(method, handler) {
20552
+ this.rpcHandlers.set(method, handler);
20553
+ }
20554
+ /**
20555
+ * Unregisters a previously registered RPC method.
20556
+ *
20557
+ * @param method - The name of the RPC method to unregister
20558
+ */
20559
+ unregisterRpcMethod(method) {
20560
+ this.rpcHandlers.delete(method);
20561
+ }
20312
20562
  /**
20313
20563
  * Control who can subscribe to LocalParticipant's published tracks.
20314
20564
  *
@@ -20334,6 +20584,140 @@ class LocalParticipant extends Participant {
20334
20584
  this.updateTrackSubscriptionPermissions();
20335
20585
  }
20336
20586
  }
20587
+ handleIncomingRpcAck(requestId) {
20588
+ const handler = this.pendingAcks.get(requestId);
20589
+ if (handler) {
20590
+ handler.resolve();
20591
+ this.pendingAcks.delete(requestId);
20592
+ } else {
20593
+ console.error('Ack received for unexpected RPC request', requestId);
20594
+ }
20595
+ }
20596
+ handleIncomingRpcResponse(requestId, payload, error) {
20597
+ const handler = this.pendingResponses.get(requestId);
20598
+ if (handler) {
20599
+ handler.resolve(payload, error);
20600
+ this.pendingResponses.delete(requestId);
20601
+ } else {
20602
+ console.error('Response received for unexpected RPC request', requestId);
20603
+ }
20604
+ }
20605
+ handleIncomingRpcRequest(callerIdentity, requestId, method, payload, responseTimeout, version) {
20606
+ return __awaiter(this, void 0, void 0, function* () {
20607
+ yield this.publishRpcAck(callerIdentity, requestId);
20608
+ if (version !== 1) {
20609
+ yield this.publishRpcResponse(callerIdentity, requestId, null, RpcError.builtIn('UNSUPPORTED_VERSION'));
20610
+ return;
20611
+ }
20612
+ const handler = this.rpcHandlers.get(method);
20613
+ if (!handler) {
20614
+ yield this.publishRpcResponse(callerIdentity, requestId, null, RpcError.builtIn('UNSUPPORTED_METHOD'));
20615
+ return;
20616
+ }
20617
+ let responseError = null;
20618
+ let responsePayload = null;
20619
+ try {
20620
+ const response = yield handler({
20621
+ requestId,
20622
+ callerIdentity,
20623
+ payload,
20624
+ responseTimeout
20625
+ });
20626
+ if (byteLength(response) > MAX_PAYLOAD_BYTES) {
20627
+ responseError = RpcError.builtIn('RESPONSE_PAYLOAD_TOO_LARGE');
20628
+ console.warn("RPC Response payload too large for ".concat(method));
20629
+ } else {
20630
+ responsePayload = response;
20631
+ }
20632
+ } catch (error) {
20633
+ if (error instanceof RpcError) {
20634
+ responseError = error;
20635
+ } else {
20636
+ console.warn("Uncaught error returned by RPC handler for ".concat(method, ". Returning APPLICATION_ERROR instead."), error);
20637
+ responseError = RpcError.builtIn('APPLICATION_ERROR');
20638
+ }
20639
+ }
20640
+ yield this.publishRpcResponse(callerIdentity, requestId, responsePayload, responseError);
20641
+ });
20642
+ }
20643
+ /** @internal */
20644
+ publishRpcRequest(destinationIdentity, requestId, method, payload, responseTimeout) {
20645
+ return __awaiter(this, void 0, void 0, function* () {
20646
+ const packet = new DataPacket({
20647
+ destinationIdentities: [destinationIdentity],
20648
+ kind: DataPacket_Kind.RELIABLE,
20649
+ value: {
20650
+ case: 'rpcRequest',
20651
+ value: new RpcRequest({
20652
+ id: requestId,
20653
+ method,
20654
+ payload,
20655
+ responseTimeoutMs: responseTimeout,
20656
+ version: 1
20657
+ })
20658
+ }
20659
+ });
20660
+ yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
20661
+ });
20662
+ }
20663
+ /** @internal */
20664
+ publishRpcResponse(destinationIdentity, requestId, payload, error) {
20665
+ return __awaiter(this, void 0, void 0, function* () {
20666
+ const packet = new DataPacket({
20667
+ destinationIdentities: [destinationIdentity],
20668
+ kind: DataPacket_Kind.RELIABLE,
20669
+ value: {
20670
+ case: 'rpcResponse',
20671
+ value: new RpcResponse({
20672
+ requestId,
20673
+ value: error ? {
20674
+ case: 'error',
20675
+ value: error.toProto()
20676
+ } : {
20677
+ case: 'payload',
20678
+ value: payload !== null && payload !== void 0 ? payload : ''
20679
+ }
20680
+ })
20681
+ }
20682
+ });
20683
+ yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
20684
+ });
20685
+ }
20686
+ /** @internal */
20687
+ publishRpcAck(destinationIdentity, requestId) {
20688
+ return __awaiter(this, void 0, void 0, function* () {
20689
+ const packet = new DataPacket({
20690
+ destinationIdentities: [destinationIdentity],
20691
+ kind: DataPacket_Kind.RELIABLE,
20692
+ value: {
20693
+ case: 'rpcAck',
20694
+ value: new RpcAck({
20695
+ requestId
20696
+ })
20697
+ }
20698
+ });
20699
+ yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
20700
+ });
20701
+ }
20702
+ /** @internal */
20703
+ handleParticipantDisconnected(participantIdentity) {
20704
+ for (const [id, {
20705
+ participantIdentity: pendingIdentity
20706
+ }] of this.pendingAcks) {
20707
+ if (pendingIdentity === participantIdentity) {
20708
+ this.pendingAcks.delete(id);
20709
+ }
20710
+ }
20711
+ for (const [id, {
20712
+ participantIdentity: pendingIdentity,
20713
+ resolve
20714
+ }] of this.pendingResponses) {
20715
+ if (pendingIdentity === participantIdentity) {
20716
+ resolve(null, RpcError.builtIn('RECIPIENT_DISCONNECTED'));
20717
+ this.pendingResponses.delete(id);
20718
+ }
20719
+ }
20720
+ }
20337
20721
  /** @internal */
20338
20722
  setEnabledPublishCodecs(codecs) {
20339
20723
  this.enabledPublishVideoCodecs = codecs.filter(c => c.mime.split('/')[0].toLowerCase() === 'video');
@@ -20388,8 +20772,8 @@ class LocalParticipant extends Participant {
20388
20772
  }
20389
20773
  waitForPendingPublicationOfSource(source) {
20390
20774
  return __awaiter(this, void 0, void 0, function* () {
20391
- const publishPromiseEntry = Array.from(this.pendingPublishPromises.entries()).find(_ref3 => {
20392
- let [pendingTrack] = _ref3;
20775
+ const publishPromiseEntry = Array.from(this.pendingPublishPromises.entries()).find(_ref4 => {
20776
+ let [pendingTrack] = _ref4;
20393
20777
  return pendingTrack.source === source;
20394
20778
  });
20395
20779
  if (publishPromiseEntry) {
@@ -21118,7 +21502,7 @@ class Room extends eventsExports.EventEmitter {
21118
21502
  } catch (err) {
21119
21503
  yield this.engine.close();
21120
21504
  this.recreateEngine();
21121
- const resultingError = new ConnectionError("could not establish signal connection");
21505
+ const resultingError = new ConnectionError("could not establish signal connection", ConnectionErrorReason.ServerUnreachable);
21122
21506
  if (err instanceof Error) {
21123
21507
  resultingError.message = "".concat(resultingError.message, ": ").concat(err.message);
21124
21508
  }
@@ -21134,7 +21518,7 @@ class Room extends eventsExports.EventEmitter {
21134
21518
  if (abortController.signal.aborted) {
21135
21519
  yield this.engine.close();
21136
21520
  this.recreateEngine();
21137
- throw new ConnectionError("Connection attempt aborted");
21521
+ throw new ConnectionError("Connection attempt aborted", ConnectionErrorReason.Cancelled);
21138
21522
  }
21139
21523
  try {
21140
21524
  yield this.engine.waitForPCInitialConnection(this.connOptions.peerConnectionTimeout, abortController);
@@ -21181,7 +21565,7 @@ class Room extends eventsExports.EventEmitter {
21181
21565
  _this2.log.warn('abort connection attempt', _this2.logContext);
21182
21566
  (_a = _this2.abortController) === null || _a === void 0 ? void 0 : _a.abort();
21183
21567
  // in case the abort controller didn't manage to cancel the connection attempt, reject the connect promise explicitly
21184
- (_c = (_b = _this2.connectFuture) === null || _b === void 0 ? void 0 : _b.reject) === null || _c === void 0 ? void 0 : _c.call(_b, new ConnectionError('Client initiated disconnect'));
21568
+ (_c = (_b = _this2.connectFuture) === null || _b === void 0 ? void 0 : _b.reject) === null || _c === void 0 ? void 0 : _c.call(_b, new ConnectionError('Client initiated disconnect', ConnectionErrorReason.Cancelled));
21185
21569
  _this2.connectFuture = undefined;
21186
21570
  }
21187
21571
  // send leave
@@ -22210,6 +22594,7 @@ class Room extends eventsExports.EventEmitter {
22210
22594
  }
22211
22595
  }
22212
22596
  handleParticipantDisconnected(identity, participant) {
22597
+ var _a;
22213
22598
  // remove and send event
22214
22599
  this.remoteParticipants.delete(identity);
22215
22600
  if (!participant) {
@@ -22219,6 +22604,7 @@ class Room extends eventsExports.EventEmitter {
22219
22604
  participant.unpublishTrack(publication.trackSid, true);
22220
22605
  });
22221
22606
  this.emit(RoomEvent.ParticipantDisconnected, participant);
22607
+ (_a = this.localParticipant) === null || _a === void 0 ? void 0 : _a.handleParticipantDisconnected(participant.identity);
22222
22608
  }
22223
22609
  acquireAudioContext() {
22224
22610
  return __awaiter(this, void 0, void 0, function* () {
@@ -23096,5 +23482,5 @@ function isFacingModeValue(item) {
23096
23482
  return item === undefined || allowedValues.includes(item);
23097
23483
  }
23098
23484
 
23099
- export { AudioPresets, BaseKeyProvider, CheckStatus, Checker, ConnectionCheck, ConnectionError, ConnectionErrorReason, ConnectionQuality, ConnectionState, CriticalTimers, CryptorError, CryptorErrorReason, CryptorEvent, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, LoggerNames, MediaDeviceFailure, h as Mutex, NegotiationError, Participant, ParticipantEvent, ParticipantInfo_Kind as ParticipantKind, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, compareVersions, createAudioAnalyser, createE2EEKey, createKeyMaterialFromBuffer, createKeyMaterialFromString, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, deriveKeys, detachTrack, extractProcessorsFromOptions, facingModeFromDeviceLabel, facingModeFromLocalTrack, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, importKey, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isScriptTransformSupported, isVideoFrame, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
23485
+ export { AudioPresets, BaseKeyProvider, CheckStatus, Checker, ConnectionCheck, ConnectionError, ConnectionErrorReason, ConnectionQuality, ConnectionState, CriticalTimers, CryptorError, CryptorErrorReason, CryptorEvent, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, LoggerNames, MediaDeviceFailure, h as Mutex, NegotiationError, Participant, ParticipantEvent, ParticipantInfo_Kind as ParticipantKind, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RpcError, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, compareVersions, createAudioAnalyser, createE2EEKey, createKeyMaterialFromBuffer, createKeyMaterialFromString, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, deriveKeys, detachTrack, extractProcessorsFromOptions, facingModeFromDeviceLabel, facingModeFromLocalTrack, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, importKey, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isScriptTransformSupported, isVideoFrame, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
23100
23486
  //# sourceMappingURL=livekit-client.esm.mjs.map