71 lines
2 KiB
TypeScript
71 lines
2 KiB
TypeScript
import { z } from "zod"
|
|
import { Result } from "./result"
|
|
|
|
// === Request Errors ===
|
|
export type RequestError =
|
|
| { tag: "networkError"; error: unknown }
|
|
| { tag: "httpError"; status: number; statusText: string }
|
|
| { tag: "jsonParseError"; error: unknown }
|
|
| { tag: "invalidResponse"; error: z.ZodError }
|
|
|
|
export const RequestError = {
|
|
toString(error: RequestError): string {
|
|
switch (error.tag) {
|
|
case "networkError":
|
|
return "Network error. Check your connection and try again."
|
|
case "httpError":
|
|
return `Server returned ${String(error.status)} ${error.statusText}.`
|
|
case "jsonParseError":
|
|
return "The server returned an invalid response."
|
|
case "invalidResponse":
|
|
return "The image data had an unexpected format."
|
|
}
|
|
},
|
|
}
|
|
|
|
// === Generic safe fetch functions ===
|
|
export async function fetchJsonSafe(url: string): Promise<Result<unknown, RequestError>> {
|
|
let response: Response
|
|
try {
|
|
response = await fetch(url)
|
|
} catch (error: unknown) {
|
|
return Result.err({ tag: "networkError", error })
|
|
}
|
|
|
|
if (!response.ok) {
|
|
return Result.err({
|
|
tag: "httpError",
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
})
|
|
} else {
|
|
try {
|
|
const json: unknown = await response.json()
|
|
return Result.ok(json)
|
|
} catch (error: unknown) {
|
|
return Result.err({ tag: "jsonParseError", error })
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assumes `f` is some sort of a zod parsing function that may throw `z.ZodError`
|
|
export async function fetchJsonSafeWith<A>(
|
|
url: string,
|
|
f: (json: unknown) => A,
|
|
): Promise<Result<A, RequestError>> {
|
|
const result = await fetchJsonSafe(url)
|
|
switch (result.tag) {
|
|
case "error":
|
|
return Result.err(result.error)
|
|
case "ok":
|
|
try {
|
|
return Result.ok(f(result.value))
|
|
} catch (error: unknown) {
|
|
if (error instanceof z.ZodError) {
|
|
return Result.err({ tag: "invalidResponse", error })
|
|
} else {
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
}
|