/** * 结构化认证错误 —— 本包唯一对外错误类型(§3)。 * * 所有失败一律抛 AuthError,禁止裸 Error / 静默吞错。`retryable` 给调用方决定 * 是否提示重试;`retryAfterMs` 仅 TOO_MANY_ATTEMPTS 携带。 */ /** * @typedef {'EMAIL_TAKEN'|'INVALID_CREDENTIALS'|'TOO_MANY_ATTEMPTS'| * 'OAUTH_VERIFY_FAILED'|'REFRESH_REUSED'|'CODE_EXPIRED'|'CODE_INVALID'| * 'TOKEN_INVALID'|'TOKEN_EXPIRED'|'USER_NOT_FOUND'|'UNSUPPORTED_PROVIDER'} AuthCode */ /** 默认可重试的错误码集合(瞬时/限流类)。 */ const RETRYABLE_CODES = new Set(['TOO_MANY_ATTEMPTS']) export class AuthError extends Error { /** * @param {Object} info * @param {AuthCode} info.code * @param {boolean} [info.retryable] * @param {number} [info.retryAfterMs] * @param {unknown} [info.cause] * @param {string} [info.message] */ constructor({ code, retryable, retryAfterMs, cause, message }) { super(message ?? `auth failed: ${code}`) this.name = 'AuthError' /** @type {AuthCode} */ this.code = code this.retryable = retryable ?? RETRYABLE_CODES.has(code) if (retryAfterMs != null) this.retryAfterMs = retryAfterMs if (cause != null) this.cause = cause } } /** * 把任意底层错误归一为 AuthError。已是 AuthError 的原样透传。 * @param {unknown} err * @param {AuthCode} fallbackCode * @returns {AuthError} */ export function asAuthError(err, fallbackCode) { if (err instanceof AuthError) return err return new AuthError({ code: fallbackCode, cause: err }) }