@tamyla/clodo-framework 3.1.9 → 3.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/bin/clodo-service-old.js +2 -2
- package/dist/bin/commands/create.js +1 -1
- package/dist/bin/commands/diagnose.js +1 -1
- package/dist/bin/commands/update.js +1 -1
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/database/enterprise-db-manager.js +4 -4
- package/dist/bin/deployment/enterprise-deploy.js +3 -3
- package/dist/bin/deployment/master-deploy.js +3 -3
- package/dist/bin/deployment/modular-enterprise-deploy.js +3 -3
- package/dist/bin/deployment/modules/DeploymentOrchestrator.js +1 -1
- package/dist/bin/deployment/modules/EnvironmentManager.js +2 -2
- package/dist/bin/portfolio/portfolio-manager.js +3 -3
- package/dist/bin/security/security-cli.js +1 -1
- package/dist/bin/service-management/create-service.js +1 -1
- package/dist/bin/service-management/init-service.js +1 -1
- package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
- package/dist/bin/shared/config/index.js +1 -1
- package/dist/bin/shared/deployment/index.js +2 -2
- package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
- package/package.json +1 -6
- package/bin/README.md +0 -71
- package/bin/clodo-service.js +0 -72
- package/bin/database/README.md +0 -33
- package/bin/database/deployment-db-manager.js +0 -527
- package/bin/database/enterprise-db-manager.js +0 -738
- package/bin/database/wrangler-d1-manager.js +0 -775
- package/bin/security/security-cli.js +0 -117
- package/bin/service-management/README.md +0 -74
- package/bin/service-management/create-service.js +0 -129
- package/bin/service-management/init-service.js +0 -103
- package/bin/service-management/init-service.js.backup +0 -889
- package/bin/shared/cloudflare/domain-discovery.js +0 -637
- package/bin/shared/cloudflare/domain-manager.js +0 -952
- package/bin/shared/cloudflare/index.js +0 -8
- package/bin/shared/cloudflare/ops.js +0 -401
- package/bin/shared/config/ConfigurationManager.js +0 -539
- package/bin/shared/config/cache.js +0 -1230
- package/bin/shared/config/command-config-manager.js +0 -184
- package/bin/shared/config/index.js +0 -21
- package/bin/shared/config/manager.js +0 -315
- package/bin/shared/database/connection-manager.js +0 -374
- package/bin/shared/database/index.js +0 -7
- package/bin/shared/database/orchestrator.js +0 -727
- package/bin/shared/deployment/auditor.js +0 -970
- package/bin/shared/deployment/index.js +0 -10
- package/bin/shared/deployment/rollback-manager.js +0 -570
- package/bin/shared/deployment/validator.js +0 -779
- package/bin/shared/index.js +0 -32
- package/bin/shared/logging/Logger.js +0 -214
- package/bin/shared/monitoring/health-checker.js +0 -484
- package/bin/shared/monitoring/index.js +0 -8
- package/bin/shared/monitoring/memory-manager.js +0 -387
- package/bin/shared/monitoring/production-monitor.js +0 -403
- package/bin/shared/production-tester/api-tester.js +0 -82
- package/bin/shared/production-tester/auth-tester.js +0 -132
- package/bin/shared/production-tester/core.js +0 -197
- package/bin/shared/production-tester/database-tester.js +0 -109
- package/bin/shared/production-tester/index.js +0 -77
- package/bin/shared/production-tester/load-tester.js +0 -131
- package/bin/shared/production-tester/performance-tester.js +0 -103
- package/bin/shared/security/api-token-manager.js +0 -312
- package/bin/shared/security/index.js +0 -8
- package/bin/shared/security/secret-generator.js +0 -942
- package/bin/shared/security/secure-token-manager.js +0 -398
- package/bin/shared/utils/ErrorHandler.js +0 -675
- package/bin/shared/utils/error-recovery.js +0 -245
- package/bin/shared/utils/file-manager.js +0 -162
- package/bin/shared/utils/formatters.js +0 -247
- package/bin/shared/utils/graceful-shutdown-manager.js +0 -390
- package/bin/shared/utils/index.js +0 -19
- package/bin/shared/utils/interactive-prompts.js +0 -146
- package/bin/shared/utils/interactive-utils.js +0 -530
- package/bin/shared/utils/rate-limiter.js +0 -246
- package/bin/shared/validation/ValidationRegistry.js +0 -143
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cloudflare Integration Module
|
|
3
|
-
* Exports all Cloudflare-related utilities and managers
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export { CloudflareDomainManager } from './domain-manager.js';
|
|
7
|
-
export { DomainDiscovery } from './domain-discovery.js';
|
|
8
|
-
export { CloudflareOps } from './ops.js';
|
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cloudflare Operations Module
|
|
3
|
-
* Standardized Cloudflare Workers and D1 operations
|
|
4
|
-
*
|
|
5
|
-
* Consolidates cloudflare operations across 14+ scripts
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { exec } from 'child_process';
|
|
9
|
-
import { promisify } from 'util';
|
|
10
|
-
import { executeWithRateLimit } from '../utils/rate-limiter.js';
|
|
11
|
-
import { ErrorRecoveryManager } from '../utils/index.js';
|
|
12
|
-
import { SecureTokenManager } from '../security/secure-token-manager.js';
|
|
13
|
-
import { ProductionMonitor } from '../monitoring/production-monitor.js';
|
|
14
|
-
import { DatabaseConnectionManager } from '../database/connection-manager.js';
|
|
15
|
-
import { startMemoryMonitoring } from '../monitoring/memory-manager.js';
|
|
16
|
-
import { initializeGracefulShutdown } from '../utils/graceful-shutdown-manager.js';
|
|
17
|
-
|
|
18
|
-
const execAsync = promisify(exec);
|
|
19
|
-
|
|
20
|
-
// Error recovery for critical operations
|
|
21
|
-
const errorRecovery = new ErrorRecoveryManager({
|
|
22
|
-
maxRetries: 2,
|
|
23
|
-
retryDelay: 2000,
|
|
24
|
-
circuitBreakerThreshold: 3,
|
|
25
|
-
gracefulDegradation: true
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Secure token management
|
|
29
|
-
const tokenManager = new SecureTokenManager();
|
|
30
|
-
|
|
31
|
-
// Production monitoring
|
|
32
|
-
const monitor = new ProductionMonitor({
|
|
33
|
-
logLevel: 'info',
|
|
34
|
-
enableMetrics: true,
|
|
35
|
-
enableAlerts: true
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Database connection management
|
|
39
|
-
const dbManager = new DatabaseConnectionManager({
|
|
40
|
-
maxRetries: 2,
|
|
41
|
-
retryDelay: 1000,
|
|
42
|
-
connectionTimeout: 30000,
|
|
43
|
-
queryTimeout: 60000,
|
|
44
|
-
enableConnectionPooling: true,
|
|
45
|
-
maxPoolSize: 5
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Memory management
|
|
49
|
-
const memoryManager = startMemoryMonitoring({
|
|
50
|
-
gcInterval: 300000, // 5 minutes
|
|
51
|
-
memoryThreshold: 0.8,
|
|
52
|
-
cleanupInterval: 60000, // 1 minute
|
|
53
|
-
leakDetection: true
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Graceful shutdown handling (lazy initialization to avoid sync issues)
|
|
57
|
-
let shutdownManager = null;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Initialize graceful shutdown manager
|
|
61
|
-
* Call this AFTER interactive input collection to avoid log interruption
|
|
62
|
-
* @param {boolean} silent - Suppress registration message
|
|
63
|
-
* @returns {Promise<GracefulShutdownManager>} Initialized shutdown manager
|
|
64
|
-
*/
|
|
65
|
-
export async function initializeShutdownManager(silent = false) {
|
|
66
|
-
if (!shutdownManager) {
|
|
67
|
-
shutdownManager = await initializeGracefulShutdown({
|
|
68
|
-
dbManager,
|
|
69
|
-
monitor,
|
|
70
|
-
tokenManager,
|
|
71
|
-
memoryManager
|
|
72
|
-
}, {
|
|
73
|
-
shutdownTimeout: 30000,
|
|
74
|
-
forceShutdownTimeout: 5000
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Register with optional silent mode
|
|
78
|
-
if (shutdownManager.register) {
|
|
79
|
-
shutdownManager.register(silent);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return shutdownManager;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Initialize monitoring
|
|
86
|
-
let monitoringInitialized = false;
|
|
87
|
-
async function ensureMonitoringInitialized() {
|
|
88
|
-
if (!monitoringInitialized) {
|
|
89
|
-
await monitor.startMonitoring();
|
|
90
|
-
monitoringInitialized = true;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Initialize secure token manager
|
|
95
|
-
let tokenManagerInitialized = false;
|
|
96
|
-
async function ensureTokenManagerInitialized() {
|
|
97
|
-
if (!tokenManagerInitialized) {
|
|
98
|
-
await tokenManager.initialize();
|
|
99
|
-
tokenManagerInitialized = true;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export async function checkAuth() {
|
|
104
|
-
try {
|
|
105
|
-
await ensureTokenManagerInitialized();
|
|
106
|
-
// Try to get a valid token for Cloudflare API
|
|
107
|
-
const tokens = tokenManager.listTokens('cloudflare');
|
|
108
|
-
if (tokens.length === 0) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Check if we have a non-expired token
|
|
113
|
-
const validToken = tokens.find(token => new Date() < token.expires);
|
|
114
|
-
return !!validToken;
|
|
115
|
-
} catch (error) {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export async function authenticate() {
|
|
121
|
-
try {
|
|
122
|
-
// Use wrangler auth login for initial authentication
|
|
123
|
-
await execAsync('npx wrangler auth login', { stdio: 'inherit' });
|
|
124
|
-
|
|
125
|
-
// After successful login, we could extract and store the token securely
|
|
126
|
-
// For now, rely on wrangler's built-in token management
|
|
127
|
-
console.log('Authentication successful. Use storeCloudflareToken() to store API tokens securely.');
|
|
128
|
-
} catch (error) {
|
|
129
|
-
throw new Error(`Authentication failed: ${error.message}`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export async function storeCloudflareToken(token, metadata = {}) {
|
|
134
|
-
await ensureTokenManagerInitialized();
|
|
135
|
-
const fingerprint = await tokenManager.storeToken('cloudflare', token, {
|
|
136
|
-
permissions: ['api_access', 'deployment'],
|
|
137
|
-
environment: metadata.environment || 'production',
|
|
138
|
-
...metadata
|
|
139
|
-
});
|
|
140
|
-
console.log(`Cloudflare API token stored securely with fingerprint: ${fingerprint}`);
|
|
141
|
-
return fingerprint;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export async function getCloudflareToken(requiredPermissions = ['api_access']) {
|
|
145
|
-
await ensureTokenManagerInitialized();
|
|
146
|
-
const tokens = tokenManager.listTokens('cloudflare');
|
|
147
|
-
|
|
148
|
-
// Find a valid token with required permissions
|
|
149
|
-
for (const tokenInfo of tokens) {
|
|
150
|
-
if (new Date() < tokenInfo.expires && tokenManager.validatePermissions(tokenInfo.permissions, requiredPermissions)) {
|
|
151
|
-
return await tokenManager.retrieveToken('cloudflare', tokenInfo.fingerprint, requiredPermissions);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
throw new Error('No valid Cloudflare API token found with required permissions');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export async function listWorkers() {
|
|
159
|
-
try {
|
|
160
|
-
const { stdout: list } = await executeWithRateLimit('npx wrangler list', 'workers');
|
|
161
|
-
return list;
|
|
162
|
-
} catch (error) {
|
|
163
|
-
throw new Error(`Failed to list workers: ${error.message}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export async function workerExists(workerName) {
|
|
168
|
-
try {
|
|
169
|
-
const list = await listWorkers();
|
|
170
|
-
return list.includes(workerName);
|
|
171
|
-
} catch (error) {
|
|
172
|
-
return false; // Assume doesn't exist if can't check
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export async function deployWorker(env = 'production') {
|
|
177
|
-
const startTime = Date.now();
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
await ensureMonitoringInitialized();
|
|
181
|
-
|
|
182
|
-
const result = await errorRecovery.executeWithRecovery(async () => {
|
|
183
|
-
await executeWithRateLimit(`npm run deploy:${env}`, 'workers');
|
|
184
|
-
return true;
|
|
185
|
-
}, { operationId: `deployWorker_${env}` });
|
|
186
|
-
|
|
187
|
-
const duration = Date.now() - startTime;
|
|
188
|
-
monitor.recordRequest(true, duration, { operation: 'deployWorker', env });
|
|
189
|
-
await monitor.log('info', 'Worker deployment successful', { env, duration });
|
|
190
|
-
|
|
191
|
-
return result;
|
|
192
|
-
} catch (error) {
|
|
193
|
-
const duration = Date.now() - startTime;
|
|
194
|
-
monitor.recordRequest(false, duration, { operation: 'deployWorker', env, error: error.message });
|
|
195
|
-
await monitor.log('error', 'Worker deployment failed', { env, duration, error: error.message });
|
|
196
|
-
|
|
197
|
-
throw new Error(`Worker deployment failed after retries: ${error.message}`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export async function deploySecret(key, value, env = 'production') {
|
|
202
|
-
const command = process.platform === 'win32'
|
|
203
|
-
? `powershell -Command "Write-Output '${value}' | npx wrangler secret put ${key} --env ${env}"`
|
|
204
|
-
: `echo "${value}" | npx wrangler secret put ${key} --env ${env}`;
|
|
205
|
-
|
|
206
|
-
try {
|
|
207
|
-
await executeWithRateLimit(command, 'workers', 3); // Lower retries for secrets
|
|
208
|
-
} catch (error) {
|
|
209
|
-
throw new Error(`Secret deployment failed: ${error.message}`);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export async function deleteSecret(key, env = 'production') {
|
|
214
|
-
try {
|
|
215
|
-
await executeWithRateLimit(`npx wrangler secret delete ${key} --env ${env}`, 'workers');
|
|
216
|
-
} catch (error) {
|
|
217
|
-
throw new Error(`Secret deletion failed: ${error.message}`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export async function listSecrets(env = 'production') {
|
|
222
|
-
try {
|
|
223
|
-
const { stdout: list } = await executeWithRateLimit(`npx wrangler secret list --env ${env}`, 'workers');
|
|
224
|
-
return list;
|
|
225
|
-
} catch (error) {
|
|
226
|
-
throw new Error(`Failed to list secrets: ${error.message}`);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export async function listDatabases(options = {}) {
|
|
231
|
-
const { apiToken, accountId } = options;
|
|
232
|
-
|
|
233
|
-
// Use API-based operation if credentials provided
|
|
234
|
-
if (apiToken && accountId) {
|
|
235
|
-
const { CloudflareAPI } = await import('../../../dist/utils/cloudflare/api.js');
|
|
236
|
-
const cf = new CloudflareAPI(apiToken);
|
|
237
|
-
return await cf.listD1Databases(accountId);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Fallback to CLI-based operation
|
|
241
|
-
try {
|
|
242
|
-
const { stdout: list } = await executeWithRateLimit('npx wrangler d1 list', 'd1');
|
|
243
|
-
return list;
|
|
244
|
-
} catch (error) {
|
|
245
|
-
throw new Error(`Failed to list databases: ${error.message}`);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
export async function databaseExists(databaseName, options = {}) {
|
|
250
|
-
const { apiToken, accountId } = options;
|
|
251
|
-
|
|
252
|
-
// Use API-based operation if credentials provided
|
|
253
|
-
if (apiToken && accountId) {
|
|
254
|
-
const { CloudflareAPI } = await import('../../../dist/utils/cloudflare/api.js');
|
|
255
|
-
const cf = new CloudflareAPI(apiToken);
|
|
256
|
-
return await cf.d1DatabaseExists(accountId, databaseName);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Fallback to CLI-based operation
|
|
260
|
-
try {
|
|
261
|
-
const list = await listDatabases();
|
|
262
|
-
return list.includes(databaseName);
|
|
263
|
-
} catch (error) {
|
|
264
|
-
return false; // Assume doesn't exist if can't check
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export async function createDatabase(name, options = {}) {
|
|
269
|
-
const { apiToken, accountId } = options;
|
|
270
|
-
|
|
271
|
-
// Use API-based operation if credentials provided
|
|
272
|
-
if (apiToken && accountId) {
|
|
273
|
-
const { CloudflareAPI } = await import('../../../dist/utils/cloudflare/api.js');
|
|
274
|
-
const cf = new CloudflareAPI(apiToken);
|
|
275
|
-
const result = await cf.createD1Database(accountId, name);
|
|
276
|
-
return result.uuid; // Return UUID to match CLI behavior
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Fallback to CLI-based operation
|
|
280
|
-
try {
|
|
281
|
-
const { stdout: output } = await executeWithRateLimit(`npx wrangler d1 create ${name}`, 'd1');
|
|
282
|
-
const idMatch = output.match(/database_id = "([^"]+)"/);
|
|
283
|
-
if (!idMatch) {
|
|
284
|
-
throw new Error('Could not extract database ID from creation output');
|
|
285
|
-
}
|
|
286
|
-
return idMatch[1];
|
|
287
|
-
} catch (error) {
|
|
288
|
-
throw new Error(`Database creation failed: ${error.message}`);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export async function deleteDatabase(name) {
|
|
293
|
-
try {
|
|
294
|
-
await executeWithRateLimit(`npx wrangler d1 delete ${name} --skip-confirmation`, 'd1');
|
|
295
|
-
} catch (error) {
|
|
296
|
-
throw new Error(`Database deletion failed: ${error.message}`);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export async function runMigrations(databaseName, env = 'production') {
|
|
301
|
-
const startTime = Date.now();
|
|
302
|
-
|
|
303
|
-
try {
|
|
304
|
-
await ensureMonitoringInitialized();
|
|
305
|
-
|
|
306
|
-
const result = await errorRecovery.executeWithRecovery(async () => {
|
|
307
|
-
await executeWithRateLimit(`npx wrangler d1 migrations apply ${databaseName} --env ${env} --remote`, 'd1');
|
|
308
|
-
return true;
|
|
309
|
-
}, { operationId: `runMigrations_${databaseName}_${env}` });
|
|
310
|
-
|
|
311
|
-
const duration = Date.now() - startTime;
|
|
312
|
-
monitor.recordRequest(true, duration, { operation: 'runMigrations', databaseName, env });
|
|
313
|
-
await monitor.log('info', 'Database migrations completed', { databaseName, env, duration });
|
|
314
|
-
|
|
315
|
-
return result;
|
|
316
|
-
} catch (error) {
|
|
317
|
-
const duration = Date.now() - startTime;
|
|
318
|
-
monitor.recordRequest(false, duration, { operation: 'runMigrations', databaseName, env, error: error.message });
|
|
319
|
-
await monitor.log('warn', 'Database migrations had warnings', { databaseName, env, duration, error: error.message });
|
|
320
|
-
|
|
321
|
-
return false; // Warnings are often OK
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export async function executeSql(databaseName, sql, env = 'production') {
|
|
326
|
-
const startTime = Date.now();
|
|
327
|
-
|
|
328
|
-
try {
|
|
329
|
-
await ensureMonitoringInitialized();
|
|
330
|
-
|
|
331
|
-
const result = await dbManager.executeQuery(databaseName, sql, { env });
|
|
332
|
-
|
|
333
|
-
const duration = Date.now() - startTime;
|
|
334
|
-
monitor.recordRequest(true, duration, { operation: 'executeSql', databaseName, env });
|
|
335
|
-
await monitor.log('info', 'Database query executed successfully', {
|
|
336
|
-
databaseName,
|
|
337
|
-
env,
|
|
338
|
-
duration,
|
|
339
|
-
resultLength: result.result ? result.result.length : 0
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
return result.result;
|
|
343
|
-
} catch (error) {
|
|
344
|
-
const duration = Date.now() - startTime;
|
|
345
|
-
monitor.recordRequest(false, duration, { operation: 'executeSql', databaseName, env, error: error.message });
|
|
346
|
-
await monitor.log('error', 'Database query failed', { databaseName, env, duration, error: error.message });
|
|
347
|
-
|
|
348
|
-
throw new Error(`SQL execution failed after retries: ${error.message}`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Get database ID from list output
|
|
353
|
-
export async function getDatabaseId(databaseName, options = {}) {
|
|
354
|
-
const { apiToken, accountId } = options;
|
|
355
|
-
|
|
356
|
-
// Use API-based operation if credentials provided
|
|
357
|
-
if (apiToken && accountId) {
|
|
358
|
-
const { CloudflareAPI } = await import('../../../dist/utils/cloudflare/api.js');
|
|
359
|
-
const cf = new CloudflareAPI(apiToken);
|
|
360
|
-
const db = await cf.getD1Database(accountId, databaseName);
|
|
361
|
-
return db?.uuid || null;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Fallback to CLI-based operation
|
|
365
|
-
try {
|
|
366
|
-
const list = await listDatabases();
|
|
367
|
-
const lines = list.split('\n');
|
|
368
|
-
for (const line of lines) {
|
|
369
|
-
if (line.includes(databaseName)) {
|
|
370
|
-
const match = line.match(/([a-f0-9-]{36})/);
|
|
371
|
-
if (match) {
|
|
372
|
-
return match[1];
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return null;
|
|
377
|
-
} catch (error) {
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// Validate prerequisites
|
|
383
|
-
export async function validatePrerequisites() {
|
|
384
|
-
const checks = [
|
|
385
|
-
{ name: 'Node.js', command: 'node --version' },
|
|
386
|
-
{ name: 'NPM', command: 'npm --version' },
|
|
387
|
-
{ name: 'Wrangler', command: 'npx wrangler --version' }
|
|
388
|
-
];
|
|
389
|
-
|
|
390
|
-
const results = [];
|
|
391
|
-
for (const check of checks) {
|
|
392
|
-
try {
|
|
393
|
-
const { stdout: version } = await execAsync(check.command, { encoding: 'utf8' });
|
|
394
|
-
results.push({ name: check.name, status: 'ok', version: version.trim() });
|
|
395
|
-
} catch (error) {
|
|
396
|
-
results.push({ name: check.name, status: 'failed', error: error.message });
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
return results;
|
|
401
|
-
}
|