livekit-client 2.15.7 → 2.15.8

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 (170) 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 +253 -118
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +1882 -138
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.umd.js +1 -1
  8. package/dist/livekit-client.umd.js.map +1 -1
  9. package/dist/src/connectionHelper/checks/publishVideo.d.ts.map +1 -1
  10. package/dist/src/e2ee/E2eeManager.d.ts +16 -2
  11. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  12. package/dist/src/e2ee/types.d.ts +35 -1
  13. package/dist/src/e2ee/types.d.ts.map +1 -1
  14. package/dist/src/e2ee/utils.d.ts +2 -0
  15. package/dist/src/e2ee/utils.d.ts.map +1 -1
  16. package/dist/src/e2ee/worker/DataCryptor.d.ts +15 -0
  17. package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -0
  18. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +3 -2
  19. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
  20. package/dist/src/e2ee/worker/sifPayload.d.ts +6 -6
  21. package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -1
  22. package/dist/src/index.d.ts +5 -3
  23. package/dist/src/index.d.ts.map +1 -1
  24. package/dist/src/logger.d.ts +1 -0
  25. package/dist/src/logger.d.ts.map +1 -1
  26. package/dist/src/options.d.ts +4 -2
  27. package/dist/src/options.d.ts.map +1 -1
  28. package/dist/src/room/RTCEngine.d.ts +5 -2
  29. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  30. package/dist/src/room/Room.d.ts +3 -2
  31. package/dist/src/room/Room.d.ts.map +1 -1
  32. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +2 -2
  33. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
  34. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
  35. package/dist/src/room/errors.d.ts +2 -1
  36. package/dist/src/room/errors.d.ts.map +1 -1
  37. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  38. package/dist/src/room/participant/Participant.d.ts +2 -2
  39. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  40. package/dist/src/room/token-source/TokenSource.d.ts +70 -0
  41. package/dist/src/room/token-source/TokenSource.d.ts.map +1 -0
  42. package/dist/src/room/token-source/types.d.ts +68 -0
  43. package/dist/src/room/token-source/types.d.ts.map +1 -0
  44. package/dist/src/room/token-source/utils.d.ts +5 -0
  45. package/dist/src/room/token-source/utils.d.ts.map +1 -0
  46. package/dist/src/room/track/LocalTrack.d.ts +1 -1
  47. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  48. package/dist/src/room/track/options.d.ts +7 -3
  49. package/dist/src/room/track/options.d.ts.map +1 -1
  50. package/dist/src/room/track/utils.d.ts.map +1 -1
  51. package/dist/src/room/types.d.ts +1 -0
  52. package/dist/src/room/types.d.ts.map +1 -1
  53. package/dist/src/room/utils.d.ts +2 -1
  54. package/dist/src/room/utils.d.ts.map +1 -1
  55. package/dist/src/utils/camelToSnakeCase.d.ts +8 -0
  56. package/dist/src/utils/camelToSnakeCase.d.ts.map +1 -0
  57. package/dist/ts4.2/{src/e2ee → e2ee}/E2eeManager.d.ts +16 -2
  58. package/dist/ts4.2/{src/e2ee → e2ee}/types.d.ts +35 -1
  59. package/dist/ts4.2/{src/e2ee → e2ee}/utils.d.ts +3 -0
  60. package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +15 -0
  61. package/dist/ts4.2/{src/e2ee → e2ee}/worker/ParticipantKeyHandler.d.ts +3 -2
  62. package/dist/ts4.2/{src/e2ee → e2ee}/worker/sifPayload.d.ts +6 -6
  63. package/dist/ts4.2/{src/index.d.ts → index.d.ts} +5 -3
  64. package/dist/ts4.2/{src/logger.d.ts → logger.d.ts} +1 -0
  65. package/dist/ts4.2/{src/options.d.ts → options.d.ts} +4 -2
  66. package/dist/ts4.2/{src/room → room}/RTCEngine.d.ts +5 -2
  67. package/dist/ts4.2/{src/room → room}/Room.d.ts +3 -2
  68. package/dist/ts4.2/{src/room → room}/data-stream/incoming/IncomingDataStreamManager.d.ts +2 -1
  69. package/dist/ts4.2/{src/room → room}/errors.d.ts +2 -1
  70. package/dist/ts4.2/{src/room → room}/participant/Participant.d.ts +2 -2
  71. package/dist/ts4.2/room/token-source/TokenSource.d.ts +71 -0
  72. package/dist/ts4.2/room/token-source/types.d.ts +68 -0
  73. package/dist/ts4.2/room/token-source/utils.d.ts +5 -0
  74. package/dist/ts4.2/{src/room → room}/track/LocalTrack.d.ts +1 -1
  75. package/dist/ts4.2/{src/room → room}/track/options.d.ts +10 -3
  76. package/dist/ts4.2/{src/room → room}/types.d.ts +1 -0
  77. package/dist/ts4.2/{src/room → room}/utils.d.ts +2 -1
  78. package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -0
  79. package/package.json +11 -10
  80. package/src/connectionHelper/checks/publishVideo.ts +5 -0
  81. package/src/e2ee/E2eeManager.ts +94 -2
  82. package/src/e2ee/types.ts +44 -1
  83. package/src/e2ee/utils.ts +16 -0
  84. package/src/e2ee/worker/DataCryptor.test.ts +271 -0
  85. package/src/e2ee/worker/DataCryptor.ts +147 -0
  86. package/src/e2ee/worker/ParticipantKeyHandler.ts +4 -3
  87. package/src/e2ee/worker/e2ee.worker.ts +47 -0
  88. package/src/e2ee/worker/sifPayload.ts +10 -6
  89. package/src/index.ts +14 -1
  90. package/src/logger.ts +1 -0
  91. package/src/options.ts +8 -2
  92. package/src/room/RTCEngine.ts +55 -6
  93. package/src/room/Room.ts +38 -16
  94. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +64 -17
  95. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +7 -0
  96. package/src/room/errors.ts +3 -0
  97. package/src/room/participant/LocalParticipant.ts +8 -6
  98. package/src/room/participant/Participant.ts +6 -1
  99. package/src/room/token-source/TokenSource.ts +285 -0
  100. package/src/room/token-source/types.ts +84 -0
  101. package/src/room/token-source/utils.ts +35 -0
  102. package/src/room/track/LocalAudioTrack.ts +1 -1
  103. package/src/room/track/LocalTrack.ts +1 -1
  104. package/src/room/track/options.ts +12 -4
  105. package/src/room/track/utils.ts +10 -2
  106. package/src/room/types.ts +1 -0
  107. package/src/room/utils.ts +8 -4
  108. package/src/utils/camelToSnakeCase.ts +16 -0
  109. /package/dist/ts4.2/{src/api → api}/SignalClient.d.ts +0 -0
  110. /package/dist/ts4.2/{src/api → api}/utils.d.ts +0 -0
  111. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/ConnectionCheck.d.ts +0 -0
  112. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/Checker.d.ts +0 -0
  113. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/cloudRegion.d.ts +0 -0
  114. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/connectionProtocol.d.ts +0 -0
  115. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/publishAudio.d.ts +0 -0
  116. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/publishVideo.d.ts +0 -0
  117. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/reconnect.d.ts +0 -0
  118. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/turn.d.ts +0 -0
  119. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/webrtc.d.ts +0 -0
  120. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/websocket.d.ts +0 -0
  121. /package/dist/ts4.2/{src/e2ee → e2ee}/KeyProvider.d.ts +0 -0
  122. /package/dist/ts4.2/{src/e2ee → e2ee}/constants.d.ts +0 -0
  123. /package/dist/ts4.2/{src/e2ee → e2ee}/errors.d.ts +0 -0
  124. /package/dist/ts4.2/{src/e2ee → e2ee}/events.d.ts +0 -0
  125. /package/dist/ts4.2/{src/e2ee → e2ee}/index.d.ts +0 -0
  126. /package/dist/ts4.2/{src/e2ee → e2ee}/worker/FrameCryptor.d.ts +0 -0
  127. /package/dist/ts4.2/{src/e2ee → e2ee}/worker/e2ee.worker.d.ts +0 -0
  128. /package/dist/ts4.2/{src/e2ee → e2ee}/worker/naluUtils.d.ts +0 -0
  129. /package/dist/ts4.2/{src/room → room}/DefaultReconnectPolicy.d.ts +0 -0
  130. /package/dist/ts4.2/{src/room → room}/DeviceManager.d.ts +0 -0
  131. /package/dist/ts4.2/{src/room → room}/PCTransport.d.ts +0 -0
  132. /package/dist/ts4.2/{src/room → room}/PCTransportManager.d.ts +0 -0
  133. /package/dist/ts4.2/{src/room → room}/ReconnectPolicy.d.ts +0 -0
  134. /package/dist/ts4.2/{src/room → room}/RegionUrlProvider.d.ts +0 -0
  135. /package/dist/ts4.2/{src/room → room}/attribute-typings.d.ts +0 -0
  136. /package/dist/ts4.2/{src/room → room}/data-stream/incoming/StreamReader.d.ts +0 -0
  137. /package/dist/ts4.2/{src/room → room}/data-stream/outgoing/OutgoingDataStreamManager.d.ts +0 -0
  138. /package/dist/ts4.2/{src/room → room}/data-stream/outgoing/StreamWriter.d.ts +0 -0
  139. /package/dist/ts4.2/{src/room → room}/defaults.d.ts +0 -0
  140. /package/dist/ts4.2/{src/room → room}/events.d.ts +0 -0
  141. /package/dist/ts4.2/{src/room → room}/participant/LocalParticipant.d.ts +0 -0
  142. /package/dist/ts4.2/{src/room → room}/participant/ParticipantTrackPermission.d.ts +0 -0
  143. /package/dist/ts4.2/{src/room → room}/participant/RemoteParticipant.d.ts +0 -0
  144. /package/dist/ts4.2/{src/room → room}/participant/publishUtils.d.ts +0 -0
  145. /package/dist/ts4.2/{src/room → room}/rpc.d.ts +0 -0
  146. /package/dist/ts4.2/{src/room → room}/stats.d.ts +0 -0
  147. /package/dist/ts4.2/{src/room → room}/timers.d.ts +0 -0
  148. /package/dist/ts4.2/{src/room → room}/track/LocalAudioTrack.d.ts +0 -0
  149. /package/dist/ts4.2/{src/room → room}/track/LocalTrackPublication.d.ts +0 -0
  150. /package/dist/ts4.2/{src/room → room}/track/LocalVideoTrack.d.ts +0 -0
  151. /package/dist/ts4.2/{src/room → room}/track/RemoteAudioTrack.d.ts +0 -0
  152. /package/dist/ts4.2/{src/room → room}/track/RemoteTrack.d.ts +0 -0
  153. /package/dist/ts4.2/{src/room → room}/track/RemoteTrackPublication.d.ts +0 -0
  154. /package/dist/ts4.2/{src/room → room}/track/RemoteVideoTrack.d.ts +0 -0
  155. /package/dist/ts4.2/{src/room → room}/track/Track.d.ts +0 -0
  156. /package/dist/ts4.2/{src/room → room}/track/TrackPublication.d.ts +0 -0
  157. /package/dist/ts4.2/{src/room → room}/track/create.d.ts +0 -0
  158. /package/dist/ts4.2/{src/room → room}/track/facingMode.d.ts +0 -0
  159. /package/dist/ts4.2/{src/room → room}/track/processor/types.d.ts +0 -0
  160. /package/dist/ts4.2/{src/room → room}/track/record.d.ts +0 -0
  161. /package/dist/ts4.2/{src/room → room}/track/types.d.ts +0 -0
  162. /package/dist/ts4.2/{src/room → room}/track/utils.d.ts +0 -0
  163. /package/dist/ts4.2/{src/test → test}/MockMediaStreamTrack.d.ts +0 -0
  164. /package/dist/ts4.2/{src/test → test}/mocks.d.ts +0 -0
  165. /package/dist/ts4.2/{src/utils → utils}/AsyncQueue.d.ts +0 -0
  166. /package/dist/ts4.2/{src/utils → utils}/browserParser.d.ts +0 -0
  167. /package/dist/ts4.2/{src/utils → utils}/cloneDeep.d.ts +0 -0
  168. /package/dist/ts4.2/{src/utils → utils}/dataPacketBuffer.d.ts +0 -0
  169. /package/dist/ts4.2/{src/utils → utils}/ttlmap.d.ts +0 -0
  170. /package/dist/ts4.2/{src/version.d.ts → version.d.ts} +0 -0
