refactor(app): use async/await for axios response interceptor error handler

WS-3 session 1b-iii Task 3.

Rewrites the response-interceptor error handler from
\`error => { ... void import(...).then(...) }\` to
\`async error => { ... await import(...) }\`.

Motivation: session 1b-ii's Q4 chose option-a (\`void\` prefix on
the dynamic-import chains), but empirically that doesn't satisfy
the promise/no-promise-in-callback rule — the rule fires on any
promise creation inside a callback, regardless of discard pattern.
Two warnings remained on lib/axios.ts:61, 73.

The async/await rewrite is semantically identical:
- Both call sites already end in window.location.href = ... which
  navigates away, so the few ms of \`await\` resolution latency is
  unobservable.
- The original return Promise.reject(error) becomes throw error in
  an async function (async wraps throws in rejected promises).

Verified preserved byte-for-byte:
- 403 + impersonation_ended branch: clearState + redirect to /platform
  + rejection (now via throw)
- 401 branch: handleUnauthorized when authStore.isInitialized
- 403 / 404 / 422 / 503 / 5xx / !response notification branches
  (untouched in diff — all still in same order, same messages)
- Final rejection so calling code's catch fires (now via throw)
- Request interceptor not touched
- No imports added or removed

Tests + typecheck verified green. Build smoke: pnpm build succeeded
in 11.13s, zero warnings.

Lint baseline: 3 → 1 (the 2 promise/no-promise-in-callback warnings
on axios.ts:61, 73 are gone). The remaining 1 item is a pre-existing
sonarjs/no-collapsible-if at useImpersonationStore.ts:103 — see the
1b-iii final report.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-29 18:48:28 +02:00
parent 2fc2a569e7
commit b164a4979d

View File

@@ -49,7 +49,7 @@ apiClient.interceptors.response.use(
return response return response
}, },
error => { async error => {
if (import.meta.env.DEV) if (import.meta.env.DEV)
console.error(`${error.response?.status} ${error.config?.url}`, error.response?.data) console.error(`${error.response?.status} ${error.config?.url}`, error.response?.data)
@@ -58,23 +58,21 @@ apiClient.interceptors.response.use(
// Handle impersonation session expiry // Handle impersonation session expiry
if (status === 403 && error.response?.data?.impersonation_ended) { if (status === 403 && error.response?.data?.impersonation_ended) {
void import('@/stores/useImpersonationStore').then(({ useImpersonationStore }) => { const { useImpersonationStore } = await import('@/stores/useImpersonationStore')
const impersonationStore = useImpersonationStore() const impersonationStore = useImpersonationStore()
impersonationStore.clearState() impersonationStore.clearState()
window.location.href = '/platform' window.location.href = '/platform'
})
return Promise.reject(error) throw error
} }
if (status === 401) { if (status === 401) {
// Lazy import to avoid circular dependency // Lazy import to avoid circular dependency
void import('@/stores/useAuthStore').then(({ useAuthStore }) => { const { useAuthStore } = await import('@/stores/useAuthStore')
const authStore = useAuthStore() const authStore = useAuthStore()
if (authStore.isInitialized) if (authStore.isInitialized)
authStore.handleUnauthorized() authStore.handleUnauthorized()
})
} }
else if (status === 403) { else if (status === 403) {
notificationStore.show('You don\'t have permission for this action.', 'error') notificationStore.show('You don\'t have permission for this action.', 'error')
@@ -99,7 +97,7 @@ apiClient.interceptors.response.use(
notificationStore.show('Unable to connect to the server. Check your internet connection.', 'error') notificationStore.show('Unable to connect to the server. Check your internet connection.', 'error')
} }
return Promise.reject(error) throw error
}, },
) )