chaincss 2.0.7 → 2.1.1

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 (159) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/CODE_OF_CONDUCT.md +21 -0
  3. package/CONTRIBUTING.md +28 -0
  4. package/README.md +455 -226
  5. package/demo/demo/node_modules/caniuse-db/fulldata-json/data-2.0.json +1 -0
  6. package/demo/index.html +16 -0
  7. package/demo/package.json +20 -0
  8. package/demo/src/App.tsx +117 -0
  9. package/demo/src/chaincss-barrel.ts +9 -0
  10. package/demo/src/main.tsx +8 -0
  11. package/demo/src/styles.chain.ts +300 -0
  12. package/demo/vite.config.ts +46 -0
  13. package/dist/cli/commands/build.d.ts +0 -1
  14. package/dist/cli/commands/cache.d.ts +1 -0
  15. package/dist/cli/commands/init.d.ts +6 -3
  16. package/dist/cli/commands/timeline.d.ts +0 -1
  17. package/dist/cli/commands/watch.d.ts +0 -1
  18. package/dist/cli/index.d.ts +0 -1
  19. package/dist/cli/index.js +3213 -5296
  20. package/dist/cli/types.d.ts +51 -20
  21. package/dist/cli/utils/config-loader.d.ts +0 -1
  22. package/dist/cli/utils/file-utils.d.ts +27 -3
  23. package/dist/cli/utils/logger.d.ts +0 -1
  24. package/dist/compiler/Chain.d.ts +215 -0
  25. package/dist/compiler/animations.d.ts +76 -0
  26. package/dist/compiler/atomic-optimizer.d.ts +47 -12
  27. package/dist/compiler/breakpoints.d.ts +46 -0
  28. package/dist/compiler/btt.d.ts +36 -60
  29. package/dist/compiler/cache-manager.d.ts +58 -4
  30. package/dist/compiler/commonProps.d.ts +0 -1
  31. package/dist/compiler/content-addressable-cache.d.ts +78 -0
  32. package/dist/compiler/helpers.d.ts +54 -0
  33. package/dist/compiler/index.d.ts +16 -9
  34. package/dist/compiler/index.js +4450 -4316
  35. package/dist/compiler/prefixer.d.ts +17 -1
  36. package/dist/compiler/shorthands.d.ts +28 -0
  37. package/dist/compiler/suggestions.d.ts +43 -0
  38. package/dist/compiler/theme-contract.d.ts +16 -27
  39. package/dist/compiler/token-resolver.d.ts +69 -0
  40. package/dist/compiler/tokens.d.ts +33 -8
  41. package/dist/core/auto-detector.d.ts +34 -0
  42. package/dist/core/common-utils.d.ts +97 -0
  43. package/dist/core/compiler.d.ts +63 -23
  44. package/dist/core/constants.d.ts +137 -36
  45. package/dist/core/smart-chain.d.ts +3 -0
  46. package/dist/core/types.d.ts +122 -15
  47. package/dist/core/utils.d.ts +134 -17
  48. package/dist/index.d.ts +52 -8
  49. package/dist/index.js +7090 -5578
  50. package/dist/plugins/vite.d.ts +7 -5
  51. package/dist/plugins/vite.js +2964 -25641
  52. package/dist/plugins/webpack.d.ts +24 -1
  53. package/dist/plugins/webpack.js +209 -72
  54. package/dist/runtime/Chain.d.ts +32 -0
  55. package/dist/runtime/auto-hooks.d.ts +11 -0
  56. package/dist/runtime/hmr.d.ts +22 -2
  57. package/dist/runtime/index.d.ts +3 -2
  58. package/dist/runtime/index.js +3648 -301
  59. package/dist/runtime/injector.d.ts +39 -72
  60. package/dist/runtime/react.d.ts +17 -12
  61. package/dist/runtime/svelte.d.ts +15 -0
  62. package/dist/runtime/types.d.ts +126 -4
  63. package/dist/runtime/utils.d.ts +0 -1
  64. package/dist/runtime/vue.d.ts +34 -14
  65. package/package.json +59 -66
  66. package/src/cli/commands/build.ts +133 -0
  67. package/src/cli/commands/cache.ts +371 -0
  68. package/src/cli/commands/init.ts +230 -0
  69. package/src/cli/commands/timeline.ts +435 -0
  70. package/src/cli/commands/watch.ts +211 -0
  71. package/src/cli/index.ts +226 -0
  72. package/src/cli/types.ts +100 -0
  73. package/src/cli/utils/config-loader.ts +174 -0
  74. package/src/cli/utils/file-utils.ts +139 -0
  75. package/src/cli/utils/logger.ts +74 -0
  76. package/src/compiler/Chain.ts +831 -0
  77. package/src/compiler/animations.ts +517 -0
  78. package/src/compiler/atomic-optimizer.ts +786 -0
  79. package/src/compiler/breakpoints.ts +347 -0
  80. package/src/compiler/btt.ts +1147 -0
  81. package/src/compiler/cache-manager.ts +446 -0
  82. package/src/compiler/commonProps.ts +18 -0
  83. package/src/compiler/content-addressable-cache.ts +478 -0
  84. package/src/compiler/helpers.ts +407 -0
  85. package/src/compiler/index.ts +72 -0
  86. package/src/compiler/prefixer.ts +720 -0
  87. package/src/compiler/shorthands.ts +558 -0
  88. package/src/compiler/suggestions.ts +436 -0
  89. package/src/compiler/theme-contract.ts +197 -0
  90. package/src/compiler/token-resolver.ts +241 -0
  91. package/src/compiler/tokens.ts +612 -0
  92. package/src/core/auto-detector.ts +187 -0
  93. package/src/core/common-utils.ts +423 -0
  94. package/src/core/compiler.ts +835 -0
  95. package/src/core/constants.ts +424 -0
  96. package/src/core/index.ts +107 -0
  97. package/src/core/smart-chain.ts +163 -0
  98. package/src/core/types.ts +257 -0
  99. package/src/core/utils.ts +598 -0
  100. package/src/index.ts +208 -0
  101. package/src/plugins/vite.d.ts +316 -0
  102. package/src/plugins/vite.ts +424 -0
  103. package/src/plugins/webpack.d.ts +289 -0
  104. package/src/plugins/webpack.ts +416 -0
  105. package/src/runtime/Chain.ts +242 -0
  106. package/src/runtime/auto-hooks.tsx +127 -0
  107. package/src/runtime/auto-vue.ts +72 -0
  108. package/src/runtime/hmr.ts +212 -0
  109. package/src/runtime/index.ts +82 -0
  110. package/src/runtime/injector.ts +273 -0
  111. package/src/runtime/react.tsx +269 -0
  112. package/src/runtime/svelte.ts +15 -0
  113. package/src/runtime/types.ts +256 -0
  114. package/src/runtime/utils.ts +128 -0
  115. package/src/runtime/vite-env.d.ts +120 -0
  116. package/src/runtime/vue.ts +231 -0
  117. package/tsconfig.build.json +41 -0
  118. package/tsconfig.json +25 -0
  119. package/tsconfig.runtimes.json +18 -0
  120. package/dist/cli/cli.cjs +0 -7
  121. package/dist/cli/commands/build.d.ts.map +0 -1
  122. package/dist/cli/commands/compile.d.ts +0 -3
  123. package/dist/cli/commands/compile.d.ts.map +0 -1
  124. package/dist/cli/commands/init.d.ts.map +0 -1
  125. package/dist/cli/commands/timeline.d.ts.map +0 -1
  126. package/dist/cli/commands/watch.d.ts.map +0 -1
  127. package/dist/cli/index.d.ts.map +0 -1
  128. package/dist/cli/types.d.ts.map +0 -1
  129. package/dist/cli/utils/config-loader.d.ts.map +0 -1
  130. package/dist/cli/utils/file-utils.d.ts.map +0 -1
  131. package/dist/cli/utils/logger.d.ts.map +0 -1
  132. package/dist/compiler/atomic-optimizer.d.ts.map +0 -1
  133. package/dist/compiler/btt.d.ts.map +0 -1
  134. package/dist/compiler/cache-manager.d.ts.map +0 -1
  135. package/dist/compiler/commonProps.d.ts.map +0 -1
  136. package/dist/compiler/index.d.ts.map +0 -1
  137. package/dist/compiler/prefixer.d.ts.map +0 -1
  138. package/dist/compiler/theme-contract.d.ts.map +0 -1
  139. package/dist/compiler/tokens.d.ts.map +0 -1
  140. package/dist/compiler/types.d.ts +0 -57
  141. package/dist/compiler/types.d.ts.map +0 -1
  142. package/dist/core/compiler.d.ts.map +0 -1
  143. package/dist/core/constants.d.ts.map +0 -1
  144. package/dist/core/index.d.ts +0 -4
  145. package/dist/core/index.d.ts.map +0 -1
  146. package/dist/core/types.d.ts.map +0 -1
  147. package/dist/core/utils.d.ts.map +0 -1
  148. package/dist/index.d.ts.map +0 -1
  149. package/dist/plugins/vite.d.ts.map +0 -1
  150. package/dist/plugins/webpack.d.ts.map +0 -1
  151. package/dist/runtime/hmr.d.ts.map +0 -1
  152. package/dist/runtime/index.d.ts.map +0 -1
  153. package/dist/runtime/injector.d.ts.map +0 -1
  154. package/dist/runtime/react.d.ts.map +0 -1
  155. package/dist/runtime/react.js +0 -324
  156. package/dist/runtime/types.d.ts.map +0 -1
  157. package/dist/runtime/utils.d.ts.map +0 -1
  158. package/dist/runtime/vue.d.ts.map +0 -1
  159. package/dist/runtime/vue.js +0 -286
