livekit-client 2.18.0 → 2.18.2

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 (145) 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 +8 -7
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +8026 -5883
  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/api/SignalClient.d.ts +12 -4
  10. package/dist/src/api/SignalClient.d.ts.map +1 -1
  11. package/dist/src/connectionHelper/ConnectionCheck.d.ts +1 -1
  12. package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
  13. package/dist/src/e2ee/constants.d.ts.map +1 -1
  14. package/dist/src/e2ee/types.d.ts +6 -0
  15. package/dist/src/e2ee/types.d.ts.map +1 -1
  16. package/dist/src/e2ee/utils.d.ts +2 -1
  17. package/dist/src/e2ee/utils.d.ts.map +1 -1
  18. package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
  19. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  20. package/dist/src/index.d.ts +6 -4
  21. package/dist/src/index.d.ts.map +1 -1
  22. package/dist/src/room/PCTransport.d.ts +5 -0
  23. package/dist/src/room/PCTransport.d.ts.map +1 -1
  24. package/dist/src/room/PCTransportManager.d.ts +1 -1
  25. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  26. package/dist/src/room/RTCEngine.d.ts +28 -11
  27. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  28. package/dist/src/room/Room.d.ts +14 -3
  29. package/dist/src/room/Room.d.ts.map +1 -1
  30. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
  31. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
  32. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
  33. package/dist/src/room/data-track/LocalDataTrack.d.ts +28 -5
  34. package/dist/src/room/data-track/LocalDataTrack.d.ts.map +1 -1
  35. package/dist/src/room/data-track/RemoteDataTrack.d.ts +5 -5
  36. package/dist/src/room/data-track/RemoteDataTrack.d.ts.map +1 -1
  37. package/dist/src/room/data-track/depacketizer.d.ts +4 -4
  38. package/dist/src/room/data-track/depacketizer.d.ts.map +1 -1
  39. package/dist/src/room/data-track/frame.d.ts +14 -0
  40. package/dist/src/room/data-track/frame.d.ts.map +1 -1
  41. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +19 -11
  42. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
  43. package/dist/src/room/data-track/incoming/pipeline.d.ts +6 -5
  44. package/dist/src/room/data-track/incoming/pipeline.d.ts.map +1 -1
  45. package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +57 -23
  46. package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
  47. package/dist/src/room/data-track/outgoing/errors.d.ts +16 -6
  48. package/dist/src/room/data-track/outgoing/errors.d.ts.map +1 -1
  49. package/dist/src/room/data-track/outgoing/pipeline.d.ts +7 -6
  50. package/dist/src/room/data-track/outgoing/pipeline.d.ts.map +1 -1
  51. package/dist/src/room/data-track/outgoing/types.d.ts +14 -4
  52. package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -1
  53. package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
  54. package/dist/src/room/data-track/packetizer.d.ts +4 -4
  55. package/dist/src/room/data-track/packetizer.d.ts.map +1 -1
  56. package/dist/src/room/data-track/track-interfaces.d.ts +1 -1
  57. package/dist/src/room/data-track/track-interfaces.d.ts.map +1 -1
  58. package/dist/src/room/data-track/types.d.ts +6 -1
  59. package/dist/src/room/data-track/types.d.ts.map +1 -1
  60. package/dist/src/room/events.d.ts +24 -3
  61. package/dist/src/room/events.d.ts.map +1 -1
  62. package/dist/src/room/participant/LocalParticipant.d.ts +12 -1
  63. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  64. package/dist/src/room/participant/RemoteParticipant.d.ts +13 -0
  65. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  66. package/dist/src/room/utils.d.ts +1 -0
  67. package/dist/src/room/utils.d.ts.map +1 -1
  68. package/dist/src/utils/deferrable-map.d.ts +32 -0
  69. package/dist/src/utils/deferrable-map.d.ts.map +1 -0
  70. package/dist/src/utils/serializer.d.ts +48 -0
  71. package/dist/src/utils/serializer.d.ts.map +1 -0
  72. package/dist/ts4.2/api/SignalClient.d.ts +12 -4
  73. package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +2 -1
  74. package/dist/ts4.2/e2ee/types.d.ts +6 -0
  75. package/dist/ts4.2/e2ee/utils.d.ts +2 -1
  76. package/dist/ts4.2/index.d.ts +7 -4
  77. package/dist/ts4.2/room/PCTransport.d.ts +5 -0
  78. package/dist/ts4.2/room/PCTransportManager.d.ts +1 -1
  79. package/dist/ts4.2/room/RTCEngine.d.ts +28 -11
  80. package/dist/ts4.2/room/Room.d.ts +14 -3
  81. package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
  82. package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +27 -4
  83. package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +4 -4
  84. package/dist/ts4.2/room/data-track/depacketizer.d.ts +4 -4
  85. package/dist/ts4.2/room/data-track/frame.d.ts +14 -0
  86. package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +21 -10
  87. package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +6 -5
  88. package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +57 -23
  89. package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +16 -6
  90. package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +7 -6
  91. package/dist/ts4.2/room/data-track/outgoing/types.d.ts +14 -4
  92. package/dist/ts4.2/room/data-track/packetizer.d.ts +4 -4
  93. package/dist/ts4.2/room/data-track/track-interfaces.d.ts +1 -1
  94. package/dist/ts4.2/room/data-track/types.d.ts +6 -1
  95. package/dist/ts4.2/room/events.d.ts +24 -3
  96. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +12 -1
  97. package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +13 -0
  98. package/dist/ts4.2/room/utils.d.ts +1 -0
  99. package/dist/ts4.2/utils/deferrable-map.d.ts +32 -0
  100. package/dist/ts4.2/utils/serializer.d.ts +48 -0
  101. package/package.json +1 -1
  102. package/src/api/SignalClient.test.ts +9 -4
  103. package/src/api/SignalClient.ts +116 -9
  104. package/src/connectionHelper/ConnectionCheck.ts +1 -1
  105. package/src/e2ee/constants.ts +1 -0
  106. package/src/e2ee/types.ts +6 -0
  107. package/src/e2ee/utils.ts +4 -3
  108. package/src/e2ee/worker/DataCryptor.ts +1 -4
  109. package/src/e2ee/worker/FrameCryptor.ts +1 -4
  110. package/src/e2ee/worker/ParticipantKeyHandler.ts +1 -1
  111. package/src/index.ts +13 -4
  112. package/src/room/PCTransport.ts +41 -1
  113. package/src/room/PCTransportManager.ts +1 -1
  114. package/src/room/RTCEngine.ts +312 -125
  115. package/src/room/Room.ts +168 -35
  116. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +26 -2
  117. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +7 -7
  118. package/src/room/data-track/LocalDataTrack.ts +83 -10
  119. package/src/room/data-track/RemoteDataTrack.ts +7 -9
  120. package/src/room/data-track/depacketizer.ts +21 -12
  121. package/src/room/data-track/frame.ts +28 -2
  122. package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +58 -73
  123. package/src/room/data-track/incoming/IncomingDataTrackManager.ts +139 -80
  124. package/src/room/data-track/incoming/pipeline.ts +29 -24
  125. package/src/room/data-track/outgoing/OutgoingDataTrackManager.test.ts +225 -32
  126. package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +150 -75
  127. package/src/room/data-track/outgoing/errors.ts +36 -7
  128. package/src/room/data-track/outgoing/pipeline.ts +23 -17
  129. package/src/room/data-track/outgoing/types.ts +12 -3
  130. package/src/room/data-track/packet/extensions.ts +17 -22
  131. package/src/room/data-track/packet/index.test.ts +22 -33
  132. package/src/room/data-track/packetizer.test.ts +2 -2
  133. package/src/room/data-track/packetizer.ts +4 -4
  134. package/src/room/data-track/track-interfaces.ts +1 -1
  135. package/src/room/data-track/types.ts +21 -1
  136. package/src/room/events.ts +26 -1
  137. package/src/room/participant/LocalParticipant.ts +74 -8
  138. package/src/room/participant/RemoteParticipant.ts +25 -0
  139. package/src/room/utils.ts +4 -0
  140. package/src/utils/deferrable-map.ts +109 -0
  141. package/src/utils/serializer.ts +72 -0
  142. package/dist/src/room/data-track/e2ee.d.ts +0 -12
  143. package/dist/src/room/data-track/e2ee.d.ts.map +0 -1
  144. package/dist/ts4.2/room/data-track/e2ee.d.ts +0 -12
  145. package/src/room/data-track/e2ee.ts +0 -15
