pdf-oxide 0.3.24
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.
- package/README.md +218 -0
- package/binding.gyp +35 -0
- package/package.json +78 -0
- package/src/builders/annotation-builder.ts +367 -0
- package/src/builders/conversion-options-builder.ts +257 -0
- package/src/builders/index.ts +12 -0
- package/src/builders/metadata-builder.ts +317 -0
- package/src/builders/pdf-builder.ts +386 -0
- package/src/builders/search-options-builder.ts +151 -0
- package/src/document-editor-manager.ts +318 -0
- package/src/errors.ts +1629 -0
- package/src/form-field-manager.ts +666 -0
- package/src/hybrid-ml-manager.ts +283 -0
- package/src/index.ts +453 -0
- package/src/managers/accessibility-manager.ts +338 -0
- package/src/managers/annotation-manager.ts +439 -0
- package/src/managers/barcode-manager.ts +235 -0
- package/src/managers/batch-manager.ts +533 -0
- package/src/managers/cache-manager.ts +486 -0
- package/src/managers/compliance-manager.ts +375 -0
- package/src/managers/content-manager.ts +339 -0
- package/src/managers/document-utility-manager.ts +922 -0
- package/src/managers/dom-pdf-creator.ts +365 -0
- package/src/managers/editing-manager.ts +514 -0
- package/src/managers/enterprise-manager.ts +478 -0
- package/src/managers/extended-managers.ts +437 -0
- package/src/managers/extraction-manager.ts +583 -0
- package/src/managers/final-utilities.ts +429 -0
- package/src/managers/hybrid-ml-advanced.ts +479 -0
- package/src/managers/index.ts +239 -0
- package/src/managers/layer-manager.ts +500 -0
- package/src/managers/metadata-manager.ts +303 -0
- package/src/managers/ocr-manager.ts +756 -0
- package/src/managers/optimization-manager.ts +262 -0
- package/src/managers/outline-manager.ts +196 -0
- package/src/managers/page-manager.ts +289 -0
- package/src/managers/pattern-detection.ts +440 -0
- package/src/managers/rendering-manager.ts +863 -0
- package/src/managers/search-manager.ts +385 -0
- package/src/managers/security-manager.ts +345 -0
- package/src/managers/signature-manager.ts +1664 -0
- package/src/managers/streams.ts +618 -0
- package/src/managers/xfa-manager.ts +500 -0
- package/src/pdf-creator-manager.ts +494 -0
- package/src/properties.ts +522 -0
- package/src/result-accessors-manager.ts +867 -0
- package/src/tests/advanced-features.test.ts +414 -0
- package/src/tests/advanced.test.ts +266 -0
- package/src/tests/extended-managers.test.ts +316 -0
- package/src/tests/final-utilities.test.ts +455 -0
- package/src/tests/foundation.test.ts +315 -0
- package/src/tests/high-demand.test.ts +257 -0
- package/src/tests/specialized.test.ts +97 -0
- package/src/thumbnail-manager.ts +272 -0
- package/src/types/common.ts +142 -0
- package/src/types/document-types.ts +457 -0
- package/src/types/index.ts +6 -0
- package/src/types/manager-types.ts +284 -0
- package/src/types/native-bindings.ts +517 -0
- package/src/workers/index.ts +7 -0
- package/src/workers/pool.ts +274 -0
- package/src/workers/worker.ts +131 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Manager - TypeScript/Node.js Implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides caching functionality for PDF operations:
|
|
5
|
+
* - Document-level caching
|
|
6
|
+
* - Page-level caching
|
|
7
|
+
* - Result caching with TTL
|
|
8
|
+
* - LRU eviction
|
|
9
|
+
* - Cache statistics
|
|
10
|
+
*
|
|
11
|
+
* This completes the cache coverage for 100% FFI parity.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { EventEmitter } from 'events';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Type Definitions
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Cache entry with metadata
|
|
22
|
+
*/
|
|
23
|
+
export interface CacheEntry<T = unknown> {
|
|
24
|
+
readonly key: string;
|
|
25
|
+
readonly value: T;
|
|
26
|
+
readonly timestamp: number;
|
|
27
|
+
readonly ttl?: number;
|
|
28
|
+
readonly size?: number;
|
|
29
|
+
readonly hits: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Cache options
|
|
34
|
+
*/
|
|
35
|
+
export interface CacheOptions {
|
|
36
|
+
readonly maxSize?: number;
|
|
37
|
+
readonly maxEntries?: number;
|
|
38
|
+
readonly defaultTtl?: number;
|
|
39
|
+
readonly enableStatistics?: boolean;
|
|
40
|
+
readonly evictionPolicy?: 'lru' | 'lfu' | 'fifo';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Cache statistics
|
|
45
|
+
*/
|
|
46
|
+
export interface CacheStatistics {
|
|
47
|
+
readonly totalEntries: number;
|
|
48
|
+
readonly totalSize: number;
|
|
49
|
+
readonly hitCount: number;
|
|
50
|
+
readonly missCount: number;
|
|
51
|
+
readonly hitRate: number;
|
|
52
|
+
readonly evictionCount: number;
|
|
53
|
+
readonly oldestEntry?: number;
|
|
54
|
+
readonly newestEntry?: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Cache scope enumeration
|
|
59
|
+
*/
|
|
60
|
+
export enum CacheScope {
|
|
61
|
+
/** Cache per document */
|
|
62
|
+
DOCUMENT = 'document',
|
|
63
|
+
/** Cache per page */
|
|
64
|
+
PAGE = 'page',
|
|
65
|
+
/** Global cache */
|
|
66
|
+
GLOBAL = 'global',
|
|
67
|
+
/** Session-level cache */
|
|
68
|
+
SESSION = 'session',
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Cache event data
|
|
73
|
+
*/
|
|
74
|
+
export interface CacheEventData {
|
|
75
|
+
readonly key: string;
|
|
76
|
+
readonly scope?: CacheScope;
|
|
77
|
+
readonly operation: 'set' | 'get' | 'delete' | 'evict' | 'clear';
|
|
78
|
+
readonly hit?: boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// Cache Manager Implementation
|
|
83
|
+
// ============================================================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Cache Manager - Complete caching capabilities
|
|
87
|
+
*
|
|
88
|
+
* Provides 10 functions for cache management:
|
|
89
|
+
* 1. set - Store a value
|
|
90
|
+
* 2. get - Retrieve a value
|
|
91
|
+
* 3. has - Check if key exists
|
|
92
|
+
* 4. delete - Remove a key
|
|
93
|
+
* 5. clear - Clear all entries
|
|
94
|
+
* 6. clearScope - Clear entries by scope
|
|
95
|
+
* 7. getStatistics - Get cache statistics
|
|
96
|
+
* 8. setTtl - Set TTL for an entry
|
|
97
|
+
* 9. getKeys - Get all keys
|
|
98
|
+
* 10. prune - Remove expired entries
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const cache = new CacheManager({
|
|
103
|
+
* maxEntries: 1000,
|
|
104
|
+
* defaultTtl: 60000, // 1 minute
|
|
105
|
+
* evictionPolicy: 'lru',
|
|
106
|
+
* });
|
|
107
|
+
*
|
|
108
|
+
* // Store a value
|
|
109
|
+
* cache.set('page:0:text', extractedText, CacheScope.PAGE);
|
|
110
|
+
*
|
|
111
|
+
* // Retrieve a value
|
|
112
|
+
* const text = cache.get<string>('page:0:text');
|
|
113
|
+
*
|
|
114
|
+
* // Check statistics
|
|
115
|
+
* const stats = cache.getStatistics();
|
|
116
|
+
* console.log(`Hit rate: ${stats.hitRate * 100}%`);
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export class CacheManager extends EventEmitter {
|
|
120
|
+
private readonly cache: Map<string, CacheEntry> = new Map();
|
|
121
|
+
private readonly scopeIndex: Map<CacheScope, Set<string>> = new Map();
|
|
122
|
+
private readonly options: Required<CacheOptions>;
|
|
123
|
+
|
|
124
|
+
// Statistics
|
|
125
|
+
private hitCount = 0;
|
|
126
|
+
private missCount = 0;
|
|
127
|
+
private evictionCount = 0;
|
|
128
|
+
|
|
129
|
+
constructor(options: CacheOptions = {}) {
|
|
130
|
+
super();
|
|
131
|
+
this.options = {
|
|
132
|
+
maxSize: options.maxSize ?? 100 * 1024 * 1024, // 100MB default
|
|
133
|
+
maxEntries: options.maxEntries ?? 10000,
|
|
134
|
+
defaultTtl: options.defaultTtl ?? 0, // 0 = no expiry
|
|
135
|
+
enableStatistics: options.enableStatistics ?? true,
|
|
136
|
+
evictionPolicy: options.evictionPolicy ?? 'lru',
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Initialize scope index
|
|
140
|
+
for (const scope of Object.values(CacheScope)) {
|
|
141
|
+
this.scopeIndex.set(scope, new Set());
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ==========================================================================
|
|
146
|
+
// Core Cache Operations (6 functions)
|
|
147
|
+
// ==========================================================================
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Store a value in the cache
|
|
151
|
+
*/
|
|
152
|
+
set<T>(key: string, value: T, scope: CacheScope = CacheScope.GLOBAL, ttl?: number): void {
|
|
153
|
+
// Check if we need to evict
|
|
154
|
+
this.ensureCapacity();
|
|
155
|
+
|
|
156
|
+
const entry: CacheEntry<T> = {
|
|
157
|
+
key,
|
|
158
|
+
value,
|
|
159
|
+
timestamp: Date.now(),
|
|
160
|
+
ttl: ttl ?? this.options.defaultTtl,
|
|
161
|
+
size: this.estimateSize(value),
|
|
162
|
+
hits: 0,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Remove from old scope if exists
|
|
166
|
+
if (this.cache.has(key)) {
|
|
167
|
+
this.removeFromScopeIndex(key);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
this.cache.set(key, entry as CacheEntry);
|
|
171
|
+
this.scopeIndex.get(scope)?.add(key);
|
|
172
|
+
|
|
173
|
+
this.emit('cache-set', {
|
|
174
|
+
key,
|
|
175
|
+
scope,
|
|
176
|
+
operation: 'set',
|
|
177
|
+
} as CacheEventData);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Retrieve a value from the cache
|
|
182
|
+
*/
|
|
183
|
+
get<T>(key: string): T | undefined {
|
|
184
|
+
const entry = this.cache.get(key) as CacheEntry<T> | undefined;
|
|
185
|
+
|
|
186
|
+
if (!entry) {
|
|
187
|
+
if (this.options.enableStatistics) {
|
|
188
|
+
this.missCount++;
|
|
189
|
+
}
|
|
190
|
+
this.emit('cache-miss', { key, operation: 'get', hit: false } as CacheEventData);
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Check TTL
|
|
195
|
+
if (entry.ttl && entry.ttl > 0 && Date.now() - entry.timestamp > entry.ttl) {
|
|
196
|
+
this.delete(key);
|
|
197
|
+
if (this.options.enableStatistics) {
|
|
198
|
+
this.missCount++;
|
|
199
|
+
}
|
|
200
|
+
this.emit('cache-expired', { key, operation: 'get', hit: false } as CacheEventData);
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Update hit count for LFU
|
|
205
|
+
const updatedEntry: CacheEntry<T> = {
|
|
206
|
+
...entry,
|
|
207
|
+
hits: entry.hits + 1,
|
|
208
|
+
timestamp: this.options.evictionPolicy === 'lru' ? Date.now() : entry.timestamp,
|
|
209
|
+
};
|
|
210
|
+
this.cache.set(key, updatedEntry as CacheEntry);
|
|
211
|
+
|
|
212
|
+
if (this.options.enableStatistics) {
|
|
213
|
+
this.hitCount++;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
this.emit('cache-hit', { key, operation: 'get', hit: true } as CacheEventData);
|
|
217
|
+
return entry.value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Check if a key exists in the cache
|
|
222
|
+
*/
|
|
223
|
+
has(key: string): boolean {
|
|
224
|
+
const entry = this.cache.get(key);
|
|
225
|
+
if (!entry) return false;
|
|
226
|
+
|
|
227
|
+
// Check TTL
|
|
228
|
+
if (entry.ttl && entry.ttl > 0 && Date.now() - entry.timestamp > entry.ttl) {
|
|
229
|
+
this.delete(key);
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Remove a key from the cache
|
|
238
|
+
*/
|
|
239
|
+
delete(key: string): boolean {
|
|
240
|
+
const exists = this.cache.has(key);
|
|
241
|
+
if (exists) {
|
|
242
|
+
this.removeFromScopeIndex(key);
|
|
243
|
+
this.cache.delete(key);
|
|
244
|
+
this.emit('cache-delete', { key, operation: 'delete' } as CacheEventData);
|
|
245
|
+
}
|
|
246
|
+
return exists;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Clear all entries from the cache
|
|
251
|
+
*/
|
|
252
|
+
clear(): void {
|
|
253
|
+
const count = this.cache.size;
|
|
254
|
+
this.cache.clear();
|
|
255
|
+
for (const scope of this.scopeIndex.values()) {
|
|
256
|
+
scope.clear();
|
|
257
|
+
}
|
|
258
|
+
this.hitCount = 0;
|
|
259
|
+
this.missCount = 0;
|
|
260
|
+
this.evictionCount = 0;
|
|
261
|
+
this.emit('cache-clear', { key: '*', operation: 'clear' } as CacheEventData);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Clear entries by scope
|
|
266
|
+
*/
|
|
267
|
+
clearScope(scope: CacheScope): number {
|
|
268
|
+
const keys = this.scopeIndex.get(scope);
|
|
269
|
+
if (!keys) return 0;
|
|
270
|
+
|
|
271
|
+
const count = keys.size;
|
|
272
|
+
for (const key of keys) {
|
|
273
|
+
this.cache.delete(key);
|
|
274
|
+
}
|
|
275
|
+
keys.clear();
|
|
276
|
+
|
|
277
|
+
this.emit('cache-scope-clear', { key: scope, scope, operation: 'clear' } as CacheEventData);
|
|
278
|
+
return count;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ==========================================================================
|
|
282
|
+
// Statistics and Maintenance (4 functions)
|
|
283
|
+
// ==========================================================================
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get cache statistics
|
|
287
|
+
*/
|
|
288
|
+
getStatistics(): CacheStatistics {
|
|
289
|
+
const entries = Array.from(this.cache.values());
|
|
290
|
+
const totalSize = entries.reduce((sum, e) => sum + (e.size ?? 0), 0);
|
|
291
|
+
const timestamps = entries.map((e) => e.timestamp);
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
totalEntries: this.cache.size,
|
|
295
|
+
totalSize,
|
|
296
|
+
hitCount: this.hitCount,
|
|
297
|
+
missCount: this.missCount,
|
|
298
|
+
hitRate: this.hitCount + this.missCount > 0
|
|
299
|
+
? this.hitCount / (this.hitCount + this.missCount)
|
|
300
|
+
: 0,
|
|
301
|
+
evictionCount: this.evictionCount,
|
|
302
|
+
oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : undefined,
|
|
303
|
+
newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : undefined,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Set TTL for an existing entry
|
|
309
|
+
*/
|
|
310
|
+
setTtl(key: string, ttl: number): boolean {
|
|
311
|
+
const entry = this.cache.get(key);
|
|
312
|
+
if (!entry) return false;
|
|
313
|
+
|
|
314
|
+
const updatedEntry: CacheEntry = {
|
|
315
|
+
...entry,
|
|
316
|
+
ttl,
|
|
317
|
+
};
|
|
318
|
+
this.cache.set(key, updatedEntry);
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Get all cache keys
|
|
324
|
+
*/
|
|
325
|
+
getKeys(scope?: CacheScope): string[] {
|
|
326
|
+
if (scope) {
|
|
327
|
+
return Array.from(this.scopeIndex.get(scope) ?? []);
|
|
328
|
+
}
|
|
329
|
+
return Array.from(this.cache.keys());
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Remove expired entries
|
|
334
|
+
*/
|
|
335
|
+
prune(): number {
|
|
336
|
+
const now = Date.now();
|
|
337
|
+
let pruned = 0;
|
|
338
|
+
|
|
339
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
340
|
+
if (entry.ttl && entry.ttl > 0 && now - entry.timestamp > entry.ttl) {
|
|
341
|
+
this.delete(key);
|
|
342
|
+
pruned++;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (pruned > 0) {
|
|
347
|
+
this.emit('cache-prune', { key: '*', operation: 'delete' } as CacheEventData);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return pruned;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ==========================================================================
|
|
354
|
+
// Helper Methods
|
|
355
|
+
// ==========================================================================
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Ensure cache has capacity, evicting if needed
|
|
359
|
+
*/
|
|
360
|
+
private ensureCapacity(): void {
|
|
361
|
+
// Check entry count
|
|
362
|
+
while (this.cache.size >= this.options.maxEntries) {
|
|
363
|
+
this.evictOne();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Check total size
|
|
367
|
+
let totalSize = 0;
|
|
368
|
+
for (const entry of this.cache.values()) {
|
|
369
|
+
totalSize += entry.size ?? 0;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
while (totalSize > this.options.maxSize && this.cache.size > 0) {
|
|
373
|
+
const evicted = this.evictOne();
|
|
374
|
+
totalSize -= evicted?.size ?? 0;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Evict one entry based on eviction policy
|
|
380
|
+
*/
|
|
381
|
+
private evictOne(): CacheEntry | undefined {
|
|
382
|
+
if (this.cache.size === 0) return undefined;
|
|
383
|
+
|
|
384
|
+
let keyToEvict: string | undefined;
|
|
385
|
+
|
|
386
|
+
switch (this.options.evictionPolicy) {
|
|
387
|
+
case 'lru':
|
|
388
|
+
// Find oldest timestamp
|
|
389
|
+
keyToEvict = this.findOldestKey();
|
|
390
|
+
break;
|
|
391
|
+
|
|
392
|
+
case 'lfu':
|
|
393
|
+
// Find least frequently used
|
|
394
|
+
keyToEvict = this.findLeastUsedKey();
|
|
395
|
+
break;
|
|
396
|
+
|
|
397
|
+
case 'fifo':
|
|
398
|
+
default:
|
|
399
|
+
// First key
|
|
400
|
+
keyToEvict = this.cache.keys().next().value;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (keyToEvict) {
|
|
405
|
+
const entry = this.cache.get(keyToEvict);
|
|
406
|
+
this.removeFromScopeIndex(keyToEvict);
|
|
407
|
+
this.cache.delete(keyToEvict);
|
|
408
|
+
this.evictionCount++;
|
|
409
|
+
this.emit('cache-evict', { key: keyToEvict, operation: 'evict' } as CacheEventData);
|
|
410
|
+
return entry;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Find key with oldest timestamp (LRU)
|
|
418
|
+
*/
|
|
419
|
+
private findOldestKey(): string | undefined {
|
|
420
|
+
let oldestKey: string | undefined;
|
|
421
|
+
let oldestTime = Infinity;
|
|
422
|
+
|
|
423
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
424
|
+
if (entry.timestamp < oldestTime) {
|
|
425
|
+
oldestTime = entry.timestamp;
|
|
426
|
+
oldestKey = key;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return oldestKey;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Find key with least hits (LFU)
|
|
435
|
+
*/
|
|
436
|
+
private findLeastUsedKey(): string | undefined {
|
|
437
|
+
let leastUsedKey: string | undefined;
|
|
438
|
+
let leastHits = Infinity;
|
|
439
|
+
|
|
440
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
441
|
+
if (entry.hits < leastHits) {
|
|
442
|
+
leastHits = entry.hits;
|
|
443
|
+
leastUsedKey = key;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return leastUsedKey;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Remove key from scope index
|
|
452
|
+
*/
|
|
453
|
+
private removeFromScopeIndex(key: string): void {
|
|
454
|
+
for (const keys of this.scopeIndex.values()) {
|
|
455
|
+
keys.delete(key);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Estimate size of a value in bytes
|
|
461
|
+
*/
|
|
462
|
+
private estimateSize(value: unknown): number {
|
|
463
|
+
if (value === null || value === undefined) return 0;
|
|
464
|
+
if (typeof value === 'string') return value.length * 2;
|
|
465
|
+
if (typeof value === 'number') return 8;
|
|
466
|
+
if (typeof value === 'boolean') return 4;
|
|
467
|
+
if (Buffer.isBuffer(value)) return value.length;
|
|
468
|
+
if (Array.isArray(value)) {
|
|
469
|
+
return value.reduce((sum, v) => sum + this.estimateSize(v), 0);
|
|
470
|
+
}
|
|
471
|
+
if (typeof value === 'object') {
|
|
472
|
+
return JSON.stringify(value).length * 2;
|
|
473
|
+
}
|
|
474
|
+
return 0;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Cleanup resources
|
|
479
|
+
*/
|
|
480
|
+
destroy(): void {
|
|
481
|
+
this.clear();
|
|
482
|
+
this.removeAllListeners();
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export default CacheManager;
|