@@ -0,0 +1,478 @@
1
+ // chaincss/src/compiler/content-addressable-cache.ts
2
+
3
+ import crypto from 'crypto';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+
7
+ export interface PersistentCacheEntry {
8
+ hash: string;
9
+ result: any;
10
+ dependencies: Record<string, string>;
11
+ timestamp: number;
12
+ version: string;
13
+ compilerVersion: string;
14
+ // Add these missing properties
15
+ createdAt?: number;
16
+ lastAccessed?: number;
17
+ accessCount?: number;
18
+ size?: number;
19
+ value?: any;
20
+ }
21
+
22
+ export interface PersistentCacheOptions {
23
+ cacheDir?: string;
24
+ maxAgeDays?: number;
25
+ maxSizeMB?: number;
26
+ enabled?: boolean;
27
+ verbose?: boolean;
28
+ }
29
+
30
+ export class PersistentCache {
31
+ private cacheDir: string;
32
+ private options: Required<PersistentCacheOptions>;
33
+ private memoryCache = new Map<string, PersistentCacheEntry>();
34
+ private metadataPath: string;
35
+ private metadata: {
36
+ entries: Record<string, { hash: string; size: number; timestamp: number }>;
37
+ totalSize: number;
38
+ lastCleanup: number;
39
+ };
40
+
41
+ constructor(options: PersistentCacheOptions = {}) {
42
+ this.options = {
43
+ cacheDir: options.cacheDir || './.chaincss/persistent-cache',
44
+ maxAgeDays: options.maxAgeDays || 30,
45
+ maxSizeMB: options.maxSizeMB || 500,
46
+ enabled: options.enabled !== false,
47
+ verbose: options.verbose || false
48
+ };
49
+
50
+ this.cacheDir = path.resolve(process.cwd(), this.options.cacheDir);
51
+ // Initialize metadataPath AFTER cacheDir is set
52
+ this.metadataPath = path.join(this.cacheDir, 'metadata.json');
53
+ this.metadata = { entries: {}, totalSize: 0, lastCleanup: 0 };
54
+
55
+ if (this.options.enabled) {
56
+ this.ensureDir();
57
+ this.loadMetadata();
58
+ }
59
+ }
60
+
61
+ private hash(content: string | Buffer): string {
62
+ return crypto.createHash('sha256').update(content).digest('hex');
63
+ }
64
+
65
+ private hashFile(filePath: string): string | null {
66
+ if (!fs.existsSync(filePath)) return null;
67
+ const content = fs.readFileSync(filePath, 'utf8');
68
+ return this.hash(content);
69
+ }
70
+
71
+ // ============================================================================
72
+ // Async Methods (Original)
73
+ // ============================================================================
74
+
75
+ async getByContent(source: string): Promise<any | null> {
76
+ if (!this.options.enabled) return null;
77
+ const hash = this.hash(source);
78
+ return this.getByHash(hash);
79
+ }
80
+
81
+ async getByFile(filePath: string): Promise<any | null> {
82
+ if (!this.options.enabled) return null;
83
+ if (!fs.existsSync(filePath)) return null;
84
+ const source = fs.readFileSync(filePath, 'utf8');
85
+ return this.getByContent(source);
86
+ }
87
+
88
+ async getByHash(hash: string): Promise<any | null> {
89
+ if (!this.options.enabled) return null;
90
+
91
+ if (this.memoryCache.has(hash)) {
92
+ const entry = this.memoryCache.get(hash)!;
93
+ if (!this.isExpired(entry)) {
94
+ if (this.options.verbose) {
95
+ console.log(`[persistent-cache] Memory HIT for hash ${hash.slice(0, 8)}`);
96
+ }
97
+ return entry.result;
98
+ } else {
99
+ this.memoryCache.delete(hash);
100
+ }
101
+ }
102
+
103
+ const cachePath = path.join(this.cacheDir, `${hash}.json`);
104
+ if (fs.existsSync(cachePath)) {
105
+ try {
106
+ const entry: PersistentCacheEntry = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
107
+
108
+ if (!this.isExpired(entry)) {
109
+ this.memoryCache.set(hash, entry);
110
+ if (this.options.verbose) {
111
+ console.log(`[persistent-cache] Disk HIT for hash ${hash.slice(0, 8)}`);
112
+ }
113
+ return entry.result;
114
+ } else {
115
+ fs.unlinkSync(cachePath);
116
+ if (this.options.verbose) {
117
+ console.log(`[persistent-cache] Expired entry removed: ${hash.slice(0, 8)}`);
118
+ }
119
+ }
120
+ } catch (err) {
121
+ // Ignore
122
+ }
123
+ }
124
+
125
+ return null;
126
+ }
127
+
128
+ async setByContent(source: string, result: any, dependencies: string[] = []): Promise<string> {
129
+ if (!this.options.enabled) return '';
130
+ const hash = this.hash(source);
131
+ return this.setByHash(hash, result, dependencies);
132
+ }
133
+
134
+ async setByFile(filePath: string, result: any, dependencies: string[] = []): Promise<string> {
135
+ if (!this.options.enabled) return '';
136
+ if (!fs.existsSync(filePath)) return '';
137
+ const source = fs.readFileSync(filePath, 'utf8');
138
+ return this.setByContent(source, result, dependencies);
139
+ }
140
+
141
+ async setByHash(hash: string, result: any, dependencies: string[] = []): Promise<string> {
142
+ if (!this.options.enabled) return hash;
143
+
144
+ const depHashes: Record<string, string> = {};
145
+ for (const dep of dependencies) {
146
+ const depHash = this.hashFile(dep);
147
+ if (depHash) {
148
+ depHashes[dep] = depHash;
149
+ }
150
+ }
151
+
152
+ const entry: PersistentCacheEntry = {
153
+ hash,
154
+ result,
155
+ dependencies: depHashes,
156
+ timestamp: Date.now(),
157
+ version: '2.0.0',
158
+ compilerVersion: '2.0.7'
159
+ };
160
+
161
+ this.memoryCache.set(hash, entry);
162
+
163
+ const cachePath = path.join(this.cacheDir, `${hash}.json`);
164
+ fs.writeFileSync(cachePath, JSON.stringify(entry, null, 2));
165
+
166
+ await this.updateMetadata(hash, entry);
167
+ await this.enforceSizeLimit();
168
+
169
+ if (this.options.verbose) {
170
+ console.log(`[persistent-cache] Stored hash ${hash.slice(0, 8)} (${dependencies.length} deps)`);
171
+ }
172
+
173
+ return hash;
174
+ }
175
+
176
+ // ============================================================================
177
+ // SYNC Methods (Added for compiler.ts compatibility)
178
+ // ============================================================================
179
+
180
+ getByHashSync(hash: string): any | null {
181
+ if (!this.options.enabled) return null;
182
+
183
+ if (this.memoryCache.has(hash)) {
184
+ const entry = this.memoryCache.get(hash)!;
185
+ if (!this.isExpired(entry)) {
186
+ return entry.result;
187
+ } else {
188
+ this.memoryCache.delete(hash);
189
+ }
190
+ }
191
+
192
+ const cachePath = path.join(this.cacheDir, `${hash}.json`);
193
+ if (fs.existsSync(cachePath)) {
194
+ try {
195
+ const entry: PersistentCacheEntry = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
196
+ if (!this.isExpired(entry)) {
197
+ this.memoryCache.set(hash, entry);
198
+ return entry.result;
199
+ } else {
200
+ fs.unlinkSync(cachePath);
201
+ }
202
+ } catch (err) {
203
+ // Ignore
204
+ }
205
+ }
206
+
207
+ return null;
208
+ }
209
+
210
+ setByHashSync(hash: string, result: any, source: any, dependencies: string[] = []): void {
211
+ if (!this.options.enabled) return;
212
+
213
+ const depHashes: Record<string, string> = {};
214
+ for (const dep of dependencies) {
215
+ if (typeof dep === 'string' && fs.existsSync(dep)) {
216
+ const depSource = fs.readFileSync(dep, 'utf8');
217
+ depHashes[dep] = this.hash(depSource);
218
+ }
219
+ }
220
+
221
+ const entry: PersistentCacheEntry = {
222
+ hash,
223
+ result,
224
+ dependencies: depHashes,
225
+ timestamp: Date.now(),
226
+ version: '2.0.0',
227
+ compilerVersion: '2.0.7'
228
+ };
229
+
230
+ this.memoryCache.set(hash, entry);
231
+
232
+ const cachePath = path.join(this.cacheDir, `${hash}.json`);
233
+ fs.writeFileSync(cachePath, JSON.stringify(entry, null, 2));
234
+
235
+ this.updateMetadataSync(hash, entry);
236
+ this.enforceSizeLimitSync();
237
+
238
+ if (this.options.verbose) {
239
+ console.log(`[persistent-cache] Stored hash ${hash.slice(0, 8)} (${dependencies.length} deps)`);
240
+ }
241
+ }
242
+
243
+ invalidateByHash(hash: string): void {
244
+ this.memoryCache.delete(hash);
245
+ const cachePath = path.join(this.cacheDir, `${hash}.json`);
246
+ if (fs.existsSync(cachePath)) {
247
+ fs.unlinkSync(cachePath);
248
+ }
249
+ delete this.metadata.entries[hash];
250
+ fs.writeFileSync(this.metadataPath, JSON.stringify(this.metadata, null, 2));
251
+ }
252
+
253
+ // ============================================================================
254
+ // Public Management Methods
255
+ // ============================================================================
256
+
257
+ async clear(): Promise<void> {
258
+ const files = fs.readdirSync(this.cacheDir);
259
+ for (const file of files) {
260
+ if (file.endsWith('.json')) {
261
+ fs.unlinkSync(path.join(this.cacheDir, file));
262
+ }
263
+ }
264
+ this.memoryCache.clear();
265
+ this.metadata = { entries: {}, totalSize: 0, lastCleanup: 0 };
266
+ fs.writeFileSync(this.metadataPath, JSON.stringify(this.metadata, null, 2));
267
+
268
+ if (this.options.verbose) {
269
+ console.log('[persistent-cache] Cache cleared');
270
+ }
271
+ }
272
+
273
+ async getStats(): Promise<{
274
+ entryCount: number;
275
+ totalSizeMB: number;
276
+ totalSizeBytes: number;
277
+ oldestEntry: number;
278
+ newestEntry: number;
279
+ hitRate?: number;
280
+ }> {
281
+ const entries = Object.values(this.metadata.entries);
282
+ if (entries.length === 0) {
283
+ return {
284
+ entryCount: 0,
285
+ totalSizeMB: 0,
286
+ totalSizeBytes: 0,
287
+ oldestEntry: 0,
288
+ newestEntry: 0,
289
+ hitRate: 0
290
+ };
291
+ }
292
+ const timestamps = entries.map(e => e.timestamp);
293
+ return {
294
+ entryCount: entries.length,
295
+ totalSizeMB: this.metadata.totalSize / 1024 / 1024,
296
+ totalSizeBytes: this.metadata.totalSize,
297
+ oldestEntry: Math.min(...timestamps),
298
+ newestEntry: Math.max(...timestamps),
299
+ hitRate: this.memoryCache.size / entries.length
300
+ };
301
+ }
302
+
303
+ async prune(): Promise<void> {
304
+ await this.enforceSizeLimit();
305
+ if (this.options.verbose) {
306
+ console.log('[persistent-cache] Prune completed');
307
+ }
308
+ }
309
+
310
+ // ============================================================================
311
+ // Private Methods
312
+ // ============================================================================
313
+
314
+ private isExpired(entry: PersistentCacheEntry): boolean {
315
+ const age = Date.now() - entry.timestamp;
316
+ const maxAge = this.options.maxAgeDays * 24 * 60 * 60 * 1000;
317
+ return age > maxAge;
318
+ }
319
+
320
+ private loadMetadata(): void {
321
+ if (fs.existsSync(this.metadataPath)) {
322
+ try {
323
+ const data = JSON.parse(fs.readFileSync(this.metadataPath, 'utf8'));
324
+ this.metadata = data;
325
+ } catch (err) {
326
+ this.metadata = { entries: {}, totalSize: 0, lastCleanup: 0 };
327
+ }
328
+ }
329
+ }
330
+
331
+ private async updateMetadata(hash: string, entry: PersistentCacheEntry): Promise<void> {
332
+ const cachePath = path.join(this.cacheDir, `${hash}.json`);
333
+ const stats = fs.statSync(cachePath);
334
+
335
+ this.metadata.entries[hash] = {
336
+ hash,
337
+ size: stats.size,
338
+ timestamp: entry.timestamp
339
+ };
340
+ this.metadata.totalSize += stats.size;
341
+ this.metadata.lastCleanup = Date.now();
342
+
343
+ fs.writeFileSync(this.metadataPath, JSON.stringify(this.metadata, null, 2));
344
+ }
345
+
346
+ private updateMetadataSync(hash: string, entry: PersistentCacheEntry): void {
347
+ const cachePath = path.join(this.cacheDir, `${hash}.json`);
348
+ const stats = fs.statSync(cachePath);
349
+
350
+ this.metadata.entries[hash] = {
351
+ hash,
352
+ size: stats.size,
353
+ timestamp: entry.timestamp
354
+ };
355
+ this.metadata.totalSize += stats.size;
356
+ this.metadata.lastCleanup = Date.now();
357
+
358
+ fs.writeFileSync(this.metadataPath, JSON.stringify(this.metadata, null, 2));
359
+ }
360
+
361
+ public async enforceSizeLimit(): Promise<void> {
362
+ const limitBytes = this.options.maxSizeMB * 1024 * 1024;
363
+
364
+ if (this.metadata.totalSize <= limitBytes) {
365
+ return;
366
+ }
367
+
368
+ if (this.options.verbose) {
369
+ console.log(`[persistent-cache] Cache size (${(this.metadata.totalSize / 1024 / 1024).toFixed(2)}MB) exceeds limit (${this.options.maxSizeMB}MB). Cleaning...`);
370
+ }
371
+
372
+ const entries = Object.values(this.metadata.entries)
373
+ .sort((a, b) => a.timestamp - b.timestamp);
374
+
375
+ let freedSpace = 0;
376
+ for (const entry of entries) {
377
+ if (this.metadata.totalSize - freedSpace <= limitBytes) break;
378
+
379
+ const cachePath = path.join(this.cacheDir, `${entry.hash}.json`);
380
+ if (fs.existsSync(cachePath)) {
381
+ freedSpace += entry.size;
382
+ fs.unlinkSync(cachePath);
383
+ delete this.metadata.entries[entry.hash];
384
+ this.memoryCache.delete(entry.hash);
385
+ }
386
+ }
387
+
388
+ this.metadata.totalSize -= freedSpace;
389
+ fs.writeFileSync(this.metadataPath, JSON.stringify(this.metadata, null, 2));
390
+
391
+ if (this.options.verbose) {
392
+ console.log(`[persistent-cache] Cleaned ${(freedSpace / 1024 / 1024).toFixed(2)}MB`);
393
+ }
394
+ }
395
+
396
+ private enforceSizeLimitSync(): void {
397
+ const limitBytes = this.options.maxSizeMB * 1024 * 1024;
398
+
399
+ if (this.metadata.totalSize <= limitBytes) {
400
+ return;
401
+ }
402
+
403
+ const entries = Object.values(this.metadata.entries)
404
+ .sort((a, b) => a.timestamp - b.timestamp);
405
+
406
+ let freedSpace = 0;
407
+ for (const entry of entries) {
408
+ if (this.metadata.totalSize - freedSpace <= limitBytes) break;
409
+
410
+ const cachePath = path.join(this.cacheDir, `${entry.hash}.json`);
411
+ if (fs.existsSync(cachePath)) {
412
+ freedSpace += entry.size;
413
+ fs.unlinkSync(cachePath);
414
+ delete this.metadata.entries[entry.hash];
415
+ this.memoryCache.delete(entry.hash);
416
+ }
417
+ }
418
+
419
+ this.metadata.totalSize -= freedSpace;
420
+ fs.writeFileSync(this.metadataPath, JSON.stringify(this.metadata, null, 2));
421
+ }
422
+
423
+ private ensureDir(): void {
424
+ if (!fs.existsSync(this.cacheDir)) {
425
+ fs.mkdirSync(this.cacheDir, { recursive: true });
426
+ }
427
+ }
428
+
429
+ /**
430
+ * List all cache entries
431
+ */
432
+ async listEntries(): Promise<Array<{ key: string; size: number; timestamp: number; createdAt: number; lastAccessed?: number }>> {
433
+ const entries: Array<{ key: string; size: number; timestamp: number; createdAt: number; lastAccessed?: number }> = [];
434
+
435
+ for (const [hash, metadata] of Object.entries(this.metadata.entries)) {
436
+ const entry = this.memoryCache.get(hash) || await this.getByHash(hash);
437
+ entries.push({
438
+ key: hash,
439
+ size: metadata.size,
440
+ timestamp: metadata.timestamp,
441
+ createdAt: metadata.timestamp,
442
+ lastAccessed: entry?.timestamp
443
+ });
444
+ }
445
+
446
+ return entries.sort((a, b) => b.timestamp - a.timestamp);
447
+ }
448
+
449
+ /**
450
+ * Get a cache entry by key
451
+ */
452
+ async get(key: string): Promise<PersistentCacheEntry | null> {
453
+ return this.getByHash(key);
454
+ }
455
+
456
+ /**
457
+ * Delete a cache entry by key
458
+ */
459
+ async delete(key: string): Promise<boolean> {
460
+ try {
461
+ this.invalidateByHash(key);
462
+ return true;
463
+ } catch {
464
+ return false;
465
+ }
466
+ }
467
+
468
+ /**
469
+ * Validate a cache entry
470
+ */
471
+ async validate(key: string): Promise<boolean> {
472
+ const entry = await this.getByHash(key);
473
+ if (!entry) return false;
474
+ return !this.isExpired(entry);
475
+ }
476
+
477
+
478
+ }