livekit-client 2.20.1 → 2.21.0

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 (156) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +37 -7
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +2279 -651
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.fm.worker.js +1 -1
  8. package/dist/livekit-client.fm.worker.js.map +1 -1
  9. package/dist/livekit-client.fm.worker.mjs +37 -7
  10. package/dist/livekit-client.fm.worker.mjs.map +1 -1
  11. package/dist/livekit-client.umd.js +1 -1
  12. package/dist/livekit-client.umd.js.map +1 -1
  13. package/dist/src/api/SignalClient.d.ts.map +1 -1
  14. package/dist/src/index.d.ts +1 -1
  15. package/dist/src/index.d.ts.map +1 -1
  16. package/dist/src/logger.d.ts +3 -1
  17. package/dist/src/logger.d.ts.map +1 -1
  18. package/dist/src/room/PCTransport.d.ts +2 -0
  19. package/dist/src/room/PCTransport.d.ts.map +1 -1
  20. package/dist/src/room/PCTransportManager.d.ts +1 -0
  21. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  22. package/dist/src/room/RTCEngine.d.ts +31 -26
  23. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  24. package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
  25. package/dist/src/room/Room.d.ts +4 -0
  26. package/dist/src/room/Room.d.ts.map +1 -1
  27. package/dist/src/room/data-channel/DataChannelManager.d.ts +62 -0
  28. package/dist/src/room/data-channel/DataChannelManager.d.ts.map +1 -0
  29. package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts +95 -0
  30. package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts.map +1 -0
  31. package/dist/src/room/data-channel/LossyDataChannel.d.ts +40 -0
  32. package/dist/src/room/data-channel/LossyDataChannel.d.ts.map +1 -0
  33. package/dist/src/room/data-channel/ReliableDataChannel.d.ts +56 -0
  34. package/dist/src/room/data-channel/ReliableDataChannel.d.ts.map +1 -0
  35. package/dist/src/room/data-channel/types.d.ts +12 -0
  36. package/dist/src/room/data-channel/types.d.ts.map +1 -0
  37. package/dist/src/room/data-stream/compression.d.ts +49 -0
  38. package/dist/src/room/data-stream/compression.d.ts.map +1 -0
  39. package/dist/src/room/data-stream/constants.d.ts +18 -0
  40. package/dist/src/room/data-stream/constants.d.ts.map +1 -0
  41. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +4 -0
  42. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
  43. package/dist/src/room/data-stream/incoming/StreamReader.d.ts.map +1 -1
  44. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +49 -2
  45. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
  46. package/dist/src/room/data-stream/outgoing/header-utils.d.ts +18 -0
  47. package/dist/src/room/data-stream/outgoing/header-utils.d.ts.map +1 -0
  48. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +7 -0
  49. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
  50. package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
  51. package/dist/src/room/errors.d.ts +3 -1
  52. package/dist/src/room/errors.d.ts.map +1 -1
  53. package/dist/src/room/participant/LocalParticipant.d.ts +9 -1
  54. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  55. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  56. package/dist/src/room/participant/RemoteParticipant.d.ts +11 -2
  57. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  58. package/dist/src/room/participant/publishUtils.d.ts +7 -0
  59. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  60. package/dist/src/room/rpc/client/RpcClientManager.d.ts.map +1 -1
  61. package/dist/src/room/rpc/server/RpcServerManager.d.ts.map +1 -1
  62. package/dist/src/room/track/LocalAudioTrack.d.ts +1 -0
  63. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  64. package/dist/src/room/track/Track.d.ts.map +1 -1
  65. package/dist/src/room/track/options.d.ts +11 -1
  66. package/dist/src/room/track/options.d.ts.map +1 -1
  67. package/dist/src/room/types.d.ts +17 -0
  68. package/dist/src/room/types.d.ts.map +1 -1
  69. package/dist/src/room/utils.d.ts +13 -0
  70. package/dist/src/room/utils.d.ts.map +1 -1
  71. package/dist/src/test/signalToken.d.ts +26 -0
  72. package/dist/src/test/signalToken.d.ts.map +1 -0
  73. package/dist/src/utils/dataPacketBuffer.d.ts +18 -0
  74. package/dist/src/utils/dataPacketBuffer.d.ts.map +1 -1
  75. package/dist/src/version.d.ts +5 -1
  76. package/dist/src/version.d.ts.map +1 -1
  77. package/dist/ts4.2/index.d.ts +1 -1
  78. package/dist/ts4.2/logger.d.ts +3 -1
  79. package/dist/ts4.2/room/PCTransport.d.ts +2 -0
  80. package/dist/ts4.2/room/PCTransportManager.d.ts +1 -0
  81. package/dist/ts4.2/room/RTCEngine.d.ts +31 -26
  82. package/dist/ts4.2/room/Room.d.ts +4 -0
  83. package/dist/ts4.2/room/data-channel/DataChannelManager.d.ts +62 -0
  84. package/dist/ts4.2/room/data-channel/FlowControlledDataChannel.d.ts +95 -0
  85. package/dist/ts4.2/room/data-channel/LossyDataChannel.d.ts +41 -0
  86. package/dist/ts4.2/room/data-channel/ReliableDataChannel.d.ts +57 -0
  87. package/dist/ts4.2/room/data-channel/types.d.ts +12 -0
  88. package/dist/ts4.2/room/data-stream/compression.d.ts +49 -0
  89. package/dist/ts4.2/room/data-stream/constants.d.ts +18 -0
  90. package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +4 -0
  91. package/dist/ts4.2/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +49 -2
  92. package/dist/ts4.2/room/data-stream/outgoing/header-utils.d.ts +18 -0
  93. package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +7 -0
  94. package/dist/ts4.2/room/errors.d.ts +3 -1
  95. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +9 -1
  96. package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +11 -1
  97. package/dist/ts4.2/room/participant/publishUtils.d.ts +7 -0
  98. package/dist/ts4.2/room/track/LocalAudioTrack.d.ts +1 -0
  99. package/dist/ts4.2/room/track/options.d.ts +11 -1
  100. package/dist/ts4.2/room/types.d.ts +17 -0
  101. package/dist/ts4.2/room/utils.d.ts +13 -0
  102. package/dist/ts4.2/test/signalToken.d.ts +26 -0
  103. package/dist/ts4.2/utils/dataPacketBuffer.d.ts +18 -0
  104. package/dist/ts4.2/version.d.ts +5 -1
  105. package/package.json +8 -4
  106. package/src/api/SignalClient.e2e.test.ts +264 -0
  107. package/src/api/SignalClient.ts +34 -2
  108. package/src/index.ts +2 -0
  109. package/src/logger.ts +2 -0
  110. package/src/room/PCTransport.ts +66 -31
  111. package/src/room/PCTransportManager.ts +10 -6
  112. package/src/room/RTCEngine.test.ts +238 -6
  113. package/src/room/RTCEngine.ts +101 -338
  114. package/src/room/RegionUrlProvider.ts +4 -2
  115. package/src/room/Room.test.ts +4 -1
  116. package/src/room/Room.ts +47 -9
  117. package/src/room/data-channel/DataChannelManager.test.ts +152 -0
  118. package/src/room/data-channel/DataChannelManager.ts +237 -0
  119. package/src/room/data-channel/FlowControlledDataChannel.test.ts +154 -0
  120. package/src/room/data-channel/FlowControlledDataChannel.ts +216 -0
  121. package/src/room/data-channel/LossyDataChannel.test.ts +118 -0
  122. package/src/room/data-channel/LossyDataChannel.ts +125 -0
  123. package/src/room/data-channel/ReliableDataChannel.test.ts +158 -0
  124. package/src/room/data-channel/ReliableDataChannel.ts +153 -0
  125. package/src/room/data-channel/types.ts +30 -0
  126. package/src/room/data-stream/compression.ts +117 -0
  127. package/src/room/data-stream/constants.ts +18 -0
  128. package/src/room/data-stream/incoming/IncomingDataStreamManager.test.ts +1601 -0
  129. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +438 -86
  130. package/src/room/data-stream/incoming/StreamReader.ts +8 -2
  131. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.test.ts +1292 -0
  132. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +489 -116
  133. package/src/room/data-stream/outgoing/header-utils.ts +87 -0
  134. package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +303 -0
  135. package/src/room/data-track/incoming/IncomingDataTrackManager.ts +70 -8
  136. package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +2 -0
  137. package/src/room/errors.ts +10 -4
  138. package/src/room/participant/LocalParticipant.ts +29 -6
  139. package/src/room/participant/Participant.ts +5 -0
  140. package/src/room/participant/RemoteParticipant.ts +19 -5
  141. package/src/room/participant/publishUtils.ts +14 -11
  142. package/src/room/rpc/client/RpcClientManager.test.ts +20 -27
  143. package/src/room/rpc/client/RpcClientManager.ts +1 -4
  144. package/src/room/rpc/server/RpcServerManager.test.ts +24 -23
  145. package/src/room/rpc/server/RpcServerManager.ts +1 -3
  146. package/src/room/track/LocalAudioTrack.ts +16 -0
  147. package/src/room/track/Track.ts +10 -6
  148. package/src/room/track/options.ts +11 -1
  149. package/src/room/types.ts +18 -0
  150. package/src/room/utils.ts +67 -0
  151. package/src/test/signalServerSetup.ts +151 -0
  152. package/src/test/signalToken.ts +59 -0
  153. package/src/test/vitest-context.d.ts +10 -0
  154. package/src/utils/dataPacketBuffer.test.ts +100 -0
  155. package/src/utils/dataPacketBuffer.ts +46 -1
  156. package/src/version.ts +5 -1
@@ -0,0 +1,41 @@
1
+ import type { NonSharedUint8Array } from '../../type-polyfills/non-shared-typed-arrays';
2
+ import type { FlowControlledDataChannelOptions } from './FlowControlledDataChannel';
3
+ import { FlowControlledDataChannel } from './FlowControlledDataChannel';
4
+ export interface LossyDataChannelOptions extends FlowControlledDataChannelOptions {
5
+ /**
6
+ * What to do with a send while the buffer is full: `drop` discards it to keep latency bounded
7
+ * (the classic lossy channel), `wait` backpressures the producer until there is headroom (the
8
+ * data-track channel, whose producer decides what to skip at frame granularity).
9
+ */
10
+ bufferFullBehavior: 'drop' | 'wait';
11
+ /** Sends are silently discarded while this is true (a reconnect attempt is underway). */
12
+ shouldSkipSends: () => boolean;
13
+ }
14
+ /**
15
+ * A lossy channel: flow control plus a per-instance full-buffer policy.
16
+ *
17
+ * Each instance owns its own byterate stat, drop counter, and (when tuning is started) the
18
+ * dynamic `bufferedAmountLowThreshold` adjustment that keeps the drop gate at roughly 100ms of
19
+ * buffered latency. Keeping these per instance is what prevents one channel's traffic from
20
+ * steering another channel's policy.
21
+ */
22
+ export declare class LossyDataChannel extends FlowControlledDataChannel {
23
+ private bufferFullBehavior;
24
+ private shouldSkipSends;
25
+ private statCurrentBytes;
26
+ private statByterate;
27
+ private statInterval;
28
+ private dropCount;
29
+ constructor(opts: LossyDataChannelOptions);
30
+ /** Sends prepared bytes with this channel's full-buffer policy (drop or wait). */
31
+ send(msg: NonSharedUint8Array): Promise<void>;
32
+ /**
33
+ * Starts the once-per-second adjustment of the channel's `bufferedAmountLowThreshold` to the
34
+ * observed byterate, keeping the drop gate at roughly 100ms of buffered latency (clamped to
35
+ * the watermarks). Restarts cleanly if already running.
36
+ */
37
+ startThresholdTuning(): void;
38
+ /** Stops the threshold tuning and resets the stats and drop counter. */
39
+ stopThresholdTuning(): void;
40
+ }
41
+ //# sourceMappingURL=LossyDataChannel.d.ts.map
@@ -0,0 +1,57 @@
1
+ import type { NonSharedUint8Array } from '../../type-polyfills/non-shared-typed-arrays';
2
+ import type { FlowControlledDataChannelOptions } from './FlowControlledDataChannel';
3
+ import { FlowControlledDataChannel } from './FlowControlledDataChannel';
4
+ export interface ReliableDataChannelOptions extends FlowControlledDataChannelOptions {
5
+ /**
6
+ * Whether sends should currently be deferred to the resume replay instead of hitting the wire
7
+ * (i.e. a reconnect attempt is underway). Read at send time so the reliable channel matches the
8
+ * engine's reconnect state without owning it.
9
+ */
10
+ isDeferringSends: () => boolean;
11
+ }
12
+ /**
13
+ * The reliable channel: flow control plus delivery-across-resume semantics.
14
+ *
15
+ * Every packet gets a monotonic sequence (stamped into the protobuf by the caller before
16
+ * serialization, via {@link nextSequence}) and is retained in a replay buffer until the channel's
17
+ * `bufferedAmount` confirms it has been handed to the transport. Sends that land in a reconnect
18
+ * window — or whose headroom wait is torn down transiently — are queued unsent and resolve;
19
+ * {@link replay} delivers them (plus any unacked packets) after a resume. Only an engine close
20
+ * rejects, because no replay is coming after that.
21
+ */
22
+ export declare class ReliableDataChannel extends FlowControlledDataChannel {
23
+ private messageBuffer;
24
+ private sequence;
25
+ private isDeferringSends;
26
+ constructor(opts: ReliableDataChannelOptions);
27
+ /**
28
+ * Claims the next packet sequence. The caller stamps it into the packet before serialization,
29
+ * then passes it back to {@link send} so the replay buffer stays keyed by wire sequence.
30
+ */
31
+ nextSequence(): number;
32
+ /**
33
+ * Sends prepared bytes with reliable semantics. Resolves once the packet has either been handed
34
+ * to the channel or queued for the resume replay; throws only when the engine is closed.
35
+ */
36
+ send(msg: NonSharedUint8Array, sequence: number): Promise<void>;
37
+ /**
38
+ * Replays the buffered backlog after a resume: drops everything the server acked
39
+ * (`lastMessageSeq`), then re-sends the rest in order. The headroom lock is held across the
40
+ * whole replay — releasing it between messages would let a concurrent send (whose newer
41
+ * sequence was already assigned before it queued on the lock) hit the wire mid-replay, and
42
+ * receivers would then discard the remaining lower-sequence resent messages as duplicates.
43
+ */
44
+ replay(lastMessageSeq: number): Promise<void>;
45
+ /**
46
+ * Before recomputing status, trim packets the transport has now delivered — a send or a drain
47
+ * may have acked buffered packets, and the replay buffer is keyed off the channel's buffered
48
+ * bytes.
49
+ */
50
+ refreshBufferStatus(): void;
51
+ /**
52
+ * Drops all replay state and restarts sequencing. Only valid on a full reconnect, where the
53
+ * session (and the receivers' sequence tracking) starts over.
54
+ */
55
+ reset(): void;
56
+ }
57
+ //# sourceMappingURL=ReliableDataChannel.d.ts.map
@@ -0,0 +1,12 @@
1
+ export declare enum DataChannelKind {
2
+ RELIABLE = 0,
3
+ LOSSY = 1,
4
+ DATA_TRACK_LOSSY = 2
5
+ }
6
+ export declare const reliableDataChannelWaterMarkLow: number;
7
+ export declare const reliableDataChannelWaterMarkHigh: number;
8
+ export declare const lossyDataChannelWaterMarkLow: number;
9
+ export declare const lossyDataChannelWaterMarkHigh: number;
10
+ export declare function dataChannelLowWaterMark(kind: DataChannelKind): number;
11
+ export declare function dataChannelHighWaterMark(kind: DataChannelKind): number;
12
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,49 @@
1
+ /*
2
+ * Compression helpers for data streams. The buffered deflate-raw variant ({@link deflateRawCompress})
3
+ * is for the inline (single-packet) case where the payload is small and bounded;
4
+ * {@link deflateRawTransform} / {@link inflateRawTransform} serve the chunked (multi-packet)
5
+ * `sendText`/`sendBytes`/`sendFile` paths, streaming the bytes through without buffering the whole
6
+ * payload.
7
+ *
8
+ * These operate on bytes (not strings) so a single set of helpers serves both text and byte streams;
9
+ * the `TextEncoder`/`TextDecoder` boundary lives at the manager/reader edges.
10
+ *
11
+ * Both streaming variants are exposed as `ReadableWritablePair`s so they drop straight into a
12
+ * `pipeThrough` chain. Each needs one localized cast to bridge a DOM lib-type mismatch: the platform
13
+ * `CompressionStream`/`DecompressionStream` type their `writable` as `WritableStream<BufferSource>`
14
+ * (a wider element type than `Uint8Array`), and `WritableStream<W>` is covariant in `W`, so neither
15
+ * is structurally a `ReadableWritablePair<Uint8Array, Uint8Array>` without help.
16
+ *
17
+ * @internal
18
+ */
19
+ import type { NonSharedUint8Array } from '../../type-polyfills/non-shared-typed-arrays';
20
+ /**
21
+ * A `deflate-raw` compression transform (inverse of {@link inflateRawTransform}): pipe a byte stream
22
+ * through it to get the compressed bytes without buffering the whole payload. Used for the chunked
23
+ * `sendText`/`sendBytes`/`sendFile` paths, where the full payload is known up front but is streamed
24
+ * (e.g. from `file.stream()`) rather than held in memory.
25
+ */
26
+ export declare function deflateRawTransform(): ReadableWritablePair<NonSharedUint8Array, NonSharedUint8Array>;
27
+ /**
28
+ * A `deflate-raw` decompression transform (inverse of {@link deflateRawTransform}): pipe a
29
+ * stream of compressed bytes through it to get the decompressed bytes. Inflate emits output greedily,
30
+ * so as long as the sender flushed at write boundaries each write's content is produced as soon as
31
+ * its compressed bytes arrive.
32
+ */
33
+ export declare function inflateRawTransform(): ReadableWritablePair<NonSharedUint8Array, NonSharedUint8Array>;
34
+ /** deflate-raw compresses a byte array in full. Use for inline payloads; prefer the streaming
35
+ * path for the chunked case. */
36
+ export declare function deflateRawCompress(data: NonSharedUint8Array): Promise<NonSharedUint8Array>;
37
+ /**
38
+ * Decompresses a raw-deflate byte array in full (inverse of {@link deflateRawCompress}).
39
+ * `maxByteLength` bounds the decompressed output (decompression-bomb guard); exceeding it rejects
40
+ * with a `PayloadTooLarge` error.
41
+ */
42
+ export declare function deflateRawDecompress(data: NonSharedUint8Array, maxByteLength?: number): Promise<NonSharedUint8Array>;
43
+ /**
44
+ * Drains a byte stream, concatenating all of its chunks into a single array. When
45
+ * `maxByteLength` is given, drops the stream and throws `PayloadTooLarge` as soon as the
46
+ * accumulated output exceeds it.
47
+ */
48
+ export declare function collect(stream: ReadableStream<NonSharedUint8Array>, maxByteLength?: number): Promise<NonSharedUint8Array>;
49
+ //# sourceMappingURL=compression.d.ts.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Maximum size of a single data-stream chunk in bytes, and the budget used to decide whether a
3
+ * payload can be sent inline as a single header packet. Kept below the ~16k data-channel MTU to
4
+ * leave headroom for protocol framing and E2EE overhead.
5
+ *
6
+ * @internal
7
+ */
8
+ export declare const STREAM_CHUNK_SIZE_BYTES = 15000;
9
+ /**
10
+ * Default cap on the number of decompressed bytes a single incoming compressed data stream may
11
+ * produce (5 GB). A tiny compressed payload can inflate to an arbitrarily large output
12
+ * (decompression bomb), so the decompressor's output is bounded rather than trusting the wire
13
+ * size; streams exceeding the cap error with `PayloadTooLarge`.
14
+ *
15
+ * @internal
16
+ */
17
+ export declare const DEFAULT_MAX_PAYLOAD_BYTE_LENGTH = 5000000000;
18
+ //# sourceMappingURL=constants.d.ts.map
@@ -3,6 +3,10 @@ import { Encryption_Type } from '@livekit/protocol';
3
3
  import type { ByteStreamHandler, TextStreamHandler } from './StreamReader';
4
4
  export default class IncomingDataStreamManager {
5
5
  private log;
6
+ /** Max number of decompressed bytes an incoming compressed stream may produce before it is
7
+ * errored (decompression-bomb guard). */
8
+ private maxPayloadByteLength;
9
+ constructor(maxPayloadByteLength?: number);
6
10
  private byteStreamControllers;
7
11
  private textStreamControllers;
8
12
  private byteStreamHandlers;
@@ -1,6 +1,7 @@
1
+ import { ClientInfo_Capability } from '@livekit/protocol';
1
2
  import type { StructuredLogger } from '../../../logger';
2
3
  import type RTCEngine from '../../RTCEngine';
3
- import type { SendFileOptions, SendTextOptions, StreamBytesOptions, StreamTextOptions, TextStreamInfo } from '../../types';
4
+ import type { ByteStreamInfo, SendBytesOptions, SendFileOptions, SendTextOptions, StreamBytesOptions, StreamTextOptions, TextStreamInfo } from '../../types';
4
5
  import { ByteStreamWriter, TextStreamWriter } from './StreamWriter';
5
6
  /**
6
7
  * Manages sending custom user data via data channels.
@@ -9,10 +10,49 @@ import { ByteStreamWriter, TextStreamWriter } from './StreamWriter';
9
10
  export default class OutgoingDataStreamManager {
10
11
  protected engine: RTCEngine;
11
12
  protected log: StructuredLogger;
12
- constructor(engine: RTCEngine, log: StructuredLogger);
13
+ /** Returns the advertised client protocol of a remote participant, used to decide whether a
14
+ * recipient can receive single-packet (inline) data streams. */
15
+ protected getRemoteParticipantClientProtocol: (identity: string) => number;
16
+ /** Returns the client capabilities a remote participant advertises, used to decide whether a
17
+ * recipient can decompress a deflate-raw compressed stream. */
18
+ protected getRemoteParticipantCapabilities: (identity: string) => Array<ClientInfo_Capability>;
19
+ /** Returns the identities of every remote participant currently in the room, used to decide
20
+ * whether a broadcast (no explicit destinations) can be sent inline. */
21
+ protected getAllRemoteParticipantIdentities: () => Array<string>;
22
+ constructor(engine: RTCEngine, log: StructuredLogger, getRemoteParticipantClientProtocol: (identity: string) => number, getRemoteParticipantCapabilities: (identity: string) => Array<ClientInfo_Capability>, getAllRemoteParticipantIdentities: () => Array<string>);
13
23
  setupEngine(engine: RTCEngine): void;
14
24
  /** {@inheritDoc LocalParticipant.sendText} */
15
25
  sendText(text: string, options?: SendTextOptions): Promise<TextStreamInfo>;
26
+ /**
27
+ * Sends a complete in-memory byte payload. Mirrors {@link sendText}'s semantics: when every
28
+ * recipient supports data streams v2 the payload rides inline in a single header packet
29
+ * (optionally deflate-raw compressed), otherwise it is sent as a (optionally compressed)
30
+ * chunked byte stream. Unlike {@link sendFile}, the whole payload is already in memory, so the
31
+ * inline single-packet fast path applies.
32
+ */
33
+ sendBytes(bytes: Uint8Array, options?: SendBytesOptions): Promise<ByteStreamInfo>;
34
+ /**
35
+ * Returns true only if every recipient is known to support data streams v2 (single-packet inline
36
+ * streams and compression). For a targeted send this checks the named destination identities; for
37
+ * a broadcast (no explicit destinations) it checks every remote participant currently in the room.
38
+ * An empty room (nobody to receive) is considered eligible.
39
+ */
40
+ private allRecipientsSupportV2;
41
+ /**
42
+ * Returns true only if every recipient advertises the deflate-raw compression capability (so it
43
+ * can decompress a compressed stream). Resolved the same way as {@link allRecipientsSupportV2}:
44
+ * named destinations, or every remote participant for a broadcast; an empty room is eligible.
45
+ */
46
+ private allRecipientsSupportCompression;
47
+ /**
48
+ * Shared chunked-stream send for `sendText`/`sendFile`: sends the prebuilt header packet, then
49
+ * forwards `source` (optionally deflate-raw compressed) as `streamChunk` packets re-chunked to
50
+ * the MTU budget with contiguous indices, then sends the trailer. The source is consumed
51
+ * incrementally, so a `file.stream()` is never buffered in full. The platform compressor can't
52
+ * flush mid-stream, so compression is only used when the whole payload is available as a stream
53
+ * up front (not for incremental writers like `streamText`/`streamBytes`).
54
+ */
55
+ private sendChunkedByteStream;
16
56
  /**
17
57
  * @internal
18
58
  */
@@ -20,6 +60,13 @@ export default class OutgoingDataStreamManager {
20
60
  sendFile(file: File, options?: SendFileOptions): Promise<{
21
61
  id: string;
22
62
  }>;
63
+ /**
64
+ * Streams a file as a chunked byte stream, compressed (deflate-raw) when the runtime supports it
65
+ * and every recipient is on data streams v2. The file is piped `file.stream()` →
66
+ * (`CompressionStream`) → chunk packets via {@link sendChunkedByteStream}, so it is never fully
67
+ * buffered in memory — unlike {@link sendBytes}, there is no inline single-packet fast path for
68
+ * files (the compressed size can't be known up front without buffering the whole file).
69
+ */
23
70
  private _sendFile;
24
71
  streamBytes(options?: StreamBytesOptions): Promise<ByteStreamWriter>;
25
72
  }
@@ -0,0 +1,18 @@
1
+ import { DataPacket, DataStream_CompressionType, DataStream_Header } from '@livekit/protocol';
2
+ import type { ByteStreamInfo, StreamTextOptions, TextStreamInfo } from '../../types';
3
+ /** The data-streams-v2 wire signals carried directly on the header: the compression flag and the
4
+ * inline single-packet payload. Both used to live in reserved header attributes; they are now
5
+ * first-class protobuf fields on `DataStream.Header`. */
6
+ export interface StreamHeaderV2Fields {
7
+ /** Compression applied to the inline/chunked payload. Defaults to `NONE` when omitted. */
8
+ compression?: DataStream_CompressionType;
9
+ /** The full payload smuggled into the header for single-packet (inline) sends. */
10
+ inlineContent?: Uint8Array;
11
+ }
12
+ /** Builds the `DataStream_Header` for a text stream from its info and stream options. */
13
+ export declare function buildTextStreamHeader(info: TextStreamInfo, options?: Pick<StreamTextOptions, 'version' | 'replyToStreamId' | 'type'>, v2?: StreamHeaderV2Fields): DataStream_Header;
14
+ /** Builds the `DataStream_Header` for a byte stream from its info. */
15
+ export declare function buildByteStreamHeader(info: ByteStreamInfo, v2?: StreamHeaderV2Fields): DataStream_Header;
16
+ /** Wraps a `DataStream_Header` in a `DataPacket` ready to be sent over a data channel. */
17
+ export declare function createStreamHeaderPacket(header: DataStream_Header, destinationIdentities?: Array<string>): DataPacket;
18
+ //# sourceMappingURL=header-utils.d.ts.map
@@ -91,6 +91,13 @@ export default class IncomingDataTrackManager extends IncomingDataTrackManager_b
91
91
  * @internal */
92
92
  queryPublications(): Promise<DataTrackInfo[]>;
93
93
  handleTrackPublished(publisherIdentity: Participant['identity'], info: DataTrackInfo): Promise<void>;
94
+ /**
95
+ * Detects and handles SID reassignment, which occurs when the publisher
96
+ * republishes its tracks after a full reconnect.
97
+ *
98
+ * Returns `true` if an SID reassignment occurred, `false` otherwise.
99
+ */
100
+ private handleSidReassigned;
94
101
  handleTrackUnpublished(sid: DataTrackSid): void;
95
102
  /** SFU notification that handles have been assigned for requested subscriptions. */
96
103
  receivedSfuSubscriberHandles(
@@ -135,7 +135,9 @@ export declare enum DataStreamErrorReason {
135
135
  LengthExceeded = 3,
136
136
  Incomplete = 4,
137
137
  HandlerAlreadyRegistered = 7,
138
- EncryptionTypeMismatch = 8
138
+ EncryptionTypeMismatch = 8,
139
+ HeaderTooLarge = 9,
140
+ PayloadTooLarge = 10
139
141
  }
140
142
  export declare class DataStreamError extends LivekitReasonedError<DataStreamErrorReason> {
141
143
  readonly name = "DataStreamError";
@@ -14,7 +14,7 @@ import LocalTrack from '../track/LocalTrack';
14
14
  import LocalTrackPublication from '../track/LocalTrackPublication';
15
15
  import { Track } from '../track/Track';
16
16
  import type { AudioCaptureOptions, BackupVideoCodec, CreateLocalTracksOptions, ScreenShareCaptureOptions, TrackPublishOptions, VideoCaptureOptions } from '../track/options';
17
- import type { ChatMessage, DataPublishOptions, SendFileOptions, SendTextOptions, StreamBytesOptions, StreamTextOptions, TextStreamInfo } from '../types';
17
+ import type { ByteStreamInfo, ChatMessage, DataPublishOptions, SendBytesOptions, SendFileOptions, SendTextOptions, StreamBytesOptions, StreamTextOptions, TextStreamInfo } from '../types';
18
18
  import Participant from './Participant';
19
19
  import type { ParticipantTrackPermission } from './ParticipantTrackPermission';
20
20
  import type RemoteParticipant from './RemoteParticipant';
@@ -208,6 +208,14 @@ export default class LocalParticipant extends Participant {
208
208
  sendFile(file: File, options?: SendFileOptions): Promise<{
209
209
  id: string;
210
210
  }>;
211
+ /**
212
+ * Sends the given bytes to participants in the room via the data channel.
213
+ * For files, consider using {@link sendFile}; for longer/incremental payloads, {@link streamBytes}.
214
+ *
215
+ * @param bytes The byte payload
216
+ * @param options.topic Topic identifier used to route the stream to appropriate handlers.
217
+ */
218
+ sendBytes(bytes: Uint8Array, options?: SendBytesOptions): Promise<ByteStreamInfo>;
211
219
  /**
212
220
  * Stream bytes incrementally to participants in the room via the data channel.
213
221
  * For sending files, consider using {@link sendFile} instead.
@@ -1,4 +1,5 @@
1
1
  import type { ParticipantInfo } from '@livekit/protocol';
2
+ import { ClientInfo_Capability } from '@livekit/protocol';
2
3
  import type { SignalClient } from '../../api/SignalClient';
3
4
  import { DeferrableMap } from '../../utils/deferrable-map';
4
5
  import RemoteDataTrack from '../data-track/RemoteDataTrack';
@@ -26,6 +27,15 @@ export default class RemoteParticipant extends Participant {
26
27
  * @internal
27
28
  **/
28
29
  clientProtocol: number;
30
+ /** The client capabilities the remote participant advertises (e.g. deflate-raw compression
31
+ * support). Used to decide which peer-to-peer features can be used when sending to them.
32
+ *
33
+ * Differs from clientProtocol in that these are truely optional "additions" which can be used
34
+ * or not depending on client specific attributes rather than protocol level invariants.
35
+ *
36
+ * @internal
37
+ **/
38
+ capabilities: Array<ClientInfo_Capability>;
29
39
  private volumeMap;
30
40
  private audioOutput?;
31
41
  /** @internal */
@@ -35,7 +45,7 @@ export default class RemoteParticipant extends Participant {
35
45
  remoteParticipant: string;
36
46
  };
37
47
  /** @internal */
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);
48
+ constructor(signalClient: SignalClient, sid: string, identity?: string, name?: string, metadata?: string, attributes?: Record<string, string>, loggerOptions?: LoggerOptions, kind?: ParticipantKind, remoteDataTracks?: Array<RemoteDataTrack>, clientProtocol?: number, capabilities?: Array<ClientInfo_Capability>);
39
49
  protected addTrackPublication(publication: RemoteTrackPublication): void;
40
50
  getTrackPublication(source: Track.Source): RemoteTrackPublication | undefined;
41
51
  getTrackPublicationByName(name: string): RemoteTrackPublication | undefined;
@@ -26,5 +26,12 @@ export declare class ScalabilityMode {
26
26
  constructor(scalabilityMode: string);
27
27
  toString(): string;
28
28
  }
29
+ /**
30
+ * Returns the appropriate degradation preference for a video track based on its source.
31
+ *
32
+ * - Camera: 'maintain-framerate' (smoother video for real-time communication)
33
+ * - Screen share: 'maintain-resolution' (clarity is critical for reading text/UI)
34
+ * - Other/unknown: 'balanced'
35
+ */
29
36
  export declare function getDefaultDegradationPreference(track: LocalVideoTrack): RTCDegradationPreference;
30
37
  //# sourceMappingURL=publishUtils.d.ts.map
@@ -24,6 +24,7 @@ export default class LocalAudioTrack extends LocalTrack<Track.Kind.Audio> {
24
24
  mute(): Promise<typeof this>;
25
25
  unmute(): Promise<typeof this>;
26
26
  restartTrack(options?: AudioCaptureOptions): Promise<void>;
27
+ applyConstraints(constraints: Pick<AudioCaptureOptions, 'autoGainControl' | 'noiseSuppression' | 'echoCancellation' | 'voiceIsolation'>): Promise<void>;
27
28
  protected restart(constraints?: MediaTrackConstraints, isUnmuting?: boolean): Promise<typeof this>;
28
29
  startMonitor(): void;
29
30
  protected monitorSender: () => Promise<void>;
@@ -71,7 +71,17 @@ export interface TrackPublishDefaults {
71
71
  */
72
72
  scalabilityMode?: ScalabilityMode;
73
73
  /**
74
- * degradation preference
74
+ * Controls how the encoder trades off between resolution and framerate
75
+ * when bandwidth is constrained.
76
+ *
77
+ * - 'maintain-framerate': Prioritizes framerate, reduces resolution if needed
78
+ * - 'maintain-resolution': Prioritizes resolution, drops frames if needed
79
+ * - 'balanced': Balances between both
80
+ *
81
+ * If not set, the SDK uses defaults based on track source:
82
+ * - Camera: 'maintain-framerate' (smoother video for real-time communication)
83
+ * - Screen share: 'maintain-resolution' (clarity is critical for text/UI)
84
+ * - Other/unknown: 'balanced'
75
85
  */
76
86
  degradationPreference?: RTCDegradationPreference;
77
87
  /**
@@ -18,6 +18,20 @@ export interface SendTextOptions {
18
18
  attachments?: Array<File>;
19
19
  onProgress?: (progress: number) => void;
20
20
  attributes?: Record<string, string>;
21
+ /** Whether to compress the payload (deflate-raw). Defaults to true. Compression is only applied
22
+ * when every recipient supports data streams v2 and the runtime can compress. */
23
+ compress?: boolean;
24
+ }
25
+ export interface SendBytesOptions {
26
+ topic?: string;
27
+ destinationIdentities?: Array<string>;
28
+ attributes?: Record<string, string>;
29
+ onProgress?: (progress: number) => void;
30
+ /** Whether to compress the payload (deflate-raw). Defaults to true. Compression is only applied
31
+ * when every recipient supports data streams v2 and the runtime can compress. */
32
+ compress?: boolean;
33
+ name?: string;
34
+ mimeType?: string;
21
35
  }
22
36
  export interface StreamTextOptions {
23
37
  topic?: string;
@@ -42,6 +56,9 @@ export type StreamBytesOptions = {
42
56
  export type SendFileOptions = Pick<StreamBytesOptions, 'topic' | 'mimeType' | 'destinationIdentities'> & {
43
57
  onProgress?: (progress: number) => void;
44
58
  encryptionType?: Encryption_Type.NONE;
59
+ /** Whether to compress the payload (deflate-raw). Defaults to true. Compression is only applied
60
+ * when every recipient supports data streams v2 and the runtime can compress. */
61
+ compress?: boolean;
45
62
  };
46
63
  export type DataPublishOptions = {
47
64
  /**
@@ -146,6 +146,19 @@ export declare function isRemoteVideoTrack(track: Track | undefined): track is R
146
146
  export declare function isLocalParticipant(p: Participant): p is LocalParticipant;
147
147
  export declare function isRemoteParticipant(p: Participant): p is RemoteParticipant;
148
148
  export declare function splitUtf8(s: string, n: number): NonSharedUint8Array[];
149
+ /** Wraps a byte array in a `ReadableStream` that yields it as a single chunk and then closes. */
150
+ export declare function readableFromBytes(bytes: NonSharedUint8Array): ReadableStream<NonSharedUint8Array>;
151
+ /**
152
+ * Re-chunks a byte stream into pieces of exactly `chunkSize` bytes (the final piece may be
153
+ * smaller), coalescing or splitting the source's pieces as needed. Memory use is bounded to roughly
154
+ * `chunkSize` plus one source read, so it never buffers the whole stream — used to pack
155
+ * `CompressionStream`/`file.stream()` output into MTU-sized data-stream chunks.
156
+ */
157
+ export declare function readBytesInChunks(source: ReadableStream<NonSharedUint8Array>, chunkSize: number): AsyncGenerator<NonSharedUint8Array>;
158
+ /** Encodes a byte array as a base64 string (suitable for embedding binary data in a string field). */
159
+ export declare function encodeBase64(bytes: Uint8Array): string;
160
+ /** Decodes a base64 string (as produced by {@link encodeBase64}) back into a byte array. */
161
+ export declare function decodeBase64(base64: string): Uint8Array;
149
162
  export declare function extractMaxAgeFromRequestHeaders(headers: Headers): number | undefined;
150
163
  export declare function isCompressionStreamSupported(): boolean;
151
164
  export declare function isPublisherOfferWithJoinSupported(): boolean;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Mint a LiveKit access token for the mock test-server (HS256, dev secret).
3
+ *
4
+ * The mock's signal behavior mode is selected via a participant attribute
5
+ * under the reserved key `lk.mock` (dot notation, matching LiveKit's internal
6
+ * attribute convention), whose value is the JSON control object
7
+ * `{"signal":"<mode>"}` — mirroring the X-Lk-Mock header protocol. Attributes
8
+ * are a standard AccessToken field (a string map), so a dedicated `lk-mock`
9
+ * key coexists with any real metadata/attributes and needs no bespoke token
10
+ * construction. The room name is just a room; it no longer encodes behavior.
11
+ */
12
+ export interface TokenOptions {
13
+ /** Mock behavior mode (e.g. 'no_pong'); omitted → the mock defaults to 'happy'. */
14
+ signal?: string;
15
+ /** LeaveRequest action the leave modes should send (0=DISCONNECT,1=RESUME,2=RECONNECT). */
16
+ leaveAction?: number;
17
+ room?: string;
18
+ identity?: string;
19
+ apiKey?: string;
20
+ secret?: string;
21
+ ttlSeconds?: number;
22
+ }
23
+ export declare function createToken(opts?: TokenOptions): Promise<string>;
24
+ /** A syntactically-valid token signed with the WRONG secret — for 401 tests. */
25
+ export declare function createInvalidToken(): Promise<string>;
26
+ //# sourceMappingURL=signalToken.d.ts.map
@@ -2,13 +2,31 @@ import type { NonSharedUint8Array } from '../type-polyfills/non-shared-typed-arr
2
2
  export interface DataPacketItem {
3
3
  data: NonSharedUint8Array;
4
4
  sequence: number;
5
+ /**
6
+ * Whether the packet has been handed to the data channel. Unsent packets are queued for the
7
+ * resume replay (e.g. sends that landed in a reconnect window) and must never be trimmed by
8
+ * {@link DataPacketBuffer.alignBufferedAmount}, which reasons about the channel's buffered
9
+ * bytes — those only ever contain sent packets.
10
+ */
11
+ sent: boolean;
5
12
  }
6
13
  export declare class DataPacketBuffer {
7
14
  private buffer;
8
15
  private _totalSize;
16
+ private _sentSize;
9
17
  push(item: DataPacketItem): void;
10
18
  pop(): DataPacketItem | undefined;
11
19
  getAll(): DataPacketItem[];
20
+ /** Every queued packet not yet handed to the channel, in sequence order. */
21
+ getUnsent(): DataPacketItem[];
22
+ /** Marks a single queued packet as handed to the channel. */
23
+ markSent(item: DataPacketItem): void;
24
+ /**
25
+ * Marks every queued packet as not-yet-sent. Used at the start of a resume replay: whatever is
26
+ * still buffered was sent on the previous channel (or deferred) and must be re-handed to the
27
+ * current one, so none of it counts as sent until the replay actually transmits it.
28
+ */
29
+ markAllUnsent(): void;
12
30
  popToSequence(sequence: number): void;
13
31
  alignBufferedAmount(bufferedAmount: number): void;
14
32
  get length(): number;
@@ -5,7 +5,11 @@ export declare const CLIENT_PROTOCOL_DEFAULT = 0;
5
5
  /** Replaces RPC v1 protocol with a v2 data streams based one to support unlimited request /
6
6
  * response payload length. */
7
7
  export declare const CLIENT_PROTOCOL_DATA_STREAM_RPC = 1;
8
+ /** "Data streams v2": the client knows how to receive a single-packet data stream (a stream whose
9
+ * entire payload is smuggled into the header packet, with no chunk/trailer packets). Senders only
10
+ * use the single-packet optimization when the recipient advertises at least this protocol. */
11
+ export declare const CLIENT_PROTOCOL_DATA_STREAM_V2 = 2;
8
12
  /** The client protocol version indicates what level of support that the client has for
9
13
  * client <-> client api interactions. */
10
- export declare const clientProtocol = 1;
14
+ export declare const clientProtocol = 2;
11
15
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livekit-client",
3
- "version": "2.20.1",
3
+ "version": "2.21.0",
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",
@@ -50,7 +50,7 @@
50
50
  "license": "Apache-2.0",
51
51
  "dependencies": {
52
52
  "@livekit/mutex": "1.1.1",
53
- "@livekit/protocol": "1.46.6",
53
+ "@livekit/protocol": "1.50.4",
54
54
  "events": "^3.3.0",
55
55
  "jose": "^6.1.0",
56
56
  "loglevel": "^1.9.2",
@@ -84,6 +84,8 @@
84
84
  "@types/ua-parser-js": "0.7.39",
85
85
  "@typescript-eslint/eslint-plugin": "8.59.4",
86
86
  "@typescript-eslint/parser": "8.59.4",
87
+ "@vitest/browser": "^4.1.10",
88
+ "@vitest/browser-playwright": "^4.1.10",
87
89
  "downlevel-dts": "^0.11.0",
88
90
  "eslint": "10.4.0",
89
91
  "eslint-config-airbnb-extended": "^2.3.2",
@@ -95,6 +97,7 @@
95
97
  "glob": "^13.0.6",
96
98
  "happy-dom": "^20.0.0",
97
99
  "jsdom": "^26.1.0",
100
+ "playwright": "^1.61.1",
98
101
  "prettier": "^3.4.2",
99
102
  "publint": "^0.3.21",
100
103
  "rollup": "4.60.4",
@@ -107,7 +110,7 @@
107
110
  "typescript": "5.9.3",
108
111
  "typescript-eslint": "^8.47.0",
109
112
  "vite": "7.3.5",
110
- "vitest": "^4.0.0"
113
+ "vitest": "^4.1.10"
111
114
  },
112
115
  "scripts": {
113
116
  "build": "rollup --config --bundleConfigAsCjs && rollup --config rollup.config.worker.js --bundleConfigAsCjs && rollup --config rollup.config.fm-worker.js --bundleConfigAsCjs && pnpm downlevel-dts",
@@ -120,10 +123,11 @@
120
123
  "dev": "pnpm examples:demo",
121
124
  "lint": "eslint src",
122
125
  "test": "vitest run src",
126
+ "test:e2e": "vitest run --config vitest.e2e.config.mts",
123
127
  "deploy": "gh-pages -d examples/demo/dist",
124
128
  "format": "prettier --write src examples/**/*.ts",
125
129
  "format:check": "prettier --check src examples/**/*.ts",
126
- "throws:check": "pnpm --package=@livekit/throws-transformer dlx throws-check 'src/!(*.test).ts' 'src/**/!(*.test).ts'",
130
+ "throws:check": "pnpm throws-check 'src/!(*.test).ts' 'src/**/!(*.test).ts'",
127
131
  "type:check": "tsc --noEmit",
128
132
  "ci:publish": "pnpm type:check && pnpm build && pnpm compat && changeset publish",
129
133
  "downlevel-dts": "downlevel-dts ./dist/src ./dist/ts4.2 --to=4.2",