AGC认证失败按类别变体与类型化reason分流

- AuthenticatedClient 三个 catch 把 authNetworkFailure/authResponseInvalid 收成一个分支,服务地址 7 种失败与响应契约 5 种破损改为在 payload.reason 上二次 switch
- 每个带载荷分支先 as 取自己的具名类型,reason/default 用 expectNever 让漏接 reason 也在编译期失败
- 系统变体仍原样 throw,经全局 unhandledrejection 交给错误池
This commit is contained in:
2026-10-02 02:42:30 +08:00
parent d5516d43fa
commit e955d79984
@@ -38,6 +38,7 @@ import {
import type { PasswordLoginRejected } from '../services/generated/PasswordLoginRejected';
import type { PhoneCodeLoginRejected } from '../services/generated/PhoneCodeLoginRejected';
import type { SendCodeRejected } from '../services/generated/SendCodeRejected';
import type { ServerAddressRejected } from '../services/generated/ServerAddressRejected';
import {
beginPlatformSessionClearTransition,
beginPlatformSessionTransition,
@@ -272,44 +273,36 @@ export function AuthenticatedClient({
const failure = error.error;
switch (failure.type) {
// 预期业务拒绝:调用方补上本次操作的上下文,展示后不进错误池。
case 'serverAddressEmptyOrTooLong': {
const text = '服务器地址非法: 不能为空或超长';
setAuthCheckError(text);
setLoginStatus(text);
break;
}
case 'serverAddressNotAUrl': {
const text = '服务器地址非法: 不是合法地址';
setAuthCheckError(text);
setLoginStatus(text);
break;
}
case 'serverAddressHasCredentials': {
const text = '服务器地址非法: 不能包含用户名或密码';
setAuthCheckError(text);
setLoginStatus(text);
break;
}
case 'serverAddressHasPathOrQueryOrFragment': {
const text = '服务器地址非法: 不能带路径、查询或片段';
setAuthCheckError(text);
setLoginStatus(text);
break;
}
case 'serverAddressNotHttps': {
const text = '服务器地址非法: 远程地址必须使用 https';
setAuthCheckError(text);
setLoginStatus(text);
break;
}
case 'serverAddressUnsupportedScheme': {
const text = '服务器地址非法: 只支持 http 或 https';
setAuthCheckError(text);
setLoginStatus(text);
break;
}
case 'serverAddressOutsideChannel': {
const text = '服务器地址非法: 不在当前构建渠道范围内';
case 'serverAddressRejected': {
const payload = failure as ServerAddressRejected;
let text: string;
switch (payload.reason) {
case 'emptyOrTooLong':
text = '服务器地址非法: 不能为空或超长';
break;
case 'notAUrl':
text = '服务器地址非法: 不是合法地址';
break;
case 'hasCredentials':
text = '服务器地址非法: 不能包含用户名或密码';
break;
case 'hasPathOrQueryOrFragment':
text = '服务器地址非法: 不能带路径、查询或片段';
break;
case 'notHttps':
text = '服务器地址非法: 远程地址必须使用 https';
break;
case 'unsupportedScheme':
text = '服务器地址非法: 只支持 http 或 https';
break;
case 'outsideChannel':
text = '服务器地址非法: 不在当前构建渠道范围内';
break;
default: {
expectNever(payload.reason);
text = '服务器地址非法';
}
}
setAuthCheckError(text);
setLoginStatus(text);
break;
@@ -376,15 +369,10 @@ export function AuthenticatedClient({
case 'permissionDenied':
break;
// 系统失败:调用方处理不了,原样抛出 → 全局 unhandledrejection 交给错误池。
case 'authNetworkTimeout':
case 'authNetworkUnreachable':
case 'authNetworkFailure':
case 'authServiceUnavailable':
case 'unexpectedRejection':
case 'authResponseNotJson':
case 'authResponseInvalidBody':
case 'authResponseMissingRefreshCookie':
case 'authResponseMissingUserIdentity':
case 'authResponseServerRejected':
case 'authResponseInvalid':
case 'clientSessionPersistFailed':
case 'runtimeSessionInstallFailed':
case 'authClientInitFailed':
@@ -457,7 +445,8 @@ export function AuthenticatedClient({
* - 承载:命令失败由 `invokeClientAuth` 装进 `ClientAuthErrorWrapper`,`error` 就是判别联合,
* `failure.type` 是唯一分流键;
* - 业务 / 会话变体:无载荷变体用本 catch 的固定文案;带载荷变体先 `as` 取具名类型,
* 再用它自己的 `serverMessage` 拼上下文(Rust 不预拼用户可见文案),永不进错误池;
* 可枚举的细分再用 `switch (payload.reason)` 在类型化 reason 上分流并拼上下文
* (Rust 不预拼用户可见文案),永不进错误池;
* - 系统变体:原样 `throw`,经全局 `unhandledrejection` 交给错误池;
* - `default: expectNever` 保证 Rust 新增变体时这里编译失败。
*
@@ -489,32 +478,37 @@ export function AuthenticatedClient({
const failure = error.error;
switch (failure.type) {
// 预期业务拒绝:调用方补上本次操作的上下文,展示后不进错误池。
case 'serverAddressEmptyOrTooLong': {
setLoginStatus('服务器地址非法: 不能为空或超长');
break;
}
case 'serverAddressNotAUrl': {
setLoginStatus('服务器地址非法: 不是合法地址');
break;
}
case 'serverAddressHasCredentials': {
setLoginStatus('服务器地址非法: 不能包含用户名或密码');
break;
}
case 'serverAddressHasPathOrQueryOrFragment': {
setLoginStatus('服务器地址非法: 不能带路径、查询或片段');
break;
}
case 'serverAddressNotHttps': {
setLoginStatus('服务器地址非法: 远程地址必须使用 https');
break;
}
case 'serverAddressUnsupportedScheme': {
setLoginStatus('服务器地址非法: 只支持 http 或 https');
break;
}
case 'serverAddressOutsideChannel': {
setLoginStatus('服务器地址非法: 不在当前构建渠道范围内');
case 'serverAddressRejected': {
const payload = failure as ServerAddressRejected;
let text: string;
switch (payload.reason) {
case 'emptyOrTooLong':
text = '服务器地址非法: 不能为空或超长';
break;
case 'notAUrl':
text = '服务器地址非法: 不是合法地址';
break;
case 'hasCredentials':
text = '服务器地址非法: 不能包含用户名或密码';
break;
case 'hasPathOrQueryOrFragment':
text = '服务器地址非法: 不能带路径、查询或片段';
break;
case 'notHttps':
text = '服务器地址非法: 远程地址必须使用 https';
break;
case 'unsupportedScheme':
text = '服务器地址非法: 只支持 http 或 https';
break;
case 'outsideChannel':
text = '服务器地址非法: 不在当前构建渠道范围内';
break;
default: {
expectNever(payload.reason);
text = '服务器地址非法';
}
}
setLoginStatus(text);
break;
}
case 'phoneNumberInvalid': {
@@ -571,15 +565,10 @@ export function AuthenticatedClient({
break;
}
// 系统失败:调用方处理不了,原样抛出 → 全局 unhandledrejection 交给错误池。
case 'authNetworkTimeout':
case 'authNetworkUnreachable':
case 'authNetworkFailure':
case 'authServiceUnavailable':
case 'unexpectedRejection':
case 'authResponseNotJson':
case 'authResponseInvalidBody':
case 'authResponseMissingRefreshCookie':
case 'authResponseMissingUserIdentity':
case 'authResponseServerRejected':
case 'authResponseInvalid':
case 'clientSessionPersistFailed':
case 'runtimeSessionInstallFailed':
case 'authClientInitFailed':
@@ -657,32 +646,37 @@ export function AuthenticatedClient({
const failure = error.error;
switch (failure.type) {
// 预期业务拒绝:调用方补上本次操作的上下文,展示后不进错误池。
case 'serverAddressEmptyOrTooLong': {
setLoginStatus('服务器地址非法: 不能为空或超长');
break;
}
case 'serverAddressNotAUrl': {
setLoginStatus('服务器地址非法: 不是合法地址');
break;
}
case 'serverAddressHasCredentials': {
setLoginStatus('服务器地址非法: 不能包含用户名或密码');
break;
}
case 'serverAddressHasPathOrQueryOrFragment': {
setLoginStatus('服务器地址非法: 不能带路径、查询或片段');
break;
}
case 'serverAddressNotHttps': {
setLoginStatus('服务器地址非法: 远程地址必须使用 https');
break;
}
case 'serverAddressUnsupportedScheme': {
setLoginStatus('服务器地址非法: 只支持 http 或 https');
break;
}
case 'serverAddressOutsideChannel': {
setLoginStatus('服务器地址非法: 不在当前构建渠道范围内');
case 'serverAddressRejected': {
const payload = failure as ServerAddressRejected;
let text: string;
switch (payload.reason) {
case 'emptyOrTooLong':
text = '服务器地址非法: 不能为空或超长';
break;
case 'notAUrl':
text = '服务器地址非法: 不是合法地址';
break;
case 'hasCredentials':
text = '服务器地址非法: 不能包含用户名或密码';
break;
case 'hasPathOrQueryOrFragment':
text = '服务器地址非法: 不能带路径、查询或片段';
break;
case 'notHttps':
text = '服务器地址非法: 远程地址必须使用 https';
break;
case 'unsupportedScheme':
text = '服务器地址非法: 只支持 http 或 https';
break;
case 'outsideChannel':
text = '服务器地址非法: 不在当前构建渠道范围内';
break;
default: {
expectNever(payload.reason);
text = '服务器地址非法';
}
}
setLoginStatus(text);
break;
}
case 'phoneNumberInvalid': {
@@ -739,15 +733,10 @@ export function AuthenticatedClient({
break;
}
// 系统失败:调用方处理不了,原样抛出 → 全局 unhandledrejection 交给错误池。
case 'authNetworkTimeout':
case 'authNetworkUnreachable':
case 'authNetworkFailure':
case 'authServiceUnavailable':
case 'unexpectedRejection':
case 'authResponseNotJson':
case 'authResponseInvalidBody':
case 'authResponseMissingRefreshCookie':
case 'authResponseMissingUserIdentity':
case 'authResponseServerRejected':
case 'authResponseInvalid':
case 'clientSessionPersistFailed':
case 'runtimeSessionInstallFailed':
case 'authClientInitFailed':