import path from "node:path"; import { type ClosableStateStores, createClosableStateStores, createStateStores, type StateStores, } from "../infrastructure/adapters/sqlite/create-state-stores.js"; import { acquireSyncLock } from "../infrastructure/locking/tff-lock.js"; import { STATE_DB_FILE } from "@tff/core"; export interface SyncLockResult { ok: true; data: { action: "skipped"; reason: string; }; } function resolveLockPath(dbPath?: string): string { return dbPath ?? path.join(process.cwd(), STATE_DB_FILE); } export async function withSyncLock( fn: (stores: StateStores) => Promise, opts?: { dbPath?: string }, ): Promise { const lockPath = resolveLockPath(opts?.dbPath); const release = await acquireSyncLock(lockPath, 5000); if (release === null) { return { ok: true, data: { action: "skipped", reason: "Lock held by another process" } }; } try { const stores = createStateStores(opts?.dbPath); return await fn(stores); } finally { await release(); } } export async function withClosableSyncLock( fn: (stores: ClosableStateStores) => Promise, opts?: { dbPath?: string }, ): Promise { const lockPath = resolveLockPath(opts?.dbPath); const release = await acquireSyncLock(lockPath, 5000); if (release === null) { return { ok: true, data: { action: "skipped", reason: "Lock held by another process" } }; } try { const stores = createClosableStateStores(opts?.dbPath); return await fn(stores); } finally { await release(); } }