fix: guard generateResearch against POST failure, surface partial fetch errors

- Check error from POST /research/generate; only start pollTask on success to prevent unresolvable polling intervals
- Surface contacts and fullJob fetch errors in fetchFor; silently ignore research 404 (expected when no research yet)
- Remove redundant type assertions (as Contact[], as TaskStatus, as FullJobDetail)
- Add @internal JSDoc to pollTask
- Remove redundant vi.runAllTimersAsync() after vi.advanceTimersByTimeAsync(3000) in test
This commit is contained in:
pyr0ball 2026-03-20 18:44:11 -07:00
parent ff0dd8b3cd
commit 7693abf79d
2 changed files with 27 additions and 6 deletions

View file

@ -123,7 +123,6 @@ describe('usePrepStore', () => {
store.pollTask(1)
await vi.advanceTimersByTimeAsync(3000)
await vi.runAllTimersAsync()
expect(store.research?.company_brief).toBe('Updated!')
})

View file

@ -77,10 +77,24 @@ export const usePrepStore = defineStore('prep', () => {
useApiFetch<FullJobDetail>(`/api/jobs/${jobId}`),
])
// Research 404 is expected (no research yet) — only surface non-404 errors
if (researchResult.error && !(researchResult.error.kind === 'http' && researchResult.error.status === 404)) {
error.value = 'Failed to load research data'
return
}
if (contactsResult.error) {
error.value = 'Failed to load contacts'
return
}
if (jobResult.error) {
error.value = 'Failed to load job details'
return
}
research.value = researchResult.data ?? null
contacts.value = (contactsResult.data as Contact[]) ?? []
taskStatus.value = (taskResult.data as TaskStatus) ?? { status: null, stage: null, message: null }
fullJob.value = (jobResult.data as FullJobDetail) ?? null
contacts.value = contactsResult.data ?? []
taskStatus.value = taskResult.data ?? { status: null, stage: null, message: null }
fullJob.value = jobResult.data ?? null
// If a task is already running/queued, start polling
const ts = taskStatus.value.status
@ -95,16 +109,24 @@ export const usePrepStore = defineStore('prep', () => {
}
async function generateResearch(jobId: number) {
await useApiFetch<unknown>(`/api/jobs/${jobId}/research/generate`, { method: 'POST' })
const { data, error: fetchError } = await useApiFetch<{ task_id: number; is_new: boolean }>(
`/api/jobs/${jobId}/research/generate`,
{ method: 'POST' }
)
if (fetchError || !data) {
error.value = 'Failed to start research generation'
return
}
pollTask(jobId)
}
/** @internal — called by fetchFor and generateResearch; not for component use */
function pollTask(jobId: number) {
_clearInterval()
pollInterval = setInterval(async () => {
const { data } = await useApiFetch<TaskStatus>(`/api/jobs/${jobId}/research/task`)
if (data) {
taskStatus.value = data as TaskStatus
taskStatus.value = data
if (data.status === 'completed' || data.status === 'failed') {
_clearInterval()
if (data.status === 'completed') {