From e7337145d2b9a4b8f4e92f24634009648b4fced6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Fri, 20 Mar 2026 20:38:04 -0500 Subject: [PATCH] feat: some caching --- packages/website-ue/src/core/api/abstract.ts | 49 ++++++++++++-------- packages/website-ue/src/core/api/server.ts | 8 ++-- tsconfig.json | 3 +- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/packages/website-ue/src/core/api/abstract.ts b/packages/website-ue/src/core/api/abstract.ts index f0ba8ef21..5a6121871 100644 --- a/packages/website-ue/src/core/api/abstract.ts +++ b/packages/website-ue/src/core/api/abstract.ts @@ -11,25 +11,8 @@ import type { TankList } from "../../protos/tank_list"; import type { Tanks } from "../../protos/tanks"; export abstract class AbstractAPI { - private _tankListCache: TankList | undefined; - protected abstract _tankList(): Promise; - async tankList() { - if (this._tankListCache === undefined) { - this._tankListCache = await this._tankList(); - } - - return this._tankListCache; - } - - private _tankCache: Record = {}; - protected abstract _tank(id: string): Promise; - async tank(id: string) { - if (this._tankCache[id] === undefined) { - this._tankCache[id] = await this._tank(id); - } - - return this._tankCache[id]; - } + abstract tankList(): Promise; + abstract tank(id: string): Promise; private _tanksCache: Tanks | undefined; protected abstract _tanks(): Promise; @@ -181,3 +164,31 @@ export abstract class AbstractAPI { return this._popularTanksCache; } } + +export function Cache( + discriminator: (...args: unknown[]) => string = () => "", +) { + const cache = new WeakMap>(); + + return function ( + _target: unknown, + _propertyKey: string, + descriptor: PropertyDescriptor, + ) { + const original = descriptor.value; + + descriptor.value = async function (...args: unknown[]) { + const key = discriminator(...args); + const thisCache = cache.get(this) ?? new Map(); + + if (!cache.has(this)) cache.set(this, thisCache); + if (thisCache.has(key)) return thisCache.get(key); + + const result = await original.apply(this, args); + + thisCache.set(key, result); + + return result; + }; + }; +} diff --git a/packages/website-ue/src/core/api/server.ts b/packages/website-ue/src/core/api/server.ts index 50c176865..d1b6588a7 100644 --- a/packages/website-ue/src/core/api/server.ts +++ b/packages/website-ue/src/core/api/server.ts @@ -15,7 +15,7 @@ import type { PopularTanks } from "../../protos/popular_tanks"; import type { Tank } from "../../protos/tank"; import type { TankListEntry } from "../../protos/tank_list"; import type { Tanks } from "../../protos/tanks"; -import { AbstractAPI } from "./abstract"; +import { AbstractAPI, Cache } from "./abstract"; if (typeof window !== "undefined") { throw new Error("ServerAPI is being evaluated in the browser"); @@ -133,7 +133,8 @@ export class ServerAPI extends AbstractAPI { return strings; } - protected async _tankList() { + @Cache() + async tankList() { const group = this.metadata.group("TankEntity"); const list: TankListEntry[] = []; const strings = await this.groupedGameStrings("en", "TankEntity", true); @@ -167,7 +168,8 @@ export class ServerAPI extends AbstractAPI { return data; } - protected async _tank(id: string) { + @Cache((id) => id) + async tank(id: string) { const tankList = await this.tankList(); const tankListEntry = tankList.list.find((tank) => tank.id === id); diff --git a/tsconfig.json b/tsconfig.json index 7d6c174bf..c7327a6a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,8 @@ "paths": { "@protos/*": ["./packages/closed/protos/*"] }, - "strictNullChecks": true + "strictNullChecks": true, + "experimentalDecorators": true }, "exclude": ["node_modules/**/*", "dist"] }