@tamyla/clodo-framework 3.0.13 → 3.0.15

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.
@@ -17,7 +17,6 @@ import { ValidationHandler } from './handlers/ValidationHandler.js';
17
17
  // Legacy imports for backward compatibility
18
18
  import { ServiceCreator } from './ServiceCreator.js';
19
19
  import { ErrorTracker } from './ErrorTracker.js';
20
- import { CapabilityAssessmentEngine } from './CapabilityAssessmentEngine.js';
21
20
  import chalk from 'chalk';
22
21
  import fs from 'fs/promises';
23
22
  import path from 'path';
@@ -61,17 +60,11 @@ export class ServiceOrchestrator {
61
60
  // Tier 2: Smart confirmations for 15 derived values
62
61
  const confirmedValues = await this.confirmationHandler.generateAndConfirm(coreInputs);
63
62
 
64
- // Pre-generation assessment for final validation
65
- console.log(chalk.yellow('šŸ” Pre-Generation Assessment'));
66
- console.log(chalk.white('Final validation of deployment readiness...\n'));
67
- const finalAssessment = await this.runFinalAssessment(coreInputs, confirmedValues);
68
-
69
63
  // Tier 3: Automated generation of 67 components
70
64
  console.log(chalk.yellow('āš™ļø Tier 3: Automated Generation'));
71
65
  console.log(chalk.white('Generating 67 configuration files and service components...\n'));
72
66
  const generationResult = await this.generationHandler.generateService(coreInputs, confirmedValues, {
73
- outputPath: this.outputPath,
74
- assessment: finalAssessment // Pass assessment for generation optimization
67
+ outputPath: this.outputPath
75
68
  });
76
69
 
77
70
  // Display results
@@ -631,271 +624,10 @@ export class ServiceOrchestrator {
631
624
  return [];
632
625
  }
633
626
 
634
- /**
635
- * Run final assessment before generation
636
- */
637
- async runFinalAssessment(coreInputs, confirmedValues) {
638
- try {
639
- // Import assessment engine dynamically to avoid circular dependencies
640
- const {
641
- CapabilityAssessmentEngine
642
- } = await import('./CapabilityAssessmentEngine.js');
643
-
644
- // Combine core inputs and confirmed values for assessment
645
- const assessmentInputs = {
646
- ...coreInputs,
647
- ...confirmedValues,
648
- // Ensure we have the right field names
649
- serviceName: coreInputs.serviceName || coreInputs['service-name'],
650
- serviceType: coreInputs.serviceType || coreInputs['service-type'] || 'generic',
651
- domainName: coreInputs.domainName || coreInputs['domain-name'],
652
- environment: coreInputs.environment || coreInputs['environment'] || 'development',
653
- cloudflareToken: coreInputs.cloudflareToken || coreInputs['cloudflare-api-token']
654
- };
655
-
656
- // Create assessment engine with caching
657
- const assessmentEngine = new CapabilityAssessmentEngine(this.outputPath, {
658
- cacheEnabled: true,
659
- cache: {
660
- ttl: 5 * 60 * 1000
661
- } // 5 minutes for final assessment
662
- });
663
-
664
- // Run comprehensive assessment
665
- const assessment = await assessmentEngine.assessCapabilities(assessmentInputs);
666
-
667
- // Check for deployment blockers
668
- const blockers = assessment.gapAnalysis.missing.filter(gap => gap.priority === 'blocked');
669
- if (blockers.length > 0) {
670
- console.log(chalk.red('\n🚫 Deployment Blockers Detected:'));
671
- blockers.forEach(blocker => {
672
- console.log(chalk.red(` • ${blocker.capability}: ${blocker.reason}`));
673
- });
674
- if (this.interactive) {
675
- const proceed = await this.confirmationHandler.promptHandler.confirm('\nContinue with generation despite blockers? (Service may not deploy successfully)', false);
676
- if (!proceed) {
677
- throw new Error('Service creation cancelled due to deployment blockers');
678
- }
679
- } else {
680
- console.log(chalk.yellow('āš ļø Proceeding with generation despite blockers (non-interactive mode)'));
681
- }
682
- } else {
683
- console.log(chalk.green('āœ… No deployment blockers detected'));
684
- }
685
- return assessment;
686
- } catch (error) {
687
- console.log(chalk.yellow(`āš ļø Final assessment failed: ${error.message}`));
688
- console.log(chalk.gray('Continuing with service generation...'));
689
- return null;
690
- }
691
- }
692
-
693
627
  /**
694
628
  * Escape special regex characters for safe replacement
695
629
  */
696
630
  escapeRegExp(string) {
697
631
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
698
632
  }
699
-
700
- /**
701
- * Run pre-deploy assessment to validate deployment readiness
702
- */
703
- async runPreDeployAssessment(deployConfig) {
704
- console.log(chalk.yellow('šŸ” Running Pre-Deploy Assessment...'));
705
- try {
706
- // Create assessment engine
707
- const assessmentEngine = new CapabilityAssessmentEngine(this.outputPath, {
708
- cacheEnabled: true,
709
- cache: {
710
- ttl: 10 * 60 * 1000
711
- } // 10 minutes for deploy assessment
712
- });
713
-
714
- // Run assessment
715
- const assessment = await assessmentEngine.assessCapabilities(deployConfig);
716
-
717
- // Check for critical gaps
718
- const criticalGaps = assessment.gapAnalysis.missing.filter(gap => gap.priority === 'blocked');
719
- const warningGaps = assessment.gapAnalysis.missing.filter(gap => gap.priority === 'warning');
720
- if (criticalGaps.length > 0) {
721
- console.log(chalk.red('\n🚫 Critical Deployment Blockers:'));
722
- criticalGaps.forEach(gap => {
723
- console.log(chalk.red(` • ${gap.capability}: ${gap.reason}`));
724
- });
725
- console.log(chalk.red('\nDeployment blocked due to critical gaps.'));
726
- return {
727
- ...assessment,
728
- deployable: false,
729
- reason: 'Critical deployment blockers detected'
730
- };
731
- }
732
- if (warningGaps.length > 0) {
733
- console.log(chalk.yellow('\nāš ļø Deployment Warnings:'));
734
- warningGaps.forEach(gap => {
735
- console.log(chalk.yellow(` • ${gap.capability}: ${gap.reason}`));
736
- });
737
- console.log(chalk.yellow('Deployment possible but may have issues.'));
738
- return {
739
- ...assessment,
740
- deployable: true,
741
- warnings: warningGaps.length
742
- };
743
- }
744
- console.log(chalk.green('āœ… Deployment assessment passed - no blockers detected'));
745
- return {
746
- ...assessment,
747
- deployable: true
748
- };
749
- } catch (error) {
750
- console.log(chalk.red(`āŒ Pre-deploy assessment failed: ${error.message}`));
751
- throw error;
752
- }
753
- }
754
-
755
- /**
756
- * Deploy service with assessment integration
757
- */
758
- async deployService(deployOptions) {
759
- console.log(chalk.cyan('šŸš€ Deploying Service with Assessment Integration...'));
760
- const {
761
- skipAssessment = false,
762
- forceDeployment = false,
763
- saveAssessment = false
764
- } = deployOptions;
765
- try {
766
- // Run pre-deploy assessment unless skipped
767
- if (!skipAssessment) {
768
- console.log(chalk.yellow('šŸ” Running Pre-Deploy Assessment...'));
769
- const assessmentResult = await this.runPreDeployAssessment(deployOptions);
770
-
771
- // Check if deployment should be blocked
772
- if (!assessmentResult.deployable && !forceDeployment) {
773
- console.log(chalk.red('Deployment blocked by assessment. Use --force to override.'));
774
- return {
775
- success: false,
776
- reason: 'Assessment blocked deployment',
777
- assessment: assessmentResult
778
- };
779
- }
780
- if (assessmentResult.warnings && !forceDeployment) {
781
- const proceed = await this.confirmDeploymentWithAssessment(assessmentResult);
782
- if (!proceed) {
783
- console.log(chalk.yellow('Deployment cancelled by user.'));
784
- return {
785
- success: false,
786
- reason: 'User cancelled deployment',
787
- assessment: assessmentResult
788
- };
789
- }
790
- }
791
-
792
- // Save assessment results if requested
793
- if (saveAssessment) {
794
- await this.saveAssessmentResults(assessmentResult, deployOptions.servicePath || this.outputPath);
795
- }
796
- deployOptions.assessment = assessmentResult;
797
- }
798
-
799
- // Execute deployment
800
- console.log(chalk.yellow('āš™ļø Executing Deployment...'));
801
- const deployResult = await this.executeDeployment(deployOptions);
802
- console.log(chalk.green('āœ… Deployment completed successfully'));
803
- return {
804
- success: true,
805
- deployment: deployResult
806
- };
807
- } catch (error) {
808
- console.log(chalk.red(`āŒ Deployment failed: ${error.message}`));
809
- throw error;
810
- }
811
- }
812
-
813
- /**
814
- * Execute the actual deployment process
815
- */
816
- async executeDeployment(deployOptions) {
817
- // This would integrate with actual deployment logic
818
- // For now, return a mock successful result
819
- console.log(chalk.gray(' • Validating deployment configuration...'));
820
- console.log(chalk.gray(' • Uploading worker script...'));
821
- console.log(chalk.gray(' • Configuring routes...'));
822
- console.log(chalk.gray(' • Deployment completed'));
823
- return {
824
- workerId: 'mock-worker-id',
825
- deploymentId: 'mock-deployment-id',
826
- urls: {
827
- worker: `https://${deployOptions.workerName || 'worker'}.example.com`,
828
- api: `https://${deployOptions.serviceName || 'service'}.example.com`
829
- },
830
- timestamp: new Date().toISOString()
831
- };
832
- }
833
-
834
- /**
835
- * Confirm deployment with assessment warnings
836
- */
837
- async confirmDeploymentWithAssessment(assessmentResult) {
838
- if (!this.interactive) {
839
- return true; // Auto-confirm in non-interactive mode
840
- }
841
- if (!assessmentResult || !assessmentResult.gapAnalysis) {
842
- return true; // Auto-confirm if no assessment result
843
- }
844
- const warnings = (assessmentResult.gapAnalysis.missing || []).filter(gap => gap.priority === 'warning');
845
- const recommendations = (assessmentResult.recommendations || []).slice(0, 3);
846
- console.log(chalk.yellow('\nāš ļø Assessment detected warnings:'));
847
- warnings.forEach(warning => {
848
- console.log(chalk.yellow(` • ${warning.capability}: ${warning.reason}`));
849
- });
850
- if (recommendations.length > 0) {
851
- console.log(chalk.blue('\nšŸ’” Recommendations:'));
852
- recommendations.forEach(rec => {
853
- console.log(chalk.blue(` • ${rec.action}`));
854
- });
855
- }
856
- const proceed = await this.confirmationHandler.promptHandler.confirm('\nContinue with deployment despite warnings?', true);
857
- return proceed;
858
- }
859
-
860
- /**
861
- * Save assessment results for post-deploy reference
862
- */
863
- async saveAssessmentResults(assessment, servicePath) {
864
- const assessmentFile = path.join(servicePath, '.clodo-assessment.json');
865
- try {
866
- await fs.writeFile(assessmentFile, JSON.stringify({
867
- ...assessment,
868
- savedAt: new Date().toISOString(),
869
- version: '1.0'
870
- }, null, 2));
871
- console.log(chalk.gray(` • Assessment results saved to ${assessmentFile}`));
872
- } catch (error) {
873
- console.log(chalk.yellow(`āš ļø Failed to save assessment results: ${error.message}`));
874
- }
875
- }
876
-
877
- /**
878
- * Load previous assessment results
879
- */
880
- async loadPreviousAssessment(servicePath) {
881
- const assessmentFile = path.join(servicePath, '.clodo-assessment.json');
882
- try {
883
- const data = await fs.readFile(assessmentFile, 'utf8');
884
- const assessment = JSON.parse(data);
885
- console.log(chalk.gray(` • Loaded previous assessment from ${assessmentFile}`));
886
- return assessment;
887
- } catch (error) {
888
- console.log(chalk.yellow(`āš ļø No previous assessment found: ${error.message}`));
889
- return null;
890
- }
891
- }
892
-
893
- /**
894
- * Create readline interface for interactive prompts
895
- */
896
- createReadlineInterface() {
897
- // This would create a readline interface for interactive prompts
898
- // For now, return null as this is handled by the confirmation handler
899
- return null;
900
- }
901
633
  }
