78 lines
2.8 KiB
TypeScript
78 lines
2.8 KiB
TypeScript
export const NETWORK_ERROR_CODES = [
|
|
'NETWORK',
|
|
'TIMEOUT',
|
|
'ABORTED',
|
|
'ENOTFOUND',
|
|
'EAI_AGAIN',
|
|
'ECONNREFUSED',
|
|
'ECONNRESET',
|
|
'ETIMEDOUT',
|
|
'UND_ERR_CONNECT_TIMEOUT',
|
|
'CERT_HAS_EXPIRED',
|
|
'DEPTH_ZERO_SELF_SIGNED_CERT',
|
|
'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
|
|
'ERR_TLS_CERT_ALTNAME_INVALID',
|
|
] as const
|
|
export type NetworkErrorCode = (typeof NETWORK_ERROR_CODES)[number]
|
|
export class NetworkError extends Error {
|
|
constructor(
|
|
message: string,
|
|
public code: NetworkErrorCode = 'NETWORK',
|
|
) {
|
|
super(message)
|
|
}
|
|
}
|
|
export class SessionChangedError extends Error {
|
|
constructor() {
|
|
super('Сессия изменилась. Повторите подключение игры')
|
|
}
|
|
}
|
|
export class ApiError extends Error {
|
|
constructor(public status: number) {
|
|
super(
|
|
status === 401
|
|
? 'Сессия истекла. Войдите снова'
|
|
: status === 403
|
|
? 'Нет доступа к действию'
|
|
: status === 400
|
|
? 'Действие пока недоступно для этой игры'
|
|
: `Ошибка сервера: HTTP ${status}`,
|
|
)
|
|
}
|
|
}
|
|
|
|
function causeCode(error: unknown, depth = 0): NetworkErrorCode | undefined {
|
|
if (!error || typeof error !== 'object' || depth > 3) return
|
|
const value = error as { code?: unknown; cause?: unknown; errors?: unknown[] }
|
|
if (NETWORK_ERROR_CODES.includes(value.code as NetworkErrorCode))
|
|
return value.code as NetworkErrorCode
|
|
return (
|
|
causeCode(value.cause, depth + 1) ??
|
|
(Array.isArray(value.errors)
|
|
? value.errors.map((e) => causeCode(e, depth + 1)).find(Boolean)
|
|
: undefined)
|
|
)
|
|
}
|
|
export function networkFailure(error: unknown, signal: AbortSignal): NetworkError {
|
|
if (signal.aborted) {
|
|
return signal.reason?.name === 'TimeoutError'
|
|
? new NetworkError('Превышено время ожидания ответа сервера', 'TIMEOUT')
|
|
: new NetworkError('Подготовка отменена', 'ABORTED')
|
|
}
|
|
if (error instanceof NetworkError) return error
|
|
const code = causeCode(error) ?? 'NETWORK'
|
|
const message =
|
|
code === 'ENOTFOUND' || code === 'EAI_AGAIN'
|
|
? 'Не удалось определить адрес сервера'
|
|
: code.includes('CERT') || code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'
|
|
? 'Ошибка защищённого соединения с сервером'
|
|
: code === 'ETIMEDOUT' || code === 'UND_ERR_CONNECT_TIMEOUT'
|
|
? 'Превышено время ожидания соединения с сервером'
|
|
: code === 'ECONNREFUSED'
|
|
? 'Сервер отклонил соединение'
|
|
: code === 'ECONNRESET'
|
|
? 'Соединение с сервером прервано'
|
|
: 'Сервер недоступен'
|
|
return new NetworkError(message, code)
|
|
}
|