@@ -0,0 +1,285 @@
1
+ import { Mutex } from '@livekit/mutex';
2
+ import {
3
+ RoomAgentDispatch,
4
+ RoomConfiguration,
5
+ TokenSourceRequest,
6
+ TokenSourceResponse,
7
+ } from '@livekit/protocol';
8
+ import {
9
+ TokenSourceConfigurable,
10
+ type TokenSourceFetchOptions,
11
+ TokenSourceFixed,
12
+ type TokenSourceResponseObject,
13
+ } from './types';
14
+ import { decodeTokenPayload, isResponseExpired } from './utils';
15
+
16
+ /** A TokenSourceCached is a TokenSource which caches the last {@link TokenSourceResponseObject} value and returns it
17
+ * until a) it expires or b) the {@link TokenSourceFetchOptions} provided to .fetch(...) change. */
18
+ abstract class TokenSourceCached extends TokenSourceConfigurable {
19
+ private cachedFetchOptions: TokenSourceFetchOptions | null = null;
20
+
21
+ private cachedResponse: TokenSourceResponse | null = null;
22
+
23
+ private fetchMutex = new Mutex();
24
+
25
+ private isSameAsCachedFetchOptions(options: TokenSourceFetchOptions) {
26
+ if (!this.cachedFetchOptions) {
27
+ return false;
28
+ }
29
+
30
+ for (const key of Object.keys(this.cachedFetchOptions) as Array<
31
+ keyof TokenSourceFetchOptions
32
+ >) {
33
+ switch (key) {
34
+ case 'roomName':
35
+ case 'participantName':
36
+ case 'participantIdentity':
37
+ case 'participantMetadata':
38
+ case 'participantAttributes':
39
+ case 'agentName':
40
+ case 'agentMetadata':
41
+ if (this.cachedFetchOptions[key] !== options[key]) {
42
+ return false;
43
+ }
44
+ break;
45
+ default:
46
+ // ref: https://stackoverflow.com/a/58009992
47
+ const exhaustiveCheckedKey: never = key;
48
+ throw new Error(`Options key ${exhaustiveCheckedKey} not being checked for equality!`);
49
+ }
50
+ }
51
+
52
+ return true;
53
+ }
54
+
55
+ private shouldReturnCachedValueFromFetch(fetchOptions: TokenSourceFetchOptions) {
56
+ if (!this.cachedResponse) {
57
+ return false;
58
+ }
59
+ if (isResponseExpired(this.cachedResponse)) {
60
+ return false;
61
+ }
62
+ if (this.isSameAsCachedFetchOptions(fetchOptions)) {
63
+ return false;
64
+ }
65
+ return true;
66
+ }
67
+
68
+ getCachedResponseJwtPayload() {
69
+ if (!this.cachedResponse) {
70
+ return null;
71
+ }
72
+ return decodeTokenPayload(this.cachedResponse.participantToken);
73
+ }
74
+
75
+ async fetch(options: TokenSourceFetchOptions): Promise<TokenSourceResponseObject> {
76
+ const unlock = await this.fetchMutex.lock();
77
+ try {
78
+ if (this.shouldReturnCachedValueFromFetch(options)) {
79
+ return this.cachedResponse!.toJson() as TokenSourceResponseObject;
80
+ }
81
+ this.cachedFetchOptions = options;
82
+
83
+ const tokenResponse = await this.update(options);
84
+ this.cachedResponse = tokenResponse;
85
+ return tokenResponse.toJson() as TokenSourceResponseObject;
86
+ } finally {
87
+ unlock();
88
+ }
89
+ }
90
+
91
+ protected abstract update(options: TokenSourceFetchOptions): Promise<TokenSourceResponse>;
92
+ }
93
+
94
+ type LiteralOrFn =
95
+ | TokenSourceResponseObject
96
+ | (() => TokenSourceResponseObject | Promise<TokenSourceResponseObject>);
97
+ export class TokenSourceLiteral extends TokenSourceFixed {
98
+ private literalOrFn: LiteralOrFn;
99
+
100
+ constructor(literalOrFn: LiteralOrFn) {
101
+ super();
102
+ this.literalOrFn = literalOrFn;
103
+ }
104
+
105
+ async fetch(): Promise<TokenSourceResponseObject> {
106
+ if (typeof this.literalOrFn === 'function') {
107
+ return this.literalOrFn();
108
+ } else {
109
+ return this.literalOrFn;
110
+ }
111
+ }
112
+ }
113
+
114
+ type CustomFn = (
115
+ options: TokenSourceFetchOptions,
116
+ ) => TokenSourceResponseObject | Promise<TokenSourceResponseObject>;
117
+ export class TokenSourceCustom extends TokenSourceCached {
118
+ private customFn: CustomFn;
119
+
120
+ constructor(customFn: CustomFn) {
121
+ super();
122
+ this.customFn = customFn;
123
+ }
124
+
125
+ protected async update(options: TokenSourceFetchOptions) {
126
+ const resultMaybePromise = this.customFn(options);
127
+
128
+ let result;
129
+ if (resultMaybePromise instanceof Promise) {
130
+ result = await resultMaybePromise;
131
+ } else {
132
+ result = resultMaybePromise;
133
+ }
134
+
135
+ return TokenSourceResponse.fromJson(result, {
136
+ // NOTE: it could be possible that the response body could contain more fields than just
137
+ // what's in TokenSourceResponse depending on the implementation
138
+ ignoreUnknownFields: true,
139
+ });
140
+ }
141
+ }
142
+
143
+ export type EndpointOptions = Omit<RequestInit, 'body'>;
144
+
145
+ export class TokenSourceEndpoint extends TokenSourceCached {
146
+ private url: string;
147
+
148
+ private endpointOptions: EndpointOptions;
149
+
150
+ constructor(url: string, options: EndpointOptions = {}) {
151
+ super();
152
+ this.url = url;
153
+ this.endpointOptions = options;
154
+ }
155
+
156
+ private createRequestFromOptions(options: TokenSourceFetchOptions) {
157
+ const request = new TokenSourceRequest();
158
+
159
+ for (const key of Object.keys(options) as Array<keyof TokenSourceFetchOptions>) {
160
+ switch (key) {
161
+ case 'roomName':
162
+ case 'participantName':
163
+ case 'participantIdentity':
164
+ case 'participantMetadata':
165
+ request[key] = options[key];
166
+ break;
167
+
168
+ case 'participantAttributes':
169
+ request.participantAttributes = options.participantAttributes ?? {};
170
+ break;
171
+
172
+ case 'agentName':
173
+ request.roomConfig = request.roomConfig ?? new RoomConfiguration();
174
+ if (request.roomConfig.agents.length === 0) {
175
+ request.roomConfig.agents.push(new RoomAgentDispatch());
176
+ }
177
+ request.roomConfig.agents[0].agentName = options.agentName!;
178
+ break;
179
+
180
+ case 'agentMetadata':
181
+ request.roomConfig = request.roomConfig ?? new RoomConfiguration();
182
+ if (request.roomConfig.agents.length === 0) {
183
+ request.roomConfig.agents.push(new RoomAgentDispatch());
184
+ }
185
+ request.roomConfig.agents[0].metadata = options.agentMetadata!;
186
+ break;
187
+
188
+ default:
189
+ // ref: https://stackoverflow.com/a/58009992
190
+ const exhaustiveCheckedKey: never = key;
191
+ throw new Error(
192
+ `Options key ${exhaustiveCheckedKey} not being included in forming request!`,
193
+ );
194
+ }
195
+ }
196
+
197
+ return request;
198
+ }
199
+
200
+ protected async update(options: TokenSourceFetchOptions) {
201
+ const request = this.createRequestFromOptions(options);
202
+
203
+ const response = await fetch(this.url, {
204
+ ...this.endpointOptions,
205
+ method: this.endpointOptions.method ?? 'POST',
206
+ headers: {
207
+ 'Content-Type': 'application/json',
208
+ ...this.endpointOptions.headers,
209
+ },
210
+ body: request.toJsonString({
211
+ useProtoFieldName: true,
212
+ }),
213
+ });
214
+
215
+ if (!response.ok) {
216
+ throw new Error(
217
+ `Error generating token from endpoint ${this.url}: received ${response.status} / ${await response.text()}`,
218
+ );
219
+ }
220
+
221
+ const body = await response.json();
222
+ return TokenSourceResponse.fromJson(body, {
223
+ // NOTE: it could be possible that the response body could contain more fields than just
224
+ // what's in TokenSourceResponse depending on the implementation (ie, SandboxTokenServer)
225
+ ignoreUnknownFields: true,
226
+ });
227
+ }
228
+ }
229
+
230
+ export type SandboxTokenServerOptions = {
231
+ baseUrl?: string;
232
+ };
233
+
234
+ export class TokenSourceSandboxTokenServer extends TokenSourceEndpoint {
235
+ constructor(sandboxId: string, options: SandboxTokenServerOptions) {
236
+ const { baseUrl = 'https://cloud-api.livekit.io', ...rest } = options;
237
+
238
+ super(`${baseUrl}/api/v2/sandbox/connection-details`, {
239
+ ...rest,
240
+ headers: {
241
+ 'X-Sandbox-ID': sandboxId,
242
+ },
243
+ });
244
+ }
245
+ }
246
+
247
+ export const TokenSource = {
248
+ /** TokenSource.literal contains a single, literal set of {@link TokenSourceResponseObject}
249
+ * credentials, either provided directly or returned from a provided function. */
250
+ literal(literalOrFn: LiteralOrFn) {
251
+ return new TokenSourceLiteral(literalOrFn);
252
+ },
253
+
254
+ /**
255
+ * TokenSource.custom allows a user to define a manual function which generates new
256
+ * {@link TokenSourceResponseObject} values on demand.
257
+ *
258
+ * Use this to get credentials from custom backends / etc.
259
+ */
260
+ custom(customFn: CustomFn) {
261
+ return new TokenSourceCustom(customFn);
262
+ },
263
+
264
+ /**
265
+ * TokenSource.endpoint creates a token source that fetches credentials from a given URL using
266
+ * the standard endpoint format:
267
+ * FIXME: add docs link here in the future!
268
+ */
269
+ endpoint(url: string, options: EndpointOptions = {}) {
270
+ return new TokenSourceEndpoint(url, options);
271
+ },
272
+
273
+ /**
274
+ * TokenSource.sandboxTokenServer queries a sandbox token server for credentials,
275
+ * which supports quick prototyping / getting started types of use cases.
276
+ *
277
+ * This token provider is INSECURE and should NOT be used in production.
278
+ *
279
+ * For more info:
280
+ * @see https://cloud.livekit.io/projects/p_/sandbox/templates/token-server
281
+ */
282
+ sandboxTokenServer(sandboxId: string, options: SandboxTokenServerOptions = {}) {
283
+ return new TokenSourceSandboxTokenServer(sandboxId, options);
284
+ },
285
+ };
@@ -0,0 +1,84 @@
1
+ import { RoomConfiguration, TokenSourceRequest, TokenSourceResponse } from '@livekit/protocol';
2
+ import type { JWTPayload } from 'jose';
3
+ import type { ValueToSnakeCase } from '../../utils/camelToSnakeCase';
4
+ // The below imports are being linked in tsdoc comments, so they have to be imported even if they
5
+ // aren't being used.
6
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
7
+ import type { TokenSourceCustom, TokenSourceEndpoint, TokenSourceLiteral } from './TokenSource';
8
+
9
+ export type TokenSourceRequestObject = Required<
10
+ NonNullable<ConstructorParameters<typeof TokenSourceRequest>[0]>
11
+ >;
12
+ export type TokenSourceResponseObject = Required<
13
+ NonNullable<ConstructorParameters<typeof TokenSourceResponse>[0]>
14
+ >;
15
+
16
+ /** The `TokenSource` request object sent to the server as part of fetching a configurable
17
+ * `TokenSource` like {@link TokenSourceEndpoint}.
18
+ *
19
+ * Use this as a type for your request body if implementing a server endpoint in node.js.
20
+ */
21
+ export type TokenSourceRequestPayload = ValueToSnakeCase<TokenSourceRequestObject>;
22
+
23
+ /** The `TokenSource` response object sent from the server as part of fetching a configurable
24
+ * `TokenSource` like {@link TokenSourceEndpoint}.
25
+ *
26
+ * Use this as a type for your response body if implementing a server endpoint in node.js.
27
+ */
28
+ export type TokenSourceResponsePayload = ValueToSnakeCase<TokenSourceResponseObject>;
29
+
30
+ /** The payload of a LiveKit JWT token. */
31
+ export type TokenPayload = JWTPayload & {
32
+ name?: string;
33
+ metadata?: string;
34
+ attributes?: Record<string, string>;
35
+ video?: {
36
+ room?: string;
37
+ roomJoin?: boolean;
38
+ canPublish?: boolean;
39
+ canPublishData?: boolean;
40
+ canSubscribe?: boolean;
41
+ };
42
+ roomConfig?: RoomConfigurationObject;
43
+ };
44
+ export type RoomConfigurationObject = NonNullable<
45
+ ConstructorParameters<typeof RoomConfiguration>[0]
46
+ >;
47
+
48
+ /** A Fixed TokenSource is a token source that takes no parameters and returns a completely
49
+ * independently derived value on each fetch() call.
50
+ *
51
+ * The most common downstream implementer is {@link TokenSourceLiteral}.
52
+ */
53
+ export abstract class TokenSourceFixed {
54
+ abstract fetch(): Promise<TokenSourceResponseObject>;
55
+ }
56
+
57
+ export type TokenSourceFetchOptions = {
58
+ roomName?: string;
59
+ participantName?: string;
60
+ participantIdentity?: string;
61
+ participantMetadata?: string;
62
+ participantAttributes?: { [key: string]: string };
63
+
64
+ agentName?: string;
65
+ agentMetadata?: string;
66
+ };
67
+
68
+ /** A Configurable TokenSource is a token source that takes a
69
+ * {@link TokenSourceFetchOptions} object as input and returns a deterministic
70
+ * {@link TokenSourceResponseObject} output based on the options specified.
71
+ *
72
+ * For example, if options.participantName is set, it should be expected that
73
+ * all tokens that are generated will have participant name field set to the
74
+ * provided value.
75
+ *
76
+ * A few common downstream implementers are {@link TokenSourceEndpoint}
77
+ * and {@link TokenSourceCustom}.
78
+ */
79
+ export abstract class TokenSourceConfigurable {
80
+ abstract fetch(options: TokenSourceFetchOptions): Promise<TokenSourceResponseObject>;
81
+ }
82
+
83
+ /** A TokenSource is a mechanism for fetching credentials required to connect to a LiveKit Room. */
84
+ export type TokenSourceBase = TokenSourceFixed | TokenSourceConfigurable;
@@ -0,0 +1,35 @@
1
+ import { RoomConfiguration, type TokenSourceResponse } from '@livekit/protocol';
2
+ import { decodeJwt } from 'jose';
3
+ import type { RoomConfigurationObject, TokenPayload } from './types';
4
+
5
+ const ONE_SECOND_IN_MILLISECONDS = 1000;
6
+ const ONE_MINUTE_IN_MILLISECONDS = 60 * ONE_SECOND_IN_MILLISECONDS;
7
+
8
+ export function isResponseExpired(response: TokenSourceResponse) {
9
+ const jwtPayload = decodeTokenPayload(response.participantToken);
10
+ if (!jwtPayload?.exp) {
11
+ return true;
12
+ }
13
+ const expInMilliseconds = jwtPayload.exp * ONE_SECOND_IN_MILLISECONDS;
14
+ const expiresAt = new Date(expInMilliseconds - ONE_MINUTE_IN_MILLISECONDS);
15
+
16
+ const now = new Date();
17
+ return expiresAt >= now;
18
+ }
19
+
20
+ export function decodeTokenPayload(token: string) {
21
+ const payload = decodeJwt<Omit<TokenPayload, 'roomConfig'>>(token);
22
+
23
+ const { roomConfig, ...rest } = payload;
24
+
25
+ const mappedPayload: TokenPayload = {
26
+ ...rest,
27
+ roomConfig: payload.roomConfig
28
+ ? (RoomConfiguration.fromJson(
29
+ payload.roomConfig as Record<string, any>,
30
+ ) as RoomConfigurationObject)
31
+ : undefined,
32
+ };
33
+
34
+ return mappedPayload;
35
+ }
@@ -244,7 +244,7 @@ export default class LocalAudioTrack extends LocalTrack<Track.Kind.Audio> {
244
244
  const trackIsSilent = await detectSilence(this);
245
245
  if (trackIsSilent) {
246
246
  if (!this.isMuted) {
247
- this.log.warn('silence detected on local audio track', this.logContext);
247
+ this.log.debug('silence detected on local audio track', this.logContext);
248
248
  }
249
249
  this.emit(TrackEvent.AudioSilenceDetected);
250
250
  }
@@ -670,7 +670,7 @@ export default abstract class LocalTrack<
670
670
  }