@@ -5,6 +5,10 @@
5
5
 
6
6
  export { ServiceCreator, createService } from './ServiceCreator.js';
7
7
  export { ServiceInitializer, initializeService } from './ServiceInitializer.js';
8
- export { AssessmentCache } from './AssessmentCache.js';
9
- export { CapabilityAssessmentEngine } from './CapabilityAssessmentEngine.js';
10
- export { ServiceAutoDiscovery } from './ServiceAutoDiscovery.js';
8
+
9
+ // Assessment capabilities moved to @tamyla/clodo-orchestration
10
+ // - AssessmentCache
11
+ // - CapabilityAssessmentEngine
12
+ // - ServiceAutoDiscovery
13
+ // - Data-bridge components
14
+ // These are only available in the professional edition (clodo-orchestration)
@@ -16,8 +16,13 @@ import { join, dirname } from 'path';
16
16
  import { execSync } from 'child_process';
17
17
  import { fileURLToPath } from 'url';
18
18
  const __dirname = (() => {
19
- const filename = fileURLToPath(import.meta.url);
20
- return dirname(filename);
19
+ try {
20
+ const filename = fileURLToPath(import.meta.url);
21
+ return dirname(filename);
22
+ } catch (error) {
23
+ // Fallback for test environments - use current working directory
24
+ return process.cwd();
25
+ }
21
26
  })();
