livekit-client 2.15.6 → 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.
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +253 -118
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +1892 -153
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/connectionHelper/checks/publishVideo.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +16 -2
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +35 -1
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/utils.d.ts +2 -0
- package/dist/src/e2ee/utils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/DataCryptor.d.ts +15 -0
- package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -0
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +3 -2
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
- package/dist/src/e2ee/worker/sifPayload.d.ts +6 -6
- package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -1
- package/dist/src/index.d.ts +5 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logger.d.ts +1 -0
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/options.d.ts +4 -2
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +5 -2
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +3 -2
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +2 -2
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/errors.d.ts +2 -1
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +1 -3
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +2 -2
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/token-source/TokenSource.d.ts +70 -0
- package/dist/src/room/token-source/TokenSource.d.ts.map +1 -0
- package/dist/src/room/token-source/types.d.ts +68 -0
- package/dist/src/room/token-source/types.d.ts.map +1 -0
- package/dist/src/room/token-source/utils.d.ts +5 -0
- package/dist/src/room/token-source/utils.d.ts.map +1 -0
- package/dist/src/room/track/LocalTrack.d.ts +1 -1
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +7 -3
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/utils.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +1 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +2 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/utils/camelToSnakeCase.d.ts +8 -0
- package/dist/src/utils/camelToSnakeCase.d.ts.map +1 -0
- package/dist/ts4.2/{src/e2ee → e2ee}/E2eeManager.d.ts +16 -2
- package/dist/ts4.2/{src/e2ee → e2ee}/types.d.ts +35 -1
- package/dist/ts4.2/{src/e2ee → e2ee}/utils.d.ts +3 -0
- package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +15 -0
- package/dist/ts4.2/{src/e2ee → e2ee}/worker/ParticipantKeyHandler.d.ts +3 -2
- package/dist/ts4.2/{src/e2ee → e2ee}/worker/sifPayload.d.ts +6 -6
- package/dist/ts4.2/{src/index.d.ts → index.d.ts} +5 -3
- package/dist/ts4.2/{src/logger.d.ts → logger.d.ts} +1 -0
- package/dist/ts4.2/{src/options.d.ts → options.d.ts} +4 -2
- package/dist/ts4.2/{src/room → room}/RTCEngine.d.ts +5 -2
- package/dist/ts4.2/{src/room → room}/Room.d.ts +3 -2
- package/dist/ts4.2/{src/room → room}/data-stream/incoming/IncomingDataStreamManager.d.ts +2 -1
- package/dist/ts4.2/{src/room → room}/errors.d.ts +2 -1
- package/dist/ts4.2/{src/room → room}/participant/LocalParticipant.d.ts +1 -3
- package/dist/ts4.2/{src/room → room}/participant/Participant.d.ts +2 -2
- package/dist/ts4.2/room/token-source/TokenSource.d.ts +71 -0
- package/dist/ts4.2/room/token-source/types.d.ts +68 -0
- package/dist/ts4.2/room/token-source/utils.d.ts +5 -0
- package/dist/ts4.2/{src/room → room}/track/LocalTrack.d.ts +1 -1
- package/dist/ts4.2/{src/room → room}/track/options.d.ts +10 -3
- package/dist/ts4.2/{src/room → room}/types.d.ts +1 -0
- package/dist/ts4.2/{src/room → room}/utils.d.ts +2 -1
- package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -0
- package/package.json +14 -12
- package/src/connectionHelper/checks/publishVideo.ts +5 -0
- package/src/e2ee/E2eeManager.ts +94 -2
- package/src/e2ee/types.ts +44 -1
- package/src/e2ee/utils.ts +16 -0
- package/src/e2ee/worker/DataCryptor.test.ts +271 -0
- package/src/e2ee/worker/DataCryptor.ts +147 -0
- package/src/e2ee/worker/ParticipantKeyHandler.ts +4 -3
- package/src/e2ee/worker/e2ee.worker.ts +47 -0
- package/src/e2ee/worker/sifPayload.ts +10 -6
- package/src/index.ts +14 -1
- package/src/logger.ts +1 -0
- package/src/options.ts +8 -2
- package/src/room/PCTransport.ts +14 -5
- package/src/room/RTCEngine.ts +55 -6
- package/src/room/Room.ts +39 -17
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +64 -17
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +7 -0
- package/src/room/errors.ts +3 -0
- package/src/room/participant/LocalParticipant.ts +17 -29
- package/src/room/participant/Participant.ts +6 -1
- package/src/room/token-source/TokenSource.ts +285 -0
- package/src/room/token-source/types.ts +84 -0
- package/src/room/token-source/utils.ts +35 -0
- package/src/room/track/LocalAudioTrack.ts +1 -1
- package/src/room/track/LocalTrack.ts +1 -1
- package/src/room/track/options.ts +12 -4
- package/src/room/track/utils.ts +10 -2
- package/src/room/types.ts +1 -0
- package/src/room/utils.ts +8 -4
- package/src/utils/camelToSnakeCase.ts +16 -0
- /package/dist/ts4.2/{src/api → api}/SignalClient.d.ts +0 -0
- /package/dist/ts4.2/{src/api → api}/utils.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/ConnectionCheck.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/Checker.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/cloudRegion.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/connectionProtocol.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/publishAudio.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/publishVideo.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/reconnect.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/turn.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/webrtc.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/websocket.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/KeyProvider.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/constants.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/errors.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/events.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/index.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/worker/FrameCryptor.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/worker/e2ee.worker.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/worker/naluUtils.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/DefaultReconnectPolicy.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/DeviceManager.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/PCTransport.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/PCTransportManager.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/ReconnectPolicy.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/RegionUrlProvider.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/attribute-typings.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/data-stream/incoming/StreamReader.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/data-stream/outgoing/OutgoingDataStreamManager.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/data-stream/outgoing/StreamWriter.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/defaults.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/events.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/participant/ParticipantTrackPermission.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/participant/RemoteParticipant.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/participant/publishUtils.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/rpc.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/stats.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/timers.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/LocalAudioTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/LocalTrackPublication.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/LocalVideoTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/RemoteAudioTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/RemoteTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/RemoteTrackPublication.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/RemoteVideoTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/Track.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/TrackPublication.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/create.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/facingMode.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/processor/types.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/record.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/types.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/utils.d.ts +0 -0
- /package/dist/ts4.2/{src/test → test}/MockMediaStreamTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/test → test}/mocks.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/AsyncQueue.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/browserParser.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/cloneDeep.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/dataPacketBuffer.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/ttlmap.d.ts +0 -0
- /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.
|
247
|
+
this.log.debug('silence detected on local audio track', this.logContext);
|
248
248
|
}
|
249
249
|
this.emit(TrackEvent.AudioSilenceDetected);
|
250
250
|
}
|
@@ -390,18 +390,26 @@ export interface AudioPreset {
|
|
390
390
|
priority?: RTCPriorityType;
|
391
391
|
}
|
392
392
|
|
393
|
-
|
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
|
404
|
+
export type BackupVideoCodec = (typeof backupVideoCodecs)[number];
|
400
405
|
|
401
|
-
export function
|
402
|
-
return !!
|
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,
|
package/src/room/track/utils.ts
CHANGED
@@ -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/
|
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/
|
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/
|
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
|
/package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/connectionProtocol.d.ts
RENAMED
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
|
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
|
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
|
File without changes
|
File without changes
|