77 lines
2.2 KiB
JavaScript
77 lines
2.2 KiB
JavaScript
const crypto = require("crypto");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
class ToolCache {
|
|
constructor(options = {}) {
|
|
this.directory = options.directory;
|
|
this.now = options.now || Date.now;
|
|
}
|
|
|
|
get(key, ttlSeconds) {
|
|
if (!ttlSeconds) return null;
|
|
const file = this.fileFor(key);
|
|
try {
|
|
const value = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
if (!Number.isFinite(value.created_at) || value.created_at + ttlSeconds * 1000 <= this.now()) {
|
|
fs.rmSync(file, { force: true });
|
|
return null;
|
|
}
|
|
return value.data;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
set(key, data) {
|
|
fs.mkdirSync(this.directory, { recursive: true });
|
|
const file = this.fileFor(key);
|
|
const temporary = `${file}.${process.pid}.tmp`;
|
|
fs.writeFileSync(temporary, `${JSON.stringify({ created_at: this.now(), data })}\n`);
|
|
try { fs.renameSync(temporary, file); }
|
|
catch (error) {
|
|
if (!["EEXIST", "EPERM"].includes(error.code)) throw error;
|
|
fs.rmSync(file, { force: true });
|
|
fs.renameSync(temporary, file);
|
|
}
|
|
this.prune(200);
|
|
return data;
|
|
}
|
|
|
|
clear() {
|
|
fs.rmSync(this.directory, { recursive: true, force: true });
|
|
fs.mkdirSync(this.directory, { recursive: true });
|
|
}
|
|
|
|
stats() {
|
|
try {
|
|
const files = fs.readdirSync(this.directory).filter((name) => name.endsWith(".json"));
|
|
return {
|
|
entries: files.length,
|
|
bytes: files.reduce((total, name) => total + fs.statSync(path.join(this.directory, name)).size, 0)
|
|
};
|
|
} catch {
|
|
return { entries: 0, bytes: 0 };
|
|
}
|
|
}
|
|
|
|
prune(maximum) {
|
|
let files;
|
|
try {
|
|
files = fs.readdirSync(this.directory)
|
|
.filter((name) => name.endsWith(".json"))
|
|
.map((name) => ({ name, mtime: fs.statSync(path.join(this.directory, name)).mtimeMs }))
|
|
.sort((left, right) => right.mtime - left.mtime);
|
|
} catch {
|
|
return;
|
|
}
|
|
for (const file of files.slice(maximum)) fs.rmSync(path.join(this.directory, file.name), { force: true });
|
|
}
|
|
|
|
fileFor(key) {
|
|
return path.join(this.directory, `${crypto.createHash("sha256").update(String(key)).digest("hex")}.json`);
|
|
}
|
|
}
|
|
|
|
module.exports = { ToolCache };
|