feat: root error boundary and network-error normalisation
A load() that rethrew (e.g. the profile overview) fell through to SvelteKit's bare default error page, and a connection-refused fetch rejected as a raw TypeError that blanked the view. Wrap fetch network failures in the client as ApiError(status 0, network_error) so callers handle them uniformly, and add a root +error.svelte with a friendly message plus Try again / Go home actions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -80,6 +80,17 @@ describe('request error envelope parsing', () => {
|
||||
expect(err.code).toBe('http_error');
|
||||
});
|
||||
|
||||
it('wraps a network-level fetch rejection in ApiError (status 0)', async () => {
|
||||
// Connection refused / offline / DNS: fetch rejects with a TypeError.
|
||||
// It must surface as an ApiError, not a bare TypeError that crashes a
|
||||
// load function into SvelteKit's default error page.
|
||||
fetchSpy.mockRejectedValueOnce(new TypeError('Failed to fetch'));
|
||||
const err = (await getManga('x').catch((e) => e)) as ApiError;
|
||||
expect(err).toBeInstanceOf(ApiError);
|
||||
expect(err.status).toBe(0);
|
||||
expect(err.code).toBe('network_error');
|
||||
});
|
||||
|
||||
it('treats empty 200/201 bodies as undefined (no JSON.parse crash)', async () => {
|
||||
// Regression: addMangaToCollection is typed `void` and the
|
||||
// backend returns 201 (created) / 200 (already there) with
|
||||
|
||||
@@ -103,7 +103,21 @@ export async function request<T>(
|
||||
// working. For same-origin requests this is a no-op compared to the
|
||||
// default 'same-origin', so the same-origin happy path is
|
||||
// unchanged.
|
||||
const res = await fetch(`${BASE}${path}`, { credentials: 'include', ...init });
|
||||
let res: Response;
|
||||
try {
|
||||
res = await fetch(`${BASE}${path}`, { credentials: 'include', ...init });
|
||||
} catch {
|
||||
// A network-level failure (connection refused, DNS, offline, blocked by
|
||||
// CORS) rejects `fetch` with a bare TypeError instead of returning a
|
||||
// response. Normalise it to an ApiError (status 0 = "no response reached
|
||||
// us") so callers and SvelteKit load functions handle it uniformly
|
||||
// instead of crashing on an unexpected TypeError.
|
||||
throw new ApiError(
|
||||
0,
|
||||
'network_error',
|
||||
'Could not reach the server. Check your connection and try again.'
|
||||
);
|
||||
}
|
||||
if (!res.ok) {
|
||||
let code = 'http_error';
|
||||
let message = `${res.status} ${res.statusText}`;
|
||||
|
||||
Reference in New Issue
Block a user