Skip to content

Conversation

@mfairley
Copy link

@mfairley mfairley commented Jan 31, 2026

Summary

Vendors the ts-debounce library and modifies it to use CriticalTimers instead of the native setTimeout/clearTimeout.

This allows the debounce function to work correctly when users override CriticalTimers with 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

    • Replaced external debounce with an internal vendored debounce implementation that improves timer reliability, provides promise-based behavior, stronger cancellation, and max-wait handling.
  • Chores

    • Removed the external debounce dependency and added a changeset recording the patch update.

@changeset-bot
Copy link

changeset-bot bot commented Jan 31, 2026

🦋 Changeset detected

Latest commit: c9f26c9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
livekit-client Patch

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 31, 2026

📝 Walkthrough

Walkthrough

Replaces external ts-debounce with a vendored debounce implementation at src/room/debounce.ts, updates imports in three source files to use the new module, and removes ts-debounce from package.json.

Changes

Cohort / File(s) Summary
Dependency removal
package.json
Removed the ts-debounce dependency entry.
Vendored debounce implementation
src/room/debounce.ts
Added a Promise-based debounce utility with Options (isImmediate, maxWait, callback), DebouncedFunction interface, debounce factory, pending-resolvers queue, cancel support, and CriticalTimers-based timing.
Import updates
src/room/PCTransport.ts, src/room/track/LocalTrack.ts, src/room/track/RemoteVideoTrack.ts
Replaced imports from external ts-debounce to the new local ./debounce / ../debounce module; usage sites unchanged.
Changeset
.changeset/silly-views-win.md
Added a changeset noting vendoring of debounce and related patch update.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I stitched a little timer, snug and bright,
Promises waiting in a cozy line,
No extra package — just local light,
Hops and callbacks, tidy and fine.
🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: vendor ts-debounce' accurately summarizes the main change: replacing external ts-debounce dependency with a vendored internal implementation.
Linked Issues check ✅ Passed The PR addresses the core requirement from issue #158 by vendoring ts-debounce and implementing it with CriticalTimers to ensure debounce respects user-overridden timers needed for background-compatible iOS operation.
Out of Scope Changes check ✅ Passed All changes are directly scoped to vendoring ts-debounce and integrating CriticalTimers: package.json dependency removal, import path updates in multiple files, new debounce.ts implementation, and changeset documentation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between c49f048 and 93e705c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • package.json
  • src/room/PCTransport.ts
  • src/room/debounce.ts
  • src/room/track/LocalTrack.ts
  • src/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.

Comment on lines +29 to +111
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 = [];
};
Copy link

@coderabbitai coderabbitai bot Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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).

Copy link
Author

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.

Copy link

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.

Copy link
Contributor

@lukasIO lukasIO left a 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

@mfairley mfairley force-pushed the vendor-ts-debounce branch 2 times, most recently from cfff0bd to 2bb0483 Compare February 3, 2026 16:47
Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2bb0483 and c9f26c9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • .changeset/silly-views-win.md
  • package.json
  • src/room/PCTransport.ts
  • src/room/debounce.ts
  • src/room/track/LocalTrack.ts
  • src/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/clearTimeout with CriticalTimers is 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.

Comment on lines +29 to +31
/* 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';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto disconnect fails after screen lock/unlock

2 participants