22
27
  const SECRET_CONFIGS = {
23
28
  'AUTH_JWT_SECRET': {
@@ -1,7 +1,13 @@
1
1
  import { featureManager, COMMON_FEATURES } from '../config/features.js';
2
2
  import { getDomainFromEnv, createEnvironmentConfig } from '../config/domains.js';
3
- import { createLogger } from '../utils/index.js';
4
- const logger = createLogger('WorkerIntegration');
3
+
4
+ // Simple inline logger to avoid circular dependency with index.js
5
+ const logger = {
6
+ info: (message, ...args) => console.log(`[WorkerIntegration] ${message}`, ...args),
7
+ error: (message, ...args) => console.error(`[WorkerIntegration] ${message}`, ...args),
8
+ warn: (message, ...args) => console.warn(`[WorkerIntegration] ${message}`, ...args),
9
+ debug: (message, ...args) => console.debug(`[WorkerIntegration] ${message}`, ...args)
10
+ };
5
11
 
6
12
  /**
7
13
  * Initializes a service with domain and feature context
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamyla/clodo-framework",
3
- "version": "3.0.13",
3
+ "version": "3.0.15",
4
4
  "description": "Reusable framework for Clodo-style software architecture on Cloudflare Workers + D1",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -74,7 +74,7 @@
74
74
  "clean": "rimraf dist",
75
75
  "clean:generated": "rimraf generated",
76
76
  "clean:all": "npm run clean && npm run clean:generated",
77
- "test": "jest --passWithNoTests",
77
+ "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests",
78
78
  "test:watch": "jest --watch",
79
79
  "test:coverage": "jest --coverage",
80
80
  "lint": "eslint --config ./eslint.config.js src",
@@ -112,7 +112,7 @@
112
112
  "modular"
113
113
  ],
114
114
  "author": "Tamyla",
115
- "license": "GPL-3.0-or-later",
115
+ "license": "MIT",
116
116
  "dependencies": {
117
117
  "@iarna/toml": "^2.2.5",
118
118
  "chalk": "^5.3.0",
@@ -129,6 +129,7 @@
129
129
  "@semantic-release/git": "^10.0.1",
130
130
  "@types/node": "^20.19.19",
131
131
  "babel-plugin-transform-import-meta": "^2.3.3",
132
+ "cross-env": "^10.1.0",
132
133
  "eslint": "^8.54.0",
133
134
  "jest": "^29.7.0",
134
135
  "rimraf": "^5.0.10",
@@ -148,6 +149,7 @@
148
149
  "homepage": "https://clodo-framework.tamyla.com",
149
150
  "release": {
150
151
  "branches": [
152
+ "main",
151
153
  "master"
152
154
  ],
153
155
  "plugins": [
@@ -1,303 +0,0 @@
1
- /**
2
- * Assessment Cache System
3
- *
4
- * Caches assessment results to avoid redundant analysis of project artifacts.
5
- * Provides intelligent cache invalidation based on file changes and time-based expiration.
6
- */
7
-
8
- import crypto from 'crypto';
9
- import fs from 'fs/promises';
10
- import path from 'path';
11
- import { existsSync } from 'fs';
12
- export class AssessmentCache {
13
- constructor(options = {}) {
14
- this.cacheDir = options.cacheDir || './.clodo-cache/assessment';
15
- this.ttl = options.ttl || 5 * 60 * 1000; // 5 minutes default
16
- this.maxEntries = options.maxEntries || 50;
17
- this.enableDiskCache = options.enableDiskCache !== false;
18
- this.memoryCache = new Map();
19
- this.initialized = false;
20
- }
21
-
22
- /**
23
- * Initialize the cache system
24
- */
25
- async initialize() {
26
- if (this.initialized) return;
27
- if (this.enableDiskCache) {
28
- await this.ensureCacheDirectory();
29
- await this.loadFromDisk();
30
- }
31
- this.initialized = true;
32
- }
33
-
34
- /**
35
- * Generate cache key from project state and inputs
36
- */
37
- async generateCacheKey(projectPath, inputs = {}) {
38
- const keyData = {
39
- projectPath: path.resolve(projectPath),
40
- inputs: this.sanitizeInputs(inputs),
41
- projectFiles: await this.getProjectFileHashes(projectPath)
42
- };
43
- const keyString = JSON.stringify(keyData, Object.keys(keyData).sort());
44
- return crypto.createHash('sha256').update(keyString).digest('hex');
45
- }
46
-
47
- /**
48
- * Sanitize inputs for cache key generation (remove sensitive data)
49
- */
50
- sanitizeInputs(inputs) {
51
- const sanitized = {
52
- ...inputs
53
- };
54
-
55
- // Remove sensitive fields but keep their presence for cache invalidation
56
- const sensitiveFields = ['apiToken', 'cloudflareToken', 'token', 'secret', 'password'];
57
- sensitiveFields.forEach(field => {
58
- if (sanitized[field]) {
59
- sanitized[field] = 'present'; // Just mark presence, not value
60
- }
61
- });
62
- return sanitized;
63
- }
64
-
65
- /**
66
- * Get hashes of relevant project files for cache invalidation
67
- */
68
- async getProjectFileHashes(projectPath) {
69
- const relevantFiles = ['package.json', 'wrangler.toml', 'src/index.js', 'src/worker.js', 'dist/index.js'];
70
- const hashes = {};
71
- for (const file of relevantFiles) {
72
- const filePath = path.join(projectPath, file);
73
- try {
74
- const stats = await fs.stat(filePath);
75
- const content = await fs.readFile(filePath, 'utf8');
76
- hashes[file] = {
77
- mtime: stats.mtime.getTime(),
78
- size: stats.size,
79
- hash: crypto.createHash('md5').update(content).digest('hex').substring(0, 8)
80
- };
81
- } catch (error) {
82
- // File doesn't exist, that's fine
83
- hashes[file] = null;
84
- }
85
- }
86
- return hashes;
87
- }
88
-
89
- /**
90
- * Get cached assessment result
91
- */
92
- async get(cacheKey) {
93
- await this.initialize();
94
-
95
- // Check memory cache first
96
- const memoryEntry = this.memoryCache.get(cacheKey);
97
- if (memoryEntry && !this.isExpired(memoryEntry)) {
98
- return memoryEntry.data;
99
- }
100
-
101
- // Check disk cache if enabled
102
- if (this.enableDiskCache) {
103
- const diskEntry = await this.loadFromDiskCache(cacheKey);
104
- if (diskEntry && !this.isExpired(diskEntry)) {
105
- // Restore to memory cache
106
- this.memoryCache.set(cacheKey, diskEntry);
107
- return diskEntry.data;
108
- }
109
- }
110
- return null;
111
- }
112
-
113
- /**
114
- * Store assessment result in cache
115
- */
116
- async set(cacheKey, data) {
117
- await this.initialize();
118
- const entry = {
119
- data,
120
- timestamp: Date.now(),
121
- key: cacheKey
122
- };
123
-
124
- // Store in memory
125
- this.memoryCache.set(cacheKey, entry);
126
-
127
- // Store on disk if enabled
128
- if (this.enableDiskCache) {
129
- await this.saveToDiskCache(cacheKey, entry);
130
- }
131
-
132
- // Maintain cache size limits
133
- await this.cleanup();
134
- }
135
-
136
- /**
137
- * Check if cache entry is expired
138
- */
139
- isExpired(entry) {
140
- return Date.now() - entry.timestamp > this.ttl;
141
- }
142
-
143
- /**
144
- * Clear expired entries and maintain size limits
145
- */
146
- async cleanup() {
147
- const now = Date.now();
148
- const validEntries = new Map();
149
-
150
- // Clean memory cache
151
- for (const [key, entry] of this.memoryCache) {
152
- if (!this.isExpired(entry)) {
153
- validEntries.set(key, entry);
154
- }
155
- }
156
-
157
- // If still too many entries, remove oldest
158
- if (validEntries.size > this.maxEntries) {
159
- const sortedEntries = Array.from(validEntries.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp);
160
- const toKeep = sortedEntries.slice(-this.maxEntries);
161
- validEntries.clear();
162
- toKeep.forEach(([key, entry]) => validEntries.set(key, entry));
163
- }
164
- this.memoryCache = validEntries;
165
-
166
- // Clean disk cache
167
- if (this.enableDiskCache) {
168
- await this.cleanupDiskCache();
169
- }
170
- }
171
-
172
- /**
173
- * Clear all cache entries
174
- */
175
- async clear() {
176
- this.memoryCache.clear();
177
- if (this.enableDiskCache) {
178
- await this.clearDiskCache();
179
- }
180
- }
181
-
182
- /**
183
- * Get cache statistics
184
- */
185
- async getStats() {
186
- const now = Date.now();
187
- const memoryEntries = Array.from(this.memoryCache.values());
188
- return {
189
- memory: {
190
- total: memoryEntries.length,
191
- valid: memoryEntries.filter(entry => !this.isExpired(entry)).length,
192
- expired: memoryEntries.filter(entry => this.isExpired(entry)).length
193
- },
194
- disk: this.enableDiskCache ? await this.getDiskStats() : null,
195
- ttl: this.ttl,
196
- maxEntries: this.maxEntries
197
- };
198
- }
199
-
200
- // Disk cache implementation
201
- async ensureCacheDirectory() {
202
- try {
203
- await fs.mkdir(this.cacheDir, {
204
- recursive: true
205
- });
206
- } catch (error) {
207
- // Directory might already exist, ignore
208
- }
209
- }
210
- getCacheFilePath(key) {
211
- return path.join(this.cacheDir, `${key}.json`);
212
- }
213
- async saveToDiskCache(key, entry) {
214
- try {
215
- const filePath = this.getCacheFilePath(key);
216
- await fs.writeFile(filePath, JSON.stringify(entry, null, 2));
217
- } catch (error) {
218
- // Disk cache failure shouldn't break functionality
219
- console.warn('Failed to save to disk cache:', error.message);
220
- }
221
- }
222
- async loadFromDiskCache(key) {
223
- try {
224
- const filePath = this.getCacheFilePath(key);
225
- const content = await fs.readFile(filePath, 'utf8');
226
- return JSON.parse(content);
227
- } catch (error) {
228
- return null;
229
- }
230
- }
231
- async loadFromDisk() {
232
- try {
233
- const files = await fs.readdir(this.cacheDir);
234
- const cacheFiles = files.filter(file => file.endsWith('.json'));
235
- for (const file of cacheFiles) {
236
- const key = file.replace('.json', '');
237
- const entry = await this.loadFromDiskCache(key);
238
- if (entry && !this.isExpired(entry)) {
239
- this.memoryCache.set(key, entry);
240
- }
241
- }
242
- } catch (error) {
243
- // Disk cache loading failure is not critical
244
- }
245
- }
246
- async cleanupDiskCache() {
247
- try {
248
- const files = await fs.readdir(this.cacheDir);
249
- const cacheFiles = files.filter(file => file.endsWith('.json'));
250
- for (const file of cacheFiles) {
251
- const key = file.replace('.json', '');
252
- const entry = await this.loadFromDiskCache(key);
253
- if (!entry || this.isExpired(entry)) {
254
- await fs.unlink(path.join(this.cacheDir, file));
255
- }
256
- }
257
- } catch (error) {
258
- // Cleanup failure is not critical
259
- }
260
- }
261
- async clearDiskCache() {
262
- try {
263
- const files = await fs.readdir(this.cacheDir);
264
- for (const file of files) {
265
- if (file.endsWith('.json')) {
266
- await fs.unlink(path.join(this.cacheDir, file));
267
- }
268
- }
269
- } catch (error) {
270
- // Clear failure is not critical
271
- }
272
- }
273
- async getDiskStats() {
274
- try {
275
- const files = await fs.readdir(this.cacheDir);
276
- const cacheFiles = files.filter(file => file.endsWith('.json'));
277
- let valid = 0;
278
- let expired = 0;
279
- for (const file of cacheFiles) {
280
- const key = file.replace('.json', '');
281
- const entry = await this.loadFromDiskCache(key);
282
- if (entry) {
283
- if (this.isExpired(entry)) {
284
- expired++;
285
- } else {
286
- valid++;
287
- }
288
- }
289
- }
290
- return {
291
- total: cacheFiles.length,
292
- valid,
293
- expired
294
- };
295
- } catch (error) {
296
- return {
297
- total: 0,
298
- valid: 0,
299
- expired: 0
300
- };
301
- }
302
- }
303
- }