@moonwall/cli
Version:
Testing framework for the Moon family of projects
84 lines (81 loc) • 3.21 kB
JavaScript
// src/internal/effect/FileLock.ts
import { FileSystem } from "@effect/platform";
import { Duration, Effect, Schedule } from "effect";
import * as os from "os";
// src/internal/effect/errors.ts
import { Data } from "effect";
var PortDiscoveryError = class extends Data.TaggedError("PortDiscoveryError") {
};
var NodeLaunchError = class extends Data.TaggedError("NodeLaunchError") {
};
var NodeReadinessError = class extends Data.TaggedError("NodeReadinessError") {
};
var ProcessError = class extends Data.TaggedError("ProcessError") {
};
var StartupCacheError = class extends Data.TaggedError("StartupCacheError") {
};
var FileLockError = class extends Data.TaggedError("FileLockError") {
};
// src/internal/effect/FileLock.ts
var LOCK_MAX_AGE = Duration.minutes(2);
var LOCK_POLL_INTERVAL = Duration.millis(500);
var isProcessAlive = (pid) => Effect.try(() => {
process.kill(pid, 0);
return true;
}).pipe(Effect.orElseSucceed(() => false));
var isLockStale = (info) => Effect.gen(function* () {
const isTimedOut = Date.now() - info.timestamp > Duration.toMillis(LOCK_MAX_AGE);
if (isTimedOut) return true;
const isSameHost = info.hostname === os.hostname();
if (!isSameHost) return false;
const alive = yield* isProcessAlive(info.pid);
return !alive;
});
var cleanupStaleLock = (lockPath) => Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const infoPath = `${lockPath}/lock.json`;
const exists = yield* fs.exists(infoPath).pipe(Effect.orElseSucceed(() => false));
if (!exists) return;
const content = yield* fs.readFileString(infoPath).pipe(Effect.orElseSucceed(() => ""));
const info = yield* Effect.try(() => JSON.parse(content)).pipe(
Effect.orElseSucceed(() => null)
);
if (!info) return;
const stale = yield* isLockStale(info);
if (stale) {
yield* fs.remove(lockPath, { recursive: true }).pipe(Effect.ignore);
}
});
var writeLockInfo = (lockPath) => Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const info = {
pid: process.pid,
timestamp: Date.now(),
hostname: os.hostname()
};
yield* fs.writeFileString(`${lockPath}/lock.json`, JSON.stringify(info)).pipe(Effect.ignore);
});
var tryAcquireLock = (lockPath) => Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* cleanupStaleLock(lockPath);
yield* fs.makeDirectory(lockPath).pipe(Effect.mapError(() => new FileLockError({ reason: "acquisition_failed", lockPath })));
yield* writeLockInfo(lockPath);
});
var acquireFileLock = (lockPath, timeout = Duration.minutes(2)) => tryAcquireLock(lockPath).pipe(
Effect.retry(Schedule.fixed(LOCK_POLL_INTERVAL).pipe(Schedule.upTo(timeout))),
Effect.catchAll(() => Effect.fail(new FileLockError({ reason: "timeout", lockPath })))
);
var releaseFileLock = (lockPath) => Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
yield* fs.remove(lockPath, { recursive: true }).pipe(Effect.ignore);
});
var withFileLock = (lockPath, effect, timeout = Duration.minutes(2)) => Effect.acquireUseRelease(
acquireFileLock(lockPath, timeout),
() => effect,
() => releaseFileLock(lockPath)
);
export {
acquireFileLock,
releaseFileLock,
withFileLock
};