import type { ConversionJobView } from '@dorfteich/shared'; import { ApiError, apiGet } from '../lib/api'; const POLL_INTERVAL_MS = 1000; /** Default poll budget — a whole-account export (#68) can take longer than a * single-page conversion, so callers may raise or lower it. */ const DEFAULT_MAX_POLLS = 300; const delay = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); /** Poll a conversion job until it succeeds or fails, or the poll budget runs * out (returns whatever the last status was). Shared by every export hook. */ export async function pollJob( job: ConversionJobView, maxPolls = DEFAULT_MAX_POLLS, ): Promise { let current = job; for ( let poll = 0; current.status !== 'succeeded' && current.status !== 'failed' && poll < maxPolls; poll += 1 ) { await delay(POLL_INTERVAL_MS); current = await apiGet(`/jobs/${current.id}`); } return current; } /** Download a finished job's result and save it as `fileName`. The result * endpoint is session-authenticated, so a plain `fetch` carries the cookie. */ export async function downloadJobResult(jobId: string, fileName: string): Promise { const response = await fetch(`/api/v1/jobs/${jobId}/result`); if (!response.ok) { throw new ApiError(response.status, { code: `http_${response.status}`, message: '' }); } const blob = await response.blob(); const url = URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = fileName; document.body.appendChild(anchor); anchor.click(); anchor.remove(); URL.revokeObjectURL(url); }