import type { Hooks } from 'ky' import { isHTTPError } from 'ky' import { extractErrorMessage, normalizeError } from './error' import { emitHttpError } from './http-error' import { emitHttpToast } from './http-toast' import { emitForceLogout } from './session' import { tokenService } from './token' export function createHooks(): Hooks { return { beforeRequest: [ ({ request }) => { const token = tokenService.getToken() const url = request.url const shouldAttachToken = !url.includes('/Auth/login') if (token && shouldAttachToken) { request.headers.set('Authorization', `Bearer ${token}`) } }, ], beforeError: [ ({ request, options, error }) => { const normalized = normalizeError(error) if (normalized.name === 'CanceledRequestError') { return normalized } const requestUrl = request.url const isLoginRequest = requestUrl.includes('/Auth/login') const silentToast = Boolean(options.context.silentToast) const status = isHTTPError(error) ? error.response.status : undefined switch (status) { case 401: { if (requestUrl.includes('/Auth/login')) break const hasAuthHeader = request.headers.has('Authorization') if (hasAuthHeader) { tokenService.clearToken() const backendMessage = isHTTPError(error) ? extractErrorMessage(error.data) : undefined emitForceLogout({ message: backendMessage }) } break } case 403: { emitHttpError({ status, message: normalized.message }) break } case 404: { emitHttpError({ status, message: normalized.message }) break } case 500: { break } case 503: { emitHttpError({ status, message: normalized.message }) break } default: } const shouldToast = !silentToast && !isLoginRequest && status !== 401 && status !== 403 && status !== 404 && status !== 503 && (status === 500 || !status || (typeof status === 'number' && status >= 500)) if (shouldToast) { emitHttpToast({ level: status ? 'error' : 'warning', message: normalized.message, }) } return normalized }, ], } }