// @generated by scripts/build-runtime.mjs; do not edit. // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader. import { ensureStateDir, lockPath } from "./chunk-APZVBSM6.ts"; import { LOCKFILE_FS_ADAPTER, LOCK_GUARD_STALE_MS, LOCK_GUARD_UPDATE_MS } from "./chunk-F5QL2GPY.ts"; // src/lock.ts import { randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import lockfile from "proper-lockfile"; var LOCK_STALE_MS = 30 * 60 * 1e3; var MAX_PROCESS_ID = 2147483647; async function withLock(command, fn, options = {}) { await ensureStateDir(); const lock = { id: randomUUID(), pid: process.pid, command, startedAt: (/* @__PURE__ */ new Date()).toISOString() }; let guard; let result; let failed = false; let failure; try { try { guard = await acquireGuard(); } catch (error) { if (!isLockHeldError(error)) throw error; throw await describeHeldLock(); } let inspection = await inspectLock(); if (inspection.status === "valid" && isStaleLock(inspection.lock)) { if (!options.reclaimStale) { throw new Error( `pi-sync lock is stale (pid ${inspection.lock.pid}). Run /sync unlock --stale, then retry.` ); } guard.throwIfCompromised(); const rechecked = await inspectLock(); if (rechecked.status !== "valid" || rechecked.lock.id !== inspection.lock.id || !isStaleLock(rechecked.lock)) { throw new Error("pi-sync lock changed while preparing transaction recovery; retry."); } await fs.rm(lockPath(), { force: true }); inspection = { status: "missing" }; } if (inspection.status === "valid") { throw new Error( `pi-sync is already running (${inspection.lock.command}, pid ${inspection.lock.pid}, started ${inspection.lock.startedAt}).` ); } if (inspection.status === "unreadable") { throw new Error( "pi-sync lock metadata is unreadable. Run /sync unlock --stale after verifying no sync is running." ); } await fs.writeFile(lockPath(), JSON.stringify(lock, null, " "), { flag: "wx" }); guard.throwIfCompromised(); result = await fn(); guard.throwIfCompromised(); } catch (error) { failed = true; failure = error; } try { const current = await readLock(); if (current?.id === lock.id) await fs.rm(lockPath(), { force: true }); } catch (error) { if (!failed) { failed = true; failure = error; } } if (guard) { const releaseError = await releaseGuard(guard); if (releaseError && !failed) { failed = true; failure = releaseError; } } if (failed) throw failure; return result; } async function inspectLock() { try { const text = await fs.readFile(lockPath(), "utf8"); if (text.trim().length === 0) return { status: "unreadable" }; const parsed = JSON.parse(text); return isLockFile(parsed) ? { status: "valid", lock: parsed } : { status: "unreadable" }; } catch (error) { if (error.code === "ENOENT") return { status: "missing" }; if (error instanceof SyntaxError) return { status: "unreadable" }; throw error; } } async function readLock() { const inspection = await inspectLock(); return inspection.status === "valid" ? inspection.lock : void 0; } function isLockGuardHeld() { return lockfile.check(lockPath(), { fs: LOCKFILE_FS_ADAPTER, lockfilePath: `${lockPath()}.guard`, realpath: false, stale: LOCK_GUARD_STALE_MS }); } function isStaleLock(lock) { try { process.kill(lock.pid, 0); return false; } catch (error) { if (error.code === "ESRCH") return true; return Date.now() - Date.parse(lock.startedAt) > LOCK_STALE_MS; } } async function unlock(ctx, options) { throwIfAborted(options.signal); await ensureStateDir(); throwIfAborted(options.signal); let guard; try { guard = await acquireGuard(); } catch (error) { if (!isLockHeldError(error)) throw error; ctx.ui.notify((await describeHeldLock()).message, "warning"); return; } let failed = false; let failure; try { await unlockGuarded(ctx, options); } catch (error) { failed = true; failure = error; } const releaseError = await releaseGuard(guard); if (releaseError && !failed) { failed = true; failure = releaseError; } if (failed) throw failure; } async function unlockGuarded(ctx, options) { throwIfAborted(options.signal); let inspection = await inspectLock(); throwIfAborted(options.signal); if (inspection.status === "missing") { ctx.ui.notify("No pi-sync lock is present.", "info"); return; } if (inspection.status === "unreadable") { if (!options.stale) { ctx.ui.notify( "Pi-sync lock metadata is unreadable. Use /sync unlock --stale only after verifying no sync is running.", "warning" ); return; } inspection = await inspectLock(); throwIfAborted(options.signal); if (inspection.status === "unreadable") { throwIfAborted(options.signal); await fs.rm(lockPath(), { force: true }); if (!options.signal?.aborted) { ctx.ui.notify( "Removed unreadable pi-sync lock. No settings, files, sync state, or remote data were changed.", "info" ); } return; } if (inspection.status === "missing") { ctx.ui.notify("No pi-sync lock is present.", "info"); return; } } if (!isStaleLock(inspection.lock)) { ctx.ui.notify("Lock owner is still live; refusing to remove it.", "warning"); return; } throwIfAborted(options.signal); await fs.rm(lockPath(), { force: true }); if (!options.signal?.aborted) { ctx.ui.notify( "Removed stale pi-sync lock. No settings, files, sync state, or remote data were changed.", "info" ); } } function throwIfAborted(signal) { if (!signal?.aborted) return; throw signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted", "AbortError"); } async function releaseGuard(guard) { try { await guard.release(); return void 0; } catch (error) { return guard.isCompromised() ? void 0 : error; } } async function acquireGuard() { let compromisedError; const release = await lockfile.lock(lockPath(), { fs: LOCKFILE_FS_ADAPTER, lockfilePath: `${lockPath()}.guard`, realpath: false, stale: LOCK_GUARD_STALE_MS, update: LOCK_GUARD_UPDATE_MS, onCompromised: (error) => { compromisedError = error; } }); return { release, throwIfCompromised: () => { if (compromisedError) throw compromisedError; }, isCompromised: () => compromisedError !== void 0 }; } async function describeHeldLock() { const current = await readLock(); if (current && isStaleLock(current)) { return new Error("pi-sync lock owner exited; retry shortly while the lock guard expires."); } if (current) { return new Error( `Pi-sync is currently running (${current.command}, pid ${current.pid}, started ${current.startedAt}).` ); } return new Error( "Pi-sync is currently running (lock metadata is unreadable or still being written)." ); } function isLockHeldError(error) { return error.code === "ELOCKED"; } function isLockFile(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return false; const lock = value; return typeof lock.id === "string" && lock.id.length > 0 && Number.isInteger(lock.pid) && (lock.pid ?? 0) > 0 && (lock.pid ?? 0) <= MAX_PROCESS_ID && typeof lock.command === "string" && lock.command.length > 0 && typeof lock.startedAt === "string" && Number.isFinite(Date.parse(lock.startedAt)); } export { withLock, inspectLock, isLockGuardHeld, isStaleLock, unlock }; //# sourceMappingURL=chunk-LJDSHNS3.ts.map