deflake 1.2.35 → 1.2.37
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/cli.js +42 -7
- package/client.js +3 -3
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -127,17 +127,25 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '') {
|
|
|
127
127
|
try {
|
|
128
128
|
const content = fs.readFileSync(art.htmlPath, 'utf8');
|
|
129
129
|
|
|
130
|
+
// ALWAYS extract the relevant error block from captured output for this artifact
|
|
131
|
+
// This gives us: failing locator string, error type, call log, stack trace
|
|
132
|
+
const relevantOutput = cleanOutput ? extractRelevantOutput(cleanOutput, art.name) : '';
|
|
133
|
+
if (relevantOutput) {
|
|
134
|
+
console.log(` ${C.GRAY}🔬 Error context: ${relevantOutput.length} chars captured${C.RESET}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
130
137
|
// SMART Location Extraction: try multiple sources
|
|
131
138
|
let loc = extractLoc(content);
|
|
132
139
|
let locSource = 'error-context.md';
|
|
133
140
|
|
|
134
|
-
if (!loc &&
|
|
135
|
-
|
|
136
|
-
const relevantOutput = extractRelevantOutput(cleanOutput, art.name);
|
|
137
|
-
console.log(` ${C.GRAY}🔬 Relevant output: ${relevantOutput.length} chars for artifact${C.RESET}`);
|
|
138
|
-
loc = extractLoc(relevantOutput || cleanOutput);
|
|
141
|
+
if (!loc && relevantOutput) {
|
|
142
|
+
loc = extractLoc(relevantOutput);
|
|
139
143
|
locSource = 'console output';
|
|
140
144
|
}
|
|
145
|
+
if (!loc && cleanOutput) {
|
|
146
|
+
loc = extractLoc(cleanOutput);
|
|
147
|
+
locSource = 'full output';
|
|
148
|
+
}
|
|
141
149
|
|
|
142
150
|
// Step 1: Location extraction
|
|
143
151
|
if (loc) {
|
|
@@ -150,9 +158,9 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '') {
|
|
|
150
158
|
const source = loc && fs.existsSync(loc.path) ? fs.readFileSync(loc.path, 'utf8') : null;
|
|
151
159
|
console.log(` ${C.GRAY}📄 Source:${C.RESET} ${source ? 'Loaded (' + source.split('\n').length + ' lines)' : C.YELLOW + 'Not available' + C.RESET}`);
|
|
152
160
|
|
|
153
|
-
// Step 3: API call — send BOTH error-context AND console
|
|
161
|
+
// Step 3: API call — send BOTH error-context AND console error block for richer diagnosis
|
|
154
162
|
console.log(` ${C.GRAY}🌐 Calling DeFlake API...${C.RESET}`);
|
|
155
|
-
const res = await client.heal(null, art.htmlPath, loc, source, argv.fix);
|
|
163
|
+
const res = await client.heal(null, art.htmlPath, loc, source, argv.fix, relevantOutput);
|
|
156
164
|
|
|
157
165
|
if (res && res.status === 'success') {
|
|
158
166
|
console.log(` ${C.GREEN}✅ API Response: SUCCESS${C.RESET}`);
|
|
@@ -208,7 +216,27 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '') {
|
|
|
208
216
|
}
|
|
209
217
|
console.log(`${C.BRIGHT}━━━ DeFlake Summary: ${count}/${artifacts.length} fix(es) applied ━━━${C.RESET}\n`);
|
|
210
218
|
return count;
|
|
219
|
+
}
|
|
211
220
|
|
|
221
|
+
function checkBalance(code) {
|
|
222
|
+
const pairs = { '(': ')', '{': '}', '[': ']' };
|
|
223
|
+
const stack = [];
|
|
224
|
+
// Skip strings and comments for accuracy
|
|
225
|
+
let inString = false, stringChar = '', escaped = false;
|
|
226
|
+
for (const ch of code) {
|
|
227
|
+
if (escaped) { escaped = false; continue; }
|
|
228
|
+
if (ch === '\\') { escaped = true; continue; }
|
|
229
|
+
if (inString) { if (ch === stringChar) inString = false; continue; }
|
|
230
|
+
if (ch === '"' || ch === "'" || ch === '`') { inString = true; stringChar = ch; continue; }
|
|
231
|
+
if (pairs[ch]) stack.push(pairs[ch]);
|
|
232
|
+
else if (ch === ')' || ch === '}' || ch === ']') {
|
|
233
|
+
if (stack.length === 0 || stack.pop() !== ch) {
|
|
234
|
+
return { balanced: false, detail: `unexpected '${ch}'` };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (stack.length > 0) return { balanced: false, detail: `unclosed '${stack[stack.length - 1] === ')' ? '(' : stack[stack.length - 1] === '}' ? '{' : '['}'` };
|
|
239
|
+
return { balanced: true, detail: 'ok' };
|
|
212
240
|
}
|
|
213
241
|
|
|
214
242
|
async function applyFix(res, loc) {
|
|
@@ -262,6 +290,13 @@ async function applyFix(res, loc) {
|
|
|
262
290
|
}
|
|
263
291
|
}
|
|
264
292
|
if (appliedCount > 0) {
|
|
293
|
+
// SAFETY: Bracket balance check — rollback if patches broke syntax
|
|
294
|
+
const originalBalance = checkBalance(original);
|
|
295
|
+
const patchedBalance = checkBalance(content);
|
|
296
|
+
if (originalBalance.balanced && !patchedBalance.balanced) {
|
|
297
|
+
console.log(` ${C.YELLOW}⏭️ Rollback: patches broke bracket balance (${patchedBalance.detail}). Reverting file.${C.RESET}`);
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
265
300
|
fs.writeFileSync(loc.path, content);
|
|
266
301
|
return true;
|
|
267
302
|
}
|
package/client.js
CHANGED
|
@@ -103,7 +103,7 @@ class DeFlakeClient {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
async heal(logPath, htmlPath, failureLocation = null, sourceCode = null, applyFix = false) {
|
|
106
|
+
async heal(logPath, htmlPath, failureLocation = null, sourceCode = null, applyFix = false, errorContext = '') {
|
|
107
107
|
try {
|
|
108
108
|
if (!fs.existsSync(htmlPath)) throw new Error(`HTML file not found: ${htmlPath}`);
|
|
109
109
|
|
|
@@ -126,9 +126,9 @@ class DeFlakeClient {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
const payload = {
|
|
129
|
-
error_log: logContent || "",
|
|
129
|
+
error_log: logContent || errorContext || "",
|
|
130
130
|
html_snapshot: htmlContent || "",
|
|
131
|
-
failing_line: failureLocation ? `Line ${failureLocation.
|
|
131
|
+
failing_line: failureLocation ? `Line ${failureLocation.line}` : "",
|
|
132
132
|
source_code: sourceCode || "",
|
|
133
133
|
framework: this.framework,
|
|
134
134
|
apply_fix: applyFix
|