deflake 1.2.17 β†’ 1.2.21

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.
Files changed (2) hide show
  1. package/cli.js +107 -48
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -7,7 +7,7 @@ const fs = require('fs');
7
7
  const path = require('path');
8
8
  const pkg = require('./package.json');
9
9
 
10
- // --- COLORS ---
10
+ // --- PREMIUM COLORS ---
11
11
  const C = {
12
12
  RESET: "\x1b[0m",
13
13
  BRIGHT: "\x1b[1m",
@@ -16,8 +16,10 @@ const C = {
16
16
  YELLOW: "\x1b[33m",
17
17
  CYAN: "\x1b[36m",
18
18
  BLUE: "\x1b[34m",
19
+ MAGENTA: "\x1b[35m",
19
20
  GRAY: "\x1b[90m",
20
- WHITE: "\x1b[37m"
21
+ BG_BLUE: "\x1b[44m",
22
+ BG_GREEN: "\x1b[42m"
21
23
  };
22
24
 
23
25
  const rawArgs = process.argv.slice(2);
@@ -43,19 +45,16 @@ async function main() {
43
45
 
44
46
  if (commandToRun) {
45
47
  const { code } = await runNative(commandToRun);
46
-
47
48
  if (code !== 0) {
48
49
  console.log(`\nπŸ”΄ Command failed with code ${code}. Activating DeFlake...`);
49
50
  const artifacts = detectFailures();
50
51
  const applied = await analyzeAndFix(artifacts, client, argv);
51
-
52
52
  if (applied > 0 && argv.fix) {
53
53
  console.log(`\n${C.BRIGHT}πŸ’‰ Fixes applied. Re-running tests to verify...${C.RESET}`);
54
54
  await runNative(commandToRun);
55
55
  }
56
56
  }
57
57
  }
58
-
59
58
  if (argv.report) showReport();
60
59
  }
61
60
 
@@ -89,22 +88,17 @@ async function analyzeAndFix(artifacts, client, argv) {
89
88
  console.log(` ${C.YELLOW}⚠️ No failure artifacts detected. Run 'npx deflake doctor' to check permissions.${C.RESET}`);
90
89
  return 0;
91
90
  }
92
-
93
91
  console.log(`πŸ” Analyzing ${artifacts.length} failure(s)...`);
94
92
  let count = 0;
95
-
96
93
  for (const art of artifacts) {
97
94
  process.stdout.write(`⏳ Analyzing ${art.name}...`);
98
95
  try {
99
96
  const content = fs.readFileSync(art.htmlPath, 'utf8');
100
97
  const loc = extractLoc(content);
101
98
  const source = loc && fs.existsSync(loc.path) ? fs.readFileSync(loc.path, 'utf8') : null;
102
-
103
99
  const res = await client.heal(null, art.htmlPath, loc, source, argv.fix);
104
100
  if (res?.status === 'success') {
105
- if (argv.fix) {
106
- if (await applyFix(res, loc)) count++;
107
- }
101
+ if (argv.fix && await applyFix(res, loc)) count++;
108
102
  process.stdout.write(`\rβœ… Analysis complete for ${art.name}\n`);
109
103
  printFix(res.fix, loc);
110
104
  }
@@ -121,20 +115,13 @@ async function applyFix(res, loc) {
121
115
  const patches = JSON.parse(res.fix).patches || [];
122
116
  const original = fs.readFileSync(loc.path, 'utf8');
123
117
  let content = original;
124
-
125
118
  for (const p of patches) {
126
119
  const lines = content.split('\n');
127
120
  const idx = p.line - 1;
128
121
  if (idx >= 0 && idx < lines.length) {
129
122
  const indent = lines[idx].match(/^\s*/)[0];
130
123
  const cleanLine = indent + p.new_line.trim();
131
-
132
- // πŸ›‘ SAFETY GUARD: Prevent top-level await in class bodies
133
- if (cleanLine.includes('await') && !content.includes('async')) {
134
- console.log(` ⚠️ Blocked illegal await in non-async scope: ${path.basename(loc.path)}`);
135
- continue;
136
- }
137
-
124
+ if (cleanLine.includes('await') && !content.includes('async')) continue;
138
125
  if (!content.includes(p.new_line.trim())) {
139
126
  if (p.action === 'INSERT_AFTER') lines.splice(p.line, 0, cleanLine);
140
127
  else lines[idx] = cleanLine;
@@ -142,15 +129,9 @@ async function applyFix(res, loc) {
142
129
  }
143
130
  }
144
131
  }
145
-
146
132
  fs.writeFileSync(loc.path, content);
147
-
148
- // πŸ§ͺ SMARTER SYNTAX CHECK
149
133
  try {
150
- // Check for obvious syntax errors in TS (top level await in CommonJS/Class)
151
- if (content.match(/class\s+\w+\s+\{[\s\S]*?await\s+/)) {
152
- throw new Error("Illegal await in class body");
153
- }
134
+ if (content.match(/class\s+\w+\s+\{[\s\S]*?await\s+/)) throw new Error("Illegal await");
154
135
  return true;
155
136
  } catch(e) {
156
137
  fs.writeFileSync(loc.path, original);
@@ -173,36 +154,114 @@ function printFix(fix, loc) {
173
154
  }
174
155
 
175
156
  async function runDoctor() {
176
- console.log(`\nπŸ‘¨β€βš•οΈ ${C.BRIGHT}DeFlake Doctor - Diagnostic Tool${C.RESET}\n`);
177
- const dirs = ['test-results', 'playwright-report'];
178
- let error = false;
157
+ console.log(`\n${C.BG_BLUE}${C.WHITE}${C.BRIGHT} DEFLAKE MISSION CONTROL - DIAGNOSTIC CENTER ${C.RESET}\n`);
158
+
159
+ // 1. SYSTEM HUB
160
+ console.log(`${C.CYAN}1. SYSTEM CORE${C.RESET}`);
161
+ console.log(` ${C.GRAY}β”œβ”€ Version: ${C.RESET}${C.BRIGHT}v${pkg.version}${C.RESET}`);
162
+ console.log(` ${C.GRAY}β”œβ”€ Platform: ${C.RESET}${process.platform} (${process.arch})`);
163
+ console.log(` ${C.GRAY}└─ Project: ${C.RESET}${path.basename(process.cwd())}\n`);
179
164
 
180
- for (const d of dirs) {
181
- if (!fs.existsSync(d)) continue;
182
- process.stdout.write(` Checking ${d.padEnd(20)}... `);
165
+ // 2. PROJECT AUDIT (Discovery Phase)
166
+ console.log(`${C.CYAN}2. PROJECT DISCOVERY & VALIDATION${C.RESET}`);
167
+ const fw = DeFlakeClient.detectFramework();
168
+ console.log(` ${C.GRAY}β”œβ”€ Framework: ${C.RESET}${C.GREEN}${fw.toUpperCase()}${C.RESET}`);
169
+
170
+ const testDirs = ['tests', 'cypress/e2e', 'test', 'pages'];
171
+ let allFiles = [];
172
+ testDirs.forEach(d => {
173
+ if (fs.existsSync(d)) {
174
+ const files = fs.readdirSync(d, { recursive: true }).filter(f => f.endsWith('.ts') || f.endsWith('.js') || f.endsWith('.cy.js'));
175
+ files.forEach(f => allFiles.push({ name: f, full: path.join(d, f) }));
176
+ }
177
+ });
178
+
179
+ if (allFiles.length > 0) {
180
+ console.log(` ${C.GRAY}β”œβ”€ Discovery: ${C.RESET}${C.GREEN}${allFiles.length} files detected${C.RESET}`);
181
+ allFiles.slice(0, 8).forEach(file => {
182
+ process.stdout.write(` ${C.GRAY}β”‚ β”œβ”€ ${path.basename(file.name).padEnd(25)}${C.RESET} `);
183
+ try {
184
+ execSync(`node --check "${file.full}"`, { stdio: 'ignore' });
185
+ console.log(`${C.GREEN}βœ“ Syntax OK${C.RESET}`);
186
+ } catch (e) {
187
+ console.log(`${C.RED}❗ SYNTAX ERROR${C.RESET}`);
188
+ }
189
+ });
190
+ if (allFiles.length > 8) console.log(` ${C.GRAY}β”‚ └─ ... and ${allFiles.length - 8} more files validated.${C.RESET}`);
191
+ else console.log(` ${C.GRAY}β”‚ └─ All source files validated.${C.RESET}`);
192
+ } else {
193
+ console.log(` ${C.GRAY}└─ Discovery: ${C.RED}No test/page files found!${C.RESET}`);
194
+ }
195
+ console.log("");
196
+
197
+ // 3. API & SUBSCRIPTION HUB
198
+ console.log(`${C.CYAN}3. API & CLOUD HUB${C.RESET}`);
199
+ const apiKey = process.env.DEFLAKE_API_KEY || "NOT_SET";
200
+ const masked = apiKey === "NOT_SET" ? apiKey : (apiKey.substring(0, 4) + "****" + apiKey.substring(apiKey.length - 4));
201
+ console.log(` ${C.GRAY}β”œβ”€ API Key: ${C.RESET}${apiKey === "NOT_SET" ? C.RED : C.GREEN}${masked}${C.RESET}`);
202
+
203
+ if (apiKey !== "NOT_SET") {
183
204
  try {
184
- fs.accessSync(d, fs.constants.R_OK);
185
- const checkDeep = (p) => {
186
- fs.accessSync(p, fs.constants.R_OK);
187
- if (fs.statSync(p).isDirectory()) {
188
- fs.readdirSync(p).forEach(f => checkDeep(path.join(p, f)));
189
- }
190
- };
191
- checkDeep(d);
192
- console.log(`${C.GREEN}Healthy${C.RESET}`);
205
+ process.stdout.write(` ${C.GRAY}β”œβ”€ Gateway: ${C.RESET}πŸ“‘ Pinging...`);
206
+ const client = new DeFlakeClient();
207
+ const start = Date.now();
208
+ const usage = await client.getUsage();
209
+ const latency = Date.now() - start;
210
+ if (usage?.status === 'success') {
211
+ process.stdout.write(`\r ${C.GRAY}β”œβ”€ Gateway: ${C.RESET}${C.GREEN}ONLINE (${latency}ms)${C.RESET} \n`);
212
+ console.log(` ${C.GRAY}└─ Quota: ${C.RESET}${C.MAGENTA}${usage.data.usage}/${usage.data.limit} fixes used${C.RESET}`);
213
+ } else {
214
+ process.stdout.write(`\r ${C.GRAY}β”œβ”€ Gateway: ${C.RESET}${C.RED}Connection Failed (${usage?.message || 'Unauthorized'})${C.RESET} \n`);
215
+ }
193
216
  } catch (e) {
194
- console.log(`${C.RED}Access Denied${C.RESET}`);
195
- error = true;
217
+ process.stdout.write(`\r ${C.GRAY}β”œβ”€ Gateway: ${C.RESET}${C.RED}Link Down (${e.message})${C.RESET} \n`);
218
+ }
219
+ } else {
220
+ console.log(` ${C.GRAY}└─ Status: ${C.RED}Awaiting credentials...${C.RESET}`);
221
+ }
222
+ console.log("");
223
+
224
+ // 4. THE LIBERATION AUDIT (Permission Status)
225
+ console.log(`${C.CYAN}4. LIBERATION AUDIT (Permission Status)${C.RESET}`);
226
+ const artDirs = [
227
+ { path: 'test-results', label: 'Test Results' },
228
+ { path: 'playwright-report', label: 'HTML Report' }
229
+ ];
230
+ let allLiberated = true;
231
+ let totalItems = 0;
232
+
233
+ for (const d of artDirs) {
234
+ if (fs.existsSync(d.path)) {
235
+ process.stdout.write(` ${C.GRAY}β”œβ”€ ${d.label.padEnd(16)}: ${C.RESET}πŸ” Checking...`);
236
+ let dItems = 0;
237
+ try {
238
+ const scan = (p) => {
239
+ dItems++;
240
+ totalItems++;
241
+ fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK);
242
+ if (fs.statSync(p).isDirectory()) fs.readdirSync(p).forEach(f => scan(path.join(p, f)));
243
+ };
244
+ scan(d.path);
245
+ process.stdout.write(`\r ${C.GRAY}β”œβ”€ ${d.label.padEnd(16)}: ${C.RESET}${C.GREEN}${C.BRIGHT}[LIBERADOS]${C.RESET}${C.GREEN} (${dItems} Γ­tems accesibles)${C.RESET} \n`);
246
+ } catch (e) {
247
+ process.stdout.write(`\r ${C.GRAY}β”œβ”€ ${d.label.padEnd(16)}: ${C.RESET}${C.RED}${C.BRIGHT}[BLOQUEADOS]${C.RESET}${C.RED} (Error en: ${path.basename(e.path)})${C.RESET} \n`);
248
+ allLiberated = false;
249
+ }
250
+ } else {
251
+ console.log(` ${C.GRAY}β”œβ”€ ${d.label.padEnd(16)}: ${C.RESET}${C.GRAY}Sin carpeta (AΓΊn no hay resultados)${C.RESET}`);
196
252
  }
197
253
  }
198
254
 
199
- if (error) {
200
- console.log(`\n ${C.YELLOW}⚠️ Files are locked!${C.RESET}`);
201
- const cmd = process.platform === 'win32' ? `icacls . /grant \${env:USERNAME}:(OI)(CI)F /T` : `sudo chown -R $(whoami) .`;
202
- console.log(` ${C.BRIGHT}Fix:${C.RESET} ${C.CYAN}${cmd}${C.RESET}`);
255
+ if (!allLiberated) {
256
+ console.log(`\n ${C.BG_BLUE}${C.WHITE}${C.BRIGHT} ACCIΓ“N REQUERIDA: LIBERAR PERMISOS ${C.RESET}`);
257
+ const fix = process.platform === 'win32' ? 'icacls . /grant ${env:USERNAME}:(OI)(CI)F /T' : 'sudo chown -R $(whoami) .';
258
+ console.log(` Ejecuta esto en tu terminal: ${C.CYAN}${fix}${C.RESET}`);
203
259
  } else {
204
- console.log(`\n ✨ ${C.GREEN}All checks passed. System is ready.${C.RESET}`);
260
+ console.log(` ${C.GRAY}└─ AuditorΓ­a: ${C.RESET}${C.GREEN}${totalItems} archivos verificados con Γ©xito.${C.RESET}`);
261
+ console.log(`\n${C.BG_GREEN}${C.WHITE}${C.BRIGHT} ESTADO DEL SISTEMA: LIBERADO Y OPERATIVO ${C.RESET}`);
262
+ console.log(` Ya puedes correr: ${C.CYAN}npx deflake --fix --report npx playwright test${C.RESET}`);
205
263
  }
264
+ console.log("\n" + C.GRAY + "─".repeat(50) + C.RESET + "\n");
206
265
  }
207
266
 
208
267
  function showReport() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deflake",
3
- "version": "1.2.17",
3
+ "version": "1.2.21",
4
4
  "description": "AI-powered self-healing tool for Playwright, Cypress, and WebdriverIO tests.",
5
5
  "main": "client.js",
6
6
  "bin": {