errors.js 1.53 KB
/**
 * 结构化认证错误 —— 本包唯一对外错误类型(§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 })
}