recker 1.0.26 → 1.0.27-next.8396df6

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.
Files changed (181) hide show
  1. package/dist/browser/browser/cache.d.ts +40 -0
  2. package/dist/browser/browser/cache.js +199 -0
  3. package/dist/browser/browser/crypto.d.ts +24 -0
  4. package/dist/browser/browser/crypto.js +80 -0
  5. package/dist/browser/browser/index.d.ts +31 -0
  6. package/dist/browser/browser/index.js +31 -0
  7. package/dist/browser/browser/recker.d.ts +26 -0
  8. package/dist/browser/browser/recker.js +61 -0
  9. package/dist/browser/cache/basic-file-storage.d.ts +12 -0
  10. package/dist/browser/cache/basic-file-storage.js +50 -0
  11. package/dist/browser/cache/memory-limits.d.ts +20 -0
  12. package/dist/browser/cache/memory-limits.js +96 -0
  13. package/dist/browser/cache/memory-storage.d.ts +132 -0
  14. package/dist/browser/cache/memory-storage.js +454 -0
  15. package/dist/browser/cache.d.ts +40 -0
  16. package/dist/browser/cache.js +199 -0
  17. package/dist/browser/constants/http-status.d.ts +73 -0
  18. package/dist/browser/constants/http-status.js +156 -0
  19. package/dist/browser/cookies/memory-cookie-jar.d.ts +30 -0
  20. package/dist/browser/cookies/memory-cookie-jar.js +210 -0
  21. package/dist/browser/core/client.d.ts +118 -0
  22. package/dist/browser/core/client.js +667 -0
  23. package/dist/browser/core/errors.d.ts +142 -0
  24. package/dist/browser/core/errors.js +308 -0
  25. package/dist/browser/core/index.d.ts +5 -0
  26. package/dist/browser/core/index.js +5 -0
  27. package/dist/browser/core/request-promise.d.ts +23 -0
  28. package/dist/browser/core/request-promise.js +82 -0
  29. package/dist/browser/core/request.d.ts +20 -0
  30. package/dist/browser/core/request.js +76 -0
  31. package/dist/browser/core/response.d.ts +34 -0
  32. package/dist/browser/core/response.js +178 -0
  33. package/dist/browser/crypto.d.ts +24 -0
  34. package/dist/browser/crypto.js +80 -0
  35. package/dist/browser/index.d.ts +31 -0
  36. package/dist/browser/index.js +31 -0
  37. package/dist/browser/plugins/auth/api-key.d.ts +8 -0
  38. package/dist/browser/plugins/auth/api-key.js +27 -0
  39. package/dist/browser/plugins/auth/auth0.d.ts +33 -0
  40. package/dist/browser/plugins/auth/auth0.js +94 -0
  41. package/dist/browser/plugins/auth/aws-sigv4.d.ts +10 -0
  42. package/dist/browser/plugins/auth/aws-sigv4.js +88 -0
  43. package/dist/browser/plugins/auth/azure-ad.d.ts +48 -0
  44. package/dist/browser/plugins/auth/azure-ad.js +152 -0
  45. package/dist/browser/plugins/auth/basic.d.ts +7 -0
  46. package/dist/browser/plugins/auth/basic.js +13 -0
  47. package/dist/browser/plugins/auth/bearer.d.ts +8 -0
  48. package/dist/browser/plugins/auth/bearer.js +17 -0
  49. package/dist/browser/plugins/auth/cognito.d.ts +45 -0
  50. package/dist/browser/plugins/auth/cognito.js +208 -0
  51. package/dist/browser/plugins/auth/digest.d.ts +8 -0
  52. package/dist/browser/plugins/auth/digest.js +100 -0
  53. package/dist/browser/plugins/auth/firebase.d.ts +32 -0
  54. package/dist/browser/plugins/auth/firebase.js +195 -0
  55. package/dist/browser/plugins/auth/github-app.d.ts +36 -0
  56. package/dist/browser/plugins/auth/github-app.js +170 -0
  57. package/dist/browser/plugins/auth/google-service-account.d.ts +49 -0
  58. package/dist/browser/plugins/auth/google-service-account.js +172 -0
  59. package/dist/browser/plugins/auth/index.d.ts +15 -0
  60. package/dist/browser/plugins/auth/index.js +15 -0
  61. package/dist/browser/plugins/auth/mtls.d.ts +37 -0
  62. package/dist/browser/plugins/auth/mtls.js +140 -0
  63. package/dist/browser/plugins/auth/oauth2.d.ts +8 -0
  64. package/dist/browser/plugins/auth/oauth2.js +26 -0
  65. package/dist/browser/plugins/auth/oidc.d.ts +55 -0
  66. package/dist/browser/plugins/auth/oidc.js +222 -0
  67. package/dist/browser/plugins/auth/okta.d.ts +47 -0
  68. package/dist/browser/plugins/auth/okta.js +157 -0
  69. package/dist/browser/plugins/auth.d.ts +1 -0
  70. package/dist/browser/plugins/auth.js +1 -0
  71. package/dist/browser/plugins/cache.d.ts +15 -0
  72. package/dist/browser/plugins/cache.js +486 -0
  73. package/dist/browser/plugins/circuit-breaker.d.ts +13 -0
  74. package/dist/browser/plugins/circuit-breaker.js +100 -0
  75. package/dist/browser/plugins/compression.d.ts +4 -0
  76. package/dist/browser/plugins/compression.js +130 -0
  77. package/dist/browser/plugins/cookie-jar.d.ts +5 -0
  78. package/dist/browser/plugins/cookie-jar.js +72 -0
  79. package/dist/browser/plugins/dedup.d.ts +5 -0
  80. package/dist/browser/plugins/dedup.js +35 -0
  81. package/dist/browser/plugins/graphql.d.ts +13 -0
  82. package/dist/browser/plugins/graphql.js +58 -0
  83. package/dist/browser/plugins/grpc-web.d.ts +79 -0
  84. package/dist/browser/plugins/grpc-web.js +261 -0
  85. package/dist/browser/plugins/hls.d.ts +105 -0
  86. package/dist/browser/plugins/hls.js +395 -0
  87. package/dist/browser/plugins/jsonrpc.d.ts +75 -0
  88. package/dist/browser/plugins/jsonrpc.js +143 -0
  89. package/dist/browser/plugins/logger.d.ts +13 -0
  90. package/dist/browser/plugins/logger.js +108 -0
  91. package/dist/browser/plugins/odata.d.ts +181 -0
  92. package/dist/browser/plugins/odata.js +564 -0
  93. package/dist/browser/plugins/pagination.d.ts +16 -0
  94. package/dist/browser/plugins/pagination.js +105 -0
  95. package/dist/browser/plugins/rate-limit.d.ts +15 -0
  96. package/dist/browser/plugins/rate-limit.js +162 -0
  97. package/dist/browser/plugins/retry.d.ts +14 -0
  98. package/dist/browser/plugins/retry.js +116 -0
  99. package/dist/browser/plugins/scrape.d.ts +21 -0
  100. package/dist/browser/plugins/scrape.js +82 -0
  101. package/dist/browser/plugins/server-timing.d.ts +7 -0
  102. package/dist/browser/plugins/server-timing.js +24 -0
  103. package/dist/browser/plugins/soap.d.ts +72 -0
  104. package/dist/browser/plugins/soap.js +347 -0
  105. package/dist/browser/plugins/xml.d.ts +9 -0
  106. package/dist/browser/plugins/xml.js +194 -0
  107. package/dist/browser/plugins/xsrf.d.ts +9 -0
  108. package/dist/browser/plugins/xsrf.js +48 -0
  109. package/dist/browser/recker.d.ts +26 -0
  110. package/dist/browser/recker.js +61 -0
  111. package/dist/browser/runner/request-runner.d.ts +46 -0
  112. package/dist/browser/runner/request-runner.js +89 -0
  113. package/dist/browser/scrape/document.d.ts +44 -0
  114. package/dist/browser/scrape/document.js +210 -0
  115. package/dist/browser/scrape/element.d.ts +49 -0
  116. package/dist/browser/scrape/element.js +176 -0
  117. package/dist/browser/scrape/extractors.d.ts +16 -0
  118. package/dist/browser/scrape/extractors.js +356 -0
  119. package/dist/browser/scrape/types.d.ts +107 -0
  120. package/dist/browser/scrape/types.js +1 -0
  121. package/dist/browser/transport/fetch.d.ts +11 -0
  122. package/dist/browser/transport/fetch.js +143 -0
  123. package/dist/browser/transport/undici.d.ts +38 -0
  124. package/dist/browser/transport/undici.js +897 -0
  125. package/dist/browser/types/ai.d.ts +267 -0
  126. package/dist/browser/types/ai.js +1 -0
  127. package/dist/browser/types/index.d.ts +351 -0
  128. package/dist/browser/types/index.js +1 -0
  129. package/dist/browser/types/logger.d.ts +16 -0
  130. package/dist/browser/types/logger.js +66 -0
  131. package/dist/browser/types/udp.d.ts +138 -0
  132. package/dist/browser/types/udp.js +1 -0
  133. package/dist/browser/utils/agent-manager.d.ts +29 -0
  134. package/dist/browser/utils/agent-manager.js +160 -0
  135. package/dist/browser/utils/body.d.ts +10 -0
  136. package/dist/browser/utils/body.js +148 -0
  137. package/dist/browser/utils/charset.d.ts +15 -0
  138. package/dist/browser/utils/charset.js +169 -0
  139. package/dist/browser/utils/concurrency.d.ts +20 -0
  140. package/dist/browser/utils/concurrency.js +120 -0
  141. package/dist/browser/utils/dns.d.ts +6 -0
  142. package/dist/browser/utils/dns.js +26 -0
  143. package/dist/browser/utils/header-parser.d.ts +94 -0
  144. package/dist/browser/utils/header-parser.js +617 -0
  145. package/dist/browser/utils/html-cleaner.d.ts +1 -0
  146. package/dist/browser/utils/html-cleaner.js +21 -0
  147. package/dist/browser/utils/link-header.d.ts +69 -0
  148. package/dist/browser/utils/link-header.js +190 -0
  149. package/dist/browser/utils/optional-require.d.ts +19 -0
  150. package/dist/browser/utils/optional-require.js +105 -0
  151. package/dist/browser/utils/progress.d.ts +8 -0
  152. package/dist/browser/utils/progress.js +82 -0
  153. package/dist/browser/utils/request-pool.d.ts +22 -0
  154. package/dist/browser/utils/request-pool.js +101 -0
  155. package/dist/browser/utils/sse.d.ts +7 -0
  156. package/dist/browser/utils/sse.js +67 -0
  157. package/dist/browser/utils/streaming.d.ts +17 -0
  158. package/dist/browser/utils/streaming.js +84 -0
  159. package/dist/browser/utils/try-fn.d.ts +3 -0
  160. package/dist/browser/utils/try-fn.js +59 -0
  161. package/dist/browser/utils/user-agent.d.ts +44 -0
  162. package/dist/browser/utils/user-agent.js +100 -0
  163. package/dist/browser/utils/whois.d.ts +32 -0
  164. package/dist/browser/utils/whois.js +246 -0
  165. package/dist/browser/websocket/client.d.ts +65 -0
  166. package/dist/browser/websocket/client.js +313 -0
  167. package/dist/cli/index.d.ts +1 -0
  168. package/dist/cli/index.js +99 -3
  169. package/dist/index.d.ts +1 -0
  170. package/dist/index.js +1 -0
  171. package/dist/seo/analyzer.d.ts +20 -0
  172. package/dist/seo/analyzer.js +544 -0
  173. package/dist/seo/index.d.ts +2 -0
  174. package/dist/seo/index.js +1 -0
  175. package/dist/seo/types.d.ts +100 -0
  176. package/dist/seo/types.js +1 -0
  177. package/dist/transport/fetch.d.ts +7 -1
  178. package/dist/transport/fetch.js +58 -76
  179. package/dist/utils/columns.d.ts +14 -0
  180. package/dist/utils/columns.js +69 -0
  181. package/package.json +34 -2
