Skip to content

Commit 91824ab

Browse files
authored
refactor: vendor ts-debounce (#1800)
1 parent f54d243 commit 91824ab

File tree

7 files changed

+132
-21
lines changed

7 files changed

+132
-21
lines changed

.changeset/silly-views-win.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"livekit-client": patch
3+
---
4+
5+
Vendored ts-debounce and added critical timers to debounce function

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
"jose": "^6.1.0",
6363
"loglevel": "^1.9.2",
6464
"sdp-transform": "^2.15.0",
65-
"ts-debounce": "^4.0.0",
6665
"tslib": "2.8.1",
6766
"typed-emitter": "^2.1.0",
6867
"webrtc-adapter": "^9.0.1"

pnpm-lock.yaml

Lines changed: 9 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/room/PCTransport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Mutex } from '@livekit/mutex';
22
import { EventEmitter } from 'events';
33
import { parse, write } from 'sdp-transform';
4-
import { debounce } from 'ts-debounce';
54
import type { MediaDescription, SessionDescription } from 'sdp-transform';
65
import log, { LoggerNames, getLogger } from '../logger';
6+
import { debounce } from './debounce';
77
import { NegotiationError, UnexpectedConnectionState } from './errors';
88
import type { LoggerOptions } from './types';
99
import { ddExtensionURI, isSVCCodec, isSafari } from './utils';

src/room/debounce.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* Originally from ts-debounce (https://github.com/chodorowicz/ts-debounce)
3+
* with the following license:
4+
*
5+
* MIT License
6+
*
7+
* Copyright (c) 2017 Jakub Chodorowicz
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in all
17+
* copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
* SOFTWARE.
26+
*
27+
* Modified to use CriticalTimers for reliable timer execution.
28+
*/
29+
/* eslint-disable @typescript-eslint/no-this-alias, @typescript-eslint/no-unused-expressions, @typescript-eslint/no-shadow */
30+
import CriticalTimers from './timers';
31+
32+
export type Options<Result> = {
33+
isImmediate?: boolean;
34+
maxWait?: number;
35+
callback?: (data: Result) => void;
36+
};
37+
38+
export interface DebouncedFunction<Args extends any[], F extends (...args: Args) => any> {
39+
(this: ThisParameterType<F>, ...args: Args & Parameters<F>): Promise<ReturnType<F>>;
40+
cancel: (reason?: any) => void;
41+
}
42+
43+
interface DebouncedPromise<FunctionReturn> {
44+
resolve: (result: FunctionReturn) => void;
45+
reject: (reason?: any) => void;
46+
}
47+
48+
export function debounce<Args extends any[], F extends (...args: Args) => any>(
49+
func: F,
50+
waitMilliseconds = 50,
51+
options: Options<ReturnType<F>> = {},
52+
): DebouncedFunction<Args, F> {
53+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
54+
const isImmediate = options.isImmediate ?? false;
55+
const callback = options.callback ?? false;
56+
const maxWait = options.maxWait;
57+
let lastInvokeTime = Date.now();
58+
59+
let promises: DebouncedPromise<ReturnType<F>>[] = [];
60+
61+
function nextInvokeTimeout() {
62+
if (maxWait !== undefined) {
63+
const timeSinceLastInvocation = Date.now() - lastInvokeTime;
64+
65+
if (timeSinceLastInvocation + waitMilliseconds >= maxWait) {
66+
return maxWait - timeSinceLastInvocation;
67+
}
68+
}
69+
70+
return waitMilliseconds;
71+
}
72+
73+
const debouncedFunction = function (this: ThisParameterType<F>, ...args: Parameters<F>) {
74+
const context = this;
75+
return new Promise<ReturnType<F>>((resolve, reject) => {
76+
const invokeFunction = function () {
77+
timeoutId = undefined;
78+
lastInvokeTime = Date.now();
79+
if (!isImmediate) {
80+
const result = func.apply(context, args);
81+
callback && callback(result);
82+
// biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code
83+
promises.forEach(({ resolve }) => resolve(result));
84+
promises = [];
85+
}
86+
};
87+
88+
const shouldCallNow = isImmediate && timeoutId === undefined;
89+
90+
if (timeoutId !== undefined) {
91+
CriticalTimers.clearTimeout(timeoutId);
92+
}
93+
94+
timeoutId = CriticalTimers.setTimeout(invokeFunction, nextInvokeTimeout());
95+
96+
if (shouldCallNow) {
97+
const result = func.apply(context, args);
98+
callback && callback(result);
99+
return resolve(result);
100+
}
101+
promises.push({ resolve, reject });
102+
});
103+
};
104+
105+
debouncedFunction.cancel = function (reason?: any) {
106+
if (timeoutId !== undefined) {
107+
CriticalTimers.clearTimeout(timeoutId);
108+
}
109+
// biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code
110+
promises.forEach(({ reject }) => reject(reason));
111+
promises = [];
112+
};
113+
114+
return debouncedFunction;
115+
}

src/room/track/LocalTrack.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Mutex } from '@livekit/mutex';
2-
import { debounce } from 'ts-debounce';
32
import { getBrowser } from '../../utils/browserParser';
43
import DeviceManager from '../DeviceManager';
4+
import { debounce } from '../debounce';
55
import { DeviceUnsupportedError, TrackInvalidError } from '../errors';
66
import { TrackEvent } from '../events';
77
import type { LoggerOptions } from '../types';

src/room/track/RemoteVideoTrack.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { debounce } from 'ts-debounce';
1+
import { debounce } from '../debounce';
22
import { TrackEvent } from '../events';
33
import type { VideoReceiverStats } from '../stats';
44
import { computeBitrate } from '../stats';

0 commit comments

Comments
 (0)