sehawq.db 4.0.2 โ†’ 4.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,339 +1,339 @@
1
- /**
2
- * Smart Cache System - Makes everything faster with magic ๐Ÿช„
3
- *
4
- * Implements LRU cache with TTL and memory management
5
- * Because waiting is not an option in 2024 โšก
6
- */
7
-
8
- class Cache {
9
- constructor(options = {}) {
10
- this.options = {
11
- maxSize: 1000,
12
- ttl: 5 * 60 * 1000, // 5 minutes default
13
- cleanupInterval: 60 * 1000, // Clean every minute
14
- ...options
15
- };
16
-
17
- // Double-linked list for LRU + HashMap for O(1) access
18
- this.cache = new Map();
19
- this.head = { key: null, value: null, next: null, prev: null, expires: 0 };
20
- this.tail = { key: null, value: null, next: null, prev: this.head, expires: 0 };
21
- this.head.next = this.tail;
22
-
23
- this.stats = {
24
- hits: 0,
25
- misses: 0,
26
- evictions: 0,
27
- sets: 0,
28
- gets: 0,
29
- memoryUsage: 0
30
- };
31
-
32
- this._startCleanupInterval();
33
- }
34
-
35
- /**
36
- * Get value from cache - moves item to front (most recently used)
37
- */
38
- get(key) {
39
- this.stats.gets++;
40
-
41
- const node = this.cache.get(key);
42
-
43
- // Check if exists and not expired
44
- if (!node || this._isExpired(node)) {
45
- this.stats.misses++;
46
-
47
- if (node) {
48
- // Remove expired node
49
- this._removeNode(node);
50
- this.cache.delete(key);
51
- this.stats.evictions++;
52
- }
53
-
54
- return undefined;
55
- }
56
-
57
- // Move to front (most recently used)
58
- this._moveToFront(node);
59
- this.stats.hits++;
60
-
61
- return node.value;
62
- }
63
-
64
- /**
65
- * Set value in cache - handles LRU eviction if needed
66
- */
67
- set(key, value, ttl = this.options.ttl) {
68
- this.stats.sets++;
69
-
70
- let node = this.cache.get(key);
71
- const expires = Date.now() + ttl;
72
-
73
- if (node) {
74
- // Update existing node
75
- node.value = value;
76
- node.expires = expires;
77
- this._moveToFront(node);
78
- } else {
79
- // Create new node
80
- node = {
81
- key,
82
- value,
83
- expires,
84
- prev: this.head,
85
- next: this.head.next
86
- };
87
-
88
- // Add to cache and linked list
89
- this.cache.set(key, node);
90
- this.head.next.prev = node;
91
- this.head.next = node;
92
-
93
- // Evict if over capacity
94
- if (this.cache.size > this.options.maxSize) {
95
- this._evictLRU();
96
- }
97
- }
98
-
99
- // Update memory usage stats
100
- this._updateMemoryStats();
101
-
102
- return true;
103
- }
104
-
105
- /**
106
- * Check if key exists in cache (without updating LRU)
107
- */
108
- has(key) {
109
- const node = this.cache.get(key);
110
- return !!(node && !this._isExpired(node));
111
- }
112
-
113
- /**
114
- * Delete key from cache
115
- */
116
- delete(key) {
117
- const node = this.cache.get(key);
118
- if (node) {
119
- this._removeNode(node);
120
- this.cache.delete(key);
121
- return true;
122
- }
123
- return false;
124
- }
125
-
126
- /**
127
- * Clear entire cache
128
- */
129
- clear() {
130
- this.cache.clear();
131
- this.head.next = this.tail;
132
- this.tail.prev = this.head;
133
- this.stats.memoryUsage = 0;
134
- }
135
-
136
- /**
137
- * Get cache size (number of items)
138
- */
139
- size() {
140
- return this.cache.size;
141
- }
142
-
143
- /**
144
- * Get all keys in cache (for debugging)
145
- */
146
- keys() {
147
- const keys = [];
148
- let node = this.head.next;
149
-
150
- while (node !== this.tail) {
151
- if (!this._isExpired(node)) {
152
- keys.push(node.key);
153
- }
154
- node = node.next;
155
- }
156
-
157
- return keys;
158
- }
159
-
160
- /**
161
- * Get all values in cache (for debugging)
162
- */
163
- values() {
164
- const values = [];
165
- let node = this.head.next;
166
-
167
- while (node !== this.tail) {
168
- if (!this._isExpired(node)) {
169
- values.push(node.value);
170
- }
171
- node = node.next;
172
- }
173
-
174
- return values;
175
- }
176
-
177
- /**
178
- * Get cache statistics
179
- */
180
- getStats() {
181
- const hitRate = this.stats.hits + this.stats.misses > 0
182
- ? (this.stats.hits / (this.stats.hits + this.stats.misses) * 100).toFixed(2)
183
- : 0;
184
-
185
- return {
186
- ...this.stats,
187
- hitRate: `${hitRate}%`,
188
- size: this.cache.size,
189
- maxSize: this.options.maxSize,
190
- utilization: `${((this.cache.size / this.options.maxSize) * 100).toFixed(1)}%`
191
- };
192
- }
193
-
194
- /**
195
- * Move node to front of LRU list
196
- */
197
- _moveToFront(node) {
198
- // Remove from current position
199
- this._removeNode(node);
200
-
201
- // Insert after head
202
- node.prev = this.head;
203
- node.next = this.head.next;
204
- this.head.next.prev = node;
205
- this.head.next = node;
206
- }
207
-
208
- /**
209
- * Remove node from linked list
210
- */
211
- _removeNode(node) {
212
- node.prev.next = node.next;
213
- node.next.prev = node.prev;
214
- }
215
-
216
- /**
217
- * Evict least recently used item
218
- */
219
- _evictLRU() {
220
- const lruNode = this.tail.prev;
221
-
222
- if (lruNode !== this.head) {
223
- this._removeNode(lruNode);
224
- this.cache.delete(lruNode.key);
225
- this.stats.evictions++;
226
- this._updateMemoryStats();
227
- }
228
- }
229
-
230
- /**
231
- * Check if node has expired
232
- */
233
- _isExpired(node) {
234
- return Date.now() > node.expires;
235
- }
236
-
237
- /**
238
- * Start periodic cleanup of expired items
239
- */
240
- _startCleanupInterval() {
241
- setInterval(() => {
242
- this._cleanupExpired();
243
- }, this.options.cleanupInterval);
244
- }
245
-
246
- /**
247
- * Remove all expired items from cache
248
- */
249
- _cleanupExpired() {
250
- const now = Date.now();
251
- let node = this.head.next;
252
- let expiredCount = 0;
253
-
254
- while (node !== this.tail) {
255
- const nextNode = node.next;
256
-
257
- if (now > node.expires) {
258
- this._removeNode(node);
259
- this.cache.delete(node.key);
260
- expiredCount++;
261
- }
262
-
263
- node = nextNode;
264
- }
265
-
266
- if (expiredCount > 0 && this.options.debug) {
267
- console.log(`๐Ÿงน Cache cleanup: removed ${expiredCount} expired items`);
268
- }
269
-
270
- this._updateMemoryStats();
271
- }
272
-
273
- /**
274
- * Estimate memory usage (rough calculation)
275
- */
276
- _updateMemoryStats() {
277
- let totalSize = 0;
278
-
279
- for (const [key, node] of this.cache) {
280
- // Rough estimation: key size + value size (stringify for simplicity)
281
- totalSize += Buffer.byteLength(key, 'utf8');
282
- totalSize += Buffer.byteLength(JSON.stringify(node.value), 'utf8');
283
- }
284
-
285
- this.stats.memoryUsage = totalSize;
286
- }
287
-
288
- /**
289
- * Pre-warm cache with data
290
- */
291
- async warmup(dataMap, ttl = this.options.ttl) {
292
- for (const [key, value] of Object.entries(dataMap)) {
293
- this.set(key, value, ttl);
294
- }
295
-
296
- if (this.options.debug) {
297
- console.log(`๐Ÿ”ฅ Cache warmup complete: ${Object.keys(dataMap).length} items`);
298
- }
299
- }
300
-
301
- /**
302
- * Get cache snapshot for debugging
303
- */
304
- getSnapshot() {
305
- const snapshot = {};
306
- let node = this.head.next;
307
-
308
- while (node !== this.tail) {
309
- if (!this._isExpired(node)) {
310
- snapshot[node.key] = {
311
- value: node.value,
312
- expiresIn: node.expires - Date.now(),
313
- ttl: this.options.ttl
314
- };
315
- }
316
- node = node.next;
317
- }
318
-
319
- return snapshot;
320
- }
321
-
322
- /**
323
- * Resize cache (useful for dynamic memory management)
324
- */
325
- resize(newSize) {
326
- this.options.maxSize = newSize;
327
-
328
- // Evict excess items if needed
329
- while (this.cache.size > newSize) {
330
- this._evictLRU();
331
- }
332
-
333
- if (this.options.debug) {
334
- console.log(`๐Ÿ“ Cache resized: ${newSize} items`);
335
- }
336
- }
337
- }
338
-
1
+ /**
2
+ * Smart Cache System - Makes everything faster with magic ๐Ÿช„
3
+ *
4
+ * Implements LRU cache with TTL and memory management
5
+ * Because waiting is not an option in 2024 โšก
6
+ */
7
+
8
+ class Cache {
9
+ constructor(options = {}) {
10
+ this.options = {
11
+ maxSize: 1000,
12
+ ttl: 5 * 60 * 1000, // 5 minutes default
13
+ cleanupInterval: 60 * 1000, // Clean every minute
14
+ ...options
15
+ };
16
+
17
+ // Double-linked list for LRU + HashMap for O(1) access
18
+ this.cache = new Map();
19
+ this.head = { key: null, value: null, next: null, prev: null, expires: 0 };
20
+ this.tail = { key: null, value: null, next: null, prev: this.head, expires: 0 };
21
+ this.head.next = this.tail;
22
+
23
+ this.stats = {
24
+ hits: 0,
25
+ misses: 0,
26
+ evictions: 0,
27
+ sets: 0,
28
+ gets: 0,
29
+ memoryUsage: 0
30
+ };
31
+
32
+ this._startCleanupInterval();
33
+ }
34
+
35
+ /**
36
+ * Get value from cache - moves item to front (most recently used)
37
+ */
38
+ get(key) {
39
+ this.stats.gets++;
40
+
41
+ const node = this.cache.get(key);
42
+
43
+ // Check if exists and not expired
44
+ if (!node || this._isExpired(node)) {
45
+ this.stats.misses++;
46
+
47
+ if (node) {
48
+ // Remove expired node
49
+ this._removeNode(node);
50
+ this.cache.delete(key);
51
+ this.stats.evictions++;
52
+ }
53
+
54
+ return undefined;
55
+ }
56
+
57
+ // Move to front (most recently used)
58
+ this._moveToFront(node);
59
+ this.stats.hits++;
60
+
61
+ return node.value;
62
+ }
63
+
64
+ /**
65
+ * Set value in cache - handles LRU eviction if needed
66
+ */
67
+ set(key, value, ttl = this.options.ttl) {
68
+ this.stats.sets++;
69
+
70
+ let node = this.cache.get(key);
71
+ const expires = Date.now() + ttl;
72
+
73
+ if (node) {
74
+ // Update existing node
75
+ node.value = value;
76
+ node.expires = expires;
77
+ this._moveToFront(node);
78
+ } else {
79
+ // Create new node
80
+ node = {
81
+ key,
82
+ value,
83
+ expires,
84
+ prev: this.head,
85
+ next: this.head.next
86
+ };
87
+
88
+ // Add to cache and linked list
89
+ this.cache.set(key, node);
90
+ this.head.next.prev = node;
91
+ this.head.next = node;
92
+
93
+ // Evict if over capacity
94
+ if (this.cache.size > this.options.maxSize) {
95
+ this._evictLRU();
96
+ }
97
+ }
98
+
99
+ // Update memory usage stats
100
+ this._updateMemoryStats();
101
+
102
+ return true;
103
+ }
104
+
105
+ /**
106
+ * Check if key exists in cache (without updating LRU)
107
+ */
108
+ has(key) {
109
+ const node = this.cache.get(key);
110
+ return !!(node && !this._isExpired(node));
111
+ }
112
+
113
+ /**
114
+ * Delete key from cache
115
+ */
116
+ delete(key) {
117
+ const node = this.cache.get(key);
118
+ if (node) {
119
+ this._removeNode(node);
120
+ this.cache.delete(key);
121
+ return true;
122
+ }
123
+ return false;
124
+ }
125
+
126
+ /**
127
+ * Clear entire cache
128
+ */
129
+ clear() {
130
+ this.cache.clear();
131
+ this.head.next = this.tail;
132
+ this.tail.prev = this.head;
133
+ this.stats.memoryUsage = 0;
134
+ }
135
+
136
+ /**
137
+ * Get cache size (number of items)
138
+ */
139
+ size() {
140
+ return this.cache.size;
141
+ }
142
+
143
+ /**
144
+ * Get all keys in cache (for debugging)
145
+ */
146
+ keys() {
147
+ const keys = [];
148
+ let node = this.head.next;
149
+
150
+ while (node !== this.tail) {
151
+ if (!this._isExpired(node)) {
152
+ keys.push(node.key);
153
+ }
154
+ node = node.next;
155
+ }
156
+
157
+ return keys;
158
+ }
159
+
160
+ /**
161
+ * Get all values in cache (for debugging)
162
+ */
163
+ values() {
164
+ const values = [];
165
+ let node = this.head.next;
166
+
167
+ while (node !== this.tail) {
168
+ if (!this._isExpired(node)) {
169
+ values.push(node.value);
170
+ }
171
+ node = node.next;
172
+ }
173
+
174
+ return values;
175
+ }
176
+
177
+ /**
178
+ * Get cache statistics
179
+ */
180
+ getStats() {
181
+ const hitRate = this.stats.hits + this.stats.misses > 0
182
+ ? (this.stats.hits / (this.stats.hits + this.stats.misses) * 100).toFixed(2)
183
+ : 0;
184
+
185
+ return {
186
+ ...this.stats,
187
+ hitRate: `${hitRate}%`,
188
+ size: this.cache.size,
189
+ maxSize: this.options.maxSize,
190
+ utilization: `${((this.cache.size / this.options.maxSize) * 100).toFixed(1)}%`
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Move node to front of LRU list
196
+ */
197
+ _moveToFront(node) {
198
+ // Remove from current position
199
+ this._removeNode(node);
200
+
201
+ // Insert after head
202
+ node.prev = this.head;
203
+ node.next = this.head.next;
204
+ this.head.next.prev = node;
205
+ this.head.next = node;
206
+ }
207
+
208
+ /**
209
+ * Remove node from linked list
210
+ */
211
+ _removeNode(node) {
212
+ node.prev.next = node.next;
213
+ node.next.prev = node.prev;
214
+ }
215
+
216
+ /**
217
+ * Evict least recently used item
218
+ */
219
+ _evictLRU() {
220
+ const lruNode = this.tail.prev;
221
+
222
+ if (lruNode !== this.head) {
223
+ this._removeNode(lruNode);
224
+ this.cache.delete(lruNode.key);
225
+ this.stats.evictions++;
226
+ this._updateMemoryStats();
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Check if node has expired
232
+ */
233
+ _isExpired(node) {
234
+ return Date.now() > node.expires;
235
+ }
236
+
237
+ /**
238
+ * Start periodic cleanup of expired items
239
+ */
240
+ _startCleanupInterval() {
241
+ setInterval(() => {
242
+ this._cleanupExpired();
243
+ }, this.options.cleanupInterval);
244
+ }
245
+
246
+ /**
247
+ * Remove all expired items from cache
248
+ */
249
+ _cleanupExpired() {
250
+ const now = Date.now();
251
+ let node = this.head.next;
252
+ let expiredCount = 0;
253
+
254
+ while (node !== this.tail) {
255
+ const nextNode = node.next;
256
+
257
+ if (now > node.expires) {
258
+ this._removeNode(node);
259
+ this.cache.delete(node.key);
260
+ expiredCount++;
261
+ }
262
+
263
+ node = nextNode;
264
+ }
265
+
266
+ if (expiredCount > 0 && this.options.debug) {
267
+ console.log(`๐Ÿงน Cache cleanup: removed ${expiredCount} expired items`);
268
+ }
269
+
270
+ this._updateMemoryStats();
271
+ }
272
+
273
+ /**
274
+ * Estimate memory usage (rough calculation)
275
+ */
276
+ _updateMemoryStats() {
277
+ let totalSize = 0;
278
+
279
+ for (const [key, node] of this.cache) {
280
+ // Rough estimation: key size + value size (stringify for simplicity)
281
+ totalSize += Buffer.byteLength(key, 'utf8');
282
+ totalSize += Buffer.byteLength(JSON.stringify(node.value), 'utf8');
283
+ }
284
+
285
+ this.stats.memoryUsage = totalSize;
286
+ }
287
+
288
+ /**
289
+ * Pre-warm cache with data
290
+ */
291
+ async warmup(dataMap, ttl = this.options.ttl) {
292
+ for (const [key, value] of Object.entries(dataMap)) {
293
+ this.set(key, value, ttl);
294
+ }
295
+
296
+ if (this.options.debug) {
297
+ console.log(`๐Ÿ”ฅ Cache warmup complete: ${Object.keys(dataMap).length} items`);
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Get cache snapshot for debugging
303
+ */
304
+ getSnapshot() {
305
+ const snapshot = {};
306
+ let node = this.head.next;
307
+
308
+ while (node !== this.tail) {
309
+ if (!this._isExpired(node)) {
310
+ snapshot[node.key] = {
311
+ value: node.value,
312
+ expiresIn: node.expires - Date.now(),
313
+ ttl: this.options.ttl
314
+ };
315
+ }
316
+ node = node.next;
317
+ }
318
+
319
+ return snapshot;
320
+ }
321
+
322
+ /**
323
+ * Resize cache (useful for dynamic memory management)
324
+ */
325
+ resize(newSize) {
326
+ this.options.maxSize = newSize;
327
+
328
+ // Evict excess items if needed
329
+ while (this.cache.size > newSize) {
330
+ this._evictLRU();
331
+ }
332
+
333
+ if (this.options.debug) {
334
+ console.log(`๐Ÿ“ Cache resized: ${newSize} items`);
335
+ }
336
+ }
337
+ }
338
+
339
339
  module.exports = Cache;