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",
"scripts": {
"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"
},
"dependencies": {
+9 -7
View File
@@ -2,8 +2,16 @@ import cluster from "node:cluster";
import process from "node:process";
import { AssetManager } from "./lib/assets";
declare global {
var forked: boolean;
}
if (cluster.isPrimary) {
for (let i = 0; i < 2; i++) cluster.fork();
if (!globalThis.forked) {
for (let i = 0; i < 2; i++) cluster.fork();
globalThis.forked = true;
}
queueMicrotask(async () => {
const assets = new AssetManager();
@@ -13,12 +21,6 @@ if (cluster.isPrimary) {
assets.getMissingAssets();
}, 86400000); // refresh assets every 24 hours
});
import.meta.hot.dispose(() => {
for (const worker of Object.values(cluster.workers ?? {})) {
worker?.kill();
}
});
} else {
await import("./server");
console.log(`Worker ${process.pid} started`);
+33
View File
@@ -54,6 +54,39 @@ export class AssetManager {
queueMicrotask(() => write(local, 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> {
+16 -11
View File
@@ -68,24 +68,29 @@ export function card(app: App) {
}),
),
assets
.load(
.hook(
avatar.SideIconName.replace(
"AvatarIcon_Side",
"Gacha_AvatarImg",
),
(buf) => featherImage(buf, [0, 0.3, 0, 0]),
)
.then(async (buf) => ({
.then(async (data) => ({
src: "/avatar",
data: await featherImage(buf, [0, 0.3, 0, 0]),
data,
})),
assets
.fnCache("bg", () =>
generateBackground({
element: avatar.Element,
width: 1218,
height: 508,
}).then((r) => r.canvas.toBuffer("image/webp")),
)
.then((data) => ({
src: "/bg",
data,
})),
generateBackground({
element: avatar.Element,
width: 1218,
height: 508,
}).then((r) => ({
src: "/bg",
data: r.canvas.toBuffer("image/webp"),
})),
]);
console.log(
`${Date.now() - start}ms - Image resources loaded\n${resources.map((r) => `- ${r.src} ${(r.data.byteLength / 1000).toFixed(2)}Kb`).join("\n")}`,