@@ -1,31 +1,76 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import {
4
+ type DecryptDataResponseMessage,
5
+ type EncryptDataResponseMessage,
6
+ LocalDataTrack,
7
+ } from '../../..';
8
+ import { type BaseE2EEManager } from '../../../e2ee/E2eeManager';
3
9
  import { subscribeToEvents } from '../../../utils/subscribeToEvents';
4
- import { EncryptionProvider } from '../e2ee';
10
+ import RTCEngine from '../../RTCEngine';
11
+ import Room from '../../Room';
5
12
  import { DataTrackHandle } from '../handle';
6
13
  import { DataTrackPacket, FrameMarker } from '../packet';
7
14
  import OutgoingDataTrackManager, {
8
- DataTrackOutgoingManagerCallbacks,
15
+ type DataTrackOutgoingManagerCallbacks,
9
16
  Descriptor,
10
17
  } from './OutgoingDataTrackManager';
11
18
  import { DataTrackPublishError } from './errors';
12
19
 
13
- /** A fake "encryption" provider used for test purposes. Adds a prefix to the payload. */
14
- const PrefixingEncryptionProvider: EncryptionProvider = {
15
- encrypt(payload: Uint8Array) {
20
+ /** Fake encryption provider for testing e2ee data track features. */
21
+ export class PrefixingEncryptionProvider implements BaseE2EEManager {
22
+ isEnabled = true;
23
+
24
+ isDataChannelEncryptionEnabled = true;
25
+
26
+ setup(_room: Room) {}
27
+
28
+ setupEngine(_engine: RTCEngine) {}
29
+
30
+ setParticipantCryptorEnabled(_enabled: boolean, _participantIdentity: string) {}
31
+
32
+ setSifTrailer(_trailer: Uint8Array) {}
33
+
34
+ on(_event: any, _listener: any): this {
35
+ return this;
36
+ }
37
+
38
+ /** A fake "encryption" provider used for test purposes. Adds a prefix to the payload. */
39
+ async encryptData(data: Uint8Array): Promise<EncryptDataResponseMessage['data']> {
16
40
  const prefix = new Uint8Array([0xde, 0xad, 0xbe, 0xef]);
17
41
 
18
- const output = new Uint8Array(prefix.length + payload.length);
42
+ const output = new Uint8Array(prefix.length + data.length);
19
43
  output.set(prefix, 0);
20
- output.set(payload, prefix.length);
44
+ output.set(data, prefix.length);
21
45
 
22
46
  return {
47
+ uuid: crypto.randomUUID(),
23
48
  payload: output,
24
49
  iv: new Uint8Array(12), // Just leaving this empty, is this a bad idea?
25
50
  keyIndex: 0,
26
51
  };
27
- },
28
- };
52
+ }
53
+
54
+ /** A fake "decryption" provider used for test purposes. Assumes the payload is prefixed with
55
+ * 0xdeafbeef, which is stripped off. */
56
+ async handleEncryptedData(
57
+ payload: Uint8Array,
58
+ _iv: Uint8Array,
59
+ _participantIdentity: string,
60
+ _keyIndex: number,
61
+ ): Promise<DecryptDataResponseMessage['data']> {
62
+ if (payload[0] !== 0xde || payload[1] !== 0xad || payload[2] !== 0xbe || payload[3] !== 0xef) {
63
+ throw new Error(
64
+ `PrefixingEncryptionProvider: first four bytes of payload were not 0xdeadbeef, found ${payload.slice(0, 4)}`,
65
+ );
66
+ }
67
+
68
+ return {
69
+ uuid: crypto.randomUUID(),
70
+ payload: payload.slice(4),
71
+ };
72
+ }
73
+ }
29
74
 
