Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ Thumbs.db

# Temp files
*.tmp
.zero-mock-*.tmp
.zero-mock-tmp-*.json
.zero-mock-bak-*.json
57 changes: 57 additions & 0 deletions src/cleanup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import fs from 'fs';
import { printFatal } from './errors';

const activeLocks = new Set<string>();
const cleanupTasks = new Set<() => void>();

export function registerLock(lockFilePath: string): void {
activeLocks.add(lockFilePath);
}

export function releaseLock(lockFilePath: string): void {
activeLocks.delete(lockFilePath);
try {
if (fs.existsSync(lockFilePath)) {
fs.unlinkSync(lockFilePath);
}
} catch (_) {
// Ignore cleanup errors
}
}

export function registerCleanupTask(task: () => void): void {
cleanupTasks.add(task);
}

function runCleanup() {
for (const lockFilePath of activeLocks) {
try {
if (fs.existsSync(lockFilePath)) {
fs.unlinkSync(lockFilePath);
}
} catch (_) {
// Ignore cleanup errors
}
}
activeLocks.clear();

for (const task of cleanupTasks) {
try {
task();
} catch (_) {
// Ignore cleanup task errors
}
}
cleanupTasks.clear();
}

// Module load time registry
process.on('exit', runCleanup);
process.on('SIGINT', () => { runCleanup(); process.exit(0); });
process.on('SIGTERM', () => { runCleanup(); process.exit(0); });
process.on('SIGHUP', () => { runCleanup(); process.exit(0); });
process.on('uncaughtException', (err) => {
console.error(err);
runCleanup();
printFatal('SERVER_CRASH', err.message);
});
23 changes: 7 additions & 16 deletions src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import pc from 'picocolors';
import fs from 'fs';
import path from 'path';
import os from 'os';
import crypto from 'crypto';
import { registerLock } from './cleanup';

export type ErrorCode =
| 'FILE_NOT_FOUND'
Expand Down Expand Up @@ -86,7 +89,9 @@ export function validateConfig(file: string, port: number): void {
}

export function acquireLock(file: string): void {
const lockFilePath = `${file}.zero-mock.lock`;
const absoluteDbPath = path.resolve(file);
const hash = crypto.createHash('md5').update(absoluteDbPath).digest('hex');
const lockFilePath = path.join(os.tmpdir(), `zero-mock-${hash}.lock`);

if (fs.existsSync(lockFilePath)) {
try {
Expand Down Expand Up @@ -127,19 +132,5 @@ export function acquireLock(file: string): void {
}
}

const cleanup = () => {
try {
if (fs.existsSync(lockFilePath)) {
fs.unlinkSync(lockFilePath);
}
} catch (_) { /* ignore cleanup errors */ }
};

process.on('exit', cleanup);
process.on('SIGINT', () => { cleanup(); process.exit(130); });
process.on('SIGTERM', () => { cleanup(); process.exit(143); });
process.on('uncaughtException', (err) => {
cleanup();
printFatal('SERVER_CRASH', err.message);
});
registerLock(lockFilePath);
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { bootstrap } from "./server/bootstrap";
import { runWizard } from "./cli-wizard";
import { validateConfig, acquireLock, printError, printFatal } from "./errors";
import { clearSavedConfig } from "./config-store";
import { registerCleanupTask } from "./cleanup";

type CliOpts = {
file?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/store/jsonStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class JsonStoreImpl {
const run = async (): Promise<void> => {
const target = this.backingPath!;
const dir = dirname(target);
const tmp = join(dir, `.zero-mock-${randomBytes(8).toString("hex")}.tmp`);
const tmp = join(dir, `.zero-mock-tmp-${randomBytes(8).toString("hex")}.json`);
const payload = `${JSON.stringify(this.data, null, 2)}\n`;
try {
await writeFile(tmp, payload, "utf8");
Expand Down
Loading