UNPKG

@tanstack/offline-transactions

Version:

Offline-first transaction capabilities for TanStack DB

89 lines (76 loc) 2.26 kB
import { BaseLeaderElection } from './LeaderElection' export class WebLocksLeader extends BaseLeaderElection { private lockName: string private releaseLock: (() => void) | null = null constructor(lockName = `offline-executor-leader`) { super() this.lockName = lockName } async requestLeadership(): Promise<boolean> { if (!this.isWebLocksSupported()) { return false } if (this.isLeaderState) { return true } try { // First try to acquire the lock with ifAvailable const available = await navigator.locks.request( this.lockName, { mode: `exclusive`, ifAvailable: true, }, (lock) => { return lock !== null }, ) if (!available) { return false } // Set state immediately to prevent duplicate notifications // when the async lock acquisition calls notifyLeadershipChange(true). // The guard in notifyLeadershipChange checks `isLeaderState !== isLeader`, // so setting this to true here prevents the callback from firing again. this.isLeaderState = true // Lock is available, now acquire it for real and hold it navigator.locks.request( this.lockName, { mode: `exclusive`, }, async (lock) => { if (lock) { this.notifyLeadershipChange(true) // Hold the lock until released return new Promise<void>((resolve) => { this.releaseLock = () => { this.notifyLeadershipChange(false) resolve() } }) } }, ) return true } catch (error) { if (error instanceof Error && error.name === `AbortError`) { return false } console.warn(`Web Locks leadership request failed:`, error) return false } } releaseLeadership(): void { if (this.releaseLock) { this.releaseLock() this.releaseLock = null } } private isWebLocksSupported(): boolean { return typeof navigator !== `undefined` && `locks` in navigator } static isSupported(): boolean { return typeof navigator !== `undefined` && `locks` in navigator } }