-
Notifications
You must be signed in to change notification settings - Fork 247
refactor: vendor ts-debounce #1800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: c9f26c9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughReplaces external Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/room/debounce.ts`:
- Around line 29-85: Replace the current immediate/non‑immediate handling so
queued promises always get resolved when the debounced action actually runs, and
fix lint issues: remove the this‑alias (use this directly when calling
func.apply), replace short‑circuit expressions like "callback &&
callback(result)" with "if (callback) callback(result);" to avoid
no‑unused‑expressions, ensure invokeFunction always resolves all promises with
the result (for both isImmediate true/false) and then clears promises, and in
debouncedFunction.cancel set timeoutId = undefined after clearing the timer so
subsequent immediate calls detect no active timeout; update any shadowed
variables accordingly (inspect debouncedFunction, invokeFunction, cancel,
promises, timeoutId, isImmediate, callback, nextInvokeTimeout).
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
package.jsonsrc/room/PCTransport.tssrc/room/debounce.tssrc/room/track/LocalTrack.tssrc/room/track/RemoteVideoTrack.ts
💤 Files with no reviewable changes (1)
- package.json
🧰 Additional context used
🪛 Biome (2.3.13)
src/room/debounce.ts
[error] 57-57: This callback passed to forEach() iterable method should not return a value.
Either remove this return or remove the returned value.
(lint/suspicious/useIterableCallbackReturn)
[error] 83-83: This callback passed to forEach() iterable method should not return a value.
Either remove this return or remove the returned value.
(lint/suspicious/useIterableCallbackReturn)
🪛 ESLint
src/room/debounce.ts
[error] 49-49: Unexpected aliasing of 'this' to local variable.
(@typescript-eslint/no-this-alias)
[error] 56-56: Expected an assignment or function call and instead saw an expression.
(@typescript-eslint/no-unused-expressions)
[error] 57-57: 'resolve' is already declared in the upper scope on line 50 column 40.
(@typescript-eslint/no-shadow)
[error] 72-72: Expected an assignment or function call and instead saw an expression.
(@typescript-eslint/no-unused-expressions)
🪛 GitHub Actions: Test
src/room/debounce.ts
[warning] 48-48: Unexpected unnamed function
[error] 49-49: Unexpected aliasing of 'this' to local variable (@typescript-eslint/no-this-alias)
🪛 GitHub Check: test
src/room/debounce.ts
[warning] 79-79:
Unexpected unnamed function
[failure] 72-72:
Expected an assignment or function call and instead saw an expression
[failure] 57-57:
'resolve' is already declared in the upper scope on line 50 column 40
[failure] 56-56:
Expected an assignment or function call and instead saw an expression
[warning] 51-51:
Unexpected unnamed function
[failure] 49-49:
Unexpected aliasing of 'this' to local variable
[warning] 48-48:
Unexpected unnamed function
🔇 Additional comments (3)
src/room/PCTransport.ts (1)
6-6: LGTM: local debounce import swap.src/room/track/RemoteVideoTrack.ts (1)
1-1: LGTM: local debounce import swap.src/room/track/LocalTrack.ts (1)
4-4: LGTM: local debounce import swap.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| const isImmediate = options.isImmediate ?? false; | ||
| const callback = options.callback ?? false; | ||
| const maxWait = options.maxWait; | ||
| let lastInvokeTime = Date.now(); | ||
|
|
||
| let promises: DebouncedPromise<ReturnType<F>>[] = []; | ||
|
|
||
| function nextInvokeTimeout() { | ||
| if (maxWait !== undefined) { | ||
| const timeSinceLastInvocation = Date.now() - lastInvokeTime; | ||
|
|
||
| if (timeSinceLastInvocation + waitMilliseconds >= maxWait) { | ||
| return maxWait - timeSinceLastInvocation; | ||
| } | ||
| } | ||
|
|
||
| return waitMilliseconds; | ||
| } | ||
|
|
||
| const debouncedFunction = function (this: ThisParameterType<F>, ...args: Parameters<F>) { | ||
| const context = this; | ||
| return new Promise<ReturnType<F>>((resolve, reject) => { | ||
| const invokeFunction = function () { | ||
| timeoutId = undefined; | ||
| lastInvokeTime = Date.now(); | ||
| if (!isImmediate) { | ||
| const result = func.apply(context, args); | ||
| callback && callback(result); | ||
| promises.forEach(({ resolve }) => resolve(result)); | ||
| promises = []; | ||
| } | ||
| }; | ||
|
|
||
| const shouldCallNow = isImmediate && timeoutId === undefined; | ||
|
|
||
| if (timeoutId !== undefined) { | ||
| CriticalTimers.clearTimeout(timeoutId); | ||
| } | ||
|
|
||
| timeoutId = CriticalTimers.setTimeout(invokeFunction, nextInvokeTimeout()); | ||
|
|
||
| if (shouldCallNow) { | ||
| const result = func.apply(context, args); | ||
| callback && callback(result); | ||
| return resolve(result); | ||
| } | ||
| promises.push({ resolve, reject }); | ||
| }); | ||
| }; | ||
|
|
||
| debouncedFunction.cancel = function (reason?: any) { | ||
| if (timeoutId !== undefined) { | ||
| CriticalTimers.clearTimeout(timeoutId); | ||
| } | ||
| promises.forEach(({ reject }) => reject(reason)); | ||
| promises = []; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix lint failures and resolve immediate‑mode promises correctly.
CI is failing due to no‑this‑alias / no‑unused‑expressions / no‑shadow / useIterableCallbackReturn. Also, in isImmediate mode queued calls never resolve because invokeFunction skips resolving, and cancel doesn’t reset timeoutId, which breaks the next immediate call.
✅ Proposed fix
- const callback = options.callback ?? false;
+ const callback = options.callback;
const maxWait = options.maxWait;
let lastInvokeTime = Date.now();
+ let lastResult: ReturnType<F>;
+ let hasLastResult = false;
let promises: DebouncedPromise<ReturnType<F>>[] = [];
const debouncedFunction = function (this: ThisParameterType<F>, ...args: Parameters<F>) {
- const context = this;
return new Promise<ReturnType<F>>((resolve, reject) => {
- const invokeFunction = function () {
+ const invokeFunction = () => {
timeoutId = undefined;
lastInvokeTime = Date.now();
- if (!isImmediate) {
- const result = func.apply(context, args);
- callback && callback(result);
- promises.forEach(({ resolve }) => resolve(result));
- promises = [];
- }
+ if (isImmediate) {
+ if (hasLastResult) {
+ promises.forEach(({ resolve: promiseResolve }) => {
+ promiseResolve(lastResult);
+ });
+ }
+ promises = [];
+ return;
+ }
+ const result = func.apply(this, args);
+ lastResult = result;
+ hasLastResult = true;
+ callback?.(result);
+ promises.forEach(({ resolve: promiseResolve }) => {
+ promiseResolve(result);
+ });
+ promises = [];
};
const shouldCallNow = isImmediate && timeoutId === undefined;
if (timeoutId !== undefined) {
CriticalTimers.clearTimeout(timeoutId);
}
timeoutId = CriticalTimers.setTimeout(invokeFunction, nextInvokeTimeout());
if (shouldCallNow) {
- const result = func.apply(context, args);
- callback && callback(result);
- return resolve(result);
+ const result = func.apply(this, args);
+ lastResult = result;
+ hasLastResult = true;
+ callback?.(result);
+ resolve(result);
+ return;
}
promises.push({ resolve, reject });
});
};
debouncedFunction.cancel = function (reason?: any) {
if (timeoutId !== undefined) {
CriticalTimers.clearTimeout(timeoutId);
+ timeoutId = undefined;
}
- promises.forEach(({ reject }) => reject(reason));
+ promises.forEach(({ reject: promiseReject }) => {
+ promiseReject(reason);
+ });
promises = [];
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const isImmediate = options.isImmediate ?? false; | |
| const callback = options.callback ?? false; | |
| const maxWait = options.maxWait; | |
| let lastInvokeTime = Date.now(); | |
| let promises: DebouncedPromise<ReturnType<F>>[] = []; | |
| function nextInvokeTimeout() { | |
| if (maxWait !== undefined) { | |
| const timeSinceLastInvocation = Date.now() - lastInvokeTime; | |
| if (timeSinceLastInvocation + waitMilliseconds >= maxWait) { | |
| return maxWait - timeSinceLastInvocation; | |
| } | |
| } | |
| return waitMilliseconds; | |
| } | |
| const debouncedFunction = function (this: ThisParameterType<F>, ...args: Parameters<F>) { | |
| const context = this; | |
| return new Promise<ReturnType<F>>((resolve, reject) => { | |
| const invokeFunction = function () { | |
| timeoutId = undefined; | |
| lastInvokeTime = Date.now(); | |
| if (!isImmediate) { | |
| const result = func.apply(context, args); | |
| callback && callback(result); | |
| promises.forEach(({ resolve }) => resolve(result)); | |
| promises = []; | |
| } | |
| }; | |
| const shouldCallNow = isImmediate && timeoutId === undefined; | |
| if (timeoutId !== undefined) { | |
| CriticalTimers.clearTimeout(timeoutId); | |
| } | |
| timeoutId = CriticalTimers.setTimeout(invokeFunction, nextInvokeTimeout()); | |
| if (shouldCallNow) { | |
| const result = func.apply(context, args); | |
| callback && callback(result); | |
| return resolve(result); | |
| } | |
| promises.push({ resolve, reject }); | |
| }); | |
| }; | |
| debouncedFunction.cancel = function (reason?: any) { | |
| if (timeoutId !== undefined) { | |
| CriticalTimers.clearTimeout(timeoutId); | |
| } | |
| promises.forEach(({ reject }) => reject(reason)); | |
| promises = []; | |
| }; | |
| const isImmediate = options.isImmediate ?? false; | |
| const callback = options.callback; | |
| const maxWait = options.maxWait; | |
| let lastInvokeTime = Date.now(); | |
| let lastResult: ReturnType<F>; | |
| let hasLastResult = false; | |
| let promises: DebouncedPromise<ReturnType<F>>[] = []; | |
| function nextInvokeTimeout() { | |
| if (maxWait !== undefined) { | |
| const timeSinceLastInvocation = Date.now() - lastInvokeTime; | |
| if (timeSinceLastInvocation + waitMilliseconds >= maxWait) { | |
| return maxWait - timeSinceLastInvocation; | |
| } | |
| } | |
| return waitMilliseconds; | |
| } | |
| const debouncedFunction = function (this: ThisParameterType<F>, ...args: Parameters<F>) { | |
| return new Promise<ReturnType<F>>((resolve, reject) => { | |
| const invokeFunction = () => { | |
| timeoutId = undefined; | |
| lastInvokeTime = Date.now(); | |
| if (isImmediate) { | |
| if (hasLastResult) { | |
| promises.forEach(({ resolve: promiseResolve }) => { | |
| promiseResolve(lastResult); | |
| }); | |
| } | |
| promises = []; | |
| return; | |
| } | |
| const result = func.apply(this, args); | |
| lastResult = result; | |
| hasLastResult = true; | |
| callback?.(result); | |
| promises.forEach(({ resolve: promiseResolve }) => { | |
| promiseResolve(result); | |
| }); | |
| promises = []; | |
| }; | |
| const shouldCallNow = isImmediate && timeoutId === undefined; | |
| if (timeoutId !== undefined) { | |
| CriticalTimers.clearTimeout(timeoutId); | |
| } | |
| timeoutId = CriticalTimers.setTimeout(invokeFunction, nextInvokeTimeout()); | |
| if (shouldCallNow) { | |
| const result = func.apply(this, args); | |
| lastResult = result; | |
| hasLastResult = true; | |
| callback?.(result); | |
| resolve(result); | |
| return; | |
| } | |
| promises.push({ resolve, reject }); | |
| }); | |
| }; | |
| debouncedFunction.cancel = function (reason?: any) { | |
| if (timeoutId !== undefined) { | |
| CriticalTimers.clearTimeout(timeoutId); | |
| timeoutId = undefined; | |
| } | |
| promises.forEach(({ reject: promiseReject }) => { | |
| promiseReject(reason); | |
| }); | |
| promises = []; | |
| }; |
🧰 Tools
🪛 Biome (2.3.13)
[error] 57-57: This callback passed to forEach() iterable method should not return a value.
Either remove this return or remove the returned value.
(lint/suspicious/useIterableCallbackReturn)
[error] 83-83: This callback passed to forEach() iterable method should not return a value.
Either remove this return or remove the returned value.
(lint/suspicious/useIterableCallbackReturn)
🪛 ESLint
[error] 49-49: Unexpected aliasing of 'this' to local variable.
(@typescript-eslint/no-this-alias)
[error] 56-56: Expected an assignment or function call and instead saw an expression.
(@typescript-eslint/no-unused-expressions)
[error] 57-57: 'resolve' is already declared in the upper scope on line 50 column 40.
(@typescript-eslint/no-shadow)
[error] 72-72: Expected an assignment or function call and instead saw an expression.
(@typescript-eslint/no-unused-expressions)
🪛 GitHub Actions: Test
[warning] 48-48: Unexpected unnamed function
[error] 49-49: Unexpected aliasing of 'this' to local variable (@typescript-eslint/no-this-alias)
🪛 GitHub Check: test
[warning] 79-79:
Unexpected unnamed function
[failure] 72-72:
Expected an assignment or function call and instead saw an expression
[failure] 57-57:
'resolve' is already declared in the upper scope on line 50 column 40
[failure] 56-56:
Expected an assignment or function call and instead saw an expression
[warning] 51-51:
Unexpected unnamed function
[failure] 49-49:
Unexpected aliasing of 'this' to local variable
[warning] 48-48:
Unexpected unnamed function
🤖 Prompt for AI Agents
In `@src/room/debounce.ts` around lines 29 - 85, Replace the current
immediate/non‑immediate handling so queued promises always get resolved when the
debounced action actually runs, and fix lint issues: remove the this‑alias (use
this directly when calling func.apply), replace short‑circuit expressions like
"callback && callback(result)" with "if (callback) callback(result);" to avoid
no‑unused‑expressions, ensure invokeFunction always resolves all promises with
the result (for both isImmediate true/false) and then clears promises, and in
debouncedFunction.cancel set timeoutId = undefined after clearing the timer so
subsequent immediate calls detect no active timeout; update any shadowed
variables accordingly (inspect debouncedFunction, invokeFunction, cancel,
promises, timeoutId, isImmediate, callback, nextInvokeTimeout).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm copying over the code from ts-debounce exactly as is so I wouldn't want to make any changes if the current code has been working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Fetch the original ts-debounce source code from GitHub to compare
curl -s https://raw.githubusercontent.com/chodorowicz/ts-debounce/master/src/index.ts
Length of output: 2438
🏁 Script executed:
#!/bin/bash
# Find lint configuration files to understand how to disable rules
fd -t f '(eslintrc|biome\.json|tsconfig)' -d 3
# Also check if there are any existing lint disable comments in the codebase
rg -n '(eslint-disable|biome-ignore)' -g '*.ts' -C 1 | head -20
Length of output: 1379
🏁 Script executed:
#!/bin/bash
# Find ESLint and Biome config files
fd -t f '(\.eslintrc|eslintrc\.|biome\.json)' -H
# Also check if there's any documentation about vendored code
rg -i 'vendor' -g '*.md' -C 2 | head -30
Length of output: 671
🏁 Script executed:
#!/bin/bash
# Search more broadly for config files at root level
ls -la | grep -E '(eslint|biome|prettier)'
# Check package.json for lint configuration
cat package.json | jq '.eslintConfig // empty'
# Look for biome.json or biome.jsonc
fd -t f 'biome' -e json -e jsonc
# Check for .eslintrc.* files
fd -H -t f 'eslintrc'
Length of output: 344
🏁 Script executed:
#!/bin/bash
# Check the ESLint configuration to understand how to add overrides
cat eslint.config.mjs
# Also check if biome is configured elsewhere
cat biome.json 2>/dev/null || echo "No biome.json found"
Length of output: 6190
@mfairley You're absolutely right to preserve the original code! I confirmed the vendored code is identical to the upstream ts-debounce source (except for CriticalTimers). The lint failures are due to your codebase's stricter rules, not bugs in ts-debounce.
The standard approach for vendored code is to disable linting rather than modify proven implementations. Your codebase already uses this pattern in files like src/room/timers.ts and src/utils/TypedPromise.ts.
Add these comments at the top of src/room/debounce.ts (after the existing header comment):
/**
* Vendored from ts-debounce (https://github.com/chodorowicz/ts-debounce)
* Modified to use CriticalTimers for reliable timer execution.
*/
/* eslint-disable `@typescript-eslint/no-this-alias`, `@typescript-eslint/no-unused-expressions`, `@typescript-eslint/no-shadow` */
// biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code from ts-debounce
import CriticalTimers from './timers';This will silence the CI failures while keeping the working implementation intact.
✏️ Learnings added
Learnt from: mfairley
Repo: livekit/client-sdk-js PR: 1800
File: src/room/debounce.ts:53-109
Timestamp: 2026-02-03T16:54:41.064Z
Learning: When vendoring external library code into src/room/debounce.ts (or similar), preserve the original implementation exactly and use eslint-disable and biome-ignore comments to handle lint rule differences rather than modifying the proven code.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
93e705c to
fa264bf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, just the debounce files needs proper linting
cfff0bd to
2bb0483
Compare
2bb0483 to
c9f26c9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/room/debounce.ts`:
- Around line 29-31: Remove the backticks from the eslint-disable comment at the
top of debounce.ts so the rule names are valid (e.g., change
``@typescript-eslint/no-this-alias`` to `@typescript-eslint/no-this-alias`), and
move/adjust the biome-ignore so it targets the actual forEach usages rather than
only the import: add inline comments "biome-ignore
lint/suspicious/useIterableCallbackReturn: vendored code" immediately above the
two promises.forEach calls (the one resolving promises and the one rejecting
promises) so the vendored forEach usages on those lines are suppressed.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
.changeset/silly-views-win.mdpackage.jsonsrc/room/PCTransport.tssrc/room/debounce.tssrc/room/track/LocalTrack.tssrc/room/track/RemoteVideoTrack.ts
💤 Files with no reviewable changes (1)
- package.json
🚧 Files skipped from review as they are similar to previous changes (4)
- src/room/track/LocalTrack.ts
- .changeset/silly-views-win.md
- src/room/PCTransport.ts
- src/room/track/RemoteVideoTrack.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: mfairley
Repo: livekit/client-sdk-js PR: 1800
File: src/room/debounce.ts:53-109
Timestamp: 2026-02-03T16:54:41.064Z
Learning: When vendoring external library code into src/room/debounce.ts (or similar), preserve the original implementation exactly and use eslint-disable and biome-ignore comments to handle lint rule differences rather than modifying the proven code.
📚 Learning: 2026-02-03T16:54:41.064Z
Learnt from: mfairley
Repo: livekit/client-sdk-js PR: 1800
File: src/room/debounce.ts:53-109
Timestamp: 2026-02-03T16:54:41.064Z
Learning: When vendoring external library code into src/room/debounce.ts (or similar), preserve the original implementation exactly and use eslint-disable and biome-ignore comments to handle lint rule differences rather than modifying the proven code.
Applied to files:
src/room/debounce.ts
🪛 Biome (2.3.13)
src/room/debounce.ts
[error] 83-83: This callback passed to forEach() iterable method should not return a value.
Either remove this return or remove the returned value.
(lint/suspicious/useIterableCallbackReturn)
[error] 109-109: This callback passed to forEach() iterable method should not return a value.
Either remove this return or remove the returned value.
(lint/suspicious/useIterableCallbackReturn)
🪛 ESLint
src/room/debounce.ts
[error] 29-29: Definition for rule '@typescript-eslint/no-this-alias' was not found.
(@typescript-eslint/no-this-alias)
[error] 29-29: Definition for rule '@typescript-eslint/no-unused-expressions' was not found.
(@typescript-eslint/no-unused-expressions)
[error] 29-29: Definition for rule '@typescript-eslint/no-shadow' was not found.
(@typescript-eslint/no-shadow)
[error] 75-75: Unexpected aliasing of 'this' to local variable.
(@typescript-eslint/no-this-alias)
[error] 82-82: Expected an assignment or function call and instead saw an expression.
(@typescript-eslint/no-unused-expressions)
[error] 83-83: 'resolve' is already declared in the upper scope on line 76 column 40.
(@typescript-eslint/no-shadow)
[error] 98-98: Expected an assignment or function call and instead saw an expression.
(@typescript-eslint/no-unused-expressions)
🪛 GitHub Actions: Test
src/room/debounce.ts
[error] 29-29: Definition for rule '@typescript-eslint/no-this-alias' was not found
🪛 GitHub Check: test
src/room/debounce.ts
[warning] 105-105:
Unexpected unnamed function
[failure] 98-98:
Expected an assignment or function call and instead saw an expression
[failure] 83-83:
'resolve' is already declared in the upper scope on line 76 column 40
[failure] 82-82:
Expected an assignment or function call and instead saw an expression
[warning] 77-77:
Unexpected unnamed function
[failure] 75-75:
Unexpected aliasing of 'this' to local variable
[warning] 74-74:
Unexpected unnamed function
[failure] 29-29:
Definition for rule '@typescript-eslint/no-shadow' was not found
[failure] 29-29:
Definition for rule '@typescript-eslint/no-unused-expressions' was not found
[failure] 29-29:
Definition for rule '@typescript-eslint/no-this-alias' was not found
🔇 Additional comments (2)
src/room/debounce.ts (2)
33-47: LGTM!Type definitions are well-structured and properly exported for consumers.
90-94: CriticalTimers integration looks correct.The key modification replacing native
setTimeout/clearTimeoutwithCriticalTimersis properly applied. This ensures the debounce function will respect user-overridden timers (e.g.,react-native-background-timer), addressing the iOS screen lock disconnection issue.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| /* eslint-disable `@typescript-eslint/no-this-alias`, `@typescript-eslint/no-unused-expressions`, `@typescript-eslint/no-shadow` */ | ||
| // biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code from ts-debounce | ||
| import CriticalTimers from './timers'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix eslint-disable syntax: remove backticks from rule names.
The CI is failing because the rule names are wrapped in backticks, which makes ESLint search for non-existent rules like `@typescript-eslint/no-this-alias` (with literal backticks). Additionally, the biome-ignore on line 30 only suppresses the next line (the import), not the actual violations on lines 83 and 109.
🔧 Proposed fix
-/* eslint-disable `@typescript-eslint/no-this-alias`, `@typescript-eslint/no-unused-expressions`, `@typescript-eslint/no-shadow` */
-// biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code from ts-debounce
+/* eslint-disable `@typescript-eslint/no-this-alias`, `@typescript-eslint/no-unused-expressions`, `@typescript-eslint/no-shadow` */
import CriticalTimers from './timers';Then add inline biome-ignore comments before the forEach calls:
// At line 83:
+ // biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code
promises.forEach(({ resolve }) => resolve(result));
// At line 109:
+ // biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code
promises.forEach(({ reject }) => reject(reason));🧰 Tools
🪛 ESLint
[error] 29-29: Definition for rule '@typescript-eslint/no-this-alias' was not found.
(@typescript-eslint/no-this-alias)
[error] 29-29: Definition for rule '@typescript-eslint/no-unused-expressions' was not found.
(@typescript-eslint/no-unused-expressions)
[error] 29-29: Definition for rule '@typescript-eslint/no-shadow' was not found.
(@typescript-eslint/no-shadow)
🪛 GitHub Actions: Test
[error] 29-29: Definition for rule '@typescript-eslint/no-this-alias' was not found
🪛 GitHub Check: test
[failure] 29-29:
Definition for rule '@typescript-eslint/no-shadow' was not found
[failure] 29-29:
Definition for rule '@typescript-eslint/no-unused-expressions' was not found
[failure] 29-29:
Definition for rule '@typescript-eslint/no-this-alias' was not found
🤖 Prompt for AI Agents
In `@src/room/debounce.ts` around lines 29 - 31, Remove the backticks from the
eslint-disable comment at the top of debounce.ts so the rule names are valid
(e.g., change ``@typescript-eslint/no-this-alias`` to
`@typescript-eslint/no-this-alias`), and move/adjust the biome-ignore so it
targets the actual forEach usages rather than only the import: add inline
comments "biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code"
immediately above the two promises.forEach calls (the one resolving promises and
the one rejecting promises) so the vendored forEach usages on those lines are
suppressed.
Summary
Vendors the
ts-debouncelibrary and modifies it to useCriticalTimersinstead of the nativesetTimeout/clearTimeout.This allows the debounce function to work correctly when users override
CriticalTimerswith background-compatible timers (e.g.,react-native-background-timer), which is necessary for iOS apps that need to maintain LiveKit connections while the screen is locked.Related Issues
Fixes livekit/client-sdk-react-native#158
Summary by CodeRabbit
Refactor
Chores