1import * as Predicate from "effect/Predicate";
2
3/**
4 * Marker for error classes whose `message` is written for end users. The CLI's
5 * top-level handler prints a marked error's `message` as a single friendly
6 * line instead of dumping the raw cause chain.
7 *
8 * Mark a class by assigning the symbol in the class body:
9 *
10 * ```ts
11 * export class MyError extends Data.TaggedError("MyError")<{
12 * readonly message: string;
13 * }> {
14 * readonly [UserFacingError] = true;
15 * }
16 * ```
17 *
18 * Classes without a human-readable `message` prop derive one with a getter
19 * (see `AlchemistNotFound` in `Alchemist/Errors.ts`).
20 */
21export const UserFacingError: unique symbol = Symbol.for(
22 "alchemy/UserFacingError",
23);
24
25/** An error carrying a human-readable `message` that is safe to print as-is. */
26export interface UserFacingError {
27 readonly [UserFacingError]: true;
28 readonly message: string;
29}
30
31export const isUserFacing = (error: unknown): error is UserFacingError =>
32 Predicate.hasProperty(error, UserFacingError) &&
33 error[UserFacingError] === true &&
34 Predicate.hasProperty(error, "message") &&
35 typeof error.message === "string";