{"version":3,"file":"parse.cjs","names":["DetailType","hasText"],"sources":["../../src/error/parse.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport type { DefaultNamespace, Namespace, TFunction } from 'i18next';\nimport { type BadRequest, DetailType, type ErrorInfo } from './detail';\nimport type { ResolvedErrorReason } from './reason';\nimport type { ErrorBody } from './status';\n\n/**\n * `ErrorBody` as it arrives off the wire: bodies parsed from an HTTP error\n * response with `JSON.parse` guarantee no field at runtime.\n */\ntype LooseErrorBody = {\n  error?: Partial<ErrorBody['error']>;\n};\n\nexport function getErrorInfo(data: unknown): ErrorInfo | null {\n  if (typeof data !== 'object' || data === null || !('error' in data)) return null;\n  const { error } = data as LooseErrorBody;\n  const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n  return errorInfo ?? null;\n}\n\n/**\n * @example For axios:\n *\n * const { t } = useTranslation('error');\n *\n * const { message } = getErrorMessage(error.response.data, t);\n * toast.error(message);\n */\nexport function getErrorMessage<Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(\n  data: unknown,\n  t: TFunction<Ns, KPrefix>\n): { reason: ResolvedErrorReason | null; message: string } {\n  // 0. unknown error\n  if (typeof data !== 'object' || data === null || !('error' in data)) {\n    console.error('Unknown API error:', data);\n    return { reason: null, message: t('UNKNOWN') };\n  }\n\n  const { error } = data as LooseErrorBody;\n\n  // 1. from server via Accept-Language header or lng param\n  const localizedMessage = error?.details?.find((d) => d['@type'] === DetailType.LOCALIZED_MESSAGE);\n  if (localizedMessage) return { reason: null, message: localizedMessage.message };\n\n  // 2. from business logic error\n  const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n  if (errorInfo) {\n    return {\n      reason: errorInfo.reason,\n      message: t(errorInfo.reason, errorInfo.metadata),\n    };\n  }\n\n  // 3. error message in english\n  const message = error?.message;\n  if (hasText(message)) return { reason: null, message };\n\n  // 4. from server via status code\n  if (error?.status) return { reason: null, message: t(error.status) };\n\n  // 5. unknown error\n  console.error('Unknown API error:', data);\n  return { reason: null, message: t('UNKNOWN') };\n}\n\n/**\n * Whether an error body carries `ErrorInfo` with one of the given reasons. Meant for the cases a\n * component wants to handle itself (inline field error, retry, redirect) so a global error handler\n * can skip them.\n *\n * @example With a react-query MutationCache:\n *\n * const login = useMutation({\n *   mutationFn: api.auth.loginEmail,\n *   meta: { handledReasons: ['INVALID_VERIFICATION_CODE'] },\n *   onError: (e) => {\n *     if (isErrorReason(e.data, 'INVALID_VERIFICATION_CODE')) setError('code', { message });\n *   },\n * });\n *\n * new MutationCache({\n *   onError: (e, _v, _c, mutation) => {\n *     if (isErrorReason(e.data, ...(mutation.meta?.handledReasons ?? []))) return;\n *     toast.error(getErrorMessage(e.data, t).message);\n *   },\n * });\n */\nexport function isErrorReason(data: unknown, ...reasons: ResolvedErrorReason[]): boolean {\n  const reason = getErrorInfo(data)?.reason;\n  return reason !== undefined && reasons.includes(reason);\n}\n\n/**\n * @example For react-hook-form:\n *\n * const { setError } = useForm();\n * const fieldViolations = getFieldViolations(error.response.data);\n * fieldViolations.forEach((violation) => {\n *   setError(violation.field, { message: violation.description });\n * });\n */\nexport function getFieldViolations(data: unknown): BadRequest['fieldViolations'] {\n  if (typeof data !== 'object' || data === null || !('error' in data)) return [];\n  const { error } = data as LooseErrorBody;\n  const badRequest = error?.details?.find((d) => d['@type'] === DetailType.BAD_REQUEST);\n  return badRequest?.fieldViolations ?? [];\n}\n"],"mappings":";;;;AAcA,SAAgB,aAAa,MAAiC;CAC5D,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO,OAAO;CAC5E,MAAM,EAAE,UAAU;CAElB,OADkB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaA,qBAAAA,WAAW,UAAU,KAC9D;AACtB;;;;;;;;;AAUA,SAAgB,gBACd,MACA,GACyD;CAEzD,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO;EACnE,QAAQ,MAAM,sBAAsB,IAAI;EACxC,OAAO;GAAE,QAAQ;GAAM,SAAS,EAAE,SAAS;EAAE;CAC/C;CAEA,MAAM,EAAE,UAAU;CAGlB,MAAM,mBAAmB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaA,qBAAAA,WAAW,iBAAiB;CAChG,IAAI,kBAAkB,OAAO;EAAE,QAAQ;EAAM,SAAS,iBAAiB;CAAQ;CAG/E,MAAM,YAAY,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaA,qBAAAA,WAAW,UAAU;CAClF,IAAI,WACF,OAAO;EACL,QAAQ,UAAU;EAClB,SAAS,EAAE,UAAU,QAAQ,UAAU,QAAQ;CACjD;CAIF,MAAM,UAAU,OAAO;CACvB,KAAA,GAAIC,cAAAA,QAAAA,CAAQ,OAAO,GAAG,OAAO;EAAE,QAAQ;EAAM;CAAQ;CAGrD,IAAI,OAAO,QAAQ,OAAO;EAAE,QAAQ;EAAM,SAAS,EAAE,MAAM,MAAM;CAAE;CAGnE,QAAQ,MAAM,sBAAsB,IAAI;CACxC,OAAO;EAAE,QAAQ;EAAM,SAAS,EAAE,SAAS;CAAE;AAC/C;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,cAAc,MAAe,GAAG,SAAyC;CACvF,MAAM,SAAS,aAAa,IAAI,CAAC,EAAE;CACnC,OAAO,WAAW,KAAA,KAAa,QAAQ,SAAS,MAAM;AACxD;;;;;;;;;;AAWA,SAAgB,mBAAmB,MAA8C;CAC/E,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO,OAAO,CAAC;CAC7E,MAAM,EAAE,UAAU;CAElB,QADmB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaD,qBAAAA,WAAW,WAAW,EAAA,EACjE,mBAAmB,CAAC;AACzC"}