/**
* error-overlay.ts — Dev 模式 HTML 错误覆盖层渲染器
*
* 接受一个 Error 对象和 projectRoot 路径,生成完整的 HTML 错误覆盖层字符串。
* 功能:
* - 解析 Error.stack(--enable-source-maps 翻译后为 .ts 路径)
* - 区分用户代码帧(展开 + 源码上下文)与内部帧(折叠)
* - 支持 dark / light 主题
* - vscode:// 链接直接跳转到出错行
* - VextValidationError 专属:字段级错误列表
* - 最多展示 N 帧(防止超深调用栈撑爆 HTML)
*
* 设计约束:
* - 不 import 任何框架类型(error-overlay.ts 是框架内部工具,不暴露到公共 API)
* - 使用 fs.readFileSync 同步读取(仅 dev 模式错误路径,性能可接受)
* - 渲染失败时抛出异常,由外层 error-handler.ts 的 try/catch 降级到 JSON
*
* @module lib/dev/error-overlay
* @see requirements/dev-error-overlay/02-技术方案.md §2.1
*/
/**
* 解析后的堆栈帧
*/
export interface StackFrame {
/** 函数名(未知时为 '') */
fn: string;
/** 绝对文件路径(--enable-source-maps 翻译后为 .ts) */
file: string;
/** 行号(1-based) */
line: number;
/** 列号(1-based) */
col: number;
}
/**
* 源码上下文(出错行 ±3 行)
*/
export interface SourceContext {
lines: Array<{
/** 行号(1-based) */
num: number;
/** 行内容(原始文本) */
content: string;
/** 是否为出错行(高亮) */
isError: boolean;
}>;
}
/**
* renderDevErrorPage 的可选配置
*/
export interface DevOverlayOptions {
/** 主题:深色 / 浅色(默认 'dark') */
theme?: "dark" | "light";
/** 最多显示的堆栈帧数(默认 25) */
maxFrames?: number;
}
/**
* HTML 特殊字符转义(防止 XSS)
*/
export declare function escapeHtml(str: string): string;
/**
* 解析 Error.stack 字符串为 StackFrame 数组
*
* 支持两种格式:
* - " at FunctionName (file:line:col)"
* - " at file:line:col"
* - Windows 路径:反斜杠会被处理
* - node: 内置模块:file 以 "node:" 开头
*/
export declare function parseStackTrace(stack: string): StackFrame[];
/**
* 将帧文件路径解析为绝对路径
*
* --enable-source-maps 翻译后的堆栈路径存在两种偏差:
* A. 相对路径(如 "../src/routes/index.ts")
* B. 绝对但层级偏移(如 "E:/MySelf/src/routes/index.ts",
* 实际应为 "E:/MySelf/vext-test/src/routes/index.ts")
*
* 这是因为 esbuild sourcemap 的 sources 路径相对于编译产物目录(.vext/dev/),
* Node.js --enable-source-maps 解析时可能丢失中间层级。
*
* 解析策略(通用后缀匹配,按优先级):
* 1. 已是绝对路径且文件存在 → 直接返回
* 2. "node:" 前缀 → 直接返回
* 3. 从最长尾部到最短(至少 2 段)逐一拼接 projectRoot 检查是否存在
* e.g. "E:\MySelf\src\routes\index.ts" → 尝试 projectRoot/MySelf/src/routes/index.ts(否)
* → 尝试 projectRoot/src/routes/index.ts(是!)
* 该策略覆盖 src/、plugins/、middlewares/、services/ 等任意子目录
* 4. 相对路径 → path.resolve(projectRoot, file)
* 5. 以上都失败 → 返回原始路径(graceful 降级)
*/
export declare function resolveFramePath(file: string, projectRoot: string): string;
/**
* 判断帧是否为用户代码(而非框架内部或 node_modules)
*
* 条件:
* 1. file 以 projectRoot 开头
* 2. file 不包含 'node_modules'
* 3. file 不以 'node:' 开头(Node.js 内置模块)
* 4. file 不包含 '.vext/dev/'(未翻译的编译产物,--enable-source-maps 应已翻译)
*/
export declare function isUserFrame(frame: StackFrame, projectRoot: string): boolean;
/**
* 读取文件中指定行号 ±3 行的上下文
*
* 边界处理:
* - 文件不存在 → 返回 null(E2)
* - 文件读取失败 → 返回 null(E3)
* - 行号超出范围 → 取可用范围
*/
export declare function getSourceContext(file: string, errorLine: number): SourceContext | null;
/**
* renderDevErrorPage — 生成完整的 HTML 错误覆盖层字符串
*
* @param err 抛出的错误(任意类型,非 Error 对象会被转换)
* @param projectRoot 用户项目根目录(绝对路径),用于区分用户帧/内部帧
* @param options 可选配置(主题、最大帧数)
* @returns 完整的 HTML 字符串(可直接作为 HTTP 响应体)
*/
export declare function renderDevErrorPage(err: unknown, projectRoot: string, options?: DevOverlayOptions): string;