livekit-client 1.9.3 → 1.9.5

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 (38) hide show
  1. package/README.md +2 -0
  2. package/dist/livekit-client.esm.mjs +125 -908
  3. package/dist/livekit-client.esm.mjs.map +1 -1
  4. package/dist/livekit-client.umd.js +1 -1
  5. package/dist/livekit-client.umd.js.map +1 -1
  6. package/dist/src/api/SignalClient.d.ts +2 -3
  7. package/dist/src/api/SignalClient.d.ts.map +1 -1
  8. package/dist/src/room/PCTransport.d.ts +1 -2
  9. package/dist/src/room/PCTransport.d.ts.map +1 -1
  10. package/dist/src/room/Room.d.ts +1 -0
  11. package/dist/src/room/Room.d.ts.map +1 -1
  12. package/dist/src/room/participant/LocalParticipant.d.ts +0 -1
  13. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  14. package/dist/src/room/timers.d.ts +4 -5
  15. package/dist/src/room/timers.d.ts.map +1 -1
  16. package/dist/src/room/utils.d.ts.map +1 -1
  17. package/dist/src/utils/AsyncQueue.d.ts +23 -0
  18. package/dist/src/utils/AsyncQueue.d.ts.map +1 -0
  19. package/dist/src/utils/browserParser.d.ts +10 -0
  20. package/dist/src/utils/browserParser.d.ts.map +1 -0
  21. package/dist/ts4.2/src/api/SignalClient.d.ts +2 -3
  22. package/dist/ts4.2/src/room/PCTransport.d.ts +1 -2
  23. package/dist/ts4.2/src/room/Room.d.ts +1 -0
  24. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +0 -1
  25. package/dist/ts4.2/src/room/timers.d.ts +4 -5
  26. package/dist/ts4.2/src/utils/AsyncQueue.d.ts +23 -0
  27. package/dist/ts4.2/src/utils/browserParser.d.ts +10 -0
  28. package/package.json +11 -22
  29. package/src/api/SignalClient.ts +4 -5
  30. package/src/connectionHelper/ConnectionCheck.ts +1 -1
  31. package/src/room/PCTransport.ts +1 -1
  32. package/src/room/Room.ts +5 -2
  33. package/src/room/participant/LocalParticipant.ts +0 -1
  34. package/src/room/utils.ts +17 -16
  35. package/src/utils/AsyncQueue.test.ts +99 -0
  36. package/src/utils/AsyncQueue.ts +57 -0
  37. package/src/utils/browserParser.test.ts +58 -0
  38. package/src/utils/browserParser.ts +72 -0
