M9 S2: fold raw multipart fetches into api.postForm
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 5s
CI & Build / Python tests (push) Successful in 8s
CI & Build / Build & push image (push) Successful in 31s

Both notes-store uploads (uploadAttachment, importNotes) hand-rolled the
same fetch + resp.json() + !ok error parsing that api.client already does.
Add `api.postForm<T>(path, form)`: request() now detects a FormData body
and lets the browser set the multipart Content-Type (skipping the JSON
header + stringify), reusing the shared error handling — so the two
uploads gain network-error handling and the 5xx infra toast they lacked.
A too-large import returns 413 (< 500), so it still throws for inline
display rather than toasting.

DRY: net -13 lines; no raw fetch() remains in the stores.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
2026-07-23 21:18:37 -04:00
co-authored by Claude Opus 4.8
parent 3a4c3c8164
commit e8e0d86413
2 changed files with 9 additions and 22 deletions
+6 -2
View File
@@ -16,13 +16,16 @@ function notifyError(err: ApiError) {
}
async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
// FormData carries its own multipart Content-Type (with boundary), so let the
// browser set it — only JSON bodies get an explicit header + stringify.
const isForm = body instanceof FormData;
let resp: Response;
try {
resp = await fetch(path, {
method,
credentials: "include", // send/receive the signed session cookie
headers: body !== undefined ? { "Content-Type": "application/json" } : undefined,
body: body !== undefined ? JSON.stringify(body) : undefined,
headers: body !== undefined && !isForm ? { "Content-Type": "application/json" } : undefined,
body: body === undefined ? undefined : isForm ? body : JSON.stringify(body),
});
} catch {
const err: ApiError = { error: "Can't reach the server. Check your connection.", status: 0 };
@@ -59,4 +62,5 @@ export const api = {
patch: <T>(path: string, body?: unknown) => request<T>("PATCH", path, body),
put: <T>(path: string, body?: unknown) => request<T>("PUT", path, body),
del: <T>(path: string) => request<T>("DELETE", path),
postForm: <T>(path: string, form: FormData) => request<T>("POST", path, form),
};