You've already forked agentic-coding-workflow
fix(web): localStorage safety, default fetch timeout, visibility-aware polling
This commit is contained in:
+35
-20
@@ -1,6 +1,7 @@
|
||||
import { ApiException, type ApiError } from './types'
|
||||
|
||||
const BASE = '' // 同源;dev 走 vite proxy
|
||||
const DEFAULT_TIMEOUT_MS = 30_000
|
||||
|
||||
interface RequestOptions {
|
||||
method?: 'GET' | 'POST' | 'PATCH' | 'DELETE'
|
||||
@@ -27,29 +28,43 @@ export async function request<T>(path: string, opts: RequestOptions = {}): Promi
|
||||
const token = authTokenProvider()
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`
|
||||
|
||||
const resp = await fetch(BASE + path, {
|
||||
method: opts.method || 'GET',
|
||||
headers,
|
||||
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
||||
signal: opts.signal,
|
||||
})
|
||||
|
||||
if (resp.status === 401) {
|
||||
onUnauthorized()
|
||||
const controller = new AbortController()
|
||||
const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS)
|
||||
if (opts.signal) {
|
||||
if (opts.signal.aborted) {
|
||||
controller.abort()
|
||||
} else {
|
||||
opts.signal.addEventListener('abort', () => controller.abort(), { once: true })
|
||||
}
|
||||
}
|
||||
|
||||
if (resp.status === 204) {
|
||||
return undefined as T
|
||||
}
|
||||
try {
|
||||
const resp = await fetch(BASE + path, {
|
||||
method: opts.method || 'GET',
|
||||
headers,
|
||||
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
||||
signal: controller.signal,
|
||||
})
|
||||
|
||||
const text = await resp.text()
|
||||
const data = text ? JSON.parse(text) : null
|
||||
if (resp.status === 401) {
|
||||
onUnauthorized()
|
||||
}
|
||||
|
||||
if (!resp.ok) {
|
||||
throw new ApiException(
|
||||
resp.status,
|
||||
(data as ApiError) || { code: 'unknown', message: resp.statusText },
|
||||
)
|
||||
if (resp.status === 204) {
|
||||
return undefined as T
|
||||
}
|
||||
|
||||
const text = await resp.text()
|
||||
const data = text ? JSON.parse(text) : null
|
||||
|
||||
if (!resp.ok) {
|
||||
throw new ApiException(
|
||||
resp.status,
|
||||
(data as ApiError) || { code: 'unknown', message: resp.statusText },
|
||||
)
|
||||
}
|
||||
return data as T
|
||||
} finally {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
return data as T
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user