UNPKG

wellcrafted

Version:

Delightful TypeScript patterns for elegant, type-safe applications

107 lines (104 loc) 3.34 kB
import { Err } from "./result-C_ph_izC.js"; //#region src/error/defineErrors.ts /** * Defines a set of typed error factories using Rust-style namespaced variants. * * Each key is a short variant name (the namespace provides context). Every * factory returns `Err<...>` directly — ready for `trySync`/`tryAsync` catch * handlers. The variant name is stamped as `name` on the error object. * * @example * ```ts * const HttpError = defineErrors({ * Connection: ({ cause }: { cause: unknown }) => ({ * message: `Failed to connect: ${extractErrorMessage(cause)}`, * cause, * }), * Response: ({ status }: { status: number; bodyMessage?: string }) => ({ * message: `HTTP ${status}`, * status, * }), * Parse: ({ cause }: { cause: unknown }) => ({ * message: `Failed to parse response body: ${extractErrorMessage(cause)}`, * cause, * }), * }); * * type HttpError = InferErrors<typeof HttpError>; * * const result = HttpError.Connection({ cause: 'timeout' }); // Err<...> * ``` * * Inspired by Rust's {@link https://docs.rs/thiserror | thiserror} crate. The * mapping is nearly 1:1: * * - `enum HttpError` → `const HttpError = defineErrors(...)` * - Variant `Connection { cause: String }` → key `Connection: ({ cause }: { cause: unknown }) => (...)` * - `#[error("Failed: {cause}")]` → `` message: `Failed: ${extractErrorMessage(cause)}` `` * - `HttpError::Connection { ... }` → `HttpError.Connection({ ... })` * - `match error { Connection { .. } => }` → `switch (error.name) { case 'Connection': }` * * The equivalent Rust `thiserror` enum: * ```rust * #[derive(Error, Debug)] * enum HttpError { * #[error("Failed to connect: {cause}")] * Connection { cause: String }, * * #[error("HTTP {status}")] * Response { status: u16, body_message: Option<String> }, * * #[error("Failed to parse response body: {cause}")] * Parse { cause: String }, * } * ``` */ function defineErrors(config) { const result = {}; for (const [name, ctor] of Object.entries(config)) result[name] = (...args) => { const body = ctor(...args); return Err(Object.freeze({ ...body, name })); }; return result; } //#endregion //#region src/error/extractErrorMessage.ts /** * Extracts a readable error message from an unknown error value * * @param error - The unknown error to extract a message from * @returns A string representation of the error */ function extractErrorMessage(error) { if (error instanceof Error) return error.message; if (typeof error === "string") return error; if (typeof error === "number" || typeof error === "boolean" || typeof error === "bigint") return String(error); if (typeof error === "symbol") return error.toString(); if (error === null) return "null"; if (error === void 0) return "undefined"; if (Array.isArray(error)) return JSON.stringify(error); if (typeof error === "object") { const errorObj = error; const messageProps = [ "message", "error", "description", "title", "reason", "details" ]; for (const prop of messageProps) if (prop in errorObj && typeof errorObj[prop] === "string") return errorObj[prop]; try { return JSON.stringify(error); } catch { return String(error); } } return String(error); } //#endregion export { defineErrors, extractErrorMessage }; //# sourceMappingURL=error-BkeqDeUq.js.map