api.ts
3,567 bytes
| 1 | import type { |
|---|---|
| 2 | AnswerRequest, |
| 3 | AnswerResponse, |
| 4 | BlockersResponse, |
| 5 | ConfigStatus, |
| 6 | ConfigUpdateRequest, |
| 7 | ConfigUpdateResponse, |
| 8 | CreateSessionRequest, |
| 9 | FsBrowseResponse, |
| 10 | FsMkdirResponse, |
| 11 | GenerateRequest, |
| 12 | GenerateResponse, |
| 13 | Session, |
| 14 | SessionSummary, |
| 15 | } from 'shared/types' |
| 16 | |
| 17 | const BASE = '/api' |
| 18 | |
| 19 | async function readJsonOrThrow<T>(res: Response): Promise<T> { |
| 20 | if (!res.ok) { |
| 21 | const body = (await res.json().catch(() => null)) as { error?: string } | null |
| 22 | throw new Error(body?.error ?? `request failed with status ${res.status}`) |
| 23 | } |
| 24 | return res.json() as Promise<T> |
| 25 | } |
| 26 | |
| 27 | async function requestJson<T>(url: string, init?: RequestInit): Promise<T> { |
| 28 | const res = await fetch(url, { |
| 29 | ...init, |
| 30 | headers: { 'Content-Type': 'application/json', ...init?.headers }, |
| 31 | }) |
| 32 | return readJsonOrThrow<T>(res) |
| 33 | } |
| 34 | |
| 35 | export function createSession(req: CreateSessionRequest): Promise<Session> { |
| 36 | return requestJson<Session>(`${BASE}/sessions`, { method: 'POST', body: JSON.stringify(req) }) |
| 37 | } |
| 38 | |
| 39 | export function listSessions(): Promise<SessionSummary[]> { |
| 40 | return requestJson<SessionSummary[]>(`${BASE}/sessions`) |
| 41 | } |
| 42 | |
| 43 | export function getSession(id: string): Promise<Session> { |
| 44 | return requestJson<Session>(`${BASE}/sessions/${id}`) |
| 45 | } |
| 46 | |
| 47 | export async function deleteSession(id: string): Promise<void> { |
| 48 | const res = await fetch(`${BASE}/sessions/${id}`, { method: 'DELETE' }) |
| 49 | if (!res.ok) { |
| 50 | const body = (await res.json().catch(() => null)) as { error?: string } | null |
| 51 | throw new Error(body?.error ?? `request failed with status ${res.status}`) |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | export function submitAnswer(id: string, req: AnswerRequest): Promise<AnswerResponse> { |
| 56 | return requestJson<AnswerResponse>(`${BASE}/sessions/${id}/answer`, { |
| 57 | method: 'POST', |
| 58 | body: JSON.stringify(req), |
| 59 | }) |
| 60 | } |
| 61 | |
| 62 | export async function uploadAudio(id: string, audio: Blob): Promise<AnswerResponse> { |
| 63 | const form = new FormData() |
| 64 | form.append('audio', audio, 'clip.webm') |
| 65 | const res = await fetch(`${BASE}/sessions/${id}/audio`, { method: 'POST', body: form }) |
| 66 | return readJsonOrThrow<AnswerResponse>(res) |
| 67 | } |
| 68 | |
| 69 | export function generateSpecPack(id: string, req: GenerateRequest = {}): Promise<GenerateResponse> { |
| 70 | return requestJson<GenerateResponse>(`${BASE}/sessions/${id}/generate`, { |
| 71 | method: 'POST', |
| 72 | body: JSON.stringify(req), |
| 73 | }) |
| 74 | } |
| 75 | |
| 76 | export async function hasSpecPack(id: string): Promise<boolean> { |
| 77 | const res = await fetch(`${BASE}/sessions/${id}/export/spec-pack.zip`, { method: 'HEAD' }) |
| 78 | if (res.ok) return true |
| 79 | if (res.status === 404) return false |
| 80 | throw new Error(`Could not check the build brief, status ${res.status}`) |
| 81 | } |
| 82 | |
| 83 | export function importBlockers(id: string): Promise<BlockersResponse> { |
| 84 | return requestJson<BlockersResponse>(`${BASE}/sessions/${id}/blockers`, { |
| 85 | method: 'POST', |
| 86 | body: JSON.stringify({}), |
| 87 | }) |
| 88 | } |
| 89 | |
| 90 | export function getConfig(): Promise<ConfigStatus> { |
| 91 | return requestJson<ConfigStatus>(`${BASE}/config`) |
| 92 | } |
| 93 | |
| 94 | export function saveConfig(req: ConfigUpdateRequest): Promise<ConfigUpdateResponse> { |
| 95 | return requestJson<ConfigUpdateResponse>(`${BASE}/config`, { |
| 96 | method: 'POST', |
| 97 | body: JSON.stringify(req), |
| 98 | }) |
| 99 | } |
| 100 | |
| 101 | export function browseFolder(path?: string): Promise<FsBrowseResponse> { |
| 102 | const query = path ? `?path=${encodeURIComponent(path)}` : '' |
| 103 | return requestJson<FsBrowseResponse>(`${BASE}/fs/browse${query}`) |
| 104 | } |
| 105 | |
| 106 | export function createFolder(path: string, name: string): Promise<FsMkdirResponse> { |
| 107 | return requestJson<FsMkdirResponse>(`${BASE}/fs/mkdir`, { |
| 108 | method: 'POST', |
| 109 | body: JSON.stringify({ path, name }), |
| 110 | }) |
| 111 | } |
| 112 | |