修复: 安全加固+DRY 收敛+文档同步+测试补齐
安全: - ScriptNode 默认黑名单兜底(rm/del/format/shutdown/mkfs/dd) - bind_directory 分段 .. 检测替代 contains 子串(对齐 tool_registry) - ai_providers 白名单移除 api_key(防 update_field 旁路写明文) DRY: - useAiEvents 抽 cleanupTerminatedConversation 统一三分支收尾 - 新增 useStoreAction 工具,4 个 store 替换 38 处 try/catch 样板 文档: - df-core → df-types 批量替换(ARCHITECTURE/PROGRESS/SQLite-CRUD) - INDEX 补齐 9 漏列文档(单对话并行多轮/跑题试验/工程系统设计等) - Agent架构说明 死链修复(../构想审查/) - AI对话引擎工具清单改为数量+按风险分组(不再用固定数字) - ARCH 状态标签 设计阶段 → Phase 2 验证 测试: - df-relay 新增 registry_test: ConnRegistry 路由 + RelayState + 16 项单测
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Store action 统一错误处理工具(任务 #7 DRY 抽离)。
|
||||
*
|
||||
* 之前 4 个 store(knowledge/ideas/projects/tasks)共 38 处重复:
|
||||
* try { ... } catch (e: any) { state.error = e?.toString() ?? t('xxx') }
|
||||
*
|
||||
* 本工具:
|
||||
* - 用 `catch (e: unknown)` 替代 `any`(类型安全)
|
||||
* - 失败时写 state.error,返回 undefined(调用方判断)
|
||||
* - 成功时返回 fn 结果
|
||||
*
|
||||
* 注:带 seq 竞态守卫的场景调用方需在 fn 内部判断,不在本工具职责范围。
|
||||
*/
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
/** 带 error 字段的 store state 形状(仅约束 error,其他字段任意)。 */
|
||||
export interface ErrorSink {
|
||||
error: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行异步 action,失败时写 state.error + 返回 undefined。
|
||||
*
|
||||
* @param state 带 error 字段的响应式 state
|
||||
* @param i18nKey 失败时的 i18n 文案 key(兜底)
|
||||
* @param fn 异步 action
|
||||
* @returns 成功返 fn 结果;失败返 undefined
|
||||
*/
|
||||
export async function runWithCatch<T>(
|
||||
state: ErrorSink,
|
||||
i18nKey: string,
|
||||
fn: () => Promise<T>,
|
||||
): Promise<T | undefined> {
|
||||
try {
|
||||
return await fn()
|
||||
} catch (e: unknown) {
|
||||
state.error = stringifyError(e) ?? i18nKey
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 unknown 错误转为可读字符串(无则返 undefined,由调用方兜底 i18n)。
|
||||
*
|
||||
* Tauri invoke 抛出的通常是 string 或 Error 实例。
|
||||
*/
|
||||
export function stringifyError(e: unknown): string | undefined {
|
||||
if (typeof e === 'string') return e
|
||||
if (e instanceof Error) return e.message || e.toString()
|
||||
if (e && typeof e === 'object' && 'toString' in e) {
|
||||
try {
|
||||
return String((e as { toString(): string }).toString())
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant:带前置守卫的错误捕获。
|
||||
*
|
||||
* 用于「旧请求的失败不污染当前视图」场景(如 seq 守卫)。
|
||||
*
|
||||
* @param guard 返回 true 时才写 state.error;false 时静默(旧响应丢弃)
|
||||
*/
|
||||
export async function runWithCatchGuarded<T>(
|
||||
state: ErrorSink,
|
||||
i18nKey: string,
|
||||
guard: () => boolean,
|
||||
fn: () => Promise<T>,
|
||||
): Promise<T | undefined> {
|
||||
try {
|
||||
return await fn()
|
||||
} catch (e: unknown) {
|
||||
if (guard()) {
|
||||
state.error = stringifyError(e) ?? i18nKey
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 显式标记 Ref 未使用(避免未来用上时改导入)
|
||||
export type _Unused = Ref<unknown>
|
||||
Reference in New Issue
Block a user