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,133 @@
1
+ // src/cli/commands/build.ts
2
+
3
+ import path from 'path';
4
+ import chalk from 'chalk';
5
+ import { ChainCSSCompiler } from '../../core/compiler.js';
6
+ import { createLogger } from '../utils/logger.js';
7
+ import { loadConfig } from '../utils/config-loader.js';
8
+ import { findInputFiles, ensureDirectory } from '../utils/file-utils.js';
9
+ import type { BuildOptions } from '../types.js';
10
+
11
+ export async function buildCommand(options: BuildOptions): Promise<void> {
12
+ const logger = createLogger(options.verbose);
13
+
14
+ logger.header('ChainCSS Build');
15
+
16
+ const config = await loadConfig(options.config);
17
+ const inputs = config.inputs || ['src/**/*.chain.{js,ts}', 'src/**/*.chain.{jsx,tsx}'];
18
+
19
+ // Determine output directory
20
+ let outputDir = 'dist/styles';
21
+ if (typeof config.output === 'object' && config.output.cssFile) {
22
+ outputDir = path.dirname(config.output.cssFile);
23
+ } else if (typeof config.output === 'string') {
24
+ outputDir = config.output;
25
+ }
26
+
27
+ if (inputs.length === 0) {
28
+ logger.error('No input patterns found in configuration');
29
+ process.exit(1);
30
+ }
31
+
32
+ logger.info(`Input patterns: ${inputs.join(', ')}`);
33
+ const files = findInputFiles(inputs);
34
+
35
+ if (files.length === 0) {
36
+ logger.warn('No .chain.js or .chain.ts files found');
37
+ return;
38
+ }
39
+
40
+ logger.success(`Found ${files.length} file(s) to compile`);
41
+
42
+ ensureDirectory(outputDir);
43
+
44
+ // Initialize compiler
45
+ const compiler = new ChainCSSCompiler({
46
+ tokens: config.tokens,
47
+ atomic: {
48
+ enabled: options.atomic !== undefined ? options.atomic : (config.atomic?.enabled !== false),
49
+ threshold: config.atomic?.threshold || 2,
50
+ naming: config.atomic?.naming || (process.env.NODE_ENV === 'production' ? 'hash' : 'readable'),
51
+ minify: options.minify !== undefined ? options.minify : (config.atomic?.minify !== false),
52
+ mode: config.atomic?.mode || 'hybrid',
53
+ verbose: options.verbose || config.verbose || false
54
+ },
55
+ prefixer: {
56
+ enabled: config.prefixer?.enabled !== false,
57
+ browsers: config.prefixer?.browsers
58
+ },
59
+ output: {
60
+ minify: options.minify !== undefined ? options.minify : (config.output?.minify !== false),
61
+ generateGlobalCSS: config.output?.generateGlobalCSS !== false
62
+ },
63
+ verbose: options.verbose || config.verbose || false,
64
+ breakpoints: config.breakpoints,
65
+ debug: config.debug || false,
66
+ timeline: config.timeline || false
67
+ });
68
+
69
+ const startTime = Date.now();
70
+
71
+ // Compile all files using compileComponents
72
+ try {
73
+ for (let i = 0; i < files.length; i++) {
74
+ const file = files[i];
75
+ const relativePath = path.relative(process.cwd(), file);
76
+ logger.progress(i + 1, files.length, `Compiling ${relativePath}...`);
77
+
78
+ try {
79
+ await compiler.compile(file, outputDir);
80
+ } catch (error) {
81
+ logger.error(`Failed to compile ${relativePath}: ${(error as Error).message}`);
82
+ }
83
+ }
84
+
85
+ logger.progress(files.length, files.length, 'Complete!');
86
+ logger.success(`Built ${files.length} file(s) in ${Date.now() - startTime}ms`);
87
+
88
+ // Show stats
89
+ const stats = compiler.getStats();
90
+ if (stats.totalStyles > 0) {
91
+ logger.info('Compilation statistics:');
92
+ logger.table({
93
+ 'Total styles': stats.totalStyles,
94
+ 'Atomic styles': stats.atomicStyles,
95
+ 'Standard styles': (stats as any).standardStyles || 0,
96
+ 'CSS savings': stats.savings || '0%'
97
+ });
98
+ }
99
+
100
+ } catch (error) {
101
+ logger.error(`Compilation failed: ${(error as Error).message}`);
102
+ process.exit(1);
103
+ }
104
+
105
+ // Watch mode
106
+ if (options.watch || config.watch) {
107
+ logger.info('Watching for changes...');
108
+ const chokidar = await import('chokidar');
109
+ const watcher = chokidar.watch(inputs, {
110
+ ignored: ['**/node_modules/**', '**/dist/**', '**/.chaincss-cache/**']
111
+ });
112
+
113
+ watcher.on('change', async (filePath: string) => {
114
+ const ext = path.extname(filePath);
115
+ if (ext === '.js' || ext === '.ts' || ext === '.jsx' || ext === '.tsx') {
116
+ logger.step(`Change detected: ${path.basename(filePath)}`);
117
+ try {
118
+ await compiler.compile(filePath, outputDir);
119
+ logger.success(`Recompiled ${path.basename(filePath)}`);
120
+ } catch (error) {
121
+ logger.error(`Failed to recompile: ${(error as Error).message}`);
122
+ }
123
+ }
124
+ });
125
+
126
+ // Keep process alive
127
+ process.on('SIGINT', () => {
128
+ logger.info('Stopping watch mode...');
129
+ watcher.close();
130
+ process.exit(0);
131
+ });
132
+ }
133
+ }
@@ -0,0 +1,371 @@
1
+ // chaincss/src/cli/commands/cache.ts
2
+
3
+ import chalk from 'chalk';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import { PersistentCache } from '../../compiler/content-addressable-cache.js';
7
+
8
+ // Helper to format bytes
9
+ function formatBytes(bytes: number): string {
10
+ if (bytes === 0) return '0 Bytes';
11
+ const k = 1024;
12
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
13
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
14
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
15
+ }
16
+
17
+ // Helper to format duration
18
+ function formatDuration(ms: number): string {
19
+ if (ms < 1000) return `${ms}ms`;
20
+ const seconds = ms / 1000;
21
+ if (seconds < 60) return `${seconds.toFixed(1)}s`;
22
+ const minutes = seconds / 60;
23
+ if (minutes < 60) return `${minutes.toFixed(1)}min`;
24
+ const hours = minutes / 60;
25
+ return `${hours.toFixed(1)}h`;
26
+ }
27
+
28
+ // Display cache entry details
29
+ function displayCacheEntry(key: string, entry: any, index: number): void {
30
+ const created = entry.createdAt ? new Date(entry.createdAt).toLocaleString() : 'Unknown';
31
+ const lastAccessed = entry.lastAccessed ? new Date(entry.lastAccessed).toLocaleString() : 'Never';
32
+
33
+ console.log(`\n${chalk.green(`[${index}]`)} ${chalk.white.bold(key)}`);
34
+ console.log(` ${chalk.gray(`Created: ${created}`)}`);
35
+ console.log(` ${chalk.gray(`Last Accessed: ${lastAccessed}`)}`);
36
+ console.log(` ${chalk.gray(`Usage Count: ${entry.accessCount || 0}`)}`);
37
+
38
+ if (entry.size) {
39
+ console.log(` ${chalk.gray(`Size: ${formatBytes(entry.size)}`)}`);
40
+ }
41
+ }
42
+
43
+ export async function cacheCommand(action: string, options: any) {
44
+ const cacheDir = options.cacheDir || './.chaincss-cache';
45
+ const persistentCacheDir = options.persistentCacheDir || './.chaincss/persistent-cache';
46
+
47
+ // Create cache instances
48
+ const persistentCache = new PersistentCache({
49
+ cacheDir: persistentCacheDir,
50
+ maxAgeDays: options.maxAge || 30,
51
+ maxSizeMB: options.maxSize || 500,
52
+ verbose: options.verbose,
53
+ enabled: true
54
+ });
55
+
56
+ switch (action) {
57
+ case 'clear':
58
+ console.log(chalk.yellow('\n⚠️ This will delete all cached data'));
59
+ if (!options.force) {
60
+ console.log(chalk.gray(' Use --force to confirm\n'));
61
+ return;
62
+ }
63
+
64
+ try {
65
+ // Clear persistent cache
66
+ await persistentCache.clear();
67
+
68
+ // Clear regular cache directory if it exists
69
+ if (fs.existsSync(cacheDir)) {
70
+ fs.rmSync(cacheDir, { recursive: true, force: true });
71
+ }
72
+
73
+ console.log(chalk.green('\n✓ All cache cleared'));
74
+ console.log(chalk.gray(` Removed: ${cacheDir}`));
75
+ console.log(chalk.gray(` Removed: ${persistentCacheDir}\n`));
76
+ } catch (error) {
77
+ console.log(chalk.red(`\n❌ Failed to clear cache: ${(error as Error).message}\n`));
78
+ }
79
+ break;
80
+
81
+ case 'stats':
82
+ try {
83
+ const stats = await persistentCache.getStats();
84
+ const regularCacheExists = fs.existsSync(cacheDir);
85
+ const persistentCacheExists = fs.existsSync(persistentCacheDir);
86
+
87
+ console.log(chalk.cyan.bold('\n📊 Cache Statistics\n'));
88
+
89
+ // Persistent Cache Stats
90
+ console.log(chalk.white.bold('Persistent Cache:'));
91
+ console.log(` Status: ${persistentCacheExists ? chalk.green('Active') : chalk.gray('Empty')}`);
92
+ if (persistentCacheExists) {
93
+ console.log(` Entry count: ${chalk.white(stats.entryCount || 0)}`);
94
+ console.log(` Total size: ${chalk.white((stats.totalSizeMB || 0).toFixed(2))} MB`);
95
+ console.log(` Total size (bytes): ${chalk.white(formatBytes(stats.totalSizeBytes || 0))}`);
96
+
97
+ if (stats.oldestEntry) {
98
+ const age = Date.now() - new Date(stats.oldestEntry).getTime();
99
+ console.log(` Oldest entry: ${chalk.white(new Date(stats.oldestEntry).toLocaleDateString())} (${formatDuration(age)} ago)`);
100
+ }
101
+ if (stats.newestEntry) {
102
+ const age = Date.now() - new Date(stats.newestEntry).getTime();
103
+ console.log(` Newest entry: ${chalk.white(new Date(stats.newestEntry).toLocaleDateString())} (${formatDuration(age)} ago)`);
104
+ }
105
+
106
+ if (stats.hitRate !== undefined) {
107
+ const hitRateColor = stats.hitRate > 80 ? chalk.green : stats.hitRate > 50 ? chalk.yellow : chalk.red;
108
+ console.log(` Cache hit rate: ${hitRateColor(`${(stats.hitRate * 100).toFixed(1)}%`)}`);
109
+ }
110
+ }
111
+
112
+ // Regular Cache Stats
113
+ console.log(chalk.white.bold('\nRegular Cache:'));
114
+ console.log(` Status: ${regularCacheExists ? chalk.green('Active') : chalk.gray('Empty')}`);
115
+
116
+ if (regularCacheExists) {
117
+ let totalSize = 0;
118
+ let fileCount = 0;
119
+
120
+ const calculateSize = (dir: string) => {
121
+ const files = fs.readdirSync(dir);
122
+ for (const file of files) {
123
+ const filePath = path.join(dir, file);
124
+ const stat = fs.statSync(filePath);
125
+ if (stat.isDirectory()) {
126
+ calculateSize(filePath);
127
+ } else {
128
+ totalSize += stat.size;
129
+ fileCount++;
130
+ }
131
+ }
132
+ };
133
+
134
+ calculateSize(cacheDir);
135
+ console.log(` File count: ${chalk.white(fileCount)}`);
136
+ console.log(` Total size: ${chalk.white(formatBytes(totalSize))}`);
137
+ }
138
+
139
+ // Settings
140
+ console.log(chalk.white.bold('\nSettings:'));
141
+ console.log(` Max age: ${chalk.white(options.maxAge || 30)} days`);
142
+ console.log(` Max size: ${chalk.white(options.maxSize || 500)} MB`);
143
+ console.log(` Cache directory: ${chalk.gray(persistentCacheDir)}`);
144
+
145
+ console.log(); // Empty line
146
+ } catch (error) {
147
+ console.log(chalk.red(`\n❌ Failed to get cache stats: ${(error as Error).message}\n`));
148
+ }
149
+ break;
150
+
151
+ case 'prune':
152
+ try {
153
+ const beforeStats = await persistentCache.getStats();
154
+ const beforeCount = beforeStats.entryCount || 0;
155
+ const beforeSize = beforeStats.totalSizeMB || 0;
156
+
157
+ console.log(chalk.yellow(`\n🧹 Pruning cache...`));
158
+ console.log(chalk.gray(` Before: ${beforeCount} entries, ${beforeSize.toFixed(2)} MB`));
159
+
160
+ await persistentCache.prune();
161
+ await persistentCache.enforceSizeLimit(); // Also enforce size limit
162
+
163
+ const afterStats = await persistentCache.getStats();
164
+ const afterCount = afterStats.entryCount || 0;
165
+ const afterSize = afterStats.totalSizeMB || 0;
166
+
167
+ const removedCount = beforeCount - afterCount;
168
+ const removedSize = beforeSize - afterSize;
169
+
170
+ if (removedCount > 0) {
171
+ console.log(chalk.green(`✓ Cache pruned successfully`));
172
+ console.log(chalk.gray(` Removed: ${removedCount} entries, ${removedSize.toFixed(2)} MB`));
173
+ console.log(chalk.gray(` Remaining: ${afterCount} entries, ${afterSize.toFixed(2)} MB`));
174
+ } else {
175
+ console.log(chalk.gray(` No entries to prune`));
176
+ }
177
+ console.log();
178
+ } catch (error) {
179
+ console.log(chalk.red(`\n❌ Failed to prune cache: ${(error as Error).message}\n`));
180
+ }
181
+ break;
182
+
183
+ case 'list':
184
+ try {
185
+ const entries = await persistentCache.listEntries();
186
+
187
+ if (!entries || entries.length === 0) {
188
+ console.log(chalk.yellow('\n📭 No cache entries found\n'));
189
+ return;
190
+ }
191
+
192
+ console.log(chalk.cyan.bold(`\n📦 Cache Entries (${entries.length})\n`));
193
+
194
+ entries.forEach((entry: any, index: number) => {
195
+ displayCacheEntry(entry.key, entry, index);
196
+ });
197
+
198
+ console.log(); // Empty line
199
+ } catch (error) {
200
+ console.log(chalk.red(`\n❌ Failed to list cache entries: ${(error as Error).message}\n`));
201
+ }
202
+ break;
203
+
204
+ case 'inspect':
205
+ const key = options.key;
206
+ if (!key) {
207
+ console.log(chalk.red('\n❌ Please provide a cache key to inspect'));
208
+ console.log(chalk.gray(' Usage: chaincss cache inspect --key <key>\n'));
209
+ return;
210
+ }
211
+
212
+ try {
213
+ const entry = await persistentCache.get(key);
214
+
215
+ if (!entry) {
216
+ console.log(chalk.yellow(`\n❌ Cache entry not found: ${key}\n`));
217
+ return;
218
+ }
219
+
220
+ console.log(chalk.cyan.bold(`\n🔍 Cache Entry: ${key}\n`));
221
+ console.log(chalk.white('Metadata:'));
222
+ console.log(` Created: ${chalk.gray(entry.createdAt ? new Date(entry.createdAt).toLocaleString() : 'Unknown')}`);
223
+ console.log(` Last accessed: ${chalk.gray(entry.lastAccessed ? new Date(entry.lastAccessed).toLocaleString() : 'Never')}`);
224
+ console.log(` Access count: ${chalk.gray(entry.accessCount || 0)}`);
225
+
226
+ if (entry.size) {
227
+ console.log(` Size: ${chalk.gray(formatBytes(entry.size))}`);
228
+ }
229
+
230
+ // Display content preview
231
+ if (entry.value) {
232
+ console.log(chalk.white.bold('\nContent Preview:'));
233
+ const valueStr = JSON.stringify(entry.value, null, 2);
234
+ const preview = valueStr.length > 500 ? valueStr.slice(0, 500) + '...' : valueStr;
235
+ console.log(chalk.gray(preview));
236
+ }
237
+
238
+ console.log();
239
+ } catch (error) {
240
+ console.log(chalk.red(`\n❌ Failed to inspect cache entry: ${(error as Error).message}\n`));
241
+ }
242
+ break;
243
+
244
+ case 'delete':
245
+ const deleteKey = options.key;
246
+ if (!deleteKey) {
247
+ console.log(chalk.red('\n❌ Please provide a cache key to delete'));
248
+ console.log(chalk.gray(' Usage: chaincss cache delete --key <key>\n'));
249
+ return;
250
+ }
251
+
252
+ if (!options.force) {
253
+ console.log(chalk.yellow(`\n⚠️ This will delete cache entry: ${deleteKey}`));
254
+ console.log(chalk.gray(' Use --force to confirm\n'));
255
+ return;
256
+ }
257
+
258
+ try {
259
+ const deleted = await persistentCache.delete(deleteKey);
260
+
261
+ if (deleted) {
262
+ console.log(chalk.green(`\n✓ Deleted cache entry: ${deleteKey}\n`));
263
+ } else {
264
+ console.log(chalk.yellow(`\n❌ Cache entry not found: ${deleteKey}\n`));
265
+ }
266
+ } catch (error) {
267
+ console.log(chalk.red(`\n❌ Failed to delete cache entry: ${(error as Error).message}\n`));
268
+ }
269
+ break;
270
+
271
+ case 'validate':
272
+ try {
273
+ console.log(chalk.cyan.bold('\n🔍 Validating Cache Integrity\n'));
274
+
275
+ const entries = await persistentCache.listEntries();
276
+ let validCount = 0;
277
+ let invalidCount = 0;
278
+ let totalSize = 0;
279
+
280
+ for (const entry of entries) {
281
+ const isValid = await persistentCache.validate(entry.key);
282
+ if (isValid) {
283
+ validCount++;
284
+ totalSize += entry.size || 0;
285
+ } else {
286
+ invalidCount++;
287
+ console.log(chalk.yellow(` ✗ Invalid entry: ${entry.key}`));
288
+ }
289
+ }
290
+
291
+ console.log(chalk.white(`\nResults:`));
292
+ console.log(` Valid entries: ${chalk.green(validCount)}`);
293
+ console.log(` Invalid entries: ${invalidCount > 0 ? chalk.red(invalidCount) : chalk.green(invalidCount)}`);
294
+ console.log(` Total size: ${chalk.gray(formatBytes(totalSize))}`);
295
+
296
+ if (invalidCount > 0) {
297
+ console.log(chalk.yellow(`\n⚠️ Found ${invalidCount} invalid entries. Run 'chaincss cache prune' to clean them.\n`));
298
+ } else {
299
+ console.log(chalk.green(`\n✓ All cache entries are valid\n`));
300
+ }
301
+ } catch (error) {
302
+ console.log(chalk.red(`\n❌ Failed to validate cache: ${(error as Error).message}\n`));
303
+ }
304
+ break;
305
+
306
+ case 'backup':
307
+ const backupPath = options.output || `./.chaincss-cache-backup-${Date.now()}.tar.gz`;
308
+
309
+ try {
310
+ console.log(chalk.cyan.bold('\n💾 Creating Cache Backup\n'));
311
+
312
+ // This would require archiving the cache directory
313
+ // For now, just copy the directory
314
+ const backupDir = path.dirname(backupPath);
315
+ if (!fs.existsSync(backupDir)) {
316
+ fs.mkdirSync(backupDir, { recursive: true });
317
+ }
318
+
319
+ // Copy cache directories
320
+ const copyDir = (src: string, dest: string) => {
321
+ if (!fs.existsSync(src)) return;
322
+
323
+ if (!fs.existsSync(dest)) {
324
+ fs.mkdirSync(dest, { recursive: true });
325
+ }
326
+
327
+ const entries = fs.readdirSync(src, { withFileTypes: true });
328
+
329
+ for (const entry of entries) {
330
+ const srcPath = path.join(src, entry.name);
331
+ const destPath = path.join(dest, entry.name);
332
+
333
+ if (entry.isDirectory()) {
334
+ copyDir(srcPath, destPath);
335
+ } else {
336
+ fs.copyFileSync(srcPath, destPath);
337
+ }
338
+ }
339
+ };
340
+
341
+ const backupCacheDir = backupPath.replace(/\.tar\.gz$/, '');
342
+ copyDir(cacheDir, backupCacheDir);
343
+ copyDir(persistentCacheDir, path.join(backupCacheDir, 'persistent'));
344
+
345
+ console.log(chalk.green(`✓ Cache backed up to ${backupCacheDir}`));
346
+ console.log(chalk.gray(` To restore, copy the contents back to the cache directory\n`));
347
+ } catch (error) {
348
+ console.log(chalk.red(`\n❌ Failed to backup cache: ${(error as Error).message}\n`));
349
+ }
350
+ break;
351
+
352
+ default:
353
+ console.log(chalk.yellow(`\n❌ Unknown action: ${action}`));
354
+ console.log(chalk.gray('\nAvailable actions:'));
355
+ console.log(chalk.cyan(' clear ') + chalk.gray('- Clear all cache data'));
356
+ console.log(chalk.cyan(' stats ') + chalk.gray('- Show cache statistics'));
357
+ console.log(chalk.cyan(' prune ') + chalk.gray('- Remove expired entries'));
358
+ console.log(chalk.cyan(' list ') + chalk.gray('- List all cache entries'));
359
+ console.log(chalk.cyan(' inspect ') + chalk.gray('- Inspect a specific cache entry'));
360
+ console.log(chalk.cyan(' delete ') + chalk.gray('- Delete a specific cache entry'));
361
+ console.log(chalk.cyan(' validate ') + chalk.gray('- Validate cache integrity'));
362
+ console.log(chalk.cyan(' backup ') + chalk.gray('- Backup cache to file'));
363
+ console.log(chalk.gray('\nOptions:'));
364
+ console.log(chalk.gray(' --key <key> Cache key for inspect/delete'));
365
+ console.log(chalk.gray(' --force Skip confirmation prompts'));
366
+ console.log(chalk.gray(' --max-age <days> Max age for cache entries'));
367
+ console.log(chalk.gray(' --max-size <MB> Max cache size in MB'));
368
+ console.log(chalk.gray(' --output <path> Output path for backup'));
369
+ console.log(chalk.gray(' --verbose Verbose output\n'));
370
+ }
371
+ }