From b51845528f83097e584914ff42b0a06a5956ce2e Mon Sep 17 00:00:00 2001 From: Fred Boniface Date: Sat, 16 May 2026 18:19:47 +0100 Subject: [PATCH] 'Genericise' errors ready for +error.svelte upgrade to handle known errors --- src/hooks.client.ts | 16 +++++------ src/lib/owlClient.ts | 43 +++++++++++++++++++++++++++- src/routes/+error.svelte | 4 +-- src/routes/+layout.svelte | 8 +++++- src/routes/about/+page.svelte | 54 ++++++++++++++++------------------- src/routes/pis/+page.svelte | 10 +++++-- src/routes/trains/+page.ts | 22 ++++---------- 7 files changed, 97 insertions(+), 60 deletions(-) diff --git a/src/hooks.client.ts b/src/hooks.client.ts index cf68127..a2508a7 100644 --- a/src/hooks.client.ts +++ b/src/hooks.client.ts @@ -1,11 +1,11 @@ -import type { HandleClientError } from "@sveltejs/kit"; +import type { HandleClientError } from '@sveltejs/kit'; export const handleError: HandleClientError = ({ error }) => { - const err = error as any; + const err = error as any; - return { - message: err?.message || 'An unexpected application error occurred.', - code: err?.code || 'CRITICAL', - msg: err?.msg || 'UNHANDLED_EXCEPTION' - }; -}; \ No newline at end of file + return { + message: err?.message || 'An unexpected application error occurred.', + code: err?.code || 'CRITICAL', + name: err?.name || 'UNHANDLED_EXCEPTION' + }; +}; diff --git a/src/lib/owlClient.ts b/src/lib/owlClient.ts index 6e1f3de..d9f66f1 100644 --- a/src/lib/owlClient.ts +++ b/src/lib/owlClient.ts @@ -1,5 +1,6 @@ import { OwlBoardClient, ValidationError, ApiError } from '@owlboard/owlboard-ts'; import { browser, dev } from '$app/environment'; +import { error } from '@sveltejs/kit'; // Import the runes containing the API Key config Here... @@ -18,6 +19,46 @@ export const OwlClient = new OwlBoardClient( // API Key Here when ready!!! ); -export function ThrowApiError(e: any): void {} +export function ThrowApiError(e: unknown): never { + // Handle Request failure + if (e instanceof TypeError && e.message === 'Failed to fetch') { + throw error(503, { + message: 'Unable to connect to the OwlBoard server', + name: 'NETWORK_ERROR', + code: 'NETWORK_UNREACHABLE' + }); + } + + // Map ApiError 'code' values + if (e instanceof ApiError) { + let status = 500; + if (e.code === 'AUTH') { + status = 401; + } else if (e.code === 'NOT_FOUND') { + status = 404; + } else if (e.code === 'RATE_LIMIT') { + status = 421; + } else if (e.code === 'NETWORK_DISCONNECTED') { + status = 503; + } else if (e.code === 'VALIDATION') { + status = 400; + } + + throw error(status, { + message: e.message || 'An operational error has occurred', + name: e.name || 'API_ERROR', + code: e.code || 'UNKNOWN_API_ERROR' + }); + } + + // Fallback for other error kind + const genericMessage = e instanceof Error ? e.message : 'An unexpected error occurred.'; + throw error(500, { + message: genericMessage, + name: 'CRITICAL_ERROR', + code: 'INTERNAL_ERROR', + msg: 'UNHANDLED_EXCEPTION' + }); +} export { ValidationError, ApiError }; diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte index 60aa9ef..8cbe428 100644 --- a/src/routes/+error.svelte +++ b/src/routes/+error.svelte @@ -9,8 +9,8 @@ {#if page.error} -{JSON.stringify(page.error)} -STATUS: {page.status} + {JSON.stringify(page.error)} + STATUS: {page.status} {/if}
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index ac2b0e9..148cf2d 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -15,7 +15,13 @@ import logoPlain from '$lib/assets/round-logo.svg'; import favicon from '$lib/assets/round-logo.svg'; - import { IconHome, IconDialpad, IconSettings, IconHelp, IconDots } from '@tabler/icons-svelte-runes'; + import { + IconHome, + IconDialpad, + IconSettings, + IconHelp, + IconDots + } from '@tabler/icons-svelte-runes'; onMount(async () => { LOCATIONS.init(); diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte index 7215400..c62b3d1 100644 --- a/src/routes/about/+page.svelte +++ b/src/routes/about/+page.svelte @@ -11,19 +11,15 @@
- +
@@ -66,17 +62,15 @@ } .logo-btn { - background: none; - border: none; - padding: 0; - cursor: pointer; - display: inline-block; - padding-top: 25px; - margin: auto; - width: clamp(80px, 20vw, 200px); - } - - + background: none; + border: none; + padding: 0; + cursor: pointer; + display: inline-block; + padding-top: 25px; + margin: auto; + width: clamp(80px, 20vw, 200px); + } @keyframes owl-spin { 0% { @@ -91,14 +85,14 @@ } .logo-img { - width: 100%; - height: auto; - display: block; - } + width: 100%; + height: auto; + display: block; + } .logo-btn.animate { - animation: owl-spin 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; - } + animation: owl-spin 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; + } section { margin: auto; diff --git a/src/routes/pis/+page.svelte b/src/routes/pis/+page.svelte index 9aeaccc..787e75e 100644 --- a/src/routes/pis/+page.svelte +++ b/src/routes/pis/+page.svelte @@ -25,7 +25,10 @@ console.log(e); errorState = { status: 20, message: e.message }; } else { - errorState = { status: 0, message: `Unknown Error: ${e instanceof Error ? e.message : String(e)}` }; + errorState = { + status: 0, + message: `Unknown Error: ${e instanceof Error ? e.message : String(e)}` + }; } } finally { resultsLoaded = true; @@ -45,7 +48,10 @@ console.log(e); errorState = { status: 20, message: e.message }; } else { - errorState = { status: 0, message: `Unknown Error: ${e instanceof Error ? e.message : String(e)}` }; + errorState = { + status: 0, + message: `Unknown Error: ${e instanceof Error ? e.message : String(e)}` + }; } } finally { resultsLoaded = true; diff --git a/src/routes/trains/+page.ts b/src/routes/trains/+page.ts index d0dbbf1..d7bdc72 100644 --- a/src/routes/trains/+page.ts +++ b/src/routes/trains/+page.ts @@ -1,4 +1,4 @@ -import { OwlClient, ApiError, ValidationError } from '$lib/owlClient'; +import { OwlClient, ApiError, ValidationError, ThrowApiError } from '$lib/owlClient'; import type { ApiTrainsTrainByHeadcode } from '@owlboard/owlboard-ts'; import type { PageLoad } from './$types'; import { error } from '@sveltejs/kit'; @@ -10,31 +10,21 @@ export const load: PageLoad = async ({ fetch, url }) => { const date: string | Date = dateParam === '' || dateParam === null ? new Date() : dateParam; - if (!headcode) { - throw error(400, { - message: 'Headcode not provided', - owlCode: 'INVALID_DATA' - }); - } + // Validation removed, handled by the API Client Lib // Declared outside of the try so that it can be used in both the try and catch blocks let results: ApiTrainsTrainByHeadcode.TrainByHeadcodeResponse[]; try { - const response = await OwlClient.trains.getByHeadcode(headcode, date, toc, fetch); + const response = await OwlClient.trains.getByHeadcode(headcode || '', date, toc, fetch); + let title = !!headcode ? headcode.toUpperCase() : 'Error'; results = response.data; return { - title: headcode.toUpperCase(), + title: title, results: results }; } catch (e: unknown) { - if (e instanceof TypeError && e.message === 'Failed to fetch') { - error(503, { - message: 'Cannot connect to the OwlBoard server', - owlCode: 'NETWORK_DISCONNECTED' - }); - } - throw e; + ThrowApiError(e); } };