// storage/status-store/file-lock.ts — SPEC-753: File-lock for status.json / pending.json writes
//
// Uses an in-memory async queue (same pattern as file-mutex.ts) with a 5-second timeout.
// This prevents the concurrent-write race that corrupted status.json on 2026-04-24.
//
// Why not proper-lockfile? proper-lockfile requires process-level coordination; our server
// is single-process, so in-memory serialization is sufficient and has zero dep overhead.
const lockQueues = new Map();
export const STATUS_LOCK_TIMEOUT_MS = 5_000;
export const STATUS_LOCK_ERROR_CODE = 'status_lock_timeout';
/**
 * Execute `fn` while holding an exclusive write-lock on `filePath`.
 * Concurrent calls for the same path are serialized (FIFO).
 * Throws with code `status_lock_timeout` if the lock is not acquired within 5 s.
 */
export async function withStatusLock(filePath, fn) {
    // Race the wait for the previous lock against the timeout
    const prev = lockQueues.get(filePath) ?? Promise.resolve();
    let release;
    const next = new Promise((resolve) => {
        release = resolve;
    });
    lockQueues.set(filePath, next);
    // Wait for previous lock with timeout
    await Promise.race([
        prev,
        new Promise((_, reject) => setTimeout(() => {
            const err = new Error(`[Planu] status_lock_timeout: could not acquire write-lock on ${filePath} within ${STATUS_LOCK_TIMEOUT_MS}ms`);
            err.code = STATUS_LOCK_ERROR_CODE;
            reject(err);
        }, STATUS_LOCK_TIMEOUT_MS)),
    ]);
    try {
        return await fn();
    }
    finally {
        release();
        // Clean up only if no newer lock has been queued
        if (lockQueues.get(filePath) === next) {
            lockQueues.delete(filePath);
        }
    }
}
/** Active lock count — for diagnostics / tests only. */
export function activeStatusLockCount() {
    return lockQueues.size;
}
/** Clear all locks — TEST USE ONLY. */
export function clearAllStatusLocks() {
    lockQueues.clear();
}
//# sourceMappingURL=file-lock.js.map