feat: a ton of caching

This commit is contained in:
2026-03-13 17:42:39 +07:00 Unverified
parent 942ed655b5
commit 4b9a67476d
4 changed files with 59 additions and 19 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"dev": "bun run --watch src/index.ts", "dev": "bun run --hot src/index.ts",
"build": "bun build.ts" "build": "bun build.ts"
}, },
"dependencies": { "dependencies": {
+8 -6
View File
@@ -2,8 +2,16 @@ import cluster from "node:cluster";
import process from "node:process"; import process from "node:process";
import { AssetManager } from "./lib/assets"; import { AssetManager } from "./lib/assets";
declare global {
var forked: boolean;
}
if (cluster.isPrimary) { if (cluster.isPrimary) {
if (!globalThis.forked) {
for (let i = 0; i < 2; i++) cluster.fork(); for (let i = 0; i < 2; i++) cluster.fork();
globalThis.forked = true;
}
queueMicrotask(async () => { queueMicrotask(async () => {
const assets = new AssetManager(); const assets = new AssetManager();
@@ -13,12 +21,6 @@ if (cluster.isPrimary) {
assets.getMissingAssets(); assets.getMissingAssets();
}, 86400000); // refresh assets every 24 hours }, 86400000); // refresh assets every 24 hours
}); });
import.meta.hot.dispose(() => {
for (const worker of Object.values(cluster.workers ?? {})) {
worker?.kill();
}
});
} else { } else {
await import("./server"); await import("./server");
console.log(`Worker ${process.pid} started`); console.log(`Worker ${process.pid} started`);
+33
View File
@@ -54,6 +54,39 @@ export class AssetManager {
queueMicrotask(() => write(local, data)); queueMicrotask(() => write(local, data));
return data; return data;
} }
async hook(
path: string,
cb: (
data: ArrayBuffer,
) => Promise<ArrayBuffer | Buffer> | ArrayBuffer | Buffer,
): Promise<ArrayBuffer | Buffer> {
const key = `${path}-transformed`;
return this.fnCache(key, async () => {
const data = await this.load(path);
return cb(data);
});
}
async fnCache(
key: string,
get: () => Promise<ArrayBuffer | Buffer> | ArrayBuffer | Buffer,
) {
const cacheKey = join("store", hash(`${key}-fnCache`).toString(36));
if (this.cache.has(cacheKey)) return this.cache.get(cacheKey)!;
const cached = file(cacheKey);
if (await cached.exists()) return cached.arrayBuffer();
const returned = await get();
this.cache.set(
cacheKey,
returned instanceof ArrayBuffer
? returned
: (returned.buffer as ArrayBuffer),
);
queueMicrotask(() => write(file(cacheKey), returned));
return returned;
}
} }
export async function getEnkaAvatarStore(): Promise<AvatarStore> { export async function getEnkaAvatarStore(): Promise<AvatarStore> {
+10 -5
View File
@@ -68,23 +68,28 @@ export function card(app: App) {
}), }),
), ),
assets assets
.load( .hook(
avatar.SideIconName.replace( avatar.SideIconName.replace(
"AvatarIcon_Side", "AvatarIcon_Side",
"Gacha_AvatarImg", "Gacha_AvatarImg",
), ),
(buf) => featherImage(buf, [0, 0.3, 0, 0]),
) )
.then(async (buf) => ({ .then(async (data) => ({
src: "/avatar", src: "/avatar",
data: await featherImage(buf, [0, 0.3, 0, 0]), data,
})), })),
assets
.fnCache("bg", () =>
generateBackground({ generateBackground({
element: avatar.Element, element: avatar.Element,
width: 1218, width: 1218,
height: 508, height: 508,
}).then((r) => ({ }).then((r) => r.canvas.toBuffer("image/webp")),
)
.then((data) => ({
src: "/bg", src: "/bg",
data: r.canvas.toBuffer("image/webp"), data,
})), })),
]); ]);
console.log( console.log(