30
75
  describe('DataTrackOutgoingManager', () => {
31
76
  it('should test track publishing (ok case)', async () => {
@@ -34,8 +79,11 @@ describe('DataTrackOutgoingManager', () => {
34
79
  'sfuPublishRequest',
35
80
  ]);
36
81
 
82
+ const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
83
+ expect(localDataTrack.isPublished()).toStrictEqual(false);
84
+
37
85
  // 1. Publish a data track
38
- const publishRequestPromise = manager.publishRequest({ name: 'test' });
86
+ const publishRequestPromise = localDataTrack.publish();
39
87
 
40
88
  // 2. This publish request should be sent along to the SFU
41
89
  const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
@@ -55,7 +103,7 @@ describe('DataTrackOutgoingManager', () => {
55
103
  });
56
104
 
57
105
  // Make sure that the original input event resolves.
58
- const localDataTrack = await publishRequestPromise;
106
+ await publishRequestPromise;
59
107
  expect(localDataTrack.isPublished()).toStrictEqual(true);
60
108
  });
61
109
 
@@ -66,7 +114,8 @@ describe('DataTrackOutgoingManager', () => {
66
114
  ]);
67
115
 
68
116
  // 1. Publish a data track
69
- const publishRequestPromise = manager.publishRequest({ name: 'test' });
117
+ const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
118
+ const publishRequestPromise = localDataTrack.publish();
70
119
 
71
120
  // 2. This publish request should be sent along to the SFU
72
121
  const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
@@ -78,7 +127,9 @@ describe('DataTrackOutgoingManager', () => {
78
127
  });
79
128
 
80
129
  // Make sure that the rejection bubbles back to the caller
81
- expect(publishRequestPromise).rejects.toThrowError('Data track publication limit reached');
130
+ await expect(publishRequestPromise).rejects.toThrowError(
131
+ 'Data track publication limit reached',
132
+ );
82
133
  });