@@ -0,0 +1,132 @@
1
+ import { CacheEntry, CacheStorage } from '../types/index.js';
2
+ export interface CompressionConfig {
3
+ enabled: boolean;
4
+ threshold?: number;
5
+ }
6
+ export interface MemoryStorageOptions {
7
+ maxSize?: number;
8
+ maxMemoryBytes?: number;
9
+ maxMemoryPercent?: number;
10
+ ttl?: number;
11
+ evictionPolicy?: 'lru' | 'fifo';
12
+ compression?: boolean | CompressionConfig;
13
+ enableStats?: boolean;
14
+ monitorInterval?: number;
15
+ heapUsageThreshold?: number;
16
+ cleanupInterval?: number;
17
+ onEvict?: (info: EvictionInfo) => void;
18
+ onPressure?: (info: PressureInfo) => void;
19
+ }
20
+ export interface EvictionInfo {
21
+ reason: 'size' | 'memory' | 'heap' | 'expired';
22
+ key?: string;
23
+ freedBytes: number;
24
+ currentBytes: number;
25
+ maxMemoryBytes: number;
26
+ }
27
+ export interface PressureInfo {
28
+ reason: 'limit' | 'heap';
29
+ heapLimit: number;
30
+ heapUsed: number;
31
+ heapRatio?: number;
32
+ currentBytes: number;
33
+ maxMemoryBytes: number;
34
+ freedBytes: number;
35
+ }
36
+ export interface CacheStats {
37
+ enabled: boolean;
38
+ hits: number;
39
+ misses: number;
40
+ sets: number;
41
+ deletes: number;
42
+ evictions: number;
43
+ hitRate: number;
44
+ totalItems: number;
45
+ memoryUsageBytes: number;
46
+ maxMemoryBytes: number;
47
+ evictedDueToMemory: number;
48
+ }
49
+ export interface MemoryStats {
50
+ currentMemoryBytes: number;
51
+ maxMemoryBytes: number;
52
+ maxMemoryPercent: number;
53
+ memoryUsagePercent: number;
54
+ cachePercentOfSystemMemory: number;
55
+ totalItems: number;
56
+ maxSize: number;
57
+ evictedDueToMemory: number;
58
+ memoryPressureEvents: number;
59
+ averageItemSize: number;
60
+ memoryUsage: {
61
+ current: string;
62
+ max: string;
63
+ available: string;
64
+ };
65
+ systemMemory: {
66
+ total: string;
67
+ free: string;
68
+ used: string;
69
+ cachePercent: string;
70
+ };
71
+ }
72
+ export interface CompressionStats {
73
+ enabled: boolean;
74
+ totalItems: number;
75
+ compressedItems: number;
76
+ compressionThreshold: number;
77
+ totalOriginalSize: number;
78
+ totalCompressedSize: number;
79
+ averageCompressionRatio: string;
80
+ spaceSavingsPercent: string;
81
+ memoryUsage: {
82
+ uncompressed: string;
83
+ compressed: string;
84
+ saved: string;
85
+ };
86
+ }
87
+ export declare class MemoryStorage implements CacheStorage {
88
+ private storage;
89
+ private meta;
90
+ private readonly maxSize;
91
+ private readonly maxMemoryBytes;
92
+ private readonly maxMemoryPercent;
93
+ private readonly defaultTtl;
94
+ private readonly evictionPolicy;
95
+ private readonly compressionEnabled;
96
+ private readonly compressionThreshold;
97
+ private readonly enableStats;
98
+ private readonly heapUsageThreshold;
99
+ private readonly onEvict?;
100
+ private readonly onPressure?;
101
+ private currentMemoryBytes;
102
+ private evictedDueToMemory;
103
+ private memoryPressureEvents;
104
+ private accessCounter;
105
+ private monitorHandle;
106
+ private cleanupHandle;
107
+ private stats;
108
+ private compressionStats;
109
+ constructor(options?: MemoryStorageOptions);
110
+ get(key: string): Promise<CacheEntry | undefined>;
111
+ set(key: string, entry: CacheEntry, ttl?: number): Promise<void>;
112
+ delete(key: string): Promise<void>;
113
+ clear(prefix?: string): void;
114
+ size(): number;
115
+ keys(): string[];
116
+ has(key: string): boolean;
117
+ getStats(): CacheStats;
118
+ getMemoryStats(): MemoryStats;
119
+ getCompressionStats(): CompressionStats;
120
+ shutdown(): void;
121
+ private deleteInternal;
122
+ private recordStat;
123
+ private isCompressed;
124
+ private compress;
125
+ private decompress;
126
+ private selectEvictionCandidate;
127
+ private evictOne;
128
+ private enforceMemoryLimit;
129
+ private reduceMemoryTo;
130
+ private memoryHealthCheck;
131
+ private cleanupExpired;
132
+ }
@@ -0,0 +1,454 @@
1
+ import { ConfigurationError } from '../core/errors.js';
2
+ import zlib from 'node:zlib';
3
+ import os from 'node:os';
4
+ import { getEffectiveTotalMemoryBytes, resolveCacheMemoryLimit, formatBytes, getHeapStats, } from './memory-limits.js';
5
+ export class MemoryStorage {
6
+ storage = new Map();
7
+ meta = new Map();
8
+ maxSize;
9
+ maxMemoryBytes;
10
+ maxMemoryPercent;
11
+ defaultTtl;
12
+ evictionPolicy;
13
+ compressionEnabled;
14
+ compressionThreshold;
15
+ enableStats;
16
+ heapUsageThreshold;
17
+ onEvict;
18
+ onPressure;
19
+ currentMemoryBytes = 0;
20
+ evictedDueToMemory = 0;
21
+ memoryPressureEvents = 0;
22
+ accessCounter = 0;
23
+ monitorHandle = null;
24
+ cleanupHandle = null;
25
+ stats = {
26
+ hits: 0,
27
+ misses: 0,
28
+ sets: 0,
29
+ deletes: 0,
30
+ evictions: 0,
31
+ };
32
+ compressionStats = {
33
+ totalCompressed: 0,
34
+ totalOriginalSize: 0,
35
+ totalCompressedSize: 0,
36
+ };
37
+ constructor(options = {}) {
38
+ if (options.maxMemoryBytes &&
39
+ options.maxMemoryBytes > 0 &&
40
+ options.maxMemoryPercent &&
41
+ options.maxMemoryPercent > 0) {
42
+ throw new ConfigurationError('[MemoryStorage] Cannot use both maxMemoryBytes and maxMemoryPercent', { configKey: 'maxMemoryBytes|maxMemoryPercent' });
43
+ }
44
+ if (options.maxMemoryPercent !== undefined &&
45
+ (options.maxMemoryPercent < 0 || options.maxMemoryPercent > 1)) {
46
+ throw new ConfigurationError('[MemoryStorage] maxMemoryPercent must be between 0 and 1', { configKey: 'maxMemoryPercent' });
47
+ }
48
+ this.maxSize = options.maxSize ?? 1000;
49
+ this.defaultTtl = options.ttl ?? 300000;
50
+ this.evictionPolicy = options.evictionPolicy ?? 'lru';
51
+ this.enableStats = options.enableStats ?? false;
52
+ this.heapUsageThreshold = options.heapUsageThreshold ?? 0.6;
53
+ if (options.maxMemoryBytes && options.maxMemoryBytes > 0) {
54
+ this.maxMemoryBytes = options.maxMemoryBytes;
55
+ this.maxMemoryPercent = 0;
56
+ }
57
+ else if (options.maxMemoryPercent && options.maxMemoryPercent > 0) {
58
+ const effectiveTotal = getEffectiveTotalMemoryBytes();
59
+ this.maxMemoryBytes = Math.floor(effectiveTotal * options.maxMemoryPercent);
60
+ this.maxMemoryPercent = options.maxMemoryPercent;
61
+ }
62
+ else {
63
+ const resolved = resolveCacheMemoryLimit({});
64
+ this.maxMemoryBytes = resolved.maxMemoryBytes;
65
+ this.maxMemoryPercent = resolved.inferredPercent ?? 0;
66
+ }
67
+ if (options.compression === true) {
68
+ this.compressionEnabled = true;
69
+ this.compressionThreshold = 1024;
70
+ }
71
+ else if (typeof options.compression === 'object' &&
72
+ options.compression.enabled) {
73
+ this.compressionEnabled = true;
74
+ this.compressionThreshold = options.compression.threshold ?? 1024;
75
+ }
76
+ else {
77
+ this.compressionEnabled = false;
78
+ this.compressionThreshold = 1024;
79
+ }
80
+ this.onEvict = options.onEvict;
81
+ this.onPressure = options.onPressure;
82
+ const monitorInterval = options.monitorInterval ?? 15000;
83
+ if (monitorInterval > 0) {
84
+ this.monitorHandle = setInterval(() => this.memoryHealthCheck(), monitorInterval);
85
+ this.monitorHandle.unref();
86
+ }
87
+ const cleanupInterval = options.cleanupInterval ?? 60000;
88
+ if (cleanupInterval > 0) {
89
+ this.cleanupHandle = setInterval(() => this.cleanupExpired(), cleanupInterval);
90
+ this.cleanupHandle.unref();
91
+ }
92
+ }
93
+ async get(key) {
94
+ const data = this.storage.get(key);
95
+ const metadata = this.meta.get(key);
96
+ if (!data || !metadata) {
97
+ this.recordStat('misses');
98
+ return undefined;
99
+ }
100
+ const now = Date.now();
101
+ if (now > metadata.expiresAt) {
102
+ this.deleteInternal(key);
103
+ this.recordStat('misses');
104
+ return undefined;
105
+ }
106
+ if (this.evictionPolicy === 'lru') {
107
+ metadata.lastAccess = now;
108
+ metadata.accessOrder = ++this.accessCounter;
109
+ }
110
+ this.recordStat('hits');
111
+ if (this.isCompressed(data)) {
112
+ try {
113
+ const decompressed = this.decompress(data);
114
+ return JSON.parse(decompressed);
115
+ }
116
+ catch {
117
+ this.deleteInternal(key);
118
+ return undefined;
119
+ }
120
+ }
121
+ return JSON.parse(data);
122
+ }
123
+ async set(key, entry, ttl) {
124
+ const effectiveTtl = ttl ?? this.defaultTtl;
125
+ const now = Date.now();
126
+ const serialized = JSON.stringify(entry);
127
+ const originalSize = Buffer.byteLength(serialized, 'utf8');
128
+ let finalData = serialized;
129
+ let compressedSize = originalSize;
130
+ let compressed = false;
131
+ if (this.compressionEnabled && originalSize >= this.compressionThreshold) {
132
+ try {
133
+ const result = this.compress(serialized);
134
+ finalData = result;
135
+ compressedSize = Buffer.byteLength(result.__data, 'utf8');
136
+ compressed = true;
137
+ this.compressionStats.totalCompressed++;
138
+ this.compressionStats.totalOriginalSize += originalSize;
139
+ this.compressionStats.totalCompressedSize += compressedSize;
140
+ }
141
+ catch {
142
+ }
143
+ }
144
+ const existingMeta = this.meta.get(key);
145
+ if (existingMeta) {
146
+ this.currentMemoryBytes -= existingMeta.compressedSize;
147
+ }
148
+ if (!this.enforceMemoryLimit(compressedSize)) {
149
+ this.evictedDueToMemory++;
150
+ return;
151
+ }
152
+ if (!existingMeta && this.storage.size >= this.maxSize) {
153
+ this.evictOne('size');
154
+ }
155
+ this.storage.set(key, finalData);
156
+ this.meta.set(key, {
157
+ createdAt: now,
158
+ expiresAt: now + effectiveTtl,
159
+ lastAccess: now,
160
+ insertOrder: ++this.accessCounter,
161
+ accessOrder: this.accessCounter,
162
+ compressed,
163
+ originalSize,
164
+ compressedSize,
165
+ });
166
+ this.currentMemoryBytes += compressedSize;
167
+ this.recordStat('sets');
168
+ }
169
+ async delete(key) {
170
+ this.deleteInternal(key);
171
+ this.recordStat('deletes');
172
+ }
173
+ clear(prefix) {
174
+ if (!prefix) {
175
+ this.storage.clear();
176
+ this.meta.clear();
177
+ this.currentMemoryBytes = 0;
178
+ this.evictedDueToMemory = 0;
179
+ if (this.enableStats) {
180
+ this.stats = { hits: 0, misses: 0, sets: 0, deletes: 0, evictions: 0 };
181
+ }
182
+ return;
183
+ }
184
+ for (const key of this.storage.keys()) {
185
+ if (key.startsWith(prefix)) {
186
+ this.deleteInternal(key);
187
+ }
188
+ }
189
+ }
190
+ size() {
191
+ return this.storage.size;
192
+ }
193
+ keys() {
194
+ return Array.from(this.storage.keys());
195
+ }
196
+ has(key) {
197
+ const meta = this.meta.get(key);
198
+ if (!meta)
199
+ return false;
200
+ if (Date.now() > meta.expiresAt) {
201
+ this.deleteInternal(key);
202
+ return false;
203
+ }
204
+ return true;
205
+ }
206
+ getStats() {
207
+ const total = this.stats.hits + this.stats.misses;
208
+ const hitRate = total > 0 ? this.stats.hits / total : 0;
209
+ return {
210
+ enabled: this.enableStats,
211
+ ...this.stats,
212
+ hitRate,
213
+ totalItems: this.storage.size,
214
+ memoryUsageBytes: this.currentMemoryBytes,
215
+ maxMemoryBytes: this.maxMemoryBytes,
216
+ evictedDueToMemory: this.evictedDueToMemory,
217
+ };
218
+ }
219
+ getMemoryStats() {
220
+ const totalItems = this.storage.size;
221
+ const memoryUsagePercent = this.maxMemoryBytes > 0
222
+ ? (this.currentMemoryBytes / this.maxMemoryBytes) * 100
223
+ : 0;
224
+ const systemTotal = os.totalmem();
225
+ const systemFree = os.freemem();
226
+ const systemUsed = systemTotal - systemFree;
227
+ const cachePercentOfSystem = systemTotal > 0 ? (this.currentMemoryBytes / systemTotal) * 100 : 0;
228
+ return {
229
+ currentMemoryBytes: this.currentMemoryBytes,
230
+ maxMemoryBytes: this.maxMemoryBytes,
231
+ maxMemoryPercent: this.maxMemoryPercent,
232
+ memoryUsagePercent: parseFloat(memoryUsagePercent.toFixed(2)),
233
+ cachePercentOfSystemMemory: parseFloat(cachePercentOfSystem.toFixed(2)),
234
+ totalItems,
235
+ maxSize: this.maxSize,
236
+ evictedDueToMemory: this.evictedDueToMemory,
237
+ memoryPressureEvents: this.memoryPressureEvents,
238
+ averageItemSize: totalItems > 0 ? Math.round(this.currentMemoryBytes / totalItems) : 0,
239
+ memoryUsage: {
240
+ current: formatBytes(this.currentMemoryBytes),
241
+ max: formatBytes(this.maxMemoryBytes),
242
+ available: formatBytes(Math.max(0, this.maxMemoryBytes - this.currentMemoryBytes)),
243
+ },
244
+ systemMemory: {
245
+ total: formatBytes(systemTotal),
246
+ free: formatBytes(systemFree),
247
+ used: formatBytes(systemUsed),
248
+ cachePercent: `${cachePercentOfSystem.toFixed(2)}%`,
249
+ },
250
+ };
251
+ }
252
+ getCompressionStats() {
253
+ if (!this.compressionEnabled) {
254
+ return {
255
+ enabled: false,
256
+ totalItems: this.storage.size,
257
+ compressedItems: 0,
258
+ compressionThreshold: this.compressionThreshold,
259
+ totalOriginalSize: 0,
260
+ totalCompressedSize: 0,
261
+ averageCompressionRatio: '0',
262
+ spaceSavingsPercent: '0',
263
+ memoryUsage: {
264
+ uncompressed: '0 B',
265
+ compressed: '0 B',
266
+ saved: '0 B',
267
+ },
268
+ };
269
+ }
270
+ const ratio = this.compressionStats.totalOriginalSize > 0
271
+ ? (this.compressionStats.totalCompressedSize /
272
+ this.compressionStats.totalOriginalSize).toFixed(2)
273
+ : '0';
274
+ const savings = this.compressionStats.totalOriginalSize > 0
275
+ ? (((this.compressionStats.totalOriginalSize -
276
+ this.compressionStats.totalCompressedSize) /
277
+ this.compressionStats.totalOriginalSize) *
278
+ 100).toFixed(2)
279
+ : '0';
280
+ const saved = this.compressionStats.totalOriginalSize -
281
+ this.compressionStats.totalCompressedSize;
282
+ return {
283
+ enabled: true,
284
+ totalItems: this.storage.size,
285
+ compressedItems: this.compressionStats.totalCompressed,
286
+ compressionThreshold: this.compressionThreshold,
287
+ totalOriginalSize: this.compressionStats.totalOriginalSize,
288
+ totalCompressedSize: this.compressionStats.totalCompressedSize,
289
+ averageCompressionRatio: ratio,
290
+ spaceSavingsPercent: savings,
291
+ memoryUsage: {
292
+ uncompressed: formatBytes(this.compressionStats.totalOriginalSize),
293
+ compressed: formatBytes(this.compressionStats.totalCompressedSize),
294
+ saved: formatBytes(saved),
295
+ },
296
+ };
297
+ }
298
+ shutdown() {
299
+ if (this.monitorHandle) {
300
+ clearInterval(this.monitorHandle);
301
+ this.monitorHandle = null;
302
+ }
303
+ if (this.cleanupHandle) {
304
+ clearInterval(this.cleanupHandle);
305
+ this.cleanupHandle = null;
306
+ }
307
+ }
308
+ deleteInternal(key) {
309
+ const meta = this.meta.get(key);
310
+ if (meta) {
311
+ this.currentMemoryBytes -= meta.compressedSize;
312
+ }
313
+ this.storage.delete(key);
314
+ this.meta.delete(key);
315
+ }
316
+ recordStat(type) {
317
+ if (this.enableStats) {
318
+ this.stats[type]++;
319
+ }
320
+ }
321
+ isCompressed(data) {
322
+ return (typeof data === 'object' && data !== null && '__compressed' in data);
323
+ }
324
+ compress(data) {
325
+ const buffer = Buffer.from(data, 'utf8');
326
+ const compressed = zlib.gzipSync(buffer);
327
+ return {
328
+ __compressed: true,
329
+ __data: compressed.toString('base64'),
330
+ __originalSize: buffer.length,
331
+ };
332
+ }
333
+ decompress(data) {
334
+ const buffer = Buffer.from(data.__data, 'base64');
335
+ const decompressed = zlib.gunzipSync(buffer);
336
+ return decompressed.toString('utf8');
337
+ }
338
+ selectEvictionCandidate() {
339
+ if (this.meta.size === 0)
340
+ return null;
341
+ let candidate = null;
342
+ let candidateValue = this.evictionPolicy === 'lru' ? Infinity : Infinity;
343
+ for (const [key, meta] of this.meta) {
344
+ const value = this.evictionPolicy === 'lru' ? meta.accessOrder : meta.insertOrder;
345
+ if (value < candidateValue) {
346
+ candidateValue = value;
347
+ candidate = key;
348
+ }
349
+ }
350
+ return candidate;
351
+ }
352
+ evictOne(reason) {
353
+ const candidate = this.selectEvictionCandidate();
354
+ if (!candidate)
355
+ return null;
356
+ const meta = this.meta.get(candidate);
357
+ const freedBytes = meta?.compressedSize ?? 0;
358
+ this.deleteInternal(candidate);
359
+ this.stats.evictions++;
360
+ if (reason === 'memory' || reason === 'heap') {
361
+ this.evictedDueToMemory++;
362
+ }
363
+ this.onEvict?.({
364
+ reason,
365
+ key: candidate,
366
+ freedBytes,
367
+ currentBytes: this.currentMemoryBytes,
368
+ maxMemoryBytes: this.maxMemoryBytes,
369
+ });
370
+ return { key: candidate, freedBytes };
371
+ }
372
+ enforceMemoryLimit(incomingSize) {
373
+ if (incomingSize > this.maxMemoryBytes) {
374
+ return false;
375
+ }
376
+ while (this.currentMemoryBytes + incomingSize > this.maxMemoryBytes &&
377
+ this.storage.size > 0) {
378
+ const result = this.evictOne('memory');
379
+ if (!result)
380
+ break;
381
+ }
382
+ return this.currentMemoryBytes + incomingSize <= this.maxMemoryBytes;
383
+ }
384
+ reduceMemoryTo(targetBytes) {
385
+ targetBytes = Math.max(0, targetBytes);
386
+ let freedBytes = 0;
387
+ while (this.currentMemoryBytes > targetBytes && this.storage.size > 0) {
388
+ const result = this.evictOne('memory');
389
+ if (!result)
390
+ break;
391
+ freedBytes += result.freedBytes;
392
+ }
393
+ return freedBytes;
394
+ }
395
+ memoryHealthCheck() {
396
+ let totalFreed = 0;
397
+ if (this.currentMemoryBytes > this.maxMemoryBytes) {
398
+ const before = this.currentMemoryBytes;
399
+ this.enforceMemoryLimit(0);
400
+ const freed = before - this.currentMemoryBytes;
401
+ if (freed > 0) {
402
+ totalFreed += freed;
403
+ this.memoryPressureEvents++;
404
+ this.onPressure?.({
405
+ reason: 'limit',
406
+ heapLimit: getHeapStats().heapLimit,
407
+ heapUsed: getHeapStats().heapUsed,
408
+ currentBytes: this.currentMemoryBytes,
409
+ maxMemoryBytes: this.maxMemoryBytes,
410
+ freedBytes: freed,
411
+ });
412
+ }
413
+ }
414
+ const { heapUsed, heapLimit, heapRatio } = getHeapStats();
415
+ if (heapLimit > 0 && heapRatio >= this.heapUsageThreshold) {
416
+ const before = this.currentMemoryBytes;
417
+ const target = Math.floor(this.currentMemoryBytes * 0.5);
418
+ this.reduceMemoryTo(target);
419
+ const freed = before - this.currentMemoryBytes;
420
+ if (freed > 0) {
421
+ totalFreed += freed;
422
+ this.memoryPressureEvents++;
423
+ this.onPressure?.({
424
+ reason: 'heap',
425
+ heapLimit,
426
+ heapUsed,
427
+ heapRatio,
428
+ currentBytes: this.currentMemoryBytes,
429
+ maxMemoryBytes: this.maxMemoryBytes,
430
+ freedBytes: freed,
431
+ });
432
+ }
433
+ }
434
+ return totalFreed;
435
+ }
436
+ cleanupExpired() {
437
+ const now = Date.now();
438
+ let cleaned = 0;
439
+ for (const [key, meta] of this.meta) {
440
+ if (now > meta.expiresAt) {
441
+ this.deleteInternal(key);
442
+ cleaned++;
443
+ this.onEvict?.({
444
+ reason: 'expired',
445
+ key,
446
+ freedBytes: meta.compressedSize,
447
+ currentBytes: this.currentMemoryBytes,
448
+ maxMemoryBytes: this.maxMemoryBytes,
449
+ });
450
+ }
451
+ }
452
+ return cleaned;
453
+ }
454
+ }
@@ -0,0 +1,40 @@
1
+ export interface CacheEntry<T = unknown> {
2
+ key: string;
3
+ value: T;
4
+ createdAt: number;
5
+ expiresAt?: number;
6
+ etag?: string;
7
+ lastModified?: string;
8
+ }
9
+ export interface BrowserCacheOptions {
10
+ dbName?: string;
11
+ storeName?: string;
12
+ defaultTTL?: number;
13
+ maxEntries?: number;
14
+ }
15
+ export declare class IndexedDBStorage {
16
+ private dbName;
17
+ private storeName;
18
+ private defaultTTL;
19
+ private maxEntries;
20
+ private db;
21
+ private initPromise;
22
+ constructor(options?: BrowserCacheOptions);
23
+ init(): Promise<void>;
24
+ get<T = unknown>(key: string): Promise<CacheEntry<T> | null>;
25
+ set<T = unknown>(key: string, value: T, ttl?: number, metadata?: {
26
+ etag?: string;
27
+ lastModified?: string;
28
+ }): Promise<void>;
29
+ delete(key: string): Promise<boolean>;
30
+ has(key: string): Promise<boolean>;
31
+ clear(): Promise<void>;
32
+ keys(): Promise<string[]>;
33
+ size(): Promise<number>;
34
+ cleanup(): Promise<number>;
35
+ private enforceMaxEntries;
36
+ close(): void;
37
+ destroy(): Promise<void>;
38
+ }
39
+ export declare function isIndexedDBAvailable(): boolean;
40
+ export declare function getBrowserCacheStorage(options?: BrowserCacheOptions): IndexedDBStorage | null;