@tamyla/clodo-framework 3.1.21 ā 3.1.22
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 +9 -0
- package/README.md +53 -0
- package/dist/bin/clodo-service.js +47 -15
- package/dist/bin/commands/deploy.js +115 -83
- package/dist/bin/commands/helpers/deployment-ui.js +138 -0
- package/dist/bin/commands/helpers/deployment-verification.js +251 -0
- package/dist/bin/commands/helpers/error-recovery.js +80 -0
- package/dist/bin/commands/helpers/resource-detection.js +113 -0
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/security/security-cli.js +1 -1
- package/dist/bin/shared/cache/configuration-cache.js +82 -0
- package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
- package/dist/bin/shared/cloudflare/index.js +1 -1
- package/dist/bin/shared/cloudflare/ops.js +6 -4
- package/dist/bin/shared/config/ConfigurationManager.js +23 -1
- package/dist/bin/shared/config/command-config-manager.js +19 -3
- package/dist/bin/shared/config/index.js +1 -1
- package/dist/bin/shared/deployment/credential-collector.js +30 -7
- package/dist/bin/shared/deployment/index.js +2 -2
- package/dist/bin/shared/deployment/rollback-manager.js +4 -520
- package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
- package/dist/bin/shared/deployment/validator.js +40 -10
- package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
- package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
- package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
- package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
- package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
- package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
- package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
- package/dist/bin/shared/error-handling/error-classifier.js +46 -0
- package/dist/bin/shared/monitoring/health-checker.js +129 -1
- package/dist/bin/shared/monitoring/memory-manager.js +17 -6
- package/dist/bin/shared/routing/domain-router.js +1 -1
- package/dist/bin/shared/utils/deployment-validator.js +97 -0
- package/dist/bin/shared/utils/formatters.js +10 -0
- package/dist/bin/shared/utils/index.js +13 -1
- package/dist/bin/shared/utils/interactive-prompts.js +34 -18
- package/dist/bin/shared/utils/progress-manager.js +2 -2
- package/dist/bin/shared/utils/progress-spinner.js +53 -0
- package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
- package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/patterns/insecure-patterns.js +1 -1
- package/dist/utils/constants.js +102 -0
- package/dist/utils/deployment/wrangler-config-manager.js +215 -48
- package/dist/utils/framework-config.js +2 -2
- package/dist/utils/interactive-prompts.js +10 -59
- package/package.json +16 -8
- package/dist/bin/clodo-service-old.js +0 -868
- package/dist/bin/clodo-service-test.js +0 -10
- package/dist/bin/commands/assess.js +0 -91
- package/dist/bin/commands/create.js +0 -77
- package/dist/bin/commands/diagnose.js +0 -83
- package/dist/bin/commands/helpers.js +0 -138
- package/dist/bin/commands/update.js +0 -75
- package/dist/bin/database/deployment-db-manager.js +0 -423
- package/dist/bin/database/enterprise-db-manager.js +0 -457
- package/dist/bin/database/wrangler-d1-manager.js +0 -685
- package/dist/bin/deployment/enterprise-deploy.js +0 -877
- package/dist/bin/deployment/master-deploy.js +0 -1376
- package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
- package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
- package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
- package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
- package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
- package/dist/bin/deployment/modules/ValidationManager.js +0 -342
- package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/bin/deployment/test-interactive-utils.js +0 -66
- package/dist/bin/portfolio/portfolio-manager.js +0 -487
- package/dist/bin/service-management/create-service.js +0 -122
- package/dist/bin/service-management/init-service.js +0 -79
- package/dist/config/customers.js +0 -623
- package/dist/config/domains.js +0 -186
- package/dist/config/index.js +0 -6
- package/dist/database/database-orchestrator.js +0 -795
- package/dist/database/index.js +0 -4
- package/dist/deployment/index.js +0 -11
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/deployment/orchestration/index.js +0 -17
- package/dist/deployment/rollback-manager.js +0 -36
- package/dist/deployment/wrangler-deployer.js +0 -640
- package/dist/handlers/GenericRouteHandler.js +0 -532
- package/dist/migration/MigrationAdapters.js +0 -562
- package/dist/modules/ModuleManager.js +0 -668
- package/dist/modules/security.js +0 -96
- package/dist/orchestration/cross-domain-coordinator.js +0 -1083
- package/dist/orchestration/index.js +0 -5
- package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
- package/dist/orchestration/modules/DomainResolver.js +0 -198
- package/dist/orchestration/modules/StateManager.js +0 -332
- package/dist/orchestration/multi-domain-orchestrator.js +0 -724
- package/dist/routing/EnhancedRouter.js +0 -158
- package/dist/schema/SchemaManager.js +0 -778
- package/dist/service-management/ConfirmationEngine.js +0 -412
- package/dist/service-management/ErrorTracker.js +0 -299
- package/dist/service-management/GenerationEngine.js +0 -447
- package/dist/service-management/InputCollector.js +0 -619
- package/dist/service-management/ServiceCreator.js +0 -265
- package/dist/service-management/ServiceInitializer.js +0 -453
- package/dist/service-management/ServiceOrchestrator.js +0 -633
- package/dist/service-management/generators/BaseGenerator.js +0 -233
- package/dist/service-management/generators/GeneratorRegistry.js +0 -254
- package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
- package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
- package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
- package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
- package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
- package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
- package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
- package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
- package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
- package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
- package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
- package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
- package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
- package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
- package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
- package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
- package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
- package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
- package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
- package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
- package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
- package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
- package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
- package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
- package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
- package/dist/service-management/generators/utils/FileWriter.js +0 -179
- package/dist/service-management/generators/utils/PathResolver.js +0 -157
- package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
- package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
- package/dist/service-management/generators/utils/index.js +0 -18
- package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
- package/dist/service-management/handlers/GenerationHandler.js +0 -80
- package/dist/service-management/handlers/InputHandler.js +0 -59
- package/dist/service-management/handlers/ValidationHandler.js +0 -203
- package/dist/service-management/index.js +0 -14
- package/dist/service-management/routing/DomainRouteMapper.js +0 -311
- package/dist/service-management/routing/RouteGenerator.js +0 -266
- package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
- package/dist/service-management/routing/index.js +0 -14
- package/dist/service-management/services/DirectoryStructureService.js +0 -56
- package/dist/service-management/services/GenerationCoordinator.js +0 -208
- package/dist/service-management/services/GeneratorRegistry.js +0 -174
- package/dist/services/GenericDataService.js +0 -501
- package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
- package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
- package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
- package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
- package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
- package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
- package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
- package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
- package/dist/ui-structures/reference/service-manifest-template.json +0 -342
- package/dist/version/VersionDetector.js +0 -723
- package/dist/worker/index.js +0 -4
- package/dist/worker/integration.js +0 -351
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D1 Error Recovery Workflow Module
|
|
3
|
+
*
|
|
4
|
+
* Provides sophisticated D1 database binding error detection and recovery.
|
|
5
|
+
* Extracted from enterprise-deployment/master-deploy.js for modularity.
|
|
6
|
+
*
|
|
7
|
+
* @module d1-error-recovery
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* D1 Error Recovery Manager
|
|
12
|
+
* Handles D1 database binding errors during deployment with automatic recovery
|
|
13
|
+
*/
|
|
14
|
+
export class D1ErrorRecoveryManager {
|
|
15
|
+
/**
|
|
16
|
+
* @param {Object} options - Configuration options
|
|
17
|
+
* @param {Array} options.rollbackActions - Array to track rollback actions
|
|
18
|
+
* @param {Object} options.wranglerDeployer - Optional WranglerDeployer instance for testing
|
|
19
|
+
*/
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.rollbackActions = options.rollbackActions || [];
|
|
22
|
+
this.wranglerDeployer = options.wranglerDeployer;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Handle D1 database binding errors during deployment
|
|
27
|
+
*
|
|
28
|
+
* @param {Error} error - Deployment error to analyze
|
|
29
|
+
* @param {Object} config - Deployment configuration
|
|
30
|
+
* @param {string} config.environment - Target environment
|
|
31
|
+
* @param {string} [config.configPath='wrangler.toml'] - Path to wrangler config
|
|
32
|
+
* @param {string} [config.cwd] - Working directory
|
|
33
|
+
* @returns {Promise<Object>} Recovery result { handled, retry, action, message }
|
|
34
|
+
*/
|
|
35
|
+
async handleD1BindingError(error, config = {}) {
|
|
36
|
+
try {
|
|
37
|
+
// Use provided WranglerDeployer or import dynamically
|
|
38
|
+
let deployer;
|
|
39
|
+
if (this.wranglerDeployer) {
|
|
40
|
+
deployer = this.wranglerDeployer;
|
|
41
|
+
} else {
|
|
42
|
+
// Import WranglerDeployer for D1 error handling
|
|
43
|
+
const {
|
|
44
|
+
WranglerDeployer
|
|
45
|
+
} = await import('../../../deployment/wrangler-deployer.js');
|
|
46
|
+
deployer = new WranglerDeployer({
|
|
47
|
+
cwd: config.cwd || process.cwd(),
|
|
48
|
+
environment: config.environment
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Attempt D1 error recovery
|
|
53
|
+
const recoveryResult = await deployer.handleD1BindingError(error, {
|
|
54
|
+
configPath: config.configPath || 'wrangler.toml',
|
|
55
|
+
environment: config.environment
|
|
56
|
+
});
|
|
57
|
+
if (recoveryResult.handled) {
|
|
58
|
+
console.log(` š§ D1 Error Recovery: ${recoveryResult.action}`);
|
|
59
|
+
|
|
60
|
+
// Handle configuration backup if created
|
|
61
|
+
if (recoveryResult.backupPath) {
|
|
62
|
+
console.log(` š Configuration backup: ${recoveryResult.backupPath}`);
|
|
63
|
+
|
|
64
|
+
// Add rollback action to restore backup
|
|
65
|
+
this.rollbackActions.unshift({
|
|
66
|
+
type: 'restore-wrangler-config',
|
|
67
|
+
backupPath: recoveryResult.backupPath,
|
|
68
|
+
description: 'Restore wrangler.toml backup after D1 recovery'
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Determine if deployment should be retried
|
|
73
|
+
const shouldRetry = this.shouldRetryAfterRecovery(recoveryResult.action);
|
|
74
|
+
return {
|
|
75
|
+
handled: true,
|
|
76
|
+
retry: shouldRetry,
|
|
77
|
+
action: recoveryResult.action,
|
|
78
|
+
message: this.getRecoveryMessage(recoveryResult),
|
|
79
|
+
databaseName: recoveryResult.databaseName,
|
|
80
|
+
databaseId: recoveryResult.databaseId
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Error was not a D1 binding error
|
|
85
|
+
return {
|
|
86
|
+
handled: false,
|
|
87
|
+
retry: false
|
|
88
|
+
};
|
|
89
|
+
} catch (recoveryError) {
|
|
90
|
+
console.log(` ā ļø D1 error recovery failed: ${recoveryError.message}`);
|
|
91
|
+
return {
|
|
92
|
+
handled: true,
|
|
93
|
+
retry: false,
|
|
94
|
+
message: `D1 error recovery failed: ${recoveryError.message}`
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Determine if deployment should be retried after recovery
|
|
101
|
+
*
|
|
102
|
+
* @param {string} action - Recovery action taken
|
|
103
|
+
* @returns {boolean} True if deployment should retry
|
|
104
|
+
*/
|
|
105
|
+
shouldRetryAfterRecovery(action) {
|
|
106
|
+
const retryableActions = ['created_and_configured', 'database_selected_and_configured', 'binding_updated'];
|
|
107
|
+
return retryableActions.includes(action);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get user-friendly message for recovery result
|
|
112
|
+
*
|
|
113
|
+
* @param {Object} recoveryResult - Recovery result from WranglerDeployer
|
|
114
|
+
* @returns {string} User-friendly message
|
|
115
|
+
*/
|
|
116
|
+
getRecoveryMessage(recoveryResult) {
|
|
117
|
+
const messages = {
|
|
118
|
+
'created_and_configured': `Created D1 database '${recoveryResult.databaseName}' and updated configuration`,
|
|
119
|
+
'database_selected_and_configured': `Selected existing database '${recoveryResult.databaseName}' and updated configuration`,
|
|
120
|
+
'binding_updated': `Updated D1 database binding configuration`,
|
|
121
|
+
'cancelled': 'D1 error recovery was cancelled by user',
|
|
122
|
+
'creation_failed': `Failed to create D1 database: ${recoveryResult.error}`,
|
|
123
|
+
'selection_failed': `Failed to update database selection: ${recoveryResult.error}`,
|
|
124
|
+
'no_databases_available': 'No D1 databases available in account',
|
|
125
|
+
'manual': 'User chose to resolve D1 issues manually',
|
|
126
|
+
'not_d1_error': 'Error is not related to D1 database bindings'
|
|
127
|
+
};
|
|
128
|
+
return messages[recoveryResult.action] || `D1 recovery completed with action: ${recoveryResult.action}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Deploy worker with automatic D1 error recovery
|
|
133
|
+
*
|
|
134
|
+
* @param {Function} deployFunction - Function to deploy worker
|
|
135
|
+
* @param {Object} config - Deployment configuration
|
|
136
|
+
* @returns {Promise<void>}
|
|
137
|
+
*/
|
|
138
|
+
async deployWithRecovery(deployFunction, config = {}) {
|
|
139
|
+
try {
|
|
140
|
+
await deployFunction();
|
|
141
|
+
console.log(' ā
Worker deployed successfully');
|
|
142
|
+
} catch (error) {
|
|
143
|
+
// Attempt D1 error recovery
|
|
144
|
+
const recoveryResult = await this.handleD1BindingError(error, config);
|
|
145
|
+
if (recoveryResult.handled && recoveryResult.retry) {
|
|
146
|
+
console.log(' š Retrying deployment after D1 error recovery...');
|
|
147
|
+
try {
|
|
148
|
+
await deployFunction();
|
|
149
|
+
console.log(' ā
Worker deployed successfully after D1 recovery');
|
|
150
|
+
} catch (retryError) {
|
|
151
|
+
console.log(' ā Deployment failed even after D1 recovery');
|
|
152
|
+
throw retryError;
|
|
153
|
+
}
|
|
154
|
+
} else if (recoveryResult.handled) {
|
|
155
|
+
// Error was handled but no retry requested
|
|
156
|
+
throw new Error(`Deployment failed: ${recoveryResult.message || error.message}`);
|
|
157
|
+
} else {
|
|
158
|
+
// Not a D1 error, rethrow original
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get recovery statistics
|
|
166
|
+
*
|
|
167
|
+
* @returns {Object} Recovery statistics
|
|
168
|
+
*/
|
|
169
|
+
getStatistics() {
|
|
170
|
+
const d1RollbackActions = this.rollbackActions.filter(action => action.type === 'restore-wrangler-config');
|
|
171
|
+
return {
|
|
172
|
+
totalRecoveries: d1RollbackActions.length,
|
|
173
|
+
hasBackups: d1RollbackActions.length > 0,
|
|
174
|
+
latestBackup: d1RollbackActions[0]?.backupPath
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -10,8 +10,15 @@ import { access, readFile } from 'fs/promises';
|
|
|
10
10
|
import { readFileSync } from 'fs';
|
|
11
11
|
import { promisify } from 'util';
|
|
12
12
|
import { exec } from 'child_process';
|
|
13
|
-
import { join } from 'path';
|
|
13
|
+
import { join, dirname } from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
14
15
|
import { getCommandConfig } from '../config/command-config-manager.js';
|
|
16
|
+
|
|
17
|
+
// Get the directory of this module (framework's bin directory)
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = dirname(__filename);
|
|
20
|
+
// Navigate up to framework root, then to config directory
|
|
21
|
+
const FRAMEWORK_CONFIG_PATH = join(__dirname, '../../../config/validation-config.json');
|
|
15
22
|
const execAsync = promisify(exec);
|
|
16
23
|
|
|
17
24
|
/**
|
|
@@ -58,6 +65,7 @@ export class DeploymentValidator {
|
|
|
58
65
|
loadValidationConfig() {
|
|
59
66
|
const configPath = join(process.cwd(), 'validation-config.json');
|
|
60
67
|
try {
|
|
68
|
+
// First try to load from service directory
|
|
61
69
|
const configData = readFileSync(configPath, 'utf-8');
|
|
62
70
|
const userConfig = JSON.parse(configData);
|
|
63
71
|
|
|
@@ -85,15 +93,37 @@ export class DeploymentValidator {
|
|
|
85
93
|
console.log('š Loaded validation config from validation-config.json');
|
|
86
94
|
return defaultConfig;
|
|
87
95
|
} catch (error) {
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
// If service config not found, try framework's internal config
|
|
97
|
+
try {
|
|
98
|
+
const frameworkConfigData = readFileSync(FRAMEWORK_CONFIG_PATH, 'utf-8');
|
|
99
|
+
const frameworkConfig = JSON.parse(frameworkConfigData);
|
|
100
|
+
|
|
101
|
+
// Extract relevant parts for validation
|
|
102
|
+
const defaultConfig = {
|
|
103
|
+
requiredCommands: Object.values(frameworkConfig.commands?.required || {}) || ['npx', 'node', 'npm', 'wrangler'],
|
|
104
|
+
requiredFiles: ['package.json', 'wrangler.toml'],
|
|
105
|
+
requiredEnvironmentVars: [],
|
|
106
|
+
networkEndpoints: Object.values(frameworkConfig.networking?.endpoints || {}),
|
|
107
|
+
expectedEndpoints: this.getExpectedEndpoints(),
|
|
108
|
+
// Include timing and other framework config
|
|
109
|
+
timing: frameworkConfig.timing,
|
|
110
|
+
networking: frameworkConfig.networking,
|
|
111
|
+
caching: frameworkConfig.caching,
|
|
112
|
+
environments: frameworkConfig.environments
|
|
113
|
+
};
|
|
114
|
+
console.log('š Loaded validation config from framework defaults');
|
|
115
|
+
return defaultConfig;
|
|
116
|
+
} catch (frameworkError) {
|
|
117
|
+
// If neither exists, use hardcoded defaults
|
|
118
|
+
console.log('š Using minimal validation config (no config files found)');
|
|
119
|
+
return {
|
|
120
|
+
requiredCommands: Object.keys(this.cmdConfig?.config?.requiredCommands || {}) || ['npx', 'node', 'npm', 'wrangler'],
|
|
121
|
+
requiredFiles: ['package.json', 'wrangler.toml'],
|
|
122
|
+
requiredEnvironmentVars: [],
|
|
123
|
+
networkEndpoints: ['https://api.cloudflare.com', 'https://registry.npmjs.org'],
|
|
124
|
+
expectedEndpoints: this.getExpectedEndpoints()
|
|
125
|
+
};
|
|
126
|
+
}
|
|
97
127
|
}
|
|
98
128
|
}
|
|
99
129
|
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment Summary Module
|
|
3
|
+
*
|
|
4
|
+
* Provides reusable deployment success/failure summary reporting.
|
|
5
|
+
* Extracted from enterprise-deployment/master-deploy.js for modularity.
|
|
6
|
+
*
|
|
7
|
+
* @module deployment-summary
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Deployment Summary Manager
|
|
12
|
+
* Handles success summaries, enterprise summaries, and deployment statistics
|
|
13
|
+
*/
|
|
14
|
+
export class DeploymentSummary {
|
|
15
|
+
/**
|
|
16
|
+
* Display standard deployment success summary
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} deploymentState - Deployment state object
|
|
19
|
+
* @param {Object} config - Deployment configuration
|
|
20
|
+
* @param {Object} options - Display options
|
|
21
|
+
* @returns {Promise<void>}
|
|
22
|
+
*/
|
|
23
|
+
static async displaySuccessSummary(deploymentState, config, options = {}) {
|
|
24
|
+
const duration = (Date.now() - deploymentState.startTime.getTime()) / 1000;
|
|
25
|
+
console.log('\nš DEPLOYMENT SUCCESSFUL!');
|
|
26
|
+
console.log('=========================');
|
|
27
|
+
console.log(`\nš Summary:`);
|
|
28
|
+
console.log(` š Deployment ID: ${deploymentState.deploymentId}`);
|
|
29
|
+
console.log(` š Domain: ${config.domain}`);
|
|
30
|
+
console.log(` ā±ļø Duration: ${duration.toFixed(1)}s`);
|
|
31
|
+
console.log(` š Secrets: ${Object.keys(config.secrets.keys).length} deployed`);
|
|
32
|
+
this.displayEndpoints(config);
|
|
33
|
+
this.displayDistributionFiles(config);
|
|
34
|
+
this.displayNextSteps(config);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Display enterprise deployment success summary
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} deploymentState - Deployment state object
|
|
41
|
+
* @param {Object} config - Deployment configuration
|
|
42
|
+
* @param {Object} frameworkPaths - Framework paths configuration
|
|
43
|
+
* @param {Object} enterpriseModules - Enterprise modules
|
|
44
|
+
* @returns {Promise<void>}
|
|
45
|
+
*/
|
|
46
|
+
static async displayEnterpriseSuccessSummary(deploymentState, config, frameworkPaths, enterpriseModules) {
|
|
47
|
+
console.log('\nš ENTERPRISE DEPLOYMENT SUCCESSFUL!');
|
|
48
|
+
console.log('====================================');
|
|
49
|
+
const duration = (new Date() - deploymentState.startTime) / 1000;
|
|
50
|
+
this.displayDeploymentStats(deploymentState, config, duration);
|
|
51
|
+
this.displayEnterpriseEndpoints(config);
|
|
52
|
+
this.displayGeneratedFiles(frameworkPaths, config, deploymentState);
|
|
53
|
+
this.displayEnabledFeatures(config);
|
|
54
|
+
|
|
55
|
+
// End audit session successfully
|
|
56
|
+
if (enterpriseModules?.auditor) {
|
|
57
|
+
enterpriseModules.auditor.endDeploymentAudit(deploymentState.deploymentId, 'success', {
|
|
58
|
+
duration,
|
|
59
|
+
endpoints: 4,
|
|
60
|
+
testsRun: config.deployment.runTests,
|
|
61
|
+
enterpriseFeatures: Object.keys(config.enterprise).filter(k => config.enterprise[k]).length
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
this.displayEnterpriseNextSteps();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Display deployment statistics
|
|
69
|
+
*
|
|
70
|
+
* @param {Object} deploymentState - Deployment state
|
|
71
|
+
* @param {Object} config - Configuration
|
|
72
|
+
* @param {number} duration - Deployment duration in seconds
|
|
73
|
+
*/
|
|
74
|
+
static displayDeploymentStats(deploymentState, config, duration) {
|
|
75
|
+
console.log('\nš Deployment Statistics:');
|
|
76
|
+
console.log(` ā±ļø Duration: ${duration.toFixed(1)} seconds`);
|
|
77
|
+
console.log(` š Deployment ID: ${deploymentState.deploymentId}`);
|
|
78
|
+
console.log(` š Domain: ${config.domain}`);
|
|
79
|
+
console.log(` š Environment: ${config.environment}`);
|
|
80
|
+
console.log(` š± Mode: ${config.deploymentMode}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Display service endpoints
|
|
85
|
+
*
|
|
86
|
+
* @param {Object} config - Configuration
|
|
87
|
+
*/
|
|
88
|
+
static displayEndpoints(config) {
|
|
89
|
+
console.log(`\nš Your service is now live at:`);
|
|
90
|
+
console.log(` ${config.worker.url}/health`);
|
|
91
|
+
console.log(` ${config.worker.url}/auth/magic-link`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Display enterprise endpoints
|
|
96
|
+
*
|
|
97
|
+
* @param {Object} config - Configuration
|
|
98
|
+
*/
|
|
99
|
+
static displayEnterpriseEndpoints(config) {
|
|
100
|
+
console.log('\nš Enterprise Endpoints:');
|
|
101
|
+
console.log(` š Main: ${config.worker.url}`);
|
|
102
|
+
console.log(` ā¤ļø Health: ${config.worker.url}/health`);
|
|
103
|
+
console.log(` š Auth: ${config.worker.url}/auth/magic-link`);
|
|
104
|
+
console.log(` š API: ${config.worker.url}/api`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Display secret distribution files
|
|
109
|
+
*
|
|
110
|
+
* @param {Object} config - Configuration
|
|
111
|
+
*/
|
|
112
|
+
static displayDistributionFiles(config) {
|
|
113
|
+
console.log(`\nš Secret distribution files:`);
|
|
114
|
+
console.log(` secrets/distribution/${config.domain}/.env`);
|
|
115
|
+
console.log(` secrets/distribution/${config.domain}/secrets.json`);
|
|
116
|
+
console.log(` secrets/distribution/${config.domain}/deploy-secrets.sh`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Display generated enterprise files
|
|
121
|
+
*
|
|
122
|
+
* @param {Object} frameworkPaths - Framework paths
|
|
123
|
+
* @param {Object} config - Configuration
|
|
124
|
+
* @param {Object} deploymentState - Deployment state
|
|
125
|
+
*/
|
|
126
|
+
static displayGeneratedFiles(frameworkPaths, config, deploymentState) {
|
|
127
|
+
console.log('\nš Generated Enterprise Files:');
|
|
128
|
+
console.log(` š Audit Log: ${frameworkPaths.auditLogs}/deployments/session-${deploymentState.deploymentId}.log`);
|
|
129
|
+
console.log(` š Report: ${frameworkPaths.auditReports}/deployment-${deploymentState.deploymentId}.html`);
|
|
130
|
+
console.log(` š Secrets: ${frameworkPaths.secureTokens}/distribution/${config.domain}/`);
|
|
131
|
+
console.log(` āļø Config: ${frameworkPaths.configCache}/domains/${config.domain}/`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Display enabled enterprise features
|
|
136
|
+
*
|
|
137
|
+
* @param {Object} config - Configuration
|
|
138
|
+
*/
|
|
139
|
+
static displayEnabledFeatures(config) {
|
|
140
|
+
console.log('\nš Enterprise Features Enabled:');
|
|
141
|
+
console.log(` š Validation: ${config.deployment.validationLevel}`);
|
|
142
|
+
console.log(` š Audit: ${config.deployment.auditLevel}`);
|
|
143
|
+
console.log(` š§Ŗ Testing: ${config.deployment.runTests ? 'Comprehensive' : 'Disabled'}`);
|
|
144
|
+
console.log(` š Rollback: ${config.deployment.enableRollback ? 'Available' : 'Disabled'}`);
|
|
145
|
+
console.log(` š¾ Caching: Smart configuration caching enabled`);
|
|
146
|
+
console.log(` šÆ Orchestration: Enterprise orchestration active`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Display next steps
|
|
151
|
+
*
|
|
152
|
+
* @param {Object} config - Configuration
|
|
153
|
+
*/
|
|
154
|
+
static displayNextSteps(config) {
|
|
155
|
+
console.log('\nš Next steps:');
|
|
156
|
+
console.log(' 1. Test the endpoints above');
|
|
157
|
+
console.log(' 2. Distribute secrets to upstream/downstream applications');
|
|
158
|
+
console.log(' 3. Configure DNS if using custom domain');
|
|
159
|
+
console.log(' 4. Update your applications to use the new service');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Display enterprise next steps
|
|
164
|
+
*/
|
|
165
|
+
static displayEnterpriseNextSteps() {
|
|
166
|
+
console.log('\nā
Enterprise deployment completed successfully!');
|
|
167
|
+
console.log(' Visit the health endpoint to verify deployment');
|
|
168
|
+
console.log(' Check audit logs for detailed deployment history');
|
|
169
|
+
console.log(' Use enterprise modules for ongoing management');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Display failure summary
|
|
174
|
+
*
|
|
175
|
+
* @param {Error} error - Deployment error
|
|
176
|
+
* @param {Object} deploymentState - Deployment state
|
|
177
|
+
* @param {Object} config - Configuration
|
|
178
|
+
*/
|
|
179
|
+
static displayFailureSummary(error, deploymentState, config) {
|
|
180
|
+
const duration = (Date.now() - deploymentState.startTime.getTime()) / 1000;
|
|
181
|
+
console.log('\nā DEPLOYMENT FAILED');
|
|
182
|
+
console.log('===================');
|
|
183
|
+
console.log(`\nš Summary:`);
|
|
184
|
+
console.log(` š Deployment ID: ${deploymentState.deploymentId}`);
|
|
185
|
+
console.log(` š Domain: ${config.domain}`);
|
|
186
|
+
console.log(` ā±ļø Duration: ${duration.toFixed(1)}s`);
|
|
187
|
+
console.log(` ā Error: ${error.message}`);
|
|
188
|
+
if (deploymentState.currentPhase) {
|
|
189
|
+
console.log(` š Failed at: ${deploymentState.currentPhase}`);
|
|
190
|
+
}
|
|
191
|
+
console.log('\nš Rollback options:');
|
|
192
|
+
console.log(` ${deploymentState.rollbackActions.length} rollback actions available`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get deployment summary statistics
|
|
197
|
+
*
|
|
198
|
+
* @param {Object} deploymentState - Deployment state
|
|
199
|
+
* @param {Object} config - Configuration
|
|
200
|
+
* @returns {Object} Summary statistics
|
|
201
|
+
*/
|
|
202
|
+
static getSummaryStats(deploymentState, config) {
|
|
203
|
+
const duration = (Date.now() - deploymentState.startTime.getTime()) / 1000;
|
|
204
|
+
return {
|
|
205
|
+
deploymentId: deploymentState.deploymentId,
|
|
206
|
+
domain: config.domain,
|
|
207
|
+
environment: config.environment,
|
|
208
|
+
duration: duration.toFixed(1),
|
|
209
|
+
secretsDeployed: Object.keys(config.secrets.keys).length,
|
|
210
|
+
currentPhase: deploymentState.currentPhase,
|
|
211
|
+
rollbackActionsAvailable: deploymentState.rollbackActions.length
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Confirmation Module
|
|
3
|
+
*
|
|
4
|
+
* Provides reusable interactive confirmation workflows.
|
|
5
|
+
* Extracted from enterprise-deployment/master-deploy.js for modularity.
|
|
6
|
+
*
|
|
7
|
+
* @module interactive-confirmation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { askYesNo } from '../utils/interactive-prompts.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Interactive Confirmation Manager
|
|
14
|
+
* Handles deployment confirmations and configuration reviews
|
|
15
|
+
*/
|
|
16
|
+
export class InteractiveConfirmation {
|
|
17
|
+
/**
|
|
18
|
+
* Display final deployment confirmation
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} config - Deployment configuration
|
|
21
|
+
* @param {Object} deploymentState - Deployment state
|
|
22
|
+
* @param {Object} options - Confirmation options
|
|
23
|
+
* @returns {Promise<boolean>} True if confirmed, throws if cancelled
|
|
24
|
+
*/
|
|
25
|
+
static async showFinalConfirmation(config, deploymentState, options = {}) {
|
|
26
|
+
console.log('\nšÆ Final Deployment Confirmation');
|
|
27
|
+
console.log('===============================');
|
|
28
|
+
this.displayDeploymentSummary(config, deploymentState);
|
|
29
|
+
this.displayActionsToPerform(config);
|
|
30
|
+
const finalConfirm = await askYesNo('\nšØ PROCEED WITH DEPLOYMENT? This will make changes to your Cloudflare account.', options.defaultAnswer || 'n');
|
|
31
|
+
if (!finalConfirm) {
|
|
32
|
+
throw new Error('Deployment cancelled by user');
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Display deployment summary
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} config - Configuration
|
|
41
|
+
* @param {Object} deploymentState - Deployment state
|
|
42
|
+
*/
|
|
43
|
+
static displayDeploymentSummary(config, deploymentState) {
|
|
44
|
+
console.log('\nš DEPLOYMENT SUMMARY');
|
|
45
|
+
console.log('=====================');
|
|
46
|
+
console.log(`š Domain: ${config.domain}`);
|
|
47
|
+
console.log(`š Environment: ${config.environment}`);
|
|
48
|
+
console.log(`ā” Worker: ${config.worker.name}`);
|
|
49
|
+
console.log(`š URL: ${config.worker.url}`);
|
|
50
|
+
console.log(`šļø Database: ${config.database.name} (${config.database.id})`);
|
|
51
|
+
console.log(`š Secrets: ${Object.keys(config.secrets.keys).length} configured`);
|
|
52
|
+
console.log(`š Deployment ID: ${deploymentState.deploymentId}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Display actions that will be performed
|
|
57
|
+
*
|
|
58
|
+
* @param {Object} config - Configuration
|
|
59
|
+
*/
|
|
60
|
+
static displayActionsToPerform(config) {
|
|
61
|
+
console.log('\nš ACTIONS TO PERFORM:');
|
|
62
|
+
console.log('1. Update wrangler.toml configuration');
|
|
63
|
+
console.log('2. Run database migrations');
|
|
64
|
+
console.log('3. Deploy Cloudflare Worker');
|
|
65
|
+
console.log('4. Verify deployment health');
|
|
66
|
+
if (config.deployment.runTests) {
|
|
67
|
+
console.log('5. Run integration tests');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Display and confirm configuration review
|
|
73
|
+
*
|
|
74
|
+
* @param {Object} config - Configuration to review
|
|
75
|
+
* @param {Object} deploymentState - Deployment state
|
|
76
|
+
* @param {Function} reconfigureCallback - Callback to reconfigure if rejected
|
|
77
|
+
* @returns {Promise<boolean>} True if confirmed
|
|
78
|
+
*/
|
|
79
|
+
static async showConfigurationReview(config, deploymentState, reconfigureCallback) {
|
|
80
|
+
console.log('\nš Configuration Review');
|
|
81
|
+
console.log('======================');
|
|
82
|
+
console.log('\nPlease review your configuration:');
|
|
83
|
+
console.log(` š Domain: ${config.domain}`);
|
|
84
|
+
console.log(` š Environment: ${config.environment}`);
|
|
85
|
+
console.log(` ā” Worker: ${config.worker.name}`);
|
|
86
|
+
console.log(` š URL: ${config.worker.url}`);
|
|
87
|
+
console.log(` š Deployment ID: ${deploymentState.deploymentId}`);
|
|
88
|
+
const confirmed = await askYesNo('\nIs this configuration correct?', 'y');
|
|
89
|
+
if (!confirmed) {
|
|
90
|
+
if (reconfigureCallback && typeof reconfigureCallback === 'function') {
|
|
91
|
+
console.log('\nš Let\'s reconfigure...');
|
|
92
|
+
await reconfigureCallback();
|
|
93
|
+
return this.showConfigurationReview(config, deploymentState, reconfigureCallback);
|
|
94
|
+
} else {
|
|
95
|
+
throw new Error('Configuration rejected by user');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Show enterprise final confirmation with additional details
|
|
103
|
+
*
|
|
104
|
+
* @param {Object} config - Deployment configuration
|
|
105
|
+
* @param {Object} deploymentState - Deployment state
|
|
106
|
+
* @param {Object} enterpriseModules - Enterprise modules
|
|
107
|
+
* @returns {Promise<boolean>} True if confirmed
|
|
108
|
+
*/
|
|
109
|
+
static async showEnterpriseFinalConfirmation(config, deploymentState, enterpriseModules) {
|
|
110
|
+
console.log('\nšÆ Enterprise Final Confirmation');
|
|
111
|
+
console.log('================================');
|
|
112
|
+
this.displayEnterpriseDeploymentSummary(config, deploymentState);
|
|
113
|
+
this.displayEnterpriseActionsToPerform(config, enterpriseModules);
|
|
114
|
+
const finalConfirm = await askYesNo('\nšØ PROCEED WITH ENTERPRISE DEPLOYMENT?', 'n');
|
|
115
|
+
if (!finalConfirm) {
|
|
116
|
+
throw new Error('Enterprise deployment cancelled by user');
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Display enterprise deployment summary
|
|
123
|
+
*
|
|
124
|
+
* @param {Object} config - Configuration
|
|
125
|
+
* @param {Object} deploymentState - Deployment state
|
|
126
|
+
*/
|
|
127
|
+
static displayEnterpriseDeploymentSummary(config, deploymentState) {
|
|
128
|
+
console.log('\nš ENTERPRISE DEPLOYMENT SUMMARY');
|
|
129
|
+
console.log('================================');
|
|
130
|
+
console.log(`š Domain: ${config.domain}`);
|
|
131
|
+
console.log(`š Environment: ${config.environment}`);
|
|
132
|
+
console.log(`š± Mode: ${config.deploymentMode}`);
|
|
133
|
+
console.log(`ā” Worker: ${config.worker.name}`);
|
|
134
|
+
console.log(`š URL: ${config.worker.url}`);
|
|
135
|
+
console.log(`šļø Database: ${config.database.name} (${config.database.id})`);
|
|
136
|
+
console.log(`š Secrets: ${Object.keys(config.secrets.keys).length} configured`);
|
|
137
|
+
console.log(`š Deployment ID: ${deploymentState.deploymentId}`);
|
|
138
|
+
console.log(`š Validation: ${config.deployment.validationLevel}`);
|
|
139
|
+
console.log(`š Audit: ${config.deployment.auditLevel}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Display enterprise actions
|
|
144
|
+
*
|
|
145
|
+
* @param {Object} config - Configuration
|
|
146
|
+
* @param {Object} enterpriseModules - Enterprise modules
|
|
147
|
+
*/
|
|
148
|
+
static displayEnterpriseActionsToPerform(config, enterpriseModules) {
|
|
149
|
+
console.log('\nš ENTERPRISE ACTIONS TO PERFORM:');
|
|
150
|
+
console.log('1. Comprehensive validation');
|
|
151
|
+
console.log('2. Database orchestration');
|
|
152
|
+
console.log('3. Enterprise secret management');
|
|
153
|
+
console.log('4. Configuration management');
|
|
154
|
+
console.log('5. Cloudflare Worker deployment with D1 recovery');
|
|
155
|
+
console.log('6. Deployment verification');
|
|
156
|
+
if (config.deployment.runTests) {
|
|
157
|
+
console.log('7. Comprehensive integration tests');
|
|
158
|
+
}
|
|
159
|
+
console.log(`8. Audit logging and reporting`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Quick yes/no confirmation
|
|
164
|
+
*
|
|
165
|
+
* @param {string} message - Confirmation message
|
|
166
|
+
* @param {string} defaultAnswer - Default answer ('y' or 'n')
|
|
167
|
+
* @returns {Promise<boolean>} Confirmation result
|
|
168
|
+
*/
|
|
169
|
+
static async quickConfirm(message, defaultAnswer = 'y') {
|
|
170
|
+
return await askYesNo(message, defaultAnswer);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Confirm dangerous action with warning
|
|
175
|
+
*
|
|
176
|
+
* @param {string} action - Action description
|
|
177
|
+
* @param {string} warning - Warning message
|
|
178
|
+
* @returns {Promise<boolean>} Confirmation result
|
|
179
|
+
*/
|
|
180
|
+
static async confirmDangerousAction(action, warning) {
|
|
181
|
+
console.log(`\nā ļø WARNING: ${warning}`);
|
|
182
|
+
const confirm = await askYesNo(`Are you sure you want to ${action}?`, 'n');
|
|
183
|
+
if (!confirm) {
|
|
184
|
+
throw new Error(`Dangerous action cancelled: ${action}`);
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
}
|