fix(pace-caret): prevent null dereference in update() (@byseif21) (#7226)

### Description
  
* `update()` could hit a race where settings became `null` between
checks, causing a`TypeError` at runtime.

* Async callbacks (setTimeout) accessed the global settings after it was
cleared, leading to runaway errors.
<img width="1887" height="755" alt="Screenshot 2025-12-12 135316"
src="https://github.com/user-attachments/assets/ba6bd13c-c052-4870-ba7c-cfeae916b6dc"
/>


**fix**  
settings once (currentSettings) at the start of update() and use that
for all property access and scheduling, so the loop never touches a
null/stale reference.
This commit is contained in:
Seif Soliman 2025-12-14 01:10:19 +02:00 committed by GitHub
parent e51550683a
commit b1aa14c6b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -130,7 +130,12 @@ export async function init(): Promise<void> {
}
export async function update(expectedStepEnd: number): Promise<void> {
if (settings === null || !TestState.isActive || TestState.resultVisible) {
const currentSettings = settings;
if (
currentSettings === null ||
!TestState.isActive ||
TestState.resultVisible
) {
return;
}
@ -146,8 +151,8 @@ export async function update(expectedStepEnd: number): Promise<void> {
const duration = absoluteStepEnd - now;
caret.goTo({
wordIndex: settings.currentWordIndex,
letterIndex: settings.currentLetterIndex,
wordIndex: currentSettings.currentWordIndex,
letterIndex: currentSettings.currentLetterIndex,
isLanguageRightToLeft: TestState.isLanguageRightToLeft,
isDirectionReversed: TestState.isDirectionReversed,
animate: true,
@ -157,12 +162,14 @@ export async function update(expectedStepEnd: number): Promise<void> {
},
});
// Normal case - schedule next step
settings.timeout = setTimeout(
currentSettings.timeout = setTimeout(
() => {
update(expectedStepEnd + (settings?.spc ?? 0) * 1000).catch(() => {
settings = null;
});
if (settings !== currentSettings) return;
update(expectedStepEnd + (currentSettings.spc ?? 0) * 1000).catch(
() => {
if (settings === currentSettings) settings = null;
},
);
},
Math.max(0, duration),
);