From d009c3b616045f9c1f46d047fa70e557282346c8 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Tue, 21 Jul 2026 01:10:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(common):=20ValidationError=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E7=BB=A7=E6=89=BF=20Error=20=E4=BF=AE=E5=A4=8D=20Herm?= =?UTF-8?q?es=20=E6=A0=A1=E9=AA=8C=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Babel 转译子类 apply 调用原生 ZodError 在 Hermes 抛 TypeError,掩盖真实校验失败 - unexpected 分支补安全诊断日志(构造名+截断消息) - xuqm-bundles.gradle 将 file: 依赖的 src/metro 纳入任务输入 --- packages/common/src/api/diagnostics.ts | 11 +++++++++++ packages/common/src/api/useApi.ts | 9 ++++++++- packages/common/src/api/useRequest.ts | 12 ++++++++++-- packages/update/android/xuqm-bundles.gradle | 5 ++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/common/src/api/diagnostics.ts b/packages/common/src/api/diagnostics.ts index c50c051..72e1191 100644 --- a/packages/common/src/api/diagnostics.ts +++ b/packages/common/src/api/diagnostics.ts @@ -64,6 +64,17 @@ export function createAxiosDiagnostic(error: AxiosError): SafeApiDiagnostic { } } +/** + * 非 axios 异常只允许记录构造名与截断消息。 + * 不接触 cause、stack、config 或任何可能持有业务数据的对象。 + */ +export function createUnexpectedDiagnostic(error: unknown): string { + if (error instanceof Error) { + return `${error.name}: ${error.message.slice(0, 200)}` + } + return typeof error +} + type ValidationCause = { issues: z.ZodIssue[] response: AxiosResponse diff --git a/packages/common/src/api/useApi.ts b/packages/common/src/api/useApi.ts index d1a9e5b..9aec1ac 100644 --- a/packages/common/src/api/useApi.ts +++ b/packages/common/src/api/useApi.ts @@ -10,6 +10,7 @@ import { z } from 'zod' import { createAxiosDiagnostic, createResponseDiagnostic, + createUnexpectedDiagnostic, createValidationDiagnostic, } from './diagnostics' import { RequestError } from './errors' @@ -123,7 +124,13 @@ export const useApi = = z.infer return Promise.reject(new RequestError('网络请求失败', 'AxiosError', thrownError)) } - console.error('[XuqmAPI] unexpected request failure') + console.error( + '[XuqmAPI] unexpected request failure', + createUnexpectedDiagnostic(thrownError), + ) + if (thrownError instanceof Error && thrownError.stack) { + console.error('[XuqmAPI] unexpected request failure stack', thrownError.stack) + } return Promise.reject(new RequestError('网络请求失败', 'OtherError', thrownError)) }, ) diff --git a/packages/common/src/api/useRequest.ts b/packages/common/src/api/useRequest.ts index c84f28a..e7a1316 100644 --- a/packages/common/src/api/useRequest.ts +++ b/packages/common/src/api/useRequest.ts @@ -21,13 +21,21 @@ export interface RequestOptions { // ─── 内部 ValidationError(对齐 @szyx-mobile/use-axios 的 ValidationError)──── -export class ValidationError extends z.ZodError { +// 不能 extends z.ZodError:zod v4 的 ZodError 是 $constructor 工厂函数, +// 其自定义 Symbol.hasInstance 要求实例已初始化 _zod.traits。Babel 转译的子类 +// 在 super() 之前先执行 _classCallCheck(this, ValidationError),此时 _zod 尚不 +// 存在,instanceof 返回 false,直接抛 “Cannot call a class as a function”, +// 把真实的校验失败掩盖成非预期错误。 +export class ValidationError extends Error { + public readonly issues: z.ZodIssue[] + constructor( issues: z.ZodIssue[], public readonly response: AxiosResponse, ) { - super(issues) + super(issues[0]?.message ?? 'ValidationError') this.name = 'ValidationError' + this.issues = issues } } diff --git a/packages/update/android/xuqm-bundles.gradle b/packages/update/android/xuqm-bundles.gradle index c19363c..72686fe 100644 --- a/packages/update/android/xuqm-bundles.gradle +++ b/packages/update/android/xuqm-bundles.gradle @@ -142,9 +142,12 @@ def prepareXuqmBundles = tasks.register( include("*.mjs") }) // registry 依赖由不可变版本号进入 baseline;file: 依赖在版本不变时也可能修改 - // 原生源码,必须同时成为 Gradle 输入和 native baseline 内容。 + // 源码,必须同时成为 Gradle 输入和 native baseline 内容。 + // JS/TS 源码决定 bundle 内容,android/ios 决定 native baseline,缺一不可。 xuqmLocalDependencyDirectories.each { dependencyDirectory -> sourceFiles.from(project.fileTree(dependencyDirectory) { + include("src/**") + include("metro/**") include("android/**") include("ios/**") include("*.podspec")