fix(common): ValidationError 改为继承 Error 修复 Hermes 校验崩溃
- Babel 转译子类 apply 调用原生 ZodError 在 Hermes 抛 TypeError,掩盖真实校验失败 - unexpected 分支补安全诊断日志(构造名+截断消息) - xuqm-bundles.gradle 将 file: 依赖的 src/metro 纳入任务输入
这个提交包含在:
父节点
507ce3d2ee
当前提交
d009c3b616
@ -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 = {
|
type ValidationCause = {
|
||||||
issues: z.ZodIssue[]
|
issues: z.ZodIssue[]
|
||||||
response: AxiosResponse<unknown>
|
response: AxiosResponse<unknown>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { z } from 'zod'
|
|||||||
import {
|
import {
|
||||||
createAxiosDiagnostic,
|
createAxiosDiagnostic,
|
||||||
createResponseDiagnostic,
|
createResponseDiagnostic,
|
||||||
|
createUnexpectedDiagnostic,
|
||||||
createValidationDiagnostic,
|
createValidationDiagnostic,
|
||||||
} from './diagnostics'
|
} from './diagnostics'
|
||||||
import { RequestError } from './errors'
|
import { RequestError } from './errors'
|
||||||
@ -123,7 +124,13 @@ export const useApi = <S extends z.ZodTypeAny, T extends z.infer<S> = z.infer<S>
|
|||||||
return Promise.reject(new RequestError('网络请求失败', 'AxiosError', thrownError))
|
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))
|
return Promise.reject(new RequestError('网络请求失败', 'OtherError', thrownError))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -21,13 +21,21 @@ export interface RequestOptions {
|
|||||||
|
|
||||||
// ─── 内部 ValidationError(对齐 @szyx-mobile/use-axios 的 ValidationError)────
|
// ─── 内部 ValidationError(对齐 @szyx-mobile/use-axios 的 ValidationError)────
|
||||||
|
|
||||||
export class ValidationError<T = unknown, D = unknown> 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<T = unknown, D = unknown> extends Error {
|
||||||
|
public readonly issues: z.ZodIssue[]
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
issues: z.ZodIssue[],
|
issues: z.ZodIssue[],
|
||||||
public readonly response: AxiosResponse<T, D>,
|
public readonly response: AxiosResponse<T, D>,
|
||||||
) {
|
) {
|
||||||
super(issues)
|
super(issues[0]?.message ?? 'ValidationError')
|
||||||
this.name = 'ValidationError'
|
this.name = 'ValidationError'
|
||||||
|
this.issues = issues
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -142,9 +142,12 @@ def prepareXuqmBundles = tasks.register(
|
|||||||
include("*.mjs")
|
include("*.mjs")
|
||||||
})
|
})
|
||||||
// registry 依赖由不可变版本号进入 baseline;file: 依赖在版本不变时也可能修改
|
// registry 依赖由不可变版本号进入 baseline;file: 依赖在版本不变时也可能修改
|
||||||
// 原生源码,必须同时成为 Gradle 输入和 native baseline 内容。
|
// 源码,必须同时成为 Gradle 输入和 native baseline 内容。
|
||||||
|
// JS/TS 源码决定 bundle 内容,android/ios 决定 native baseline,缺一不可。
|
||||||
xuqmLocalDependencyDirectories.each { dependencyDirectory ->
|
xuqmLocalDependencyDirectories.each { dependencyDirectory ->
|
||||||
sourceFiles.from(project.fileTree(dependencyDirectory) {
|
sourceFiles.from(project.fileTree(dependencyDirectory) {
|
||||||
|
include("src/**")
|
||||||
|
include("metro/**")
|
||||||
include("android/**")
|
include("android/**")
|
||||||
include("ios/**")
|
include("ios/**")
|
||||||
include("*.podspec")
|
include("*.podspec")
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户