@tamyla/clodo-framework 1.0.0
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 +564 -0
- package/LICENSE +21 -0
- package/README.md +1393 -0
- package/bin/README.md +71 -0
- package/bin/clodo-service.js +416 -0
- package/bin/security/security-cli.js +96 -0
- package/bin/service-management/README.md +74 -0
- package/bin/service-management/create-service.js +129 -0
- package/bin/service-management/init-service.js +102 -0
- package/bin/service-management/init-service.js.backup +889 -0
- package/bin/shared/config/customer-cli.js +293 -0
- package/dist/config/ConfigurationManager.js +159 -0
- package/dist/config/CustomerConfigCLI.js +220 -0
- package/dist/config/FeatureManager.js +426 -0
- package/dist/config/customers.js +441 -0
- package/dist/config/domains.js +180 -0
- package/dist/config/features.js +225 -0
- package/dist/config/index.js +6 -0
- package/dist/database/database-orchestrator.js +730 -0
- package/dist/database/index.js +4 -0
- package/dist/deployment/auditor.js +971 -0
- package/dist/deployment/index.js +10 -0
- package/dist/deployment/rollback-manager.js +523 -0
- package/dist/deployment/testers/api-tester.js +80 -0
- package/dist/deployment/testers/auth-tester.js +129 -0
- package/dist/deployment/testers/core.js +217 -0
- package/dist/deployment/testers/database-tester.js +105 -0
- package/dist/deployment/testers/index.js +74 -0
- package/dist/deployment/testers/load-tester.js +120 -0
- package/dist/deployment/testers/performance-tester.js +105 -0
- package/dist/deployment/validator.js +558 -0
- package/dist/deployment/wrangler-deployer.js +574 -0
- package/dist/handlers/GenericRouteHandler.js +532 -0
- package/dist/index.js +39 -0
- package/dist/migration/MigrationAdapters.js +562 -0
- package/dist/modules/ModuleManager.js +668 -0
- package/dist/modules/security.js +98 -0
- package/dist/orchestration/cross-domain-coordinator.js +1083 -0
- package/dist/orchestration/index.js +5 -0
- package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
- package/dist/orchestration/modules/DomainResolver.js +196 -0
- package/dist/orchestration/modules/StateManager.js +332 -0
- package/dist/orchestration/multi-domain-orchestrator.js +255 -0
- package/dist/routing/EnhancedRouter.js +158 -0
- package/dist/schema/SchemaManager.js +778 -0
- package/dist/security/ConfigurationValidator.js +490 -0
- package/dist/security/DeploymentManager.js +208 -0
- package/dist/security/SecretGenerator.js +142 -0
- package/dist/security/SecurityCLI.js +228 -0
- package/dist/security/index.js +51 -0
- package/dist/security/patterns/environment-rules.js +66 -0
- package/dist/security/patterns/insecure-patterns.js +21 -0
- package/dist/service-management/ConfirmationEngine.js +411 -0
- package/dist/service-management/ErrorTracker.js +294 -0
- package/dist/service-management/GenerationEngine.js +3109 -0
- package/dist/service-management/InputCollector.js +237 -0
- package/dist/service-management/ServiceCreator.js +229 -0
- package/dist/service-management/ServiceInitializer.js +448 -0
- package/dist/service-management/ServiceOrchestrator.js +638 -0
- package/dist/service-management/handlers/ConfigMutator.js +130 -0
- package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
- package/dist/service-management/handlers/GenerationHandler.js +80 -0
- package/dist/service-management/handlers/InputHandler.js +59 -0
- package/dist/service-management/handlers/ValidationHandler.js +203 -0
- package/dist/service-management/index.js +7 -0
- package/dist/services/GenericDataService.js +488 -0
- package/dist/shared/cloudflare/domain-discovery.js +562 -0
- package/dist/shared/cloudflare/domain-manager.js +912 -0
- package/dist/shared/cloudflare/index.js +8 -0
- package/dist/shared/cloudflare/ops.js +387 -0
- package/dist/shared/config/cache.js +1167 -0
- package/dist/shared/config/command-config-manager.js +174 -0
- package/dist/shared/config/customer-cli.js +258 -0
- package/dist/shared/config/index.js +9 -0
- package/dist/shared/config/manager.js +289 -0
- package/dist/shared/database/connection-manager.js +338 -0
- package/dist/shared/database/index.js +7 -0
- package/dist/shared/database/orchestrator.js +632 -0
- package/dist/shared/deployment/auditor.js +971 -0
- package/dist/shared/deployment/index.js +10 -0
- package/dist/shared/deployment/rollback-manager.js +523 -0
- package/dist/shared/deployment/validator.js +558 -0
- package/dist/shared/index.js +32 -0
- package/dist/shared/monitoring/health-checker.js +250 -0
- package/dist/shared/monitoring/index.js +8 -0
- package/dist/shared/monitoring/memory-manager.js +382 -0
- package/dist/shared/monitoring/production-monitor.js +390 -0
- package/dist/shared/production-tester/api-tester.js +80 -0
- package/dist/shared/production-tester/auth-tester.js +129 -0
- package/dist/shared/production-tester/core.js +217 -0
- package/dist/shared/production-tester/database-tester.js +105 -0
- package/dist/shared/production-tester/index.js +74 -0
- package/dist/shared/production-tester/load-tester.js +120 -0
- package/dist/shared/production-tester/performance-tester.js +105 -0
- package/dist/shared/security/api-token-manager.js +296 -0
- package/dist/shared/security/index.js +8 -0
- package/dist/shared/security/secret-generator.js +918 -0
- package/dist/shared/security/secure-token-manager.js +379 -0
- package/dist/shared/utils/error-recovery.js +240 -0
- package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
- package/dist/shared/utils/index.js +9 -0
- package/dist/shared/utils/interactive-prompts.js +134 -0
- package/dist/shared/utils/rate-limiter.js +249 -0
- package/dist/utils/ErrorHandler.js +173 -0
- package/dist/utils/deployment/config-cache.js +1160 -0
- package/dist/utils/deployment/index.js +6 -0
- package/dist/utils/deployment/interactive-prompts.js +97 -0
- package/dist/utils/deployment/secret-generator.js +896 -0
- package/dist/utils/dirname-helper.js +35 -0
- package/dist/utils/domain-config.js +159 -0
- package/dist/utils/error-recovery.js +240 -0
- package/dist/utils/esm-helper.js +52 -0
- package/dist/utils/framework-config.js +481 -0
- package/dist/utils/graceful-shutdown-manager.js +379 -0
- package/dist/utils/health-checker.js +114 -0
- package/dist/utils/index.js +36 -0
- package/dist/utils/prompt-handler.js +98 -0
- package/dist/utils/usage-tracker.js +252 -0
- package/dist/utils/validation.js +112 -0
- package/dist/version/VersionDetector.js +723 -0
- package/dist/worker/index.js +4 -0
- package/dist/worker/integration.js +332 -0
- package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
- package/docs/INTEGRATION_GUIDE.md +2045 -0
- package/docs/README.md +82 -0
- package/docs/SECURITY.md +242 -0
- package/docs/deployment/deployment-guide.md +540 -0
- package/docs/overview.md +280 -0
- package/package.json +176 -0
- package/types/index.d.ts +575 -0
|
@@ -0,0 +1,8 @@
|
|
|
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';
|
|
@@ -0,0 +1,387 @@
|
|
|
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/error-recovery.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
|
+
const execAsync = promisify(exec);
|
|
18
|
+
|
|
19
|
+
// Error recovery for critical operations
|
|
20
|
+
const errorRecovery = new ErrorRecoveryManager({
|
|
21
|
+
maxRetries: 2,
|
|
22
|
+
retryDelay: 2000,
|
|
23
|
+
circuitBreakerThreshold: 3,
|
|
24
|
+
gracefulDegradation: true
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Secure token management
|
|
28
|
+
const tokenManager = new SecureTokenManager();
|
|
29
|
+
|
|
30
|
+
// Production monitoring
|
|
31
|
+
const monitor = new ProductionMonitor({
|
|
32
|
+
logLevel: 'info',
|
|
33
|
+
enableMetrics: true,
|
|
34
|
+
enableAlerts: true
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Database connection management
|
|
38
|
+
const dbManager = new DatabaseConnectionManager({
|
|
39
|
+
maxRetries: 2,
|
|
40
|
+
retryDelay: 1000,
|
|
41
|
+
connectionTimeout: 30000,
|
|
42
|
+
queryTimeout: 60000,
|
|
43
|
+
enableConnectionPooling: true,
|
|
44
|
+
maxPoolSize: 5
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Memory management
|
|
48
|
+
const memoryManager = startMemoryMonitoring({
|
|
49
|
+
gcInterval: 300000,
|
|
50
|
+
// 5 minutes
|
|
51
|
+
memoryThreshold: 0.8,
|
|
52
|
+
cleanupInterval: 60000,
|
|
53
|
+
// 1 minute
|
|
54
|
+
leakDetection: true
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Graceful shutdown handling (async initialization)
|
|
58
|
+
let shutdownManager = null;
|
|
59
|
+
(async () => {
|
|
60
|
+
shutdownManager = await initializeGracefulShutdown({
|
|
61
|
+
dbManager,
|
|
62
|
+
monitor,
|
|
63
|
+
tokenManager,
|
|
64
|
+
memoryManager
|
|
65
|
+
}, {
|
|
66
|
+
shutdownTimeout: 30000,
|
|
67
|
+
forceShutdownTimeout: 5000
|
|
68
|
+
});
|
|
69
|
+
})().catch(console.error);
|
|
70
|
+
|
|
71
|
+
// Initialize monitoring
|
|
72
|
+
let monitoringInitialized = false;
|
|
73
|
+
async function ensureMonitoringInitialized() {
|
|
74
|
+
if (!monitoringInitialized) {
|
|
75
|
+
await monitor.startMonitoring();
|
|
76
|
+
monitoringInitialized = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Initialize secure token manager
|
|
81
|
+
let tokenManagerInitialized = false;
|
|
82
|
+
async function ensureTokenManagerInitialized() {
|
|
83
|
+
if (!tokenManagerInitialized) {
|
|
84
|
+
await tokenManager.initialize();
|
|
85
|
+
tokenManagerInitialized = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function checkAuth() {
|
|
89
|
+
try {
|
|
90
|
+
await ensureTokenManagerInitialized();
|
|
91
|
+
// Try to get a valid token for Cloudflare API
|
|
92
|
+
const tokens = tokenManager.listTokens('cloudflare');
|
|
93
|
+
if (tokens.length === 0) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check if we have a non-expired token
|
|
98
|
+
const validToken = tokens.find(token => new Date() < token.expires);
|
|
99
|
+
return !!validToken;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export async function authenticate() {
|
|
105
|
+
try {
|
|
106
|
+
// Use wrangler auth login for initial authentication
|
|
107
|
+
await execAsync('npx wrangler auth login', {
|
|
108
|
+
stdio: 'inherit'
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// After successful login, we could extract and store the token securely
|
|
112
|
+
// For now, rely on wrangler's built-in token management
|
|
113
|
+
console.log('Authentication successful. Use storeCloudflareToken() to store API tokens securely.');
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new Error(`Authentication failed: ${error.message}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export async function storeCloudflareToken(token, metadata = {}) {
|
|
119
|
+
await ensureTokenManagerInitialized();
|
|
120
|
+
const fingerprint = await tokenManager.storeToken('cloudflare', token, {
|
|
121
|
+
permissions: ['api_access', 'deployment'],
|
|
122
|
+
environment: metadata.environment || 'production',
|
|
123
|
+
...metadata
|
|
124
|
+
});
|
|
125
|
+
console.log(`Cloudflare API token stored securely with fingerprint: ${fingerprint}`);
|
|
126
|
+
return fingerprint;
|
|
127
|
+
}
|
|
128
|
+
export async function getCloudflareToken(requiredPermissions = ['api_access']) {
|
|
129
|
+
await ensureTokenManagerInitialized();
|
|
130
|
+
const tokens = tokenManager.listTokens('cloudflare');
|
|
131
|
+
|
|
132
|
+
// Find a valid token with required permissions
|
|
133
|
+
for (const tokenInfo of tokens) {
|
|
134
|
+
if (new Date() < tokenInfo.expires && tokenManager.validatePermissions(tokenInfo.permissions, requiredPermissions)) {
|
|
135
|
+
return await tokenManager.retrieveToken('cloudflare', tokenInfo.fingerprint, requiredPermissions);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
throw new Error('No valid Cloudflare API token found with required permissions');
|
|
139
|
+
}
|
|
140
|
+
export async function listWorkers() {
|
|
141
|
+
try {
|
|
142
|
+
const {
|
|
143
|
+
stdout: list
|
|
144
|
+
} = await executeWithRateLimit('npx wrangler list', 'workers');
|
|
145
|
+
return list;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
throw new Error(`Failed to list workers: ${error.message}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export async function workerExists(workerName) {
|
|
151
|
+
try {
|
|
152
|
+
const list = await listWorkers();
|
|
153
|
+
return list.includes(workerName);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
return false; // Assume doesn't exist if can't check
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export async function deployWorker(env = 'production') {
|
|
159
|
+
const startTime = Date.now();
|
|
160
|
+
try {
|
|
161
|
+
await ensureMonitoringInitialized();
|
|
162
|
+
const result = await errorRecovery.executeWithRecovery(async () => {
|
|
163
|
+
await executeWithRateLimit(`npm run deploy:${env}`, 'workers');
|
|
164
|
+
return true;
|
|
165
|
+
}, {
|
|
166
|
+
operationId: `deployWorker_${env}`
|
|
167
|
+
});
|
|
168
|
+
const duration = Date.now() - startTime;
|
|
169
|
+
monitor.recordRequest(true, duration, {
|
|
170
|
+
operation: 'deployWorker',
|
|
171
|
+
env
|
|
172
|
+
});
|
|
173
|
+
await monitor.log('info', 'Worker deployment successful', {
|
|
174
|
+
env,
|
|
175
|
+
duration
|
|
176
|
+
});
|
|
177
|
+
return result;
|
|
178
|
+
} catch (error) {
|
|
179
|
+
const duration = Date.now() - startTime;
|
|
180
|
+
monitor.recordRequest(false, duration, {
|
|
181
|
+
operation: 'deployWorker',
|
|
182
|
+
env,
|
|
183
|
+
error: error.message
|
|
184
|
+
});
|
|
185
|
+
await monitor.log('error', 'Worker deployment failed', {
|
|
186
|
+
env,
|
|
187
|
+
duration,
|
|
188
|
+
error: error.message
|
|
189
|
+
});
|
|
190
|
+
throw new Error(`Worker deployment failed after retries: ${error.message}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
export async function deploySecret(key, value, env = 'production') {
|
|
194
|
+
const command = process.platform === 'win32' ? `powershell -Command "Write-Output '${value}' | npx wrangler secret put ${key} --env ${env}"` : `echo "${value}" | npx wrangler secret put ${key} --env ${env}`;
|
|
195
|
+
try {
|
|
196
|
+
await executeWithRateLimit(command, 'workers', 3); // Lower retries for secrets
|
|
197
|
+
} catch (error) {
|
|
198
|
+
throw new Error(`Secret deployment failed: ${error.message}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export async function deleteSecret(key, env = 'production') {
|
|
202
|
+
try {
|
|
203
|
+
await executeWithRateLimit(`npx wrangler secret delete ${key} --env ${env}`, 'workers');
|
|
204
|
+
} catch (error) {
|
|
205
|
+
throw new Error(`Secret deletion failed: ${error.message}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export async function listSecrets(env = 'production') {
|
|
209
|
+
try {
|
|
210
|
+
const {
|
|
211
|
+
stdout: list
|
|
212
|
+
} = await executeWithRateLimit(`npx wrangler secret list --env ${env}`, 'workers');
|
|
213
|
+
return list;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
throw new Error(`Failed to list secrets: ${error.message}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
export async function listDatabases() {
|
|
219
|
+
try {
|
|
220
|
+
const {
|
|
221
|
+
stdout: list
|
|
222
|
+
} = await executeWithRateLimit('npx wrangler d1 list', 'd1');
|
|
223
|
+
return list;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
throw new Error(`Failed to list databases: ${error.message}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
export async function databaseExists(databaseName) {
|
|
229
|
+
try {
|
|
230
|
+
const list = await listDatabases();
|
|
231
|
+
return list.includes(databaseName);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
return false; // Assume doesn't exist if can't check
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
export async function createDatabase(name) {
|
|
237
|
+
try {
|
|
238
|
+
const {
|
|
239
|
+
stdout: output
|
|
240
|
+
} = await executeWithRateLimit(`npx wrangler d1 create ${name}`, 'd1');
|
|
241
|
+
const idMatch = output.match(/database_id = "([^"]+)"/);
|
|
242
|
+
if (!idMatch) {
|
|
243
|
+
throw new Error('Could not extract database ID from creation output');
|
|
244
|
+
}
|
|
245
|
+
return idMatch[1];
|
|
246
|
+
} catch (error) {
|
|
247
|
+
throw new Error(`Database creation failed: ${error.message}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
export async function deleteDatabase(name) {
|
|
251
|
+
try {
|
|
252
|
+
await executeWithRateLimit(`npx wrangler d1 delete ${name} --skip-confirmation`, 'd1');
|
|
253
|
+
} catch (error) {
|
|
254
|
+
throw new Error(`Database deletion failed: ${error.message}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
export async function runMigrations(databaseName, env = 'production') {
|
|
258
|
+
const startTime = Date.now();
|
|
259
|
+
try {
|
|
260
|
+
await ensureMonitoringInitialized();
|
|
261
|
+
const result = await errorRecovery.executeWithRecovery(async () => {
|
|
262
|
+
await executeWithRateLimit(`npx wrangler d1 migrations apply ${databaseName} --env ${env} --remote`, 'd1');
|
|
263
|
+
return true;
|
|
264
|
+
}, {
|
|
265
|
+
operationId: `runMigrations_${databaseName}_${env}`
|
|
266
|
+
});
|
|
267
|
+
const duration = Date.now() - startTime;
|
|
268
|
+
monitor.recordRequest(true, duration, {
|
|
269
|
+
operation: 'runMigrations',
|
|
270
|
+
databaseName,
|
|
271
|
+
env
|
|
272
|
+
});
|
|
273
|
+
await monitor.log('info', 'Database migrations completed', {
|
|
274
|
+
databaseName,
|
|
275
|
+
env,
|
|
276
|
+
duration
|
|
277
|
+
});
|
|
278
|
+
return result;
|
|
279
|
+
} catch (error) {
|
|
280
|
+
const duration = Date.now() - startTime;
|
|
281
|
+
monitor.recordRequest(false, duration, {
|
|
282
|
+
operation: 'runMigrations',
|
|
283
|
+
databaseName,
|
|
284
|
+
env,
|
|
285
|
+
error: error.message
|
|
286
|
+
});
|
|
287
|
+
await monitor.log('warn', 'Database migrations had warnings', {
|
|
288
|
+
databaseName,
|
|
289
|
+
env,
|
|
290
|
+
duration,
|
|
291
|
+
error: error.message
|
|
292
|
+
});
|
|
293
|
+
return false; // Warnings are often OK
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
export async function executeSql(databaseName, sql, env = 'production') {
|
|
297
|
+
const startTime = Date.now();
|
|
298
|
+
try {
|
|
299
|
+
await ensureMonitoringInitialized();
|
|
300
|
+
const result = await dbManager.executeQuery(databaseName, sql, {
|
|
301
|
+
env
|
|
302
|
+
});
|
|
303
|
+
const duration = Date.now() - startTime;
|
|
304
|
+
monitor.recordRequest(true, duration, {
|
|
305
|
+
operation: 'executeSql',
|
|
306
|
+
databaseName,
|
|
307
|
+
env
|
|
308
|
+
});
|
|
309
|
+
await monitor.log('info', 'Database query executed successfully', {
|
|
310
|
+
databaseName,
|
|
311
|
+
env,
|
|
312
|
+
duration,
|
|
313
|
+
resultLength: result.result ? result.result.length : 0
|
|
314
|
+
});
|
|
315
|
+
return result.result;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
const duration = Date.now() - startTime;
|
|
318
|
+
monitor.recordRequest(false, duration, {
|
|
319
|
+
operation: 'executeSql',
|
|
320
|
+
databaseName,
|
|
321
|
+
env,
|
|
322
|
+
error: error.message
|
|
323
|
+
});
|
|
324
|
+
await monitor.log('error', 'Database query failed', {
|
|
325
|
+
databaseName,
|
|
326
|
+
env,
|
|
327
|
+
duration,
|
|
328
|
+
error: error.message
|
|
329
|
+
});
|
|
330
|
+
throw new Error(`SQL execution failed after retries: ${error.message}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Get database ID from list output
|
|
335
|
+
export async function getDatabaseId(databaseName) {
|
|
336
|
+
try {
|
|
337
|
+
const list = await listDatabases();
|
|
338
|
+
const lines = list.split('\n');
|
|
339
|
+
for (const line of lines) {
|
|
340
|
+
if (line.includes(databaseName)) {
|
|
341
|
+
const match = line.match(/([a-f0-9-]{36})/);
|
|
342
|
+
if (match) {
|
|
343
|
+
return match[1];
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return null;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Validate prerequisites
|
|
354
|
+
export async function validatePrerequisites() {
|
|
355
|
+
const checks = [{
|
|
356
|
+
name: 'Node.js',
|
|
357
|
+
command: 'node --version'
|
|
358
|
+
}, {
|
|
359
|
+
name: 'NPM',
|
|
360
|
+
command: 'npm --version'
|
|
361
|
+
}, {
|
|
362
|
+
name: 'Wrangler',
|
|
363
|
+
command: 'npx wrangler --version'
|
|
364
|
+
}];
|
|
365
|
+
const results = [];
|
|
366
|
+
for (const check of checks) {
|
|
367
|
+
try {
|
|
368
|
+
const {
|
|
369
|
+
stdout: version
|
|
370
|
+
} = await execAsync(check.command, {
|
|
371
|
+
encoding: 'utf8'
|
|
372
|
+
});
|
|
373
|
+
results.push({
|
|
374
|
+
name: check.name,
|
|
375
|
+
status: 'ok',
|
|
376
|
+
version: version.trim()
|
|
377
|
+
});
|
|
378
|
+
} catch (error) {
|
|
379
|
+
results.push({
|
|
380
|
+
name: check.name,
|
|
381
|
+
status: 'failed',
|
|
382
|
+
error: error.message
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return results;
|
|
387
|
+
}
|