deepsite / lib /error-formatter.ts
victor's picture
victor HF Staff
feat: add automatic error detection and fixing for preview
473668f
raw
history blame
4.38 kB
import type { PreviewError } from "@/types/preview-error";
// Sanitize error messages to prevent prompt injection
function sanitizeErrorMessage(message: string): string {
return message
.replace(/[<>]/g, "") // Remove potential HTML tags
.replace(/```/g, "'''") // Escape code blocks
.slice(0, 500); // Limit length
}
export function formatErrorsForAI(
errors: PreviewError[],
html: string
): string {
if (!errors || errors.length === 0) return "";
// Validate errors array
const validErrors = errors.filter((e) => e && typeof e.message === "string");
if (validErrors.length === 0) return "";
// Group errors by type for better organization
const errorGroups = validErrors.reduce((acc, error) => {
const type = error.errorType || error.type;
if (!acc[type]) acc[type] = [];
acc[type].push(error);
return acc;
}, {} as Record<string, PreviewError[]>);
let formattedErrors =
"The following errors were detected in the preview:\n\n";
// Format each error group
Object.entries(errorGroups).forEach(([type, groupErrors]) => {
formattedErrors += `### ${type} (${groupErrors.length} error${
groupErrors.length > 1 ? "s" : ""
})\n\n`;
groupErrors.forEach((error, index) => {
const sanitizedMessage = sanitizeErrorMessage(error.message);
formattedErrors += `${index + 1}. **${sanitizedMessage}**\n`;
if (error.lineNumber) {
formattedErrors += ` - Line: ${error.lineNumber}`;
if (error.columnNumber) {
formattedErrors += `, Column: ${error.columnNumber}`;
}
formattedErrors += "\n";
}
if (error.fileName && error.fileName !== "undefined") {
formattedErrors += ` - File: ${error.fileName}\n`;
}
// For resource errors, include the problematic resource
if (error.type === "resource-error" && error.src) {
formattedErrors += ` - Resource: ${error.src}\n`;
formattedErrors += ` - Tag: <${error.tagName?.toLowerCase()}>\n`;
}
// Include relevant code snippet if we have line numbers
if (error.lineNumber && html) {
const lines = html.split("\n");
const startLine = Math.max(0, error.lineNumber - 3);
const endLine = Math.min(lines.length, error.lineNumber + 2);
if (lines[error.lineNumber - 1]) {
formattedErrors += " - Code context:\n";
formattedErrors += " ```html\n";
for (let i = startLine; i < endLine; i++) {
const marker = i === error.lineNumber - 1 ? ">" : " ";
formattedErrors += ` ${marker} ${i + 1}: ${lines[i]}\n`;
}
formattedErrors += " ```\n";
}
}
formattedErrors += "\n";
});
});
return formattedErrors;
}
export function createErrorFixPrompt(
errors: PreviewError[],
html: string
): string {
const formattedErrors = formatErrorsForAI(errors, html);
return `${formattedErrors}
Please fix these errors in the HTML code. Focus on:
1. Fixing JavaScript syntax errors
2. Resolving undefined variables or functions
3. Fixing broken resource links (404s)
4. Ensuring all referenced libraries are properly loaded
5. Fixing any HTML structure issues
Make the minimum necessary changes to fix the errors while preserving the intended functionality.`;
}
// Check if errors are likely fixable by AI
export function areErrorsFixable(errors: PreviewError[]): boolean {
if (!errors || errors.length === 0) return false;
// Filter out errors that are likely not fixable
const fixableErrors = errors.filter((error) => {
// Skip errors from external resources
if (
error.fileName &&
(error.fileName.includes("http://") ||
error.fileName.includes("https://"))
) {
return false;
}
// Skip certain console errors that might be intentional
if (
error.type === "console-error" &&
error.message.includes("Development mode")
) {
return false;
}
return true;
});
return fixableErrors.length > 0;
}
// Deduplicate similar errors
export function deduplicateErrors(errors: PreviewError[]): PreviewError[] {
const seen = new Set<string>();
return errors.filter((error) => {
const key = `${error.type}-${error.message}-${error.lineNumber || 0}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}