@@ -0,0 +1,99 @@
1
+ import { sleep } from '../room/utils';
2
+ import { AsyncQueue } from './AsyncQueue';
3
+
4
+ describe('asyncQueue', () => {
5
+ it('runs multiple tasks in order', async () => {
6
+ const queue = new AsyncQueue();
7
+ const tasksExecuted: number[] = [];
8
+
9
+ for (let i = 0; i < 5; i++) {
10
+ queue.run(async () => {
11
+ await sleep(50);
12
+ tasksExecuted.push(i);
13
+ });
14
+ }
15
+ await queue.flush();
16
+ expect(tasksExecuted).toMatchObject([0, 1, 2, 3, 4]);
17
+ });
18
+ it('runs tasks sequentially and not in parallel', async () => {
19
+ const queue = new AsyncQueue();
20
+ const results: number[] = [];
21
+ for (let i = 0; i < 5; i++) {
22
+ queue.run(async () => {
23
+ results.push(i);
24
+ await sleep(10);
25
+ results.push(i);
26
+ });
27
+ }
28
+ await queue.flush();
29
+ expect(results).toMatchObject([0, 0, 1, 1, 2, 2, 3, 3, 4, 4]);
30
+ });
31
+ it('continues executing tasks if one task throws an error', async () => {
32
+ const queue = new AsyncQueue();
33
+
34
+ let task1threw = false;
35
+ let task2Executed = false;
36
+
37
+ queue
38
+ .run(async () => {
39
+ await sleep(100);
40
+ throw Error('task 1 throws');
41
+ })
42
+ .catch(() => {
43
+ task1threw = true;
44
+ });
45
+
46
+ await queue
47
+ .run(async () => {
48
+ task2Executed = true;
49
+ })
50
+ .catch(() => {
51
+ fail('task 2 should not have thrown');
52
+ });
53
+
54
+ expect(task1threw).toBeTruthy();
55
+ expect(task2Executed).toBeTruthy();
56
+ });
57
+ it('returns the result of the task', async () => {
58
+ const queue = new AsyncQueue();
59
+
60
+ const result = await queue.run(async () => {
61
+ await sleep(10);
62
+ return 'result';
63
+ });
64
+
65
+ expect(result).toBe('result');
66
+ });
67
+ it('returns only when the enqueued task and all previous tasks have completed', async () => {
68
+ const queue = new AsyncQueue();
69
+ const tasksExecuted: number[] = [];
70
+ for (let i = 0; i < 10; i += 1) {
71
+ queue.run(async () => {
72
+ await sleep(10);
73
+ tasksExecuted.push(i);
74
+ return i;
75
+ });
76
+ }
77
+
78
+ const result = await queue.run(async () => {
79
+ await sleep(10);
80
+ tasksExecuted.push(999);
81
+ return 'result';
82
+ });
83
+
84
+ expect(result).toBe('result');
85
+ expect(tasksExecuted).toMatchObject([...new Array(10).fill(0).map((_, idx) => idx), 999]);
86
+ });
87
+ it('can handle queue sizes of up to 10_000 tasks', async () => {
88
+ const queue = new AsyncQueue();
89
+ const tasksExecuted: number[] = [];
90
+
91
+ for (let i = 0; i < 10_000; i++) {
92
+ queue.run(async () => {
93
+ tasksExecuted.push(i);
94
+ });
95
+ }
96
+ await queue.flush();
97
+ expect(tasksExecuted).toMatchObject(new Array(10_000).fill(0).map((_, idx) => idx));
98
+ });
99
+ });
@@ -0,0 +1,57 @@
1
+ import { Mutex } from '../room/utils';
2
+
3
+ type QueueTask<T> = () => PromiseLike<T>;
4
+
5
+ enum QueueTaskStatus {
6
+ 'WAITING',
7
+ 'RUNNING',
8
+ 'COMPLETED',
9
+ }
10
+
11
+ type QueueTaskInfo = {
12
+ id: number;
13
+ enqueuedAt: number;
14
+ executedAt?: number;
15
+ status: QueueTaskStatus;
16
+ };
17
+
18
+ export class AsyncQueue {
19
+ private pendingTasks: Map<number, QueueTaskInfo>;
20
+
21
+ private taskMutex: Mutex;
22
+
23
+ private nextTaskIndex: number;
24
+
25
+ constructor() {
26
+ this.pendingTasks = new Map();
27
+ this.taskMutex = new Mutex();
28
+ this.nextTaskIndex = 0;
29
+ }
30
+
31
+ async run<T>(task: QueueTask<T>) {
32
+ const taskInfo: QueueTaskInfo = {
33
+ id: this.nextTaskIndex++,
34
+ enqueuedAt: Date.now(),
35
+ status: QueueTaskStatus.WAITING,
36
+ };
37
+ this.pendingTasks.set(taskInfo.id, taskInfo);
38
+ const unlock = await this.taskMutex.lock();
39
+ try {
40
+ taskInfo.executedAt = Date.now();
41
+ taskInfo.status = QueueTaskStatus.RUNNING;
42
+ return await task();
43
+ } finally {
44
+ taskInfo.status = QueueTaskStatus.COMPLETED;
45
+ this.pendingTasks.delete(taskInfo.id);
46
+ unlock();
47
+ }
48
+ }
49
+
50
+ async flush() {
51
+ return this.run(async () => {});
52
+ }
53
+
54
+ snapshot() {
55
+ return Array.from(this.pendingTasks.values());
56
+ }
57
+ }
@@ -0,0 +1,58 @@
1
+ import { compareVersions } from '../room/utils';
2
+ import { getBrowser } from './browserParser';
3
+
4
+ describe('browser parser', () => {
5
+ const safariUA =
6
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15';
7
+ const firefoxUA =
8
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/112.0';
9
+
10
+ const chromeUA =
11
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36';
12
+
13
+ const braveUA =
14
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36';
15
+
16
+ it('parses Safari correctly', () => {
17
+ const details = getBrowser(safariUA, true);
18
+ expect(details?.name).toBe('Safari');
19
+ expect(details?.version).toBe('16.3');
20
+ });
21
+ it('parses Firefox correctly', () => {
22
+ const details = getBrowser(firefoxUA, true);
23
+ expect(details?.name).toBe('Firefox');
24
+ expect(details?.version).toBe('112.0');
25
+ });
26
+ it('parses Chrome correctly', () => {
27
+ const details = getBrowser(chromeUA, true);
28
+ expect(details?.name).toBe('Chrome');
29
+ expect(details?.version).toBe('112.0.0.0');
30
+ });
31
+ it('detects brave as chromium based', () => {
32
+ const details = getBrowser(braveUA, true);
33
+ expect(details?.name).toBe('Chrome');
34
+ expect(details?.version).toBe('103.0.5060.134');
35
+ });
36
+ });
37
+
38
+ describe('version compare', () => {
39
+ it('compares versions correctly', () => {
40
+ expect(compareVersions('12.3.5', '11.8.9')).toBe(1);
41
+ expect(compareVersions('12.3.5', '12.3.5')).toBe(0);
42
+ expect(compareVersions('12.3.5', '14.1.5')).toBe(-1);
43
+ });
44
+ it('can handle different version lengths', () => {
45
+ expect(compareVersions('12', '11.8.9')).toBe(1);
46
+ expect(compareVersions('12', '12.0.0')).toBe(0);
47
+ expect(compareVersions('12', '14.1.5')).toBe(-1);
48
+
49
+ expect(compareVersions('12.3.5', '11')).toBe(1);
50
+ expect(compareVersions('12.0.0', '12')).toBe(0);
51
+ expect(compareVersions('12.3.5', '14')).toBe(-1);
52
+ });
53
+
54
+ it('treats empty strings as smaller', () => {
55
+ expect(compareVersions('12', '')).toBe(1);
56
+ expect(compareVersions('', '12.0.0')).toBe(-1);
57
+ });
58
+ });
@@ -0,0 +1,72 @@
1
+ // tiny, simplified version of https://github.com/lancedikson/bowser/blob/master/src/parser-browsers.js
2
+ // reduced to only differentiate Chrome(ium) based browsers / Firefox / Safari
3
+
4
+ const commonVersionIdentifier = /version\/(\d+(\.?_?\d+)+)/i;
5
+
6
+ export type DetectableBrowser = 'Chrome' | 'Firefox' | 'Safari';
7
+
8
+ export type BrowserDetails = {
9
+ name: DetectableBrowser;
10
+ version: string;
11
+ };
12
+
13
+ let browserDetails: BrowserDetails | undefined;
14
+
15
+ /**
16
+ * @internal
17
+ */
18
+ export function getBrowser(userAgent?: string, force = true) {
19
+ if (
20
+ userAgent === undefined &&
21
+ (typeof document !== 'undefined' || typeof navigator === 'undefined')
22
+ ) {
23
+ return;
24
+ }
25
+ const ua = (userAgent ?? navigator.userAgent).toLowerCase();
26
+ if (browserDetails === undefined || force) {
27
+ const browser = browsersList.find(({ test }) => test.test(ua));
28
+ browserDetails = browser?.describe(ua);
29
+ }
30
+ return browserDetails;
31
+ }
32
+
33
+ const browsersList = [
34
+ {
35
+ test: /firefox|iceweasel|fxios/i,
36
+ describe(ua: string) {
37
+ const browser: BrowserDetails = {
38
+ name: 'Firefox',
39
+ version: getMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i, ua),
40
+ };
41
+ return browser;
42
+ },
43
+ },
44
+ {
45
+ test: /chrom|crios|crmo/i,
46
+ describe(ua: string) {
47
+ const browser: BrowserDetails = {
48
+ name: 'Chrome',
49
+ version: getMatch(/(?:chrome|chromium|crios|crmo)\/(\d+(\.?_?\d+)+)/i, ua),
50
+ };
51
+
52
+ return browser;
53
+ },
54
+ },
55
+ /* Safari */
56
+ {
57
+ test: /safari|applewebkit/i,
58
+ describe(ua: string) {
59
+ const browser: BrowserDetails = {
60
+ name: 'Safari',
61
+ version: getMatch(commonVersionIdentifier, ua),
62
+ };
63
+
64
+ return browser;
65
+ },
66
+ },
67
+ ];
68
+
69
+ function getMatch(exp: RegExp, ua: string, id = 1) {
70
+ const match = ua.match(exp);
71
+ return (match && match.length >= id && match[id]) || '';
72
+ }