83
134
 
84
135
  it('should test track publishing (cancellation half way through)', async () => {
@@ -90,7 +141,8 @@ describe('DataTrackOutgoingManager', () => {
90
141
 
91
142
  // 1. Publish a data track
92
143
  const controller = new AbortController();
93
- const publishRequestPromise = manager.publishRequest({ name: 'test' }, controller.signal);
144
+ const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
145
+ const publishRequestPromise = localDataTrack.publish(controller.signal);
94
146
 
95
147
  // 2. This publish request should be sent along to the SFU
96
148
  const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
@@ -107,7 +159,7 @@ describe('DataTrackOutgoingManager', () => {
107
159
  expect(sfuUnpublishEvent.handle).toStrictEqual(handle);
108
160
 
109
161
  // 5. Make sure cancellation is bubbled up as an error to stop further execution
110
- expect(publishRequestPromise).rejects.toStrictEqual(DataTrackPublishError.cancelled());
162
+ await expect(publishRequestPromise).rejects.toStrictEqual(DataTrackPublishError.cancelled());
111
163
  });
112
164
 
113
165
  it('should test track publishing (cancellation before it starts)', async () => {
@@ -118,18 +170,87 @@ describe('DataTrackOutgoingManager', () => {
118
170
  ]);
119
171
 
120
172
  // Publish a data track
121
- const publishRequestPromise = manager.publishRequest(
122
- { name: 'test' },
123
- AbortSignal.abort(/* already aborted */),
124
- );
173
+ const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
174
+ const publishRequestPromise = localDataTrack.publish(AbortSignal.abort(/* already aborted */));
125
175
 
126
176
  // Make sure cancellation is immediately bubbled up
127
- expect(publishRequestPromise).rejects.toStrictEqual(DataTrackPublishError.cancelled());
177
+ await expect(publishRequestPromise).rejects.toStrictEqual(DataTrackPublishError.cancelled());
128
178
 
129
179
  // And there were no pending sfu publish requests sent
130
180
  expect(managerEvents.areThereBufferedEvents('sfuPublishRequest')).toBe(false);
131
181
  });
132
182
 
183
+ it('should test track publishing, unpublishing, and republishing again', async () => {
184
+ const manager = new OutgoingDataTrackManager();
185
+ const managerEvents = subscribeToEvents<DataTrackOutgoingManagerCallbacks>(manager, [
186
+ 'sfuPublishRequest',
187
+ 'sfuUnpublishRequest',
188
+ ]);
189
+
190
+ // 1. Create a local data track
191
+ const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
192
+ expect(localDataTrack.isPublished()).toStrictEqual(false);
193
+
194
+ // 2. Publish it
195
+ const publishRequestPromise = localDataTrack.publish();
196
+
197
+ // 3. This publish request should be sent along to the SFU
198
+ const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
199
+ expect(sfuPublishEvent.name).toStrictEqual('test');
200
+ expect(sfuPublishEvent.usesE2ee).toStrictEqual(false);
201
+ const handle = sfuPublishEvent.handle;
202
+
203
+ // 4. Respond to the SFU publish request with an OK response
204
+ manager.receivedSfuPublishResponse(handle, {
205
+ type: 'ok',
206
+ data: {
207
+ sid: 'bogus-sid',
208
+ pubHandle: sfuPublishEvent.handle,
209
+ name: 'test',
210
+ usesE2ee: false,
211
+ },
212
+ });
213
+
214
+ // Make sure that the original input event resolves.
215
+ await publishRequestPromise;
216
+
217
+ // 5. Now the data track should be published
218
+ expect(localDataTrack.isPublished()).toStrictEqual(true);
219
+
220
+ // 6. Unpublish the data track
221
+ const unpublishRequestPromise = localDataTrack.unpublish();
222
+ const sfuUnpublishEvent = await managerEvents.waitFor('sfuUnpublishRequest');
223
+ manager.receivedSfuUnpublishResponse(sfuUnpublishEvent.handle);
224
+ await unpublishRequestPromise;
225
+
226
+ // 7. Now the data track should be unpublished
227
+ expect(localDataTrack.isPublished()).toStrictEqual(false);
228
+
229
+ // 8. Now, republish the track and make sure that be done a second time
230
+ const publishRequestPromise2 = localDataTrack.publish();
231
+ const sfuPublishEvent2 = await managerEvents.waitFor('sfuPublishRequest');
232
+ expect(sfuPublishEvent2.name).toStrictEqual('test');
233
+ expect(sfuPublishEvent2.usesE2ee).toStrictEqual(false);
234
+ const handle2 = sfuPublishEvent2.handle;
235
+ manager.receivedSfuPublishResponse(handle2, {
236
+ type: 'ok',
237
+ data: {
238
+ sid: 'bogus-sid',
239
+ pubHandle: sfuPublishEvent2.handle,
240
+ name: 'test',
241
+ usesE2ee: false,
242
+ },
243
+ });
244
+ await publishRequestPromise2;
245
+
246
+ // 9. Ensure that the track is published again
247
+ expect(localDataTrack.isPublished()).toStrictEqual(true);
248
+
249
+ // 10. Also ensure that the handle used on the second publish attempt differs from the first
250
+ // publish attempt.
251
+ expect(handle).not.toStrictEqual(handle2);
252
+ });
253
+
133
254
  it.each([
134
255
  // Single packet payload case
135
256
  [
@@ -207,18 +328,17 @@ describe('DataTrackOutgoingManager', () => {
207
328
  ]),
208
329
  );
209
330
  const managerEvents = subscribeToEvents<DataTrackOutgoingManagerCallbacks>(manager, [
210
- 'packetsAvailable',
331
+ 'packetAvailable',
211
332
  ]);
212
333
 
213
- const localDataTrack = manager.createLocalDataTrack(5)!;
214
- expect(localDataTrack).not.toStrictEqual(null);
334
+ const localDataTrack = LocalDataTrack.withExplicitHandle({ name: 'track name' }, manager, 5);
215
335
 
216
336
  // Kick off sending the bytes...
217
- localDataTrack.tryPush(inputBytes);
337
+ localDataTrack.tryPush({ payload: inputBytes });
218
338
 
219
339
  // ... and make sure the corresponding events are emitted to tell the SFU to send the packets
220
340
  for (const outputPacketJson of outputPacketsJson) {
221
- const packetBytes = await managerEvents.waitFor('packetsAvailable');
341
+ const packetBytes = await managerEvents.waitFor('packetAvailable');
222
342
  const [packet] = DataTrackPacket.fromBinary(packetBytes.bytes);
223
343
 
224
344
  expect(packet.toJSON()).toStrictEqual(outputPacketJson);
@@ -228,15 +348,16 @@ describe('DataTrackOutgoingManager', () => {
228
348
 
229
349
  it('should send e2ee encrypted datatrack payload', async () => {
230
350
  const manager = new OutgoingDataTrackManager({
231
- encryptionProvider: PrefixingEncryptionProvider,
351
+ e2eeManager: new PrefixingEncryptionProvider(),
232
352
  });
233
353
  const managerEvents = subscribeToEvents<DataTrackOutgoingManagerCallbacks>(manager, [
234
354
  'sfuPublishRequest',
235
- 'packetsAvailable',
355
+ 'packetAvailable',
236
356
  ]);
237
357
 
238
358
  // 1. Publish a data track
239
- const publishRequestPromise = manager.publishRequest({ name: 'test' });
359
+ const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
360
+ const publishRequestPromise = localDataTrack.publish();
240
361
 
241
362
  // 2. This publish request should be sent along to the SFU
242
363
  const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
@@ -256,14 +377,14 @@ describe('DataTrackOutgoingManager', () => {
256
377
  });
257
378
 
258
379
  // Get the connected local data track
259
- const localDataTrack = await publishRequestPromise;
380
+ await publishRequestPromise;
260
381
  expect(localDataTrack.isPublished()).toStrictEqual(true);
261
382
 
262
383
  // Kick off sending the payload bytes
263
- localDataTrack.tryPush(new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]));
384
+ localDataTrack.tryPush({ payload: new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]) });
264
385
 
265
386
  // Make sure the packet that was sent was encrypted with the PrefixingEncryptionProvider
266
- const packetBytes = await managerEvents.waitFor('packetsAvailable');
387
+ const packetBytes = await managerEvents.waitFor('packetAvailable');
267
388
  const [packet] = DataTrackPacket.fromBinary(packetBytes.bytes);
268
389
 
269
390
  expect(packet.toJSON()).toStrictEqual({
@@ -331,6 +452,76 @@ describe('DataTrackOutgoingManager', () => {
331
452
  expect(manager.getDescriptor(5)).toStrictEqual(null);
332
453
  });
333
454
 
455
+ it('should test a full reconnect', async () => {
456
+ const pubHandle = 5;
457
+ // Create a manager prefilled with a descriptor
458
+ const manager = OutgoingDataTrackManager.withDescriptors(
459
+ new Map([
460
+ [
461
+ DataTrackHandle.fromNumber(5),
462
+ Descriptor.active(
463
+ {
464
+ sid: 'bogus-sid',
465
+ pubHandle,
466
+ name: 'test',
467
+ usesE2ee: false,
468
+ },
469
+ null,
470
+ ),
471
+ ],
472
+ ]),
473
+ );
474
+ const managerEvents = subscribeToEvents<DataTrackOutgoingManagerCallbacks>(manager, [
475
+ 'sfuPublishRequest',
476
+ 'packetAvailable',
477
+ 'sfuUnpublishRequest',
478
+ ]);
479
+ const localDataTrack = LocalDataTrack.withExplicitHandle({ name: 'track name' }, manager, 5);
480
+
481
+ // Make sure the descriptor is in there
482
+ expect(manager.getDescriptor(5)?.type).toStrictEqual('active');
483
+
484
+ // Simulate a full reconnect, which means that any published tracks will need to be republished.
485
+ manager.sfuWillRepublishTracks();
486
+
487
+ // Even though behind the scenes the SFU publications are not active, the user should still see
488
+ // it as "published", sfu reconnects are an implementation detail
489
+ expect(localDataTrack.isPublished()).toStrictEqual(true);
490
+
491
+ // But, even though `isPublished` is true, pushing data should drop (no sfu to send them to!)
492
+ await expect(() =>
493
+ localDataTrack.tryPush({ payload: new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]) }),
494
+ ).rejects.toThrowError('Frame was dropped');
495
+
496
+ // 2. This publish request should be sent along to the SFU
497
+ const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
498
+ expect(sfuPublishEvent.name).toStrictEqual('test');
499
+ expect(sfuPublishEvent.usesE2ee).toStrictEqual(false);
500
+ const handle = sfuPublishEvent.handle;
501
+ expect(handle).toStrictEqual(pubHandle);
502
+
503
+ // 3. Respond to the SFU publish request with an OK response
504
+ manager.receivedSfuPublishResponse(handle, {
505
+ type: 'ok',
506
+ data: {
507
+ sid: 'bogus-sid-REPUBLISHED',
508
+ pubHandle: sfuPublishEvent.handle,
509
+ name: 'test',
510
+ usesE2ee: false,
511
+ },
512
+ });
513
+
514
+ // After all this, the local data track should still be published
515
+ expect(localDataTrack.isPublished()).toStrictEqual(true);
516
+
517
+ // And the sid should be the new value
518
+ expect(localDataTrack.info!.sid).toStrictEqual('bogus-sid-REPUBLISHED');
519
+
520
+ // And now that the tracks are backed by the SFU again, pushes should function!
521
+ await localDataTrack.tryPush({ payload: new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]) });
522
+ await managerEvents.waitFor('packetAvailable');
523
+ });
524
+
334
525
  it('should query currently active descriptors', async () => {
335
526
  // Create a manager prefilled with a descriptor
336
527
  const manager = OutgoingDataTrackManager.withDescriptors(
@@ -398,7 +589,9 @@ describe('DataTrackOutgoingManager', () => {
398
589
  const shutdownPromise = manager.shutdown();
399
590
 
400
591
  // The pending data track should be cancelled
401
- expect(pendingDescriptor.completionFuture.promise).rejects.toThrowError('Room disconnected');
592
+ await expect(pendingDescriptor.completionFuture.promise).rejects.toThrowError(
593
+ 'Room disconnected',
594
+ );
402
595
 
403
596
  // And the active data track should be requested to be unpublished
404
597
  const unpublishEvent = await managerEvents.waitFor('sfuUnpublishRequest');