Merge pull request #227 from aaronsb/fix/226-window-timers-globalthis

fix: pass Obsidian source-code review — window timers + globalThis (closes #226)
This commit is contained in:
Aaron Bockelie 2026-06-09 11:15:18 -05:00 committed by GitHub
commit fad7adb1cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 20 additions and 17 deletions

View file

@ -286,8 +286,7 @@ export class SemanticRouter implements RouterContext {
// Add timeout to prevent hanging when no file is active
try {
const timeoutPromise = new Promise((_, reject) =>
// eslint-disable-next-line obsidianmd/prefer-window-timers
setTimeout(() => reject(new Error('Timeout: No active file in Obsidian. Please open a file first.')), 5000)
window.setTimeout(() => reject(new Error('Timeout: No active file in Obsidian. Please open a file first.')), 5000)
);
const activeResult = await Promise.race([
this.api.getActiveFile(),

View file

@ -10,11 +10,10 @@ export interface DebugLogger {
info(message: string, ...args: unknown[]): void;
}
// Indirect console reference — this is a debug utility that legitimately needs
// console access. Picking the host at runtime keeps this safe in both the
// Obsidian renderer (window) and the Jest/Node test environment (globalThis).
// eslint-disable-next-line obsidianmd/no-global-this
const _console: Console = (typeof window !== 'undefined' ? window : globalThis).console;
// Indirect console reference — this debug utility legitimately needs console
// access. Obsidian's renderer provides window; the Jest node env aliases
// window to globalThis in tests/setup.ts, so window.console resolves in both.
const _console: Console = window.console;
export class Debug {
private static debugEnabled = false;

View file

@ -960,9 +960,8 @@ export class ObsidianAPI {
// Exponential backoff: allow time for sync processes to stabilize
const delay = Math.pow(2, attempt) * baseDelayMs;
Debug.log(`${operationType} failed (attempt ${attempt + 1}/${maxRetries}), retrying in ${delay}ms... Error: ${errorMessage}`);
// Backoff delay; must also work in the Jest Node test env.
// eslint-disable-next-line obsidianmd/prefer-window-timers
await new Promise(resolve => setTimeout(resolve, delay));
// Backoff delay; window is aliased to globalThis in the Jest node env.
await new Promise(resolve => window.setTimeout(resolve, delay));
continue;
}

View file

@ -22,7 +22,7 @@ export class SessionManager extends EventEmitter {
private sessions: Map<string, SessionInfo> = new Map();
private sessionOrder: string[] = []; // Track session order for LRU eviction
private options: SessionManagerOptions;
private cleanupInterval?: ReturnType<typeof setInterval>;
private cleanupInterval?: number;
constructor(options: Partial<SessionManagerOptions> = {}) {
super();
@ -37,10 +37,9 @@ export class SessionManager extends EventEmitter {
* Start the session manager
*/
start(): void {
// Background cleanup; not tied to any popout window, and must also work
// in the Jest Node test env where `window` is undefined.
// eslint-disable-next-line obsidianmd/prefer-window-timers
this.cleanupInterval = setInterval(() => {
// Background cleanup; not tied to any popout window. The Jest node env
// aliases window to globalThis (tests/setup.ts) so window.setInterval works.
this.cleanupInterval = window.setInterval(() => {
this.cleanupExpiredSessions();
}, this.options.checkInterval);
@ -52,8 +51,7 @@ export class SessionManager extends EventEmitter {
*/
stop(): void {
if (this.cleanupInterval) {
// eslint-disable-next-line obsidianmd/prefer-window-timers
clearInterval(this.cleanupInterval);
window.clearInterval(this.cleanupInterval);
this.cleanupInterval = undefined;
}
this.sessions.clear();

View file

@ -1,5 +1,13 @@
// Jest setup file for Obsidian plugin testing
// Source code uses window.* timers and window.console (per obsidianmd's
// prefer-window-timers / no-global-this rules — Obsidian always provides
// window at runtime). The node test env has no window, so alias it to
// globalThis, which supplies setTimeout/setInterval/clearInterval/console.
if (typeof (globalThis as { window?: unknown }).window === 'undefined') {
(globalThis as { window?: unknown }).window = globalThis;
}
// Mock performance API if not available
if (!global.performance) {
global.performance = {