wellcrafted
Version:
Delightful TypeScript patterns for elegant, type-safe applications
107 lines (104 loc) • 3.34 kB
JavaScript
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