77 lines
2.0 KiB
TypeScript
77 lines
2.0 KiB
TypeScript
import { rmSync } from "node:fs";
|
|
import type { CloneJobResult, CloneOptions } from "@cloner/core";
|
|
|
|
/** A completed (or failed) clone held in memory for the M1 sync API. The `base` is
|
|
* the temp dir holding the run's files (kept alive so /files/* can stream them);
|
|
* it is removed on eviction. M2/M4 replace this with DB rows + S3 objects. */
|
|
export type JobRecord = {
|
|
id: string;
|
|
status: "succeeded" | "failed";
|
|
url: string;
|
|
kind: "clone" | "clone_site";
|
|
options: CloneOptions;
|
|
createdAt: number;
|
|
result?: CloneJobResult;
|
|
base?: string;
|
|
error?: string;
|
|
};
|
|
|
|
/** In-memory result store with TTL eviction. Swapped for a DB-backed store in M2. */
|
|
export class InMemoryStore {
|
|
private jobs = new Map<string, JobRecord>();
|
|
private timer: NodeJS.Timeout | null = null;
|
|
|
|
constructor(private ttlMs: number) {}
|
|
|
|
put(rec: JobRecord): void {
|
|
this.jobs.set(rec.id, rec);
|
|
}
|
|
|
|
get(id: string): JobRecord | undefined {
|
|
return this.jobs.get(id);
|
|
}
|
|
|
|
list(): JobRecord[] {
|
|
return [...this.jobs.values()].sort((a, b) => b.createdAt - a.createdAt);
|
|
}
|
|
|
|
remove(id: string): boolean {
|
|
const rec = this.jobs.get(id);
|
|
if (!rec) return false;
|
|
if (rec.base) {
|
|
try {
|
|
rmSync(rec.base, { recursive: true, force: true });
|
|
} catch {
|
|
/* best effort */
|
|
}
|
|
}
|
|
return this.jobs.delete(id);
|
|
}
|
|
|
|
sweep(): void {
|
|
const now = Date.now();
|
|
for (const rec of [...this.jobs.values()]) {
|
|
if (now - rec.createdAt > this.ttlMs) this.remove(rec.id);
|
|
}
|
|
}
|
|
|
|
/** Start a periodic sweep (unref'd so it never holds the process open). */
|
|
startSweeper(intervalMs = 60_000): void {
|
|
if (this.timer) return;
|
|
this.timer = setInterval(() => this.sweep(), intervalMs);
|
|
this.timer.unref?.();
|
|
}
|
|
|
|
stopSweeper(): void {
|
|
if (this.timer) {
|
|
clearInterval(this.timer);
|
|
this.timer = null;
|
|
}
|
|
}
|
|
|
|
/** Evict everything (used by tests / shutdown). */
|
|
clear(): void {
|
|
for (const id of [...this.jobs.keys()]) this.remove(id);
|
|
}
|
|
}
|