tlc-claude-code 1.4.8 → 1.4.9
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/package.json +1 -1
- package/server/index.js +229 -14
- package/server/lib/compliance/control-mapper.js +401 -0
- package/server/lib/compliance/control-mapper.test.js +117 -0
- package/server/lib/compliance/evidence-linker.js +296 -0
- package/server/lib/compliance/evidence-linker.test.js +121 -0
- package/server/lib/compliance/gdpr-checklist.js +416 -0
- package/server/lib/compliance/gdpr-checklist.test.js +131 -0
- package/server/lib/compliance/hipaa-checklist.js +277 -0
- package/server/lib/compliance/hipaa-checklist.test.js +101 -0
- package/server/lib/compliance/iso27001-checklist.js +287 -0
- package/server/lib/compliance/iso27001-checklist.test.js +99 -0
- package/server/lib/compliance/multi-framework-reporter.js +284 -0
- package/server/lib/compliance/multi-framework-reporter.test.js +127 -0
- package/server/lib/compliance/pci-dss-checklist.js +214 -0
- package/server/lib/compliance/pci-dss-checklist.test.js +95 -0
- package/server/lib/compliance/trust-centre.js +187 -0
- package/server/lib/compliance/trust-centre.test.js +93 -0
- package/server/lib/dashboard/api-server.js +155 -0
- package/server/lib/dashboard/api-server.test.js +155 -0
- package/server/lib/dashboard/health-api.js +199 -0
- package/server/lib/dashboard/health-api.test.js +122 -0
- package/server/lib/dashboard/notes-api.js +234 -0
- package/server/lib/dashboard/notes-api.test.js +134 -0
- package/server/lib/dashboard/router-api.js +176 -0
- package/server/lib/dashboard/router-api.test.js +132 -0
- package/server/lib/dashboard/tasks-api.js +289 -0
- package/server/lib/dashboard/tasks-api.test.js +161 -0
- package/server/lib/dashboard/tlc-introspection.js +197 -0
- package/server/lib/dashboard/tlc-introspection.test.js +138 -0
- package/server/lib/dashboard/version-api.js +222 -0
- package/server/lib/dashboard/version-api.test.js +112 -0
- package/server/lib/dashboard/websocket-server.js +104 -0
- package/server/lib/dashboard/websocket-server.test.js +118 -0
- package/server/lib/deploy/branch-classifier.js +163 -0
- package/server/lib/deploy/branch-classifier.test.js +164 -0
- package/server/lib/deploy/deployment-approval.js +299 -0
- package/server/lib/deploy/deployment-approval.test.js +296 -0
- package/server/lib/deploy/deployment-audit.js +374 -0
- package/server/lib/deploy/deployment-audit.test.js +307 -0
- package/server/lib/deploy/deployment-executor.js +335 -0
- package/server/lib/deploy/deployment-executor.test.js +329 -0
- package/server/lib/deploy/deployment-rules.js +163 -0
- package/server/lib/deploy/deployment-rules.test.js +188 -0
- package/server/lib/deploy/rollback-manager.js +379 -0
- package/server/lib/deploy/rollback-manager.test.js +321 -0
- package/server/lib/deploy/security-gates.js +236 -0
- package/server/lib/deploy/security-gates.test.js +222 -0
- package/server/lib/k8s/gitops-config.js +188 -0
- package/server/lib/k8s/gitops-config.test.js +59 -0
- package/server/lib/k8s/helm-generator.js +196 -0
- package/server/lib/k8s/helm-generator.test.js +59 -0
- package/server/lib/k8s/kustomize-generator.js +176 -0
- package/server/lib/k8s/kustomize-generator.test.js +58 -0
- package/server/lib/k8s/network-policy.js +114 -0
- package/server/lib/k8s/network-policy.test.js +53 -0
- package/server/lib/k8s/pod-security.js +114 -0
- package/server/lib/k8s/pod-security.test.js +55 -0
- package/server/lib/k8s/rbac-generator.js +132 -0
- package/server/lib/k8s/rbac-generator.test.js +57 -0
- package/server/lib/k8s/resource-manager.js +172 -0
- package/server/lib/k8s/resource-manager.test.js +60 -0
- package/server/lib/k8s/secrets-encryption.js +168 -0
- package/server/lib/k8s/secrets-encryption.test.js +49 -0
- package/server/lib/monitoring/alert-manager.js +238 -0
- package/server/lib/monitoring/alert-manager.test.js +106 -0
- package/server/lib/monitoring/health-check.js +226 -0
- package/server/lib/monitoring/health-check.test.js +176 -0
- package/server/lib/monitoring/incident-manager.js +230 -0
- package/server/lib/monitoring/incident-manager.test.js +98 -0
- package/server/lib/monitoring/log-aggregator.js +147 -0
- package/server/lib/monitoring/log-aggregator.test.js +89 -0
- package/server/lib/monitoring/metrics-collector.js +337 -0
- package/server/lib/monitoring/metrics-collector.test.js +172 -0
- package/server/lib/monitoring/status-page.js +214 -0
- package/server/lib/monitoring/status-page.test.js +105 -0
- package/server/lib/monitoring/uptime-monitor.js +194 -0
- package/server/lib/monitoring/uptime-monitor.test.js +109 -0
- package/server/lib/network/fail2ban-config.js +294 -0
- package/server/lib/network/fail2ban-config.test.js +275 -0
- package/server/lib/network/firewall-manager.js +252 -0
- package/server/lib/network/firewall-manager.test.js +254 -0
- package/server/lib/network/geoip-filter.js +282 -0
- package/server/lib/network/geoip-filter.test.js +264 -0
- package/server/lib/network/rate-limiter.js +229 -0
- package/server/lib/network/rate-limiter.test.js +293 -0
- package/server/lib/network/request-validator.js +351 -0
- package/server/lib/network/request-validator.test.js +345 -0
- package/server/lib/network/security-headers.js +251 -0
- package/server/lib/network/security-headers.test.js +283 -0
- package/server/lib/network/tls-config.js +210 -0
- package/server/lib/network/tls-config.test.js +248 -0
- package/server/lib/security/auth-security.js +369 -0
- package/server/lib/security/auth-security.test.js +448 -0
- package/server/lib/security/cis-benchmark.js +152 -0
- package/server/lib/security/cis-benchmark.test.js +137 -0
- package/server/lib/security/compose-templates.js +312 -0
- package/server/lib/security/compose-templates.test.js +229 -0
- package/server/lib/security/container-runtime.js +456 -0
- package/server/lib/security/container-runtime.test.js +503 -0
- package/server/lib/security/cors-validator.js +278 -0
- package/server/lib/security/cors-validator.test.js +310 -0
- package/server/lib/security/crypto-utils.js +253 -0
- package/server/lib/security/crypto-utils.test.js +409 -0
- package/server/lib/security/dockerfile-linter.js +459 -0
- package/server/lib/security/dockerfile-linter.test.js +483 -0
- package/server/lib/security/dockerfile-templates.js +278 -0
- package/server/lib/security/dockerfile-templates.test.js +164 -0
- package/server/lib/security/error-sanitizer.js +426 -0
- package/server/lib/security/error-sanitizer.test.js +331 -0
- package/server/lib/security/headers-generator.js +368 -0
- package/server/lib/security/headers-generator.test.js +398 -0
- package/server/lib/security/image-scanner.js +83 -0
- package/server/lib/security/image-scanner.test.js +106 -0
- package/server/lib/security/input-validator.js +352 -0
- package/server/lib/security/input-validator.test.js +330 -0
- package/server/lib/security/network-policy.js +174 -0
- package/server/lib/security/network-policy.test.js +164 -0
- package/server/lib/security/output-encoder.js +237 -0
- package/server/lib/security/output-encoder.test.js +276 -0
- package/server/lib/security/path-validator.js +359 -0
- package/server/lib/security/path-validator.test.js +293 -0
- package/server/lib/security/query-builder.js +421 -0
- package/server/lib/security/query-builder.test.js +318 -0
- package/server/lib/security/secret-detector.js +290 -0
- package/server/lib/security/secret-detector.test.js +354 -0
- package/server/lib/security/secrets-validator.js +137 -0
- package/server/lib/security/secrets-validator.test.js +120 -0
- package/server/lib/security-testing/dast-runner.js +154 -0
- package/server/lib/security-testing/dast-runner.test.js +62 -0
- package/server/lib/security-testing/dependency-scanner.js +172 -0
- package/server/lib/security-testing/dependency-scanner.test.js +64 -0
- package/server/lib/security-testing/pentest-runner.js +230 -0
- package/server/lib/security-testing/pentest-runner.test.js +60 -0
- package/server/lib/security-testing/sast-runner.js +136 -0
- package/server/lib/security-testing/sast-runner.test.js +62 -0
- package/server/lib/security-testing/secret-scanner.js +153 -0
- package/server/lib/security-testing/secret-scanner.test.js +66 -0
- package/server/lib/security-testing/security-gate.js +216 -0
- package/server/lib/security-testing/security-gate.test.js +115 -0
- package/server/lib/security-testing/security-reporter.js +303 -0
- package/server/lib/security-testing/security-reporter.test.js +114 -0
- package/server/lib/standards/audit-checker.js +546 -0
- package/server/lib/standards/audit-checker.test.js +415 -0
- package/server/lib/standards/cleanup-executor.js +452 -0
- package/server/lib/standards/cleanup-executor.test.js +293 -0
- package/server/lib/standards/refactor-stepper.js +425 -0
- package/server/lib/standards/refactor-stepper.test.js +298 -0
- package/server/lib/standards/standards-injector.js +167 -0
- package/server/lib/standards/standards-injector.test.js +232 -0
- package/server/lib/user-management.test.js +284 -0
- package/server/lib/vps/backup-manager.js +157 -0
- package/server/lib/vps/backup-manager.test.js +59 -0
- package/server/lib/vps/caddy-config.js +159 -0
- package/server/lib/vps/caddy-config.test.js +48 -0
- package/server/lib/vps/compose-orchestrator.js +219 -0
- package/server/lib/vps/compose-orchestrator.test.js +50 -0
- package/server/lib/vps/database-config.js +208 -0
- package/server/lib/vps/database-config.test.js +47 -0
- package/server/lib/vps/deploy-script.js +211 -0
- package/server/lib/vps/deploy-script.test.js +53 -0
- package/server/lib/vps/secrets-manager.js +148 -0
- package/server/lib/vps/secrets-manager.test.js +58 -0
- package/server/lib/vps/server-hardening.js +174 -0
- package/server/lib/vps/server-hardening.test.js +70 -0
- package/server/package-lock.json +19 -0
- package/server/package.json +1 -0
- package/server/templates/CLAUDE.md +37 -0
- package/server/templates/CODING-STANDARDS.md +408 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleanup Executor - Execute code cleanup operations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate environment variable name from URL or value
|
|
7
|
+
* @param {string} value - The value to convert
|
|
8
|
+
* @param {string} type - Type of config (url, port)
|
|
9
|
+
* @returns {string} Environment variable name
|
|
10
|
+
*/
|
|
11
|
+
function generateEnvVarName(value, type) {
|
|
12
|
+
if (type === 'port') {
|
|
13
|
+
return 'PORT';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Extract hostname for URL-based env var names
|
|
17
|
+
try {
|
|
18
|
+
const url = new URL(value);
|
|
19
|
+
const hostname = url.hostname.replace(/[^a-zA-Z0-9]/g, '_').toUpperCase();
|
|
20
|
+
if (hostname === 'LOCALHOST') {
|
|
21
|
+
return 'API_URL';
|
|
22
|
+
}
|
|
23
|
+
return `${hostname}_URL`;
|
|
24
|
+
} catch {
|
|
25
|
+
return 'API_URL';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Replace hardcoded URL/port with environment variable
|
|
31
|
+
* @param {string} code - Source code
|
|
32
|
+
* @param {Object} issue - Issue describing the hardcoded value
|
|
33
|
+
* @returns {Promise<Object>} Result with code and envVar
|
|
34
|
+
*/
|
|
35
|
+
async function extractHardcodedConfig(code, issue) {
|
|
36
|
+
const { type, value } = issue;
|
|
37
|
+
const envVar = generateEnvVarName(value, type);
|
|
38
|
+
|
|
39
|
+
let newCode = code;
|
|
40
|
+
|
|
41
|
+
if (type === 'port') {
|
|
42
|
+
// Replace port assignments like: const port = 3000;
|
|
43
|
+
const portPattern = new RegExp(`(const|let|var)\\s+(\\w+)\\s*=\\s*${value}\\s*;`, 'g');
|
|
44
|
+
newCode = newCode.replace(portPattern, (match, keyword, varName) => {
|
|
45
|
+
return `${keyword} ${varName} = process.env.${envVar} || ${value};`;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Also replace direct port usage
|
|
49
|
+
if (newCode === code) {
|
|
50
|
+
newCode = newCode.replace(
|
|
51
|
+
new RegExp(`\\b${value}\\b`, 'g'),
|
|
52
|
+
`(process.env.${envVar} || ${value})`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
} else if (type === 'url') {
|
|
56
|
+
const escapedValue = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
57
|
+
|
|
58
|
+
// Check if URL is used in a function call (fetch, axios, etc.) with a path appended
|
|
59
|
+
const urlWithPathPattern = new RegExp(`(['"])${escapedValue}(/[^'"]+)\\1`, 'g');
|
|
60
|
+
const hasPath = urlWithPathPattern.test(code);
|
|
61
|
+
|
|
62
|
+
if (hasPath) {
|
|
63
|
+
// Replace URL with env var, keeping the path separate
|
|
64
|
+
newCode = code.replace(urlWithPathPattern, (match, q1, path) => {
|
|
65
|
+
return `process.env.${envVar} + '${path}'`;
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
// Check if it's a simple assignment (const x = 'url';)
|
|
69
|
+
const assignmentPattern = new RegExp(`(const|let|var)\\s+(\\w+)\\s*=\\s*(['"])${escapedValue}\\3\\s*;`, 'g');
|
|
70
|
+
const isAssignment = assignmentPattern.test(code);
|
|
71
|
+
|
|
72
|
+
if (isAssignment) {
|
|
73
|
+
// For assignments, use fallback pattern
|
|
74
|
+
newCode = code.replace(
|
|
75
|
+
new RegExp(`(const|let|var)\\s+(\\w+)\\s*=\\s*(['"])${escapedValue}\\3\\s*;`, 'g'),
|
|
76
|
+
(match, keyword, varName, q) => {
|
|
77
|
+
return `${keyword} ${varName} = process.env.${envVar} || ${q}${value}${q};`;
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
} else {
|
|
81
|
+
// For inline usage without path, just use env var
|
|
82
|
+
newCode = code.replace(
|
|
83
|
+
new RegExp(`(['"])${escapedValue}\\1`, 'g'),
|
|
84
|
+
`process.env.${envVar}`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
code: newCode,
|
|
92
|
+
envVar
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Migrate file from flat folder (services/) to entity folder
|
|
98
|
+
* @param {Object} options - Migration options
|
|
99
|
+
* @param {Object} deps - Injected dependencies
|
|
100
|
+
* @returns {Promise<Object>} Result with newPath and updatedImports count
|
|
101
|
+
*/
|
|
102
|
+
async function migrateFlatFolder(options, deps = {}) {
|
|
103
|
+
const { sourcePath, entity, projectPath } = options;
|
|
104
|
+
const { fs, glob } = deps;
|
|
105
|
+
|
|
106
|
+
// Calculate new path
|
|
107
|
+
const fileName = sourcePath.split('/').pop();
|
|
108
|
+
const newPath = `src/${entity}/${fileName}`;
|
|
109
|
+
const fullNewPath = `${projectPath}/${newPath}`;
|
|
110
|
+
const fullSourcePath = `${projectPath}/${sourcePath}`;
|
|
111
|
+
|
|
112
|
+
// Create entity directory
|
|
113
|
+
await fs.mkdir(`${projectPath}/src/${entity}`, { recursive: true });
|
|
114
|
+
|
|
115
|
+
// Read source file
|
|
116
|
+
const content = await fs.readFile(fullSourcePath);
|
|
117
|
+
|
|
118
|
+
// Move file (rename)
|
|
119
|
+
await fs.rename(fullSourcePath, fullNewPath);
|
|
120
|
+
|
|
121
|
+
// Update imports in other files
|
|
122
|
+
let updatedImports = 0;
|
|
123
|
+
|
|
124
|
+
if (glob) {
|
|
125
|
+
const files = await glob(`${projectPath}/src/**/*.{ts,js,tsx,jsx}`);
|
|
126
|
+
|
|
127
|
+
for (const file of files) {
|
|
128
|
+
if (file === fullNewPath) continue;
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const fileContent = await fs.readFile(file);
|
|
132
|
+
|
|
133
|
+
// Extract the folder structure from source path (e.g., services/user.service)
|
|
134
|
+
const pathParts = sourcePath.replace(/^src\//, '').replace(/\.(ts|js|tsx|jsx)$/, '');
|
|
135
|
+
const newImportBase = `${entity}/${fileName.replace(/\.(ts|js|tsx|jsx)$/, '')}`;
|
|
136
|
+
|
|
137
|
+
// Match various import patterns:
|
|
138
|
+
// - './services/user.service'
|
|
139
|
+
// - '../services/user.service'
|
|
140
|
+
// - 'src/services/user.service'
|
|
141
|
+
const importPatterns = [
|
|
142
|
+
// Relative import: ./services/user.service or ../services/user.service
|
|
143
|
+
new RegExp(`(['"])\\.{1,2}/${pathParts.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\1`, 'g'),
|
|
144
|
+
// Absolute-style import: src/services/user.service
|
|
145
|
+
new RegExp(`(['"])${sourcePath.replace(/\.(ts|js|tsx|jsx)$/, '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\1`, 'g')
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
let updatedContent = fileContent;
|
|
149
|
+
let fileUpdated = false;
|
|
150
|
+
|
|
151
|
+
for (const pattern of importPatterns) {
|
|
152
|
+
if (pattern.test(updatedContent)) {
|
|
153
|
+
updatedContent = updatedContent.replace(pattern, (match, quote) => {
|
|
154
|
+
// Determine the prefix (relative or absolute)
|
|
155
|
+
const hasRelative = match.includes('./');
|
|
156
|
+
const prefix = hasRelative ? './' : '';
|
|
157
|
+
return `${quote}${prefix}${newImportBase}${quote}`;
|
|
158
|
+
});
|
|
159
|
+
fileUpdated = true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (fileUpdated) {
|
|
164
|
+
await fs.writeFile(file, updatedContent);
|
|
165
|
+
updatedImports++;
|
|
166
|
+
}
|
|
167
|
+
} catch (err) {
|
|
168
|
+
// Skip files that can't be read
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
newPath,
|
|
175
|
+
updatedImports
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Extract inline interface to separate types file
|
|
181
|
+
* @param {string} code - Source code with inline interface
|
|
182
|
+
* @param {Object} options - Extraction options
|
|
183
|
+
* @returns {Promise<Object>} Result with serviceCode, typesCode, typesPath
|
|
184
|
+
*/
|
|
185
|
+
async function extractInlineInterface(code, options) {
|
|
186
|
+
const { interfaceName, entity } = options;
|
|
187
|
+
|
|
188
|
+
// Match the interface definition
|
|
189
|
+
const interfacePattern = new RegExp(
|
|
190
|
+
`(interface\\s+${interfaceName}\\s*\\{[^}]*\\})`,
|
|
191
|
+
's'
|
|
192
|
+
);
|
|
193
|
+
const match = code.match(interfacePattern);
|
|
194
|
+
|
|
195
|
+
if (!match) {
|
|
196
|
+
throw new Error(`Interface ${interfaceName} not found in code`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const interfaceCode = match[1];
|
|
200
|
+
|
|
201
|
+
// Remove interface from service code
|
|
202
|
+
let serviceCode = code.replace(interfacePattern, '').trim();
|
|
203
|
+
|
|
204
|
+
// Add import statement at the top
|
|
205
|
+
const importStatement = `import { ${interfaceName} } from './types/${entity}.types';`;
|
|
206
|
+
|
|
207
|
+
// Insert import after any existing imports or at the beginning
|
|
208
|
+
const lastImportMatch = serviceCode.match(/^(import .+;\n)+/m);
|
|
209
|
+
if (lastImportMatch) {
|
|
210
|
+
serviceCode = serviceCode.replace(
|
|
211
|
+
lastImportMatch[0],
|
|
212
|
+
lastImportMatch[0] + importStatement + '\n'
|
|
213
|
+
);
|
|
214
|
+
} else {
|
|
215
|
+
serviceCode = importStatement + '\n\n' + serviceCode;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Create types file content
|
|
219
|
+
const typesCode = `export ${interfaceCode}`;
|
|
220
|
+
const typesPath = `src/${entity}/types/${entity}.types.ts`;
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
serviceCode,
|
|
224
|
+
typesCode,
|
|
225
|
+
typesPath
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Replace magic strings with named constants
|
|
231
|
+
* @param {string} code - Source code
|
|
232
|
+
* @param {Object} options - Replacement options
|
|
233
|
+
* @returns {Promise<Object>} Result with code, constants, constantsFile, constantsPath
|
|
234
|
+
*/
|
|
235
|
+
async function replaceMagicStrings(code, options) {
|
|
236
|
+
const { strings, entity } = options;
|
|
237
|
+
const constants = [];
|
|
238
|
+
let newCode = code;
|
|
239
|
+
|
|
240
|
+
for (const { value, suggestedName } of strings) {
|
|
241
|
+
const name = suggestedName || `CONST_${value.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;
|
|
242
|
+
|
|
243
|
+
// Replace occurrences of the string literal
|
|
244
|
+
const escapedValue = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
245
|
+
const pattern = new RegExp(`(['"])${escapedValue}\\1`, 'g');
|
|
246
|
+
newCode = newCode.replace(pattern, name);
|
|
247
|
+
|
|
248
|
+
constants.push({ name, value });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Generate constants file content
|
|
252
|
+
const constantsFile = constants
|
|
253
|
+
.map(c => `export const ${c.name} = '${c.value}';`)
|
|
254
|
+
.join('\n');
|
|
255
|
+
|
|
256
|
+
const constantsPath = entity
|
|
257
|
+
? `src/${entity}/constants/${entity}.constants.ts`
|
|
258
|
+
: 'src/constants/index.ts';
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
code: newCode,
|
|
262
|
+
constants,
|
|
263
|
+
constantsFile,
|
|
264
|
+
constantsPath
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Parse function signature from code
|
|
270
|
+
* @param {string} funcStr - Function string
|
|
271
|
+
* @returns {Object} Parsed function info
|
|
272
|
+
*/
|
|
273
|
+
function parseFunctionSignature(funcStr) {
|
|
274
|
+
// Match function name and parameters
|
|
275
|
+
const funcMatch = funcStr.match(
|
|
276
|
+
/(?:async\s+)?(?:function\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*(\w+))?/
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
if (!funcMatch) return null;
|
|
280
|
+
|
|
281
|
+
const [, name, paramsStr, returnType] = funcMatch;
|
|
282
|
+
|
|
283
|
+
// Parse parameters
|
|
284
|
+
const params = paramsStr
|
|
285
|
+
.split(',')
|
|
286
|
+
.map(p => p.trim())
|
|
287
|
+
.filter(p => p)
|
|
288
|
+
.map(p => {
|
|
289
|
+
const [paramName, paramType] = p.split(':').map(s => s.trim());
|
|
290
|
+
return { name: paramName, type: paramType || 'any' };
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return { name, params, returnType: returnType || 'any' };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Generate JSDoc comment for a function
|
|
298
|
+
* @param {Object} funcInfo - Function info from parsing
|
|
299
|
+
* @returns {string} JSDoc comment
|
|
300
|
+
*/
|
|
301
|
+
function generateJsDoc(funcInfo) {
|
|
302
|
+
const { params, returnType } = funcInfo;
|
|
303
|
+
|
|
304
|
+
let jsdoc = '/**\n';
|
|
305
|
+
jsdoc += ` * TODO: Add description\n`;
|
|
306
|
+
|
|
307
|
+
for (const param of params) {
|
|
308
|
+
jsdoc += ` * @param {${param.type}} ${param.name}\n`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (returnType && returnType !== 'void') {
|
|
312
|
+
jsdoc += ` * @returns {${returnType}}\n`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
jsdoc += ' */\n';
|
|
316
|
+
|
|
317
|
+
return jsdoc;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Add JSDoc comments to exported functions missing them
|
|
322
|
+
* @param {string} code - Source code
|
|
323
|
+
* @returns {Promise<Object>} Result with updated code
|
|
324
|
+
*/
|
|
325
|
+
async function addMissingJsDoc(code) {
|
|
326
|
+
const lines = code.split('\n');
|
|
327
|
+
const result = [];
|
|
328
|
+
|
|
329
|
+
for (let i = 0; i < lines.length; i++) {
|
|
330
|
+
const line = lines[i];
|
|
331
|
+
|
|
332
|
+
// Check if this is an exported function or public method
|
|
333
|
+
const isExportedFunc = /^\s*export\s+(?:async\s+)?function\s+\w+/.test(line);
|
|
334
|
+
const isPublicMethod = /^\s*(?:public\s+)?(?:async\s+)?(\w+)\s*\([^)]*\)/.test(line)
|
|
335
|
+
&& !line.includes('private')
|
|
336
|
+
&& !line.includes('//');
|
|
337
|
+
|
|
338
|
+
if (isExportedFunc || (isPublicMethod && line.includes('class') === false)) {
|
|
339
|
+
// Check if previous line(s) have JSDoc
|
|
340
|
+
let hasJsDoc = false;
|
|
341
|
+
let j = i - 1;
|
|
342
|
+
|
|
343
|
+
// Skip empty lines
|
|
344
|
+
while (j >= 0 && lines[j].trim() === '') {
|
|
345
|
+
j--;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Check for JSDoc end
|
|
349
|
+
if (j >= 0 && lines[j].trim() === '*/') {
|
|
350
|
+
// Find JSDoc start
|
|
351
|
+
while (j >= 0 && !lines[j].includes('/**')) {
|
|
352
|
+
j--;
|
|
353
|
+
}
|
|
354
|
+
if (j >= 0 && lines[j].includes('/**')) {
|
|
355
|
+
hasJsDoc = true;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (!hasJsDoc && (isExportedFunc || isPublicMethod)) {
|
|
360
|
+
// Parse function signature
|
|
361
|
+
const funcInfo = parseFunctionSignature(line);
|
|
362
|
+
|
|
363
|
+
if (funcInfo && funcInfo.params.length > 0) {
|
|
364
|
+
// Add JSDoc
|
|
365
|
+
const jsDoc = generateJsDoc(funcInfo);
|
|
366
|
+
result.push(jsDoc.trim());
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
result.push(line);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return { code: result.join('\n') };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Create a git commit for cleanup changes
|
|
379
|
+
* @param {Object} options - Commit options
|
|
380
|
+
* @param {Object} deps - Injected dependencies
|
|
381
|
+
* @returns {Promise<void>}
|
|
382
|
+
*/
|
|
383
|
+
async function commitChanges(options, deps = {}) {
|
|
384
|
+
const { type, entity, description, files } = options;
|
|
385
|
+
const { exec } = deps;
|
|
386
|
+
|
|
387
|
+
// Stage files
|
|
388
|
+
const filesToAdd = files || ['.'];
|
|
389
|
+
for (const file of Array.isArray(filesToAdd) ? filesToAdd : [filesToAdd]) {
|
|
390
|
+
await exec(`git add ${file}`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Create commit message in conventional commit format
|
|
394
|
+
const commitType = type === 'migrate' ? 'refactor' : type;
|
|
395
|
+
const scope = entity || 'cleanup';
|
|
396
|
+
const message = `${commitType}(${scope}): ${description}`;
|
|
397
|
+
|
|
398
|
+
await exec(`git commit -m "${message}"`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Run full cleanup on a project
|
|
403
|
+
* @param {string} projectPath - Path to project
|
|
404
|
+
* @param {Object} options - Cleanup options and dependencies
|
|
405
|
+
* @returns {Promise<Object>} Cleanup results
|
|
406
|
+
*/
|
|
407
|
+
async function runCleanup(projectPath, options = {}) {
|
|
408
|
+
const { injectStandards, auditProject } = options;
|
|
409
|
+
|
|
410
|
+
const result = {
|
|
411
|
+
standardsInjected: null,
|
|
412
|
+
issuesFixed: 0,
|
|
413
|
+
commits: []
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// Step 1: Ensure standards files exist
|
|
417
|
+
if (injectStandards) {
|
|
418
|
+
result.standardsInjected = await injectStandards(projectPath);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Step 2: Run audit
|
|
422
|
+
if (auditProject) {
|
|
423
|
+
const auditResults = await auditProject(projectPath);
|
|
424
|
+
|
|
425
|
+
// Count total issues that could be fixed
|
|
426
|
+
const issueCategories = [
|
|
427
|
+
'flatFolders',
|
|
428
|
+
'hardcodedUrls',
|
|
429
|
+
'magicStrings',
|
|
430
|
+
'inlineInterfaces',
|
|
431
|
+
'jsDocCoverage'
|
|
432
|
+
];
|
|
433
|
+
|
|
434
|
+
for (const category of issueCategories) {
|
|
435
|
+
if (auditResults[category]?.issues) {
|
|
436
|
+
result.issuesFixed += auditResults[category].issues.length;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
module.exports = {
|
|
445
|
+
extractHardcodedConfig,
|
|
446
|
+
migrateFlatFolder,
|
|
447
|
+
extractInlineInterface,
|
|
448
|
+
replaceMagicStrings,
|
|
449
|
+
addMissingJsDoc,
|
|
450
|
+
commitChanges,
|
|
451
|
+
runCleanup
|
|
452
|
+
};
|