You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rappli/src/server/store.ts

65 lines
1.6 KiB
TypeScript

import { writeFileSync } from "node:fs";
import { writeFile, readFile } from "node:fs/promises";
import path from "node:path";
import { onExit } from "./util";
const store = <T extends Object>(
name: string,
init: () => T,
{ persistInterval = 60 * 1000 } = {}
) => {
let state = init();
(state as any)._storeName = name;
const filePath = path.join(process.cwd(), `${name}.json`);
const persist = (sync = false) => {
const write = sync ? writeFileSync : writeFile;
return write(filePath, JSON.stringify(state));
};
const load = async () => {
try {
const newState = await readFile(filePath, { encoding: "utf8" });
state = JSON.parse(newState);
if ((state as any)._storeName !== name) {
console.error(`File at "${filePath}" expected to be a Rappli store`);
process.exit(1);
}
} catch (err) {
if (typeof err === "object" && (err as any).code !== "ENOENT") {
console.dir(err);
process.exit(1);
}
}
};
const globals = globalThis as any;
(async () => {
await load();
const intervalKey = `_store-persist-${name}`;
if (globals[intervalKey] != null) {
clearInterval(globals[intervalKey]);
}
globals[intervalKey] = setInterval(function () {
persist();
}, persistInterval);
})();
const exitKey = `_store-exit-${name}`;
if (globals[exitKey]) {
globals[exitKey]();
}
globals[exitKey] = onExit(() => persist(true));
const getState = () => state;
return [getState] as [typeof getState];
};
export const [state] = store("data", () => ({
version: 0,
likes: 0,
}));