mirror of https://github.com/openclaw/openclaw.git
58 lines
1.6 KiB
TypeScript
58 lines
1.6 KiB
TypeScript
export type ScopedExpiringIdCache<TScope extends string | number, TId extends string | number> = {
|
|
record: (scope: TScope, id: TId, now?: number) => void;
|
|
has: (scope: TScope, id: TId, now?: number) => boolean;
|
|
clear: () => void;
|
|
};
|
|
|
|
export function createScopedExpiringIdCache<
|
|
TScope extends string | number,
|
|
TId extends string | number,
|
|
>(options: {
|
|
store: Map<string, Map<string, number>>;
|
|
ttlMs: number;
|
|
cleanupThreshold: number;
|
|
}): ScopedExpiringIdCache<TScope, TId> {
|
|
const ttlMs = Math.max(0, options.ttlMs);
|
|
const cleanupThreshold = Math.max(1, Math.floor(options.cleanupThreshold));
|
|
|
|
function cleanupExpired(scopeKey: string, entry: Map<string, number>, now: number): void {
|
|
for (const [id, timestamp] of entry) {
|
|
if (now - timestamp > ttlMs) {
|
|
entry.delete(id);
|
|
}
|
|
}
|
|
if (entry.size === 0) {
|
|
options.store.delete(scopeKey);
|
|
}
|
|
}
|
|
|
|
return {
|
|
record: (scope, id, now = Date.now()) => {
|
|
const scopeKey = String(scope);
|
|
const idKey = String(id);
|
|
let entry = options.store.get(scopeKey);
|
|
if (!entry) {
|
|
entry = new Map<string, number>();
|
|
options.store.set(scopeKey, entry);
|
|
}
|
|
entry.set(idKey, now);
|
|
if (entry.size > cleanupThreshold) {
|
|
cleanupExpired(scopeKey, entry, now);
|
|
}
|
|
},
|
|
has: (scope, id, now = Date.now()) => {
|
|
const scopeKey = String(scope);
|
|
const idKey = String(id);
|
|
const entry = options.store.get(scopeKey);
|
|
if (!entry) {
|
|
return false;
|
|
}
|
|
cleanupExpired(scopeKey, entry, now);
|
|
return entry.has(idKey);
|
|
},
|
|
clear: () => {
|
|
options.store.clear();
|
|
},
|
|
};
|
|
}
|