Send multipart/form-data (FormData). Does NOT set Content-Type — browser sets it with boundary.
(path: string, formData: FormData)
| 180 | |
| 181 | /** Send multipart/form-data (FormData). Does NOT set Content-Type — browser sets it with boundary. */ |
| 182 | async postForm<T>(path: string, formData: FormData): Promise<T> { |
| 183 | const url = buildRestUrl(this.origin, path); |
| 184 | const requestId = createRequestId(); |
| 185 | const headers: Record<string, string> = { |
| 186 | 'X-Request-Id': requestId, |
| 187 | }; |
| 188 | this.addClientHeaders(headers); |
| 189 | const startedAt = Date.now(); |
| 190 | traceRestRequest({ method: 'POST', path, url, requestId, body: describeFormData(formData) }); |
| 191 | let response: Response; |
| 192 | try { |
| 193 | response = await fetch(url, { method: 'POST', headers, body: formData, signal: timeoutSignal() }); |
| 194 | } catch (err) { |
| 195 | traceRestFailure({ method: 'POST', path, requestId, phase: 'fetch', durationMs: Date.now() - startedAt, error: err }); |
| 196 | throw new DaemonNetworkError({ |
| 197 | message: `Network error calling POST ${path}`, |
| 198 | cause: err, |
| 199 | method: 'POST', |
| 200 | path, |
| 201 | url, |
| 202 | requestId, |
| 203 | phase: 'fetch', |
| 204 | timeoutMs: REQUEST_TIMEOUT_MS, |
| 205 | timestamp: Date.now(), |
| 206 | durationMs: Date.now() - startedAt, |
| 207 | }); |
| 208 | } |
| 209 | let envelope: WireEnvelope<T>; |
| 210 | const responseForDiagnostics = response.clone(); |
| 211 | try { |
| 212 | envelope = (await response.json()) as WireEnvelope<T>; |
| 213 | } catch (err) { |
| 214 | traceRestFailure({ method: 'POST', path, requestId, phase: 'parse', durationMs: Date.now() - startedAt, status: response.status, error: err }); |
| 215 | throw new DaemonNetworkError({ |
| 216 | message: `Failed to parse JSON response from POST ${path}`, |
| 217 | cause: err, |
| 218 | method: 'POST', |
| 219 | path, |
| 220 | url, |
| 221 | requestId, |
| 222 | phase: 'parse', |
| 223 | timeoutMs: REQUEST_TIMEOUT_MS, |
| 224 | status: response.status, |
| 225 | statusText: response.statusText, |
| 226 | contentType: response.headers.get('content-type') ?? undefined, |
| 227 | bodyPreview: await readResponsePreview(responseForDiagnostics), |
| 228 | timestamp: Date.now(), |
| 229 | durationMs: Date.now() - startedAt, |
| 230 | }); |
| 231 | } |
| 232 | traceRestResponse({ |
| 233 | method: 'POST', |
| 234 | path, |
| 235 | requestId, |
| 236 | status: response.status, |
| 237 | durationMs: Date.now() - startedAt, |
| 238 | code: envelope.code, |
| 239 | msg: envelope.msg, |
nothing calls this directly
no test coverage detected