const CHAT_STORAGE_GLOBAL_LOCK = 'openmaic:chat-storage:all'; const DEFAULT_EXCLUSIVE_ACQUIRE_TIMEOUT_MS = 5_000; type FallbackLockMode = 'shared' | 'exclusive'; interface FallbackLockWaiter { mode: FallbackLockMode; start(): void; } const fallbackWaiters: FallbackLockWaiter[] = []; let fallbackReaders = 0; let fallbackWriter = false; export function chatStoragePartitionLockName(key: string): string { const name = `openmaic:chat-storage:${encodeURIComponent(key)}`; return name === CHAT_STORAGE_GLOBAL_LOCK ? `${name}:partition` : name; } function locks(): LockManager | undefined { return typeof navigator !== 'undefined' ? navigator.locks : undefined; } function pumpFallbackLocks(): void { if (fallbackWriter || fallbackWaiters.length === 0) return; if (fallbackWaiters[0]!.mode === 'exclusive') { if (fallbackReaders === 0) fallbackWaiters.shift()!.start(); return; } while (fallbackWaiters[0]?.mode === 'shared' && !fallbackWriter) { fallbackWaiters.shift()!.start(); } } function withFallbackRuntimeLock( mode: FallbackLockMode, work: () => Promise, signal?: AbortSignal, ): Promise { return new Promise((resolve, reject) => { let started = false; const waiter: FallbackLockWaiter = { mode, start() { started = true; signal?.removeEventListener('abort', onAbort); if (mode === 'shared') fallbackReaders += 1; else fallbackWriter = true; void Promise.resolve() .then(work) .then(resolve, reject) .finally(() => { if (mode === 'shared') fallbackReaders -= 1; else fallbackWriter = false; pumpFallbackLocks(); }); }, }; const onAbort = (): void => { if (started) return; const index = fallbackWaiters.indexOf(waiter); if (index >= 0) fallbackWaiters.splice(index, 1); reject(signal?.reason); pumpFallbackLocks(); }; if (signal?.aborted) { reject(signal.reason); return; } signal?.addEventListener('abort', onAbort, { once: true }); fallbackWaiters.push(waiter); pumpFallbackLocks(); }); } /** Let runtime writers run together while excluding whole-store maintenance. */ export async function withRuntimeStorageSharedLock(work: () => Promise): Promise { const manager = locks(); if (manager) { return manager.request(CHAT_STORAGE_GLOBAL_LOCK, { mode: 'shared' }, work); } return typeof window === 'undefined' ? work() : withFallbackRuntimeLock('shared', work); } /** * Bound caller wait time without cancelling the protected work. The shared * lock remains held until `work` really settles, so late storage writes cannot * resume after destructive maintenance has overtaken them. */ export async function withRuntimeStorageSharedLockUntilSettled( work: () => Promise, timeoutMs: number, ): Promise { const protectedWork = withRuntimeStorageSharedLock(work); let timer: ReturnType | undefined; try { return await Promise.race([ protectedWork, new Promise((_, reject) => { timer = setTimeout(() => reject(new Error(`timed out after ${timeoutMs}ms`)), timeoutMs); }), ]); } finally { clearTimeout(timer); } } export interface RuntimeStorageExclusiveLockOptions { acquireTimeoutMs?: number; } export class RuntimeStorageLockAcquisitionTimeoutError extends Error {} /** Quiesce runtime mutations before destructive whole-store work. */ export function withRuntimeStorageExclusiveLock( work: () => Promise, options: RuntimeStorageExclusiveLockOptions = {}, ): Promise { const manager = locks(); if (!manager && typeof window === 'undefined') { return work(); } const configuredTimeout = options.acquireTimeoutMs ?? DEFAULT_EXCLUSIVE_ACQUIRE_TIMEOUT_MS; const acquireTimeoutMs = Number.isFinite(configuredTimeout) && configuredTimeout > 0 ? configuredTimeout : DEFAULT_EXCLUSIVE_ACQUIRE_TIMEOUT_MS; let acquired = false; let timer: ReturnType | undefined; const controller = new AbortController(); const timeoutError = new RuntimeStorageLockAcquisitionTimeoutError( `Timed out acquiring the runtime maintenance lock after ${acquireTimeoutMs}ms`, ); const guardedWork = async (): Promise => { acquired = true; clearTimeout(timer); return work(); }; // Cross-realm exclusion is impossible without Web Locks. The fallback still // coordinates every writer in this realm and preserves the pre-cutover // ability to perform an explicit whole-database clear. const request = manager ? manager.request(CHAT_STORAGE_GLOBAL_LOCK, { signal: controller.signal }, guardedWork) : withFallbackRuntimeLock('exclusive', guardedWork, controller.signal); return new Promise((resolve, reject) => { timer = setTimeout(() => { if (acquired) return; controller.abort(timeoutError); reject(timeoutError); }, acquireTimeoutMs); void request.then( (value) => { clearTimeout(timer); resolve(value); }, (error) => { clearTimeout(timer); reject(error); }, ); }); } /** * Let a bounded public operation finish while retaining the exclusive lock * until its underlying destructive work actually settles. `releaseCaller` * may be invoked once the caller-visible safety budget has elapsed; the lock * remains owned until `work` itself returns. */ export function withRuntimeStorageExclusiveLockUntilSettled( work: (releaseCaller: (value: T) => void) => Promise, options: RuntimeStorageExclusiveLockOptions = {}, ): Promise { let callerSettled = false; let resolveCaller!: (value: T) => void; let rejectCaller!: (reason?: unknown) => void; const caller = new Promise((resolve, reject) => { resolveCaller = resolve; rejectCaller = reject; }); const releaseCaller = (value: T): void => { if (callerSettled) return; callerSettled = true; resolveCaller(value); }; const protectedWork = withRuntimeStorageExclusiveLock(async () => { try { const value = await work(releaseCaller); releaseCaller(value); return value; } catch (error) { if (!callerSettled) { callerSettled = true; rejectCaller(error); } throw error; } }, options); void protectedWork.catch((error) => { if (!callerSettled) { callerSettled = true; rejectCaller(error); } }); return caller; } /** Compatibility aliases for the chat cutover's partitioned writers. */ export const withChatStorageSharedLock = withRuntimeStorageSharedLock; export const withChatStorageExclusiveLock = withRuntimeStorageExclusiveLock;