671
671
 
672
672
  /** @internal */
673
- getPreConnectBuffer() {
673
+ getPreConnectBuffer(): ReadableStream<Uint8Array> | undefined {
674
674
  return this.localTrackRecorder?.byteStream;
675
675
  }
676
676
 
@@ -390,18 +390,26 @@ export interface AudioPreset {
390
390
  priority?: RTCPriorityType;
391
391
  }
392
392
 
393
- const backupCodecs = ['vp8', 'h264'] as const;
393
+ // `red` is not technically a codec, but treated as one in signalling protocol
394
+ export const audioCodecs = ['opus', 'red'] as const;
395
+
396
+ export type AudioCodec = (typeof audioCodecs)[number];
397
+
398
+ const backupVideoCodecs = ['vp8', 'h264'] as const;
394
399
 
395
400
  export const videoCodecs = ['vp8', 'h264', 'vp9', 'av1', 'h265'] as const;
396
401
 
397
402
  export type VideoCodec = (typeof videoCodecs)[number];
398
403
 
399
- export type BackupVideoCodec = (typeof backupCodecs)[number];
404
+ export type BackupVideoCodec = (typeof backupVideoCodecs)[number];
400
405
 
401
- export function isBackupCodec(codec: string): codec is BackupVideoCodec {
402
- return !!backupCodecs.find((backup) => backup === codec);
406
+ export function isBackupVideoCodec(codec: string): codec is BackupVideoCodec {
407
+ return !!backupVideoCodecs.find((backup) => backup === codec);
403
408
  }
404
409
 
410
+ /** @deprecated Use {@link isBackupVideoCodec} instead */
411
+ export const isBackupCodec = isBackupVideoCodec;
412
+
405
413
  export enum BackupCodecPolicy {
406
414
  // codec regression is preferred, the sfu will try to regress codec if possible but not guaranteed
407
415
  PREFER_REGRESSION = 0,
@@ -147,10 +147,18 @@ export function getNewAudioContext(): AudioContext | void {
147
147
  }
148
148
  } catch (e) {
149
149
  console.warn('Error trying to auto-resume audio context', e);
150
+ } finally {
151
+ window.document.body?.removeEventListener('click', handleResume);
150
152
  }
151
-
152
- window.document.body?.removeEventListener('click', handleResume);
153
153
  };
154
+
155
+ // https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/statechange_event
156
+ audioContext.addEventListener('statechange', () => {
157
+ if (audioContext.state === 'closed') {
158
+ window.document.body?.removeEventListener('click', handleResume);
159
+ }
160
+ });
161
+
154
162
  window.document.body.addEventListener('click', handleResume);
155
163
  }
