fix(api): auto-stringify object bodies in LS.api (apiFetch)

LS.api was passing raw object bodies straight to fetch(), which coerces
them to '[object Object]' — the server then parsed empty JSON and 400'd
on missing fields. This silently broke every POST that uses LS.api
directly (EP.api.startMock, saveAttempt, mockAnswer, etc.).

LS.post already stringified, so most call sites worked. Now apiFetch
mirrors that behavior for plain objects, while FormData / Blob /
URLSearchParams / ArrayBuffer / strings still pass through unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-29 16:40:42 +03:00
parent 2b653c4655
commit 5356096349
+11 -1
View File
@@ -901,7 +901,17 @@ async function apiFetch(path, options = {}) {
const token = getToken();
const headers = { 'Content-Type': 'application/json', ...(options.headers || {}) };
if (token) headers['Authorization'] = `Bearer ${token}`;
const res = await fetch(path, { ...options, headers });
// Auto-stringify plain object bodies so callers can pass `{ body: { ... } }`
// like LS.post does. Strings / FormData / Blob / URLSearchParams pass through.
const opts = { ...options, headers };
if (opts.body && typeof opts.body === 'object'
&& !(opts.body instanceof FormData)
&& !(opts.body instanceof Blob)
&& !(opts.body instanceof URLSearchParams)
&& !(opts.body instanceof ArrayBuffer)) {
opts.body = JSON.stringify(opts.body);
}
const res = await fetch(path, opts);
if (res.status === 401) {
removeToken(); removeUser();
window.location.href = '/login';