@tamyla/clodo-framework 4.0.12 → 4.0.14
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 +708 -426
- package/README.md +7 -0
- package/dist/cli/commands/create.js +2 -1
- package/dist/deployment/wrangler-deployer.js +1 -1
- package/dist/middleware/Composer.js +38 -0
- package/dist/middleware/Registry.js +14 -0
- package/dist/middleware/index.js +3 -0
- package/dist/middleware/shared/basicAuth.js +21 -0
- package/dist/middleware/shared/cors.js +28 -0
- package/dist/middleware/shared/index.js +3 -0
- package/dist/middleware/shared/logging.js +14 -0
- package/dist/service-management/GenerationEngine.js +13 -2
- package/dist/service-management/ServiceOrchestrator.js +6 -2
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +156 -10
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +75 -9
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +1 -1
- package/dist/simple-api.js +32 -1
- package/docs/MIDDLEWARE_MIGRATION_SUMMARY.md +121 -0
- package/package.json +7 -2
- package/scripts/DEPLOY_COMMAND_NEW.js +128 -0
- package/scripts/README-automated-testing-suite.md +356 -0
- package/scripts/README-test-clodo-deployment.md +157 -0
- package/scripts/README.md +50 -0
- package/scripts/analyze-imports.ps1 +104 -0
- package/scripts/analyze-mixed-code.js +163 -0
- package/scripts/analyze-mixed-rationale.js +149 -0
- package/scripts/automated-testing-suite.js +776 -0
- package/scripts/deployment/README.md +31 -0
- package/scripts/deployment/deploy-domain.ps1 +449 -0
- package/scripts/deployment/deploy-staging.js +120 -0
- package/scripts/deployment/validate-staging.js +166 -0
- package/scripts/diagnose-imports.js +362 -0
- package/scripts/framework-diagnostic.js +368 -0
- package/scripts/migration/migrate-middleware-legacy-to-contract.js +47 -0
- package/scripts/post-publish-test.js +663 -0
- package/scripts/scan-worker-issues.js +52 -0
- package/scripts/service-management/README.md +27 -0
- package/scripts/service-management/setup-interactive.ps1 +693 -0
- package/scripts/test-clodo-deployment.js +588 -0
- package/scripts/test-downstream-install.js +237 -0
- package/scripts/test-local-package.ps1 +126 -0
- package/scripts/test-local-package.sh +166 -0
- package/scripts/test-package.js +339 -0
- package/scripts/testing/README.md +49 -0
- package/scripts/testing/test-first.ps1 +0 -0
- package/scripts/testing/test-first50.ps1 +0 -0
- package/scripts/testing/test.ps1 +0 -0
- package/scripts/utilities/README.md +61 -0
- package/scripts/utilities/check-bin.js +8 -0
- package/scripts/utilities/check-bundle.js +23 -0
- package/scripts/utilities/check-dist-imports.js +65 -0
- package/scripts/utilities/check-import-paths.js +191 -0
- package/scripts/utilities/cleanup-cli.js +159 -0
- package/scripts/utilities/deployment-helpers.ps1 +199 -0
- package/scripts/utilities/fix-dist-imports.js +135 -0
- package/scripts/utilities/generate-secrets.js +159 -0
- package/scripts/utilities/safe-push.ps1 +51 -0
- package/scripts/utilities/setup-helpers.ps1 +206 -0
- package/scripts/utilities/test-packaged-artifact.js +92 -0
- package/scripts/utilities/validate-dist-imports.js +189 -0
- package/scripts/utilities/validate-schema.js +102 -0
- package/scripts/verify-exports.js +193 -0
- package/scripts/verify-worker-safety.js +73 -0
- package/types/middleware.d.ts +1 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Check Import Paths Validator
|
|
5
|
+
*
|
|
6
|
+
* Validates that all re-export wrappers AND dynamic imports have correct paths that work
|
|
7
|
+
* when compiled to dist/. This prevents module resolution errors in published packages.
|
|
8
|
+
*
|
|
9
|
+
* Purpose: Catch path issues BEFORE publication to npm
|
|
10
|
+
*
|
|
11
|
+
* Checks:
|
|
12
|
+
* 1. Static imports in re-export wrappers (src/utils/)
|
|
13
|
+
* 2. Dynamic imports in bin/ files (must NOT reference dist/)
|
|
14
|
+
* 3. Compiled dist/ structure integrity
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
|
|
21
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const projectRoot = path.join(__dirname, '../../');
|
|
23
|
+
|
|
24
|
+
const RULES = {
|
|
25
|
+
// Re-export wrappers in src/utils/ compile to dist/utils/ and need paths that work in npm packages
|
|
26
|
+
// When installed via npm, only dist/ exists at package root level
|
|
27
|
+
// So paths must resolve correctly: dist/utils/X.js -> ../lib/ (one level up to dist, then lib)
|
|
28
|
+
|
|
29
|
+
// Files at src/utils/X.js compile to dist/utils/X.js (2 levels deep) use '../lib/...'
|
|
30
|
+
'src/utils/file-manager.js': {
|
|
31
|
+
pattern: /from\s+['"]([^'"]+)['"]/,
|
|
32
|
+
shouldContain: '../lib/shared/utils/file-manager.js',
|
|
33
|
+
description: 'file-manager re-export wrapper (compiles with depth adjustment for npm)'
|
|
34
|
+
},
|
|
35
|
+
'src/utils/formatters.js': {
|
|
36
|
+
pattern: /from\s+['"]([^'"]+)['"]/,
|
|
37
|
+
shouldContain: '../lib/shared/utils/formatters.js',
|
|
38
|
+
description: 'formatters re-export wrapper (compiles with depth adjustment for npm)'
|
|
39
|
+
},
|
|
40
|
+
'src/utils/logger.js': {
|
|
41
|
+
pattern: /from\s+['"]([^'"]+)['"]/,
|
|
42
|
+
shouldContain: '../lib/shared/logging/Logger.js',
|
|
43
|
+
description: 'logger re-export wrapper (compiles with depth adjustment for npm)'
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Files at src/utils/cloudflare/X.js compile to dist/utils/cloudflare/X.js (3 levels deep) use '../../lib/...'
|
|
47
|
+
'src/utils/cloudflare/ops.js': {
|
|
48
|
+
pattern: /from\s+['"]([^'"]+)['"]/,
|
|
49
|
+
shouldContain: '../../lib/shared/cloudflare/ops.js',
|
|
50
|
+
description: 'cloudflare ops re-export wrapper (3 levels, compiles with depth adjustment)'
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Check for problematic dynamic imports that reference dist/ from bin/
|
|
55
|
+
const DYNAMIC_IMPORT_CHECKS = {
|
|
56
|
+
'lib/shared/cloudflare/ops.js': {
|
|
57
|
+
pattern: /import\(['"]([^'"]*dist[^'"]*)['"]\)/g,
|
|
58
|
+
shouldNotContain: 'dist/',
|
|
59
|
+
description: 'Dynamic imports in lib/ must not reference dist/ (causes dist/dist/ bug)'
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
let passCount = 0;
|
|
64
|
+
let failCount = 0;
|
|
65
|
+
const failures = [];
|
|
66
|
+
|
|
67
|
+
console.log('\n🔍 Checking import paths in re-export wrappers...\n');
|
|
68
|
+
|
|
69
|
+
for (const [filePath, rule] of Object.entries(RULES)) {
|
|
70
|
+
const fullPath = path.join(projectRoot, filePath);
|
|
71
|
+
|
|
72
|
+
if (!fs.existsSync(fullPath)) {
|
|
73
|
+
console.log(`❌ MISSING: ${filePath}`);
|
|
74
|
+
failCount++;
|
|
75
|
+
failures.push(`File not found: ${filePath}`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
80
|
+
const match = content.match(rule.pattern);
|
|
81
|
+
|
|
82
|
+
if (!match || match[1] !== rule.shouldContain) {
|
|
83
|
+
const actual = match ? match[1] : 'NOT FOUND';
|
|
84
|
+
console.log(`❌ INCORRECT PATH: ${filePath}`);
|
|
85
|
+
console.log(` Expected: ${rule.shouldContain}`);
|
|
86
|
+
console.log(` Actual: ${actual}`);
|
|
87
|
+
console.log(` (${rule.description})\n`);
|
|
88
|
+
failCount++;
|
|
89
|
+
failures.push(`${filePath}: expected "${rule.shouldContain}", got "${actual}"`);
|
|
90
|
+
} else {
|
|
91
|
+
console.log(`✅ CORRECT: ${filePath}`);
|
|
92
|
+
console.log(` → ${rule.shouldContain}\n`);
|
|
93
|
+
passCount++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check for problematic dynamic imports
|
|
98
|
+
console.log('\n🔍 Checking dynamic imports in bin/ files...\n');
|
|
99
|
+
|
|
100
|
+
for (const [filePath, rule] of Object.entries(DYNAMIC_IMPORT_CHECKS)) {
|
|
101
|
+
const fullPath = path.join(projectRoot, filePath);
|
|
102
|
+
|
|
103
|
+
if (!fs.existsSync(fullPath)) {
|
|
104
|
+
console.log(`⚠️ SKIPPED: ${filePath} (file not found)`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
109
|
+
const matches = [...content.matchAll(rule.pattern)];
|
|
110
|
+
|
|
111
|
+
if (matches.length > 0) {
|
|
112
|
+
console.log(`❌ PROBLEMATIC IMPORTS: ${filePath}`);
|
|
113
|
+
matches.forEach(match => {
|
|
114
|
+
console.log(` → ${match[1]}`);
|
|
115
|
+
});
|
|
116
|
+
console.log(` Issue: ${rule.description}\n`);
|
|
117
|
+
failCount += matches.length;
|
|
118
|
+
failures.push(`${filePath}: contains ${matches.length} dynamic import(s) referencing dist/`);
|
|
119
|
+
} else {
|
|
120
|
+
console.log(`✅ NO DIST/ IMPORTS: ${filePath}\n`);
|
|
121
|
+
passCount++;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Check compiled dist paths
|
|
126
|
+
console.log('\n📦 Verifying compiled dist/ structure...\n');
|
|
127
|
+
|
|
128
|
+
const distFiles = [
|
|
129
|
+
'dist/utils/file-manager.js',
|
|
130
|
+
'dist/utils/formatters.js',
|
|
131
|
+
'dist/utils/logger.js',
|
|
132
|
+
'dist/utils/cloudflare/ops.js'
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
for (const file of distFiles) {
|
|
136
|
+
const fullPath = path.join(projectRoot, file);
|
|
137
|
+
|
|
138
|
+
if (!fs.existsSync(fullPath)) {
|
|
139
|
+
console.log(`❌ MISSING: ${file}`);
|
|
140
|
+
failCount++;
|
|
141
|
+
failures.push(`Compiled file not found: ${file}`);
|
|
142
|
+
} else {
|
|
143
|
+
console.log(`✅ EXISTS: ${file}`);
|
|
144
|
+
passCount++;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check dist/cli/ and dist/lib/ exist
|
|
149
|
+
console.log('\n📂 Verifying cli/ and lib/ were compiled to dist/...\n');
|
|
150
|
+
|
|
151
|
+
const cliPath = path.join(projectRoot, 'dist/cli');
|
|
152
|
+
const libPath = path.join(projectRoot, 'dist/lib');
|
|
153
|
+
|
|
154
|
+
if (!fs.existsSync(cliPath)) {
|
|
155
|
+
console.log(`❌ MISSING: dist/cli/ directory`);
|
|
156
|
+
failCount++;
|
|
157
|
+
failures.push('dist/cli/ directory not found - cli/ not compiled');
|
|
158
|
+
} else {
|
|
159
|
+
console.log(`✅ EXISTS: dist/cli/ directory`);
|
|
160
|
+
passCount++;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!fs.existsSync(libPath)) {
|
|
164
|
+
console.log(`❌ MISSING: dist/lib/ directory`);
|
|
165
|
+
failCount++;
|
|
166
|
+
failures.push('dist/lib/ directory not found - lib/ not compiled');
|
|
167
|
+
} else {
|
|
168
|
+
console.log(`✅ EXISTS: dist/lib/ directory\n`);
|
|
169
|
+
passCount++;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Summary
|
|
173
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
174
|
+
console.log(`Validation Results: ${passCount} passed, ${failCount} failed`);
|
|
175
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
176
|
+
|
|
177
|
+
if (failCount > 0) {
|
|
178
|
+
console.log('❌ PATH VALIDATION FAILED\n');
|
|
179
|
+
console.log('Issues found:');
|
|
180
|
+
failures.forEach(f => console.log(` • ${f}`));
|
|
181
|
+
console.log('\nRecommendations:');
|
|
182
|
+
console.log('1. Ensure all src/utils/*.js re-export wrappers use "../../bin/shared/..." paths');
|
|
183
|
+
console.log('2. These paths must work AFTER Babel compilation to dist/');
|
|
184
|
+
console.log('3. Run: npm run build && npm run check:imports\n');
|
|
185
|
+
process.exit(1);
|
|
186
|
+
} else {
|
|
187
|
+
console.log('✅ ALL PATHS VALID\n');
|
|
188
|
+
console.log('The re-export wrappers are correctly configured for the npm package.');
|
|
189
|
+
console.log('Users should not encounter ERR_MODULE_NOT_FOUND errors.\n');
|
|
190
|
+
process.exit(0);
|
|
191
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Data Cleanup CLI Tool
|
|
5
|
+
* Provides command-line access to data cleanup operations
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { initD1Client } from '../../src/shared/clients/d1/index.js';
|
|
9
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
|
|
12
|
+
// Load environment variables
|
|
13
|
+
const env = {
|
|
14
|
+
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
15
|
+
...process.env
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Initialize D1 client
|
|
19
|
+
let d1Client;
|
|
20
|
+
try {
|
|
21
|
+
// For local development, we need to handle the database binding
|
|
22
|
+
if (env.NODE_ENV === 'development') {
|
|
23
|
+
// Create a mock database binding for development
|
|
24
|
+
env.DB = {}; // This will trigger in-memory database creation
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
d1Client = await initD1Client(env);
|
|
28
|
+
|
|
29
|
+
// Wait for the client to be fully initialized
|
|
30
|
+
await new Promise((resolve) => {
|
|
31
|
+
const checkInit = () => {
|
|
32
|
+
if (d1Client._isInitialized || d1Client.db) {
|
|
33
|
+
resolve();
|
|
34
|
+
} else {
|
|
35
|
+
setTimeout(checkInit, 10);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
checkInit();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('Failed to initialize D1 client:', error.message);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const command = process.argv[2];
|
|
47
|
+
const args = process.argv.slice(3);
|
|
48
|
+
|
|
49
|
+
async function main() {
|
|
50
|
+
try {
|
|
51
|
+
switch (command) {
|
|
52
|
+
case 'stats': {
|
|
53
|
+
console.log('📊 Getting database statistics...');
|
|
54
|
+
const stats = await d1Client.getDatabaseStats();
|
|
55
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case 'backup': {
|
|
60
|
+
console.log('💾 Creating database backup...');
|
|
61
|
+
const backup = await d1Client.createBackup({ reason: args[0] || 'CLI backup' });
|
|
62
|
+
|
|
63
|
+
const filename = `backup_${backup.id}.sql`;
|
|
64
|
+
writeFileSync(filename, backup.content);
|
|
65
|
+
console.log(`✅ Backup saved to: ${filename}`);
|
|
66
|
+
console.log(`📊 Records backed up: ${backup.totalRecords}`);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
case 'clear-old': {
|
|
71
|
+
const days = parseInt(args[0]);
|
|
72
|
+
if (!days || days < 1) {
|
|
73
|
+
console.error('❌ Please specify number of days (e.g., clear-old 90)');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(`🗑️ Clearing data older than ${days} days...`);
|
|
78
|
+
const oldResult = await d1Client.safeCleanup('CLEAR_OLD', { daysOld: days });
|
|
79
|
+
console.log('✅ Old data cleared:', JSON.stringify(oldResult, null, 2));
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
case 'clear-user': {
|
|
84
|
+
const userId = args[0];
|
|
85
|
+
if (!userId) {
|
|
86
|
+
console.error('❌ Please specify user ID (e.g., clear-user user123)');
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log(`🗑️ Clearing data for user: ${userId}`);
|
|
91
|
+
const userResult = await d1Client.safeCleanup('CLEAR_USER', { userId });
|
|
92
|
+
console.log('✅ User data cleared:', JSON.stringify(userResult, null, 2));
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
case 'clear-all': {
|
|
97
|
+
if (env.NODE_ENV === 'production') {
|
|
98
|
+
console.error('❌ CLEAR ALL operation blocked in production environment');
|
|
99
|
+
console.log('💡 Use selective cleanup operations instead');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log('🗑️ Clearing ALL data (this will delete everything!)...');
|
|
104
|
+
const confirm = args.includes('--confirm');
|
|
105
|
+
if (!confirm) {
|
|
106
|
+
console.log('⚠️ This operation will delete ALL data permanently');
|
|
107
|
+
console.log('💡 Add --confirm flag to proceed');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const allResult = await d1Client.safeCleanup('CLEAR_ALL');
|
|
112
|
+
console.log('✅ All data cleared:', JSON.stringify(allResult, null, 2));
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case 'validate': {
|
|
117
|
+
const operation = args[0];
|
|
118
|
+
const params = {};
|
|
119
|
+
|
|
120
|
+
if (operation === 'CLEAR_OLD') params.daysOld = parseInt(args[1]);
|
|
121
|
+
if (operation === 'CLEAR_USER') params.userId = args[1];
|
|
122
|
+
|
|
123
|
+
console.log(`🔍 Validating operation: ${operation}`);
|
|
124
|
+
const validation = await d1Client.validateCleanupOperation(operation, params);
|
|
125
|
+
console.log(JSON.stringify(validation, null, 2));
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
default:
|
|
130
|
+
console.log('🧹 Data Cleanup CLI Tool');
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log('Usage: node scripts/cleanup-cli.js <command> [args...]');
|
|
133
|
+
console.log('');
|
|
134
|
+
console.log('Commands:');
|
|
135
|
+
console.log(' stats Show database statistics');
|
|
136
|
+
console.log(' backup [reason] Create database backup');
|
|
137
|
+
console.log(' clear-old <days> Clear data older than X days');
|
|
138
|
+
console.log(' clear-user <userId> Clear all data for a user');
|
|
139
|
+
console.log(' clear-all --confirm Clear ALL data (dangerous!)');
|
|
140
|
+
console.log(' validate <op> [params] Validate if operation is safe');
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log('Examples:');
|
|
143
|
+
console.log(' node scripts/cleanup-cli.js stats');
|
|
144
|
+
console.log(' node scripts/cleanup-cli.js backup "Pre-deployment backup"');
|
|
145
|
+
console.log(' node scripts/cleanup-cli.js clear-old 90');
|
|
146
|
+
console.log(' node scripts/cleanup-cli.js clear-user user123');
|
|
147
|
+
console.log(' node scripts/cleanup-cli.js validate CLEAR_OLD 90');
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error('❌ Error:', error.message);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
main().catch(error => {
|
|
157
|
+
console.error('❌ Unexpected error:', error);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
});
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# PowerShell Deployment Helper Functions
|
|
2
|
+
# Common utilities for Clodo Framework deployment scripts
|
|
3
|
+
|
|
4
|
+
function Write-DeploymentLog {
|
|
5
|
+
param(
|
|
6
|
+
[Parameter(Mandatory=$true)]
|
|
7
|
+
[string]$Message,
|
|
8
|
+
|
|
9
|
+
[Parameter(Mandatory=$false)]
|
|
10
|
+
[ValidateSet("Info", "Warning", "Error", "Success")]
|
|
11
|
+
[string]$Level = "Info"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
15
|
+
$color = switch ($Level) {
|
|
16
|
+
"Info" { "White" }
|
|
17
|
+
"Warning" { "Yellow" }
|
|
18
|
+
"Error" { "Red" }
|
|
19
|
+
"Success" { "Green" }
|
|
20
|
+
default { "White" }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Write-Host "[$timestamp] $Level`: $Message" -ForegroundColor $color
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function Test-Prerequisites {
|
|
27
|
+
param(
|
|
28
|
+
[Parameter(Mandatory=$false)]
|
|
29
|
+
[string[]]$RequiredCommands = @("node", "npm", "wrangler")
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
Write-DeploymentLog "Checking prerequisites..." "Info"
|
|
33
|
+
|
|
34
|
+
$allPresent = $true
|
|
35
|
+
foreach ($cmd in $RequiredCommands) {
|
|
36
|
+
try {
|
|
37
|
+
$null = Get-Command $cmd -ErrorAction Stop
|
|
38
|
+
Write-DeploymentLog "✓ $cmd is available" "Success"
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
Write-DeploymentLog "✗ $cmd is not available" "Error"
|
|
42
|
+
$allPresent = $false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return $allPresent
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function Test-EnvironmentVariables {
|
|
50
|
+
param(
|
|
51
|
+
[Parameter(Mandatory=$true)]
|
|
52
|
+
[string[]]$RequiredVars
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
Write-DeploymentLog "Checking environment variables..." "Info"
|
|
56
|
+
|
|
57
|
+
$allPresent = $true
|
|
58
|
+
foreach ($var in $RequiredVars) {
|
|
59
|
+
$value = [System.Environment]::GetEnvironmentVariable($var)
|
|
60
|
+
if ([string]::IsNullOrEmpty($value)) {
|
|
61
|
+
Write-DeploymentLog "✗ Environment variable $var is not set" "Error"
|
|
62
|
+
$allPresent = $false
|
|
63
|
+
} else {
|
|
64
|
+
Write-DeploymentLog "✓ $var is set" "Success"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return $allPresent
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function Invoke-SafeCommand {
|
|
72
|
+
param(
|
|
73
|
+
[Parameter(Mandatory=$true)]
|
|
74
|
+
[string]$Command,
|
|
75
|
+
|
|
76
|
+
[Parameter(Mandatory=$false)]
|
|
77
|
+
[string]$WorkingDirectory = $PWD,
|
|
78
|
+
|
|
79
|
+
[Parameter(Mandatory=$false)]
|
|
80
|
+
[switch]$DryRun
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if ($DryRun) {
|
|
84
|
+
Write-DeploymentLog "[DRY RUN] Would execute: $Command" "Warning"
|
|
85
|
+
return @{
|
|
86
|
+
Success = $true
|
|
87
|
+
Output = "[DRY RUN] Command not executed"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
Write-DeploymentLog "Executing: $Command" "Info"
|
|
93
|
+
$output = Invoke-Expression $Command -ErrorAction Stop
|
|
94
|
+
Write-DeploymentLog "Command completed successfully" "Success"
|
|
95
|
+
|
|
96
|
+
return @{
|
|
97
|
+
Success = $true
|
|
98
|
+
Output = $output
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
Write-DeploymentLog "Command failed: $($_.Exception.Message)" "Error"
|
|
103
|
+
|
|
104
|
+
return @{
|
|
105
|
+
Success = $false
|
|
106
|
+
Error = $_.Exception.Message
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function Test-FileExists {
|
|
112
|
+
param(
|
|
113
|
+
[Parameter(Mandatory=$true)]
|
|
114
|
+
[string]$FilePath,
|
|
115
|
+
|
|
116
|
+
[Parameter(Mandatory=$false)]
|
|
117
|
+
[string]$Description = "file"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if (Test-Path $FilePath) {
|
|
121
|
+
Write-DeploymentLog "✓ $Description exists: $FilePath" "Success"
|
|
122
|
+
return $true
|
|
123
|
+
} else {
|
|
124
|
+
Write-DeploymentLog "✗ $Description not found: $FilePath" "Error"
|
|
125
|
+
return $false
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function Get-ServiceConfiguration {
|
|
130
|
+
param(
|
|
131
|
+
[Parameter(Mandatory=$true)]
|
|
132
|
+
[string]$ConfigPath
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
Write-DeploymentLog "Loading service configuration from: $ConfigPath" "Info"
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
if (Test-Path $ConfigPath) {
|
|
139
|
+
$content = Get-Content $ConfigPath -Raw
|
|
140
|
+
Write-DeploymentLog "Configuration loaded successfully" "Success"
|
|
141
|
+
return $content
|
|
142
|
+
} else {
|
|
143
|
+
throw "Configuration file not found: $ConfigPath"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
Write-DeploymentLog "Failed to load configuration: $($_.Exception.Message)" "Error"
|
|
148
|
+
throw
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function Start-DeploymentProcess {
|
|
153
|
+
param(
|
|
154
|
+
[Parameter(Mandatory=$true)]
|
|
155
|
+
[string]$ServiceName,
|
|
156
|
+
|
|
157
|
+
[Parameter(Mandatory=$true)]
|
|
158
|
+
[string]$Environment,
|
|
159
|
+
|
|
160
|
+
[Parameter(Mandatory=$false)]
|
|
161
|
+
[switch]$DryRun
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
$startTime = Get-Date
|
|
165
|
+
Write-DeploymentLog "Starting deployment for $ServiceName ($Environment)" "Info"
|
|
166
|
+
Write-DeploymentLog "Deployment started at: $startTime" "Info"
|
|
167
|
+
|
|
168
|
+
return @{
|
|
169
|
+
ServiceName = $ServiceName
|
|
170
|
+
Environment = $Environment
|
|
171
|
+
StartTime = $startTime
|
|
172
|
+
DryRun = $DryRun.IsPresent
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function Complete-DeploymentProcess {
|
|
177
|
+
param(
|
|
178
|
+
[Parameter(Mandatory=$true)]
|
|
179
|
+
[hashtable]$DeploymentInfo,
|
|
180
|
+
|
|
181
|
+
[Parameter(Mandatory=$true)]
|
|
182
|
+
[bool]$Success
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
$endTime = Get-Date
|
|
186
|
+
$duration = $endTime - $DeploymentInfo.StartTime
|
|
187
|
+
|
|
188
|
+
if ($Success) {
|
|
189
|
+
Write-DeploymentLog "✅ Deployment completed successfully" "Success"
|
|
190
|
+
} else {
|
|
191
|
+
Write-DeploymentLog "❌ Deployment failed" "Error"
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
Write-DeploymentLog "Deployment duration: $($duration.ToString('hh\:mm\:ss'))" "Info"
|
|
195
|
+
Write-DeploymentLog "Deployment ended at: $endTime" "Info"
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
# Export functions for use by other scripts
|
|
199
|
+
Export-ModuleMember -Function *
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const projectRoot = path.join(__dirname, '../..');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Minimal import path fix for dist/ after Babel compilation
|
|
10
|
+
*
|
|
11
|
+
* NOTE: This script should do MINIMAL changes. Babel preserves relative imports
|
|
12
|
+
* from source files, so most imports are already correct.
|
|
13
|
+
*
|
|
14
|
+
* Fix:
|
|
15
|
+
* 1. CLI commands that reference ../lib/ instead of ../../lib/ (depth mismatch)
|
|
16
|
+
* 2. CLI commands that reference ../config/ instead of ../../config/
|
|
17
|
+
* 3. CLI commands that reference ../service-management/ instead of ../../service-management/
|
|
18
|
+
* 4. Files importing ../../../dist/utils/ should be ../../../utils/
|
|
19
|
+
* 5. Files in dist/utils/ importing ../../../lib/shared/ should be ../../lib/shared/
|
|
20
|
+
* 6. Files in dist/lib/shared/ importing ../../../src/utils/ should be ../../utils/
|
|
21
|
+
*
|
|
22
|
+
* DO NOT apply aggressive global replacements - they break things!
|
|
23
|
+
*/
|
|
24
|
+
function fixDistImports(dir) {
|
|
25
|
+
const fullDir = path.join(projectRoot, dir);
|
|
26
|
+
if (!fs.existsSync(fullDir)) return;
|
|
27
|
+
|
|
28
|
+
const files = fs.readdirSync(fullDir, { recursive: true });
|
|
29
|
+
files.forEach(file => {
|
|
30
|
+
const filePath = path.join(fullDir, file);
|
|
31
|
+
if (fs.statSync(filePath).isFile() && filePath.endsWith('.js')) {
|
|
32
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
33
|
+
const original = content;
|
|
34
|
+
const relPath = path.relative(projectRoot, filePath);
|
|
35
|
+
|
|
36
|
+
// Only fix CLI command imports
|
|
37
|
+
// Files in dist/cli/commands/ are 2 levels deep and need ../../ to reach dist root
|
|
38
|
+
if (relPath.startsWith('dist/cli/commands/')) {
|
|
39
|
+
// Fix imports that incorrectly have ../lib/ instead of ../../lib/
|
|
40
|
+
content = content.replace(/from\s+['"]\.\.\/lib\//g, "from '../../lib/");
|
|
41
|
+
// Fix imports that incorrectly have ../config/ instead of ../../config/
|
|
42
|
+
content = content.replace(/from\s+['"]\.\.\/config\//g, "from '../../config/");
|
|
43
|
+
// Fix imports that incorrectly have ../service-management/ instead of ../../service-management/
|
|
44
|
+
content = content.replace(/from\s+['"]\.\.\/service-management\//g, "from '../../service-management/");
|
|
45
|
+
|
|
46
|
+
// Also fix dynamic imports with import()
|
|
47
|
+
content = content.replace(/import\(['"]\.\.\/lib\//g, "import('../../lib/");
|
|
48
|
+
content = content.replace(/import\(['"]\.\.\/config\//g, "import('../../config/");
|
|
49
|
+
content = content.replace(/import\(['"]\.\.\/service-management\//g, "import('../../service-management/");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Fix imports in all dist/ files
|
|
53
|
+
if (relPath.startsWith('dist')) {
|
|
54
|
+
// Fix ../../../src/utils/ to ../../utils/ for files in dist/lib/shared/
|
|
55
|
+
if (relPath.includes('dist/lib/shared/') || relPath.includes('dist\\lib\\shared\\')) {
|
|
56
|
+
content = content.replace(/\.\.\/\.\.\/\.\.\/src\/utils\//g, "../../utils/");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const normalizedRelPath = relPath.replace(/\\/g, '/');
|
|
60
|
+
const pathDepth = normalizedRelPath.split('/').length;
|
|
61
|
+
|
|
62
|
+
// Fix ../../lib/shared/ to ../lib/shared/ for files at depth 2: dist/<dir>/<file>.js
|
|
63
|
+
// Example: dist/database/database-orchestrator.js importing ../../lib/shared/utils/framework-config.js
|
|
64
|
+
// Should become: ../lib/shared/utils/framework-config.js
|
|
65
|
+
if (pathDepth === 3 && normalizedRelPath.match(/^dist\/[^\/]+\/[^\/]+\.js$/)) {
|
|
66
|
+
// File is at depth 2: dist/<dir>/<file>.js
|
|
67
|
+
// Fix specific shared lib imports
|
|
68
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/lib\/shared\//g, "from $1../lib/shared/");
|
|
69
|
+
content = content.replace(/import\s*\(\s*(['"])\.\.\/\.\.\/lib\/shared\//g, "import($1../lib/shared/");
|
|
70
|
+
// Fix generic lib imports (e.g. ../../lib/database/.. -> ../lib/database/...)
|
|
71
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/lib\//g, "from $1../lib/");
|
|
72
|
+
content = content.replace(/import\s*\(\s*(['"])\.\.\/\.\.\/lib\//g, "import($1../lib/");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fix ../../../lib/shared/ to ../../lib/shared/ for files at depth 3: dist/<dir>/<subdir>/<file>.js
|
|
76
|
+
// Example: dist/deployment/orchestration/BaseDeploymentOrchestrator.js importing ../../../lib/shared/utils/ErrorHandler.js
|
|
77
|
+
// Should become: ../../lib/shared/utils/ErrorHandler.js
|
|
78
|
+
if (pathDepth === 4 && normalizedRelPath.match(/^dist\/[^\/]+\/[^\/]+\/[^\/]+\.js$/)) {
|
|
79
|
+
// File is at depth 3: dist/<dir>/<subdir>/<file>.js
|
|
80
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/\.\.\/lib\/shared\//g, "from $1../../lib/shared/");
|
|
81
|
+
content = content.replace(/import\s*\(\s*(['"])\.\.\/\.\.\/\.\.\/lib\/shared\//g, "import($1../../lib/shared/");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Fix ../../../lib/shared/ to ../../lib/shared/ for files in dist/utils/ subdirectories
|
|
85
|
+
// Example: dist/utils/deployment/config-cache.js importing ../../../lib/shared/utils/framework-config.js
|
|
86
|
+
// Should become: ../../lib/shared/utils/framework-config.js
|
|
87
|
+
if (normalizedRelPath.match(/^dist\/utils\/[^\/]+\/[^\/]+\.js$/)) {
|
|
88
|
+
// File is at depth 3: dist/utils/<subdir>/<file>.js
|
|
89
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/\.\.\/lib\/shared\//g, "from $1../../lib/shared/");
|
|
90
|
+
content = content.replace(/import\s*\(\s*(['"])\.\.\/\.\.\/\.\.\/lib\/shared\//g, "import($1../../lib/shared/");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Fix ../../../lib/shared/ to ../../lib/shared/ for files in dist/service-management/ subdirectories
|
|
94
|
+
if (normalizedRelPath.match(/^dist\/service-management\/[^\/]+\/[^\/]+\.js$/)) {
|
|
95
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/\.\.\/lib\/shared\//g, "from $1../../lib/shared/");
|
|
96
|
+
content = content.replace(/import\s*\(\s*(['"])\.\.\/\.\.\/\.\.\/lib\/shared\//g, "import($1../../lib/shared/");
|
|
97
|
+
}
|
|
98
|
+
// Fix ../../../../lib/ to ../../../lib/ for deeper nested files in dist/service-management (e.g. generators/config/...)
|
|
99
|
+
if (normalizedRelPath.startsWith('dist/service-management/')) {
|
|
100
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/\.\.\/lib\/shared\//g, "from $1../../../lib/shared/");
|
|
101
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/\.\.\/lib\//g, "from $1../../../lib/");
|
|
102
|
+
content = content.replace(/import\s*\(\s*(['"])\.\.\/\.\.\/\.\.\/lib\/shared\//g, "import($1../../../lib/shared/");
|
|
103
|
+
content = content.replace(/import\s*\(\s*(['"])\.\.\/\.\.\/\.\.\/lib\//g, "import($1../../../lib/");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Specific fix for generators/config files that had one extra ../ and still resolve outside dist
|
|
107
|
+
if (normalizedRelPath.match(/^dist\/service-management\/generators\/config\/.+\.js$/)) {
|
|
108
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/\.\.\/lib\/shared\/utils\//g, "from $1../../../lib/shared/utils/");
|
|
109
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/\.\.\/lib\/shared\//g, "from $1../../../lib/shared/");
|
|
110
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/\.\.\/lib\/utils\//g, "from $1../../../lib/utils/");
|
|
111
|
+
content = content.replace(/from\s+(['"])\.\.\/\.\.\/\.\.\/lib\//g, "from $1../../../lib/");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// General fix: convert four-level '../../../../lib/' imports to '../../../lib/' across dist/
|
|
115
|
+
// This handles deeply nested files that mistakenly reference project root lib/ (outside dist)
|
|
116
|
+
content = content.replace(/(\.\.\/){4}lib\//g, "../../../lib/");
|
|
117
|
+
content = content.replace(/import\s*\(\s*(['"])(?:\.\.\/){4}lib\//g, "import($1../../../lib/");
|
|
118
|
+
|
|
119
|
+
// Fix ../lib/ to ./lib/ for files directly in dist/ root (like index.js)
|
|
120
|
+
if (normalizedRelPath.startsWith('dist/') && pathDepth === 2 && normalizedRelPath.endsWith('.js')) {
|
|
121
|
+
content = content.replace(/\.\.\/lib\//g, "./lib/");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (content !== original) {
|
|
126
|
+
fs.writeFileSync(filePath, content);
|
|
127
|
+
console.log('✅ Fixed dist import:', relPath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Process all dist/ where the fixes are needed
|
|
134
|
+
fixDistImports('dist');
|
|
135
|
+
console.log('✅ Dist import path fixes completed');
|