156
164
  return audioContext;
package/src/room/types.ts CHANGED
@@ -136,6 +136,7 @@ export interface BaseStreamInfo {
136
136
  /** total size in bytes for finite streams and undefined for streams of unknown size */
137
137
  size?: number;
138
138
  attributes?: Record<string, string>;
139
+ encryptionType: Encryption_Type;
139
140
  }
140
141
  export interface ByteStreamInfo extends BaseStreamInfo {
141
142
  name: string;
package/src/room/utils.ts CHANGED
@@ -22,7 +22,7 @@ import type RemoteTrackPublication from './track/RemoteTrackPublication';
22
22
  import type RemoteVideoTrack from './track/RemoteVideoTrack';
23
23
  import { Track } from './track/Track';
24
24
  import type { TrackPublication } from './track/TrackPublication';
25
- import { type VideoCodec, videoCodecs } from './track/options';
25
+ import { type AudioCodec, type VideoCodec, audioCodecs, videoCodecs } from './track/options';
26
26
  import { getNewAudioContext } from './track/utils';
27
27
  import type { ChatMessage, LiveKitReactNativeInfo, TranscriptionSegment } from './types';
28
28
 
@@ -73,7 +73,7 @@ export function supportsAV1(): boolean {
73
73
  let hasAV1 = false;
74
74
  if (capabilities) {
75
75
  for (const codec of capabilities.codecs) {
76
- if (codec.mimeType === 'video/AV1') {
76
+ if (codec.mimeType.toLowerCase() === 'video/av1') {
77
77
  hasAV1 = true;
78
78
  break;
79
79
  }
@@ -110,7 +110,7 @@ export function supportsVP9(): boolean {
110
110
  let hasVP9 = false;
111
111
  if (capabilities) {
112
112
  for (const codec of capabilities.codecs) {
113
- if (codec.mimeType === 'video/VP9') {
113
+ if (codec.mimeType.toLowerCase() === 'video/vp9') {
114
114
  hasVP9 = true;
115
115
  break;
116
116
  }
@@ -128,7 +128,7 @@ export function supportsH265(): boolean {
128
128
  let hasH265 = false;
129
129
  if (capabilities) {
130
130
  for (const codec of capabilities.codecs) {
131
- if (codec.mimeType === 'video/H265') {
131
+ if (codec.mimeType.toLowerCase() === 'video/h265') {
132
132
  hasH265 = true;
133
133
  break;
134
134
  }
@@ -533,6 +533,10 @@ export function createAudioAnalyser(
533
533
  return { calculateVolume, analyser, cleanup };
534
534
  }
535
535
 
536
+ export function isAudioCodec(maybeCodec: string): maybeCodec is AudioCodec {
537
+ return audioCodecs.includes(maybeCodec as AudioCodec);
538
+ }
539
+
536
540
  export function isVideoCodec(maybeCodec: string): maybeCodec is VideoCodec {
537
541
  return videoCodecs.includes(maybeCodec as VideoCodec);
538
542
  }
@@ -0,0 +1,16 @@
1
+ export type CamelToSnakeCase<Str extends string> = Str extends `${infer First}${infer Rest}`
2
+ ? `${First extends Capitalize<First> ? '_' : ''}${Lowercase<First>}${CamelToSnakeCase<Rest>}`
3
+ : Str;
4
+
5
+ type ArrayValuesToSnakeCase<Item> = Array<ValueToSnakeCase<Item>>;
6
+
7
+ type ObjectKeysToSnakeCase<Obj> = {
8
+ [Key in keyof Obj as CamelToSnakeCase<string & Key>]: NonNullable<ValueToSnakeCase<Obj[Key]>>;
9
+ };
10
+
11
+ export type ValueToSnakeCase<Value> =
12
+ Value extends Array<infer Item>
13
+ ? ArrayValuesToSnakeCase<Item>
14
+ : Value extends object
15
+ ? ObjectKeysToSnakeCase<Value>
16
+ : Value;
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes