Aethel Bot OSS repository!
aethel.xyz
bot
fun
ai
discord
discord-bot
aethel
1import logger from './logger';
2interface MemoryManagerOptions {
3 maxSize?: number;
4 cleanupInterval?: number;
5 maxAge?: number;
6}
7
8class MemoryManager<K, V> {
9 private map = new Map<K, V & { timestamp: number }>();
10 private maxSize: number;
11 private maxAge: number;
12 private cleanupTimer?: NodeJS.Timeout;
13
14 constructor(options: MemoryManagerOptions = {}) {
15 this.maxSize = options.maxSize || 1000;
16 this.maxAge = options.maxAge || 24 * 60 * 60 * 1000;
17
18 if (options.cleanupInterval) {
19 this.startCleanupTimer(options.cleanupInterval);
20 }
21 }
22
23 set(key: K, value: V): void {
24 if (this.map.size >= this.maxSize) {
25 const oldestKey = this.map.keys().next().value;
26 if (oldestKey !== undefined) {
27 this.map.delete(oldestKey);
28 }
29 }
30
31 const wrappedValue = Array.isArray(value)
32 ? Object.assign([...value], { timestamp: Date.now() })
33 : { ...value, timestamp: Date.now() };
34 this.map.set(key, wrappedValue as V & { timestamp: number });
35 }
36
37 get(key: K): V | undefined {
38 const entry = this.map.get(key);
39 if (!entry) return undefined;
40
41 if (Date.now() - entry.timestamp > this.maxAge) {
42 this.map.delete(key);
43 return undefined;
44 }
45
46 if (Array.isArray(entry)) {
47 const result = [...entry];
48 delete (result as unknown as Record<string, unknown>).timestamp;
49 return result as V;
50 }
51 const { timestamp: _timestamp, ...valueWithoutTimestamp } = entry;
52 return valueWithoutTimestamp as V;
53 }
54
55 delete(key: K): boolean {
56 return this.map.delete(key);
57 }
58
59 has(key: K): boolean {
60 const entry = this.map.get(key);
61 if (!entry) return false;
62
63 if (Date.now() - entry.timestamp > this.maxAge) {
64 this.map.delete(key);
65 return false;
66 }
67
68 return true;
69 }
70
71 clear(): void {
72 this.map.clear();
73 }
74
75 size(): number {
76 return this.map.size;
77 }
78
79 entries(): IterableIterator<[K, V]> {
80 const entries: [K, V][] = [];
81 for (const [key, value] of this.map.entries()) {
82 if (Date.now() - value.timestamp <= this.maxAge) {
83 if (Array.isArray(value)) {
84 const result = [...value];
85 delete (result as unknown as Record<string, unknown>).timestamp;
86 entries.push([key, result as V]);
87 } else {
88 const { timestamp: _timestamp, ...valueWithoutTimestamp } = value;
89 entries.push([key, valueWithoutTimestamp as V]);
90 }
91 }
92 }
93 return entries[Symbol.iterator]();
94 }
95
96 cleanup(): number {
97 const now = Date.now();
98 let removedCount = 0;
99
100 for (const [key, value] of this.map.entries()) {
101 if (now - value.timestamp > this.maxAge) {
102 this.map.delete(key);
103 removedCount++;
104 }
105 }
106
107 if (removedCount > 0) {
108 logger.debug(`Memory cleanup: removed ${removedCount} expired entries`);
109 }
110
111 return removedCount;
112 }
113
114 private startCleanupTimer(interval: number): void {
115 this.cleanupTimer = setInterval(() => {
116 this.cleanup();
117 }, interval);
118 }
119
120 destroy(): void {
121 if (this.cleanupTimer) {
122 clearInterval(this.cleanupTimer);
123 }
124 this.clear();
125 }
126}
127
128export function createMemoryManager<K, V>(options?: MemoryManagerOptions): MemoryManager<K, V> {
129 return new MemoryManager<K, V>(options);
130}
131
132export { MemoryManager };