/** * Dev-only error overlay. When a loader/action/render throws during `@nifrajs/web/vite` dev, this * renders a readable full-page overlay from a structured [Diagnostic] - the code, the message, a source * codeframe around the offending line, the recognised cause/fix when nifra has one, and the stack - * instead of a bare `err.stack` text dump. Dev-only by construction: it's called solely from the dev * server's catch, never in production (production maps errors to the `_error` route boundary). * * The overlay and the agent surfaces (`/__nifra/last-error`, `nifra_explain`) render from the SAME * `Diagnostic`, so a person and an agent see the identical failure, one as HTML and one as JSON. */ import type { Diagnostic } from "./diagnostic.ts" const esc = (s: string): string => s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """) /** The source codeframe block: numbered lines with the offending one highlighted. */ function codeframeHtml(diagnostic: Diagnostic): string { const cf = diagnostic.codeframe if (cf === undefined) return "" const loc = `${esc(cf.file)}:${cf.line}${cf.column !== undefined ? `:${cf.column}` : ""}` const rows = cf.lines .map( (l) => `
${l.number}${esc(l.text) || " "}
`, ) .join("") return `

Source

${loc}
${rows}
` } /** The recognised-failure callout: cause + fix + docs anchor, shown only when nifra classified the error. */ function fixHtml(diagnostic: Diagnostic): string { if (diagnostic.fix === undefined) return "" const anchor = diagnostic.docsAnchor !== undefined ? `
docs: ${esc(diagnostic.docsAnchor)}
` : "" const cause = diagnostic.cause !== undefined ? `

${esc(diagnostic.cause)}

` : "" return `
likely fix
${cause}

${esc(diagnostic.fix)}

${anchor}
` } /** Render the overlay HTML from a prebuilt Diagnostic (the same object the agent surfaces serve). */ export function renderDiagnosticOverlay(diagnostic: Diagnostic): string { const name = esc(diagnostic.name || "Error") const code = esc(diagnostic.code) const reqLine = diagnostic.request !== undefined ? `${esc(diagnostic.request.method)} ${esc(diagnostic.request.url)}` : "" const framesHtml = diagnostic.frames.length > 0 ? `
    ${diagnostic.frames.map((f) => `
  1. ${esc(f.raw)}
  2. `).join("")}
` : `

No stack frames.

` return `${name} - nifra dev
nifra dev${code}${name}${reqLine}

${esc(diagnostic.message)}

${fixHtml(diagnostic)} ${codeframeHtml(diagnostic)}

Stack

${framesHtml}
` }