mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-26 16:06:16 +00:00
refactor: consolidate core runtime state helpers
This commit is contained in:
21
src/shared/listeners.ts
Normal file
21
src/shared/listeners.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export function notifyListeners<T>(
|
||||
listeners: Iterable<(event: T) => void>,
|
||||
event: T,
|
||||
onError?: (error: unknown) => void,
|
||||
): void {
|
||||
for (const listener of listeners) {
|
||||
try {
|
||||
listener(event);
|
||||
} catch (error) {
|
||||
onError?.(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function registerListener<T>(
|
||||
listeners: Set<(event: T) => void>,
|
||||
listener: (event: T) => void,
|
||||
): () => void {
|
||||
listeners.add(listener);
|
||||
return () => listeners.delete(listener);
|
||||
}
|
||||
57
src/shared/scoped-expiring-id-cache.ts
Normal file
57
src/shared/scoped-expiring-id-cache.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
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();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user