okay-error
Advanced tools
+52
-277
@@ -1,186 +0,11 @@ | ||
| /** | ||
| * Represents a successful computation result. | ||
| * Contains a value and methods for working with it in a chain. | ||
| * | ||
| * @typeparam ValueType - The type of the contained value | ||
| */ | ||
| export declare class Ok<ValueType> implements Iterable<ValueType> { | ||
| export type Ok<ValueType> = { | ||
| readonly ok: true; | ||
| readonly value: ValueType; | ||
| /** Discriminant property, always true for Ok results. */ | ||
| readonly ok: true; | ||
| /** | ||
| * Creates a new Ok result containing the provided value. | ||
| * | ||
| * @param value - The successful result value | ||
| */ | ||
| constructor(value: ValueType); | ||
| get raw(): { | ||
| ok: boolean; | ||
| value: ValueType; | ||
| }; | ||
| get error(): undefined; | ||
| /** | ||
| * Transform the contained value using the provided function. | ||
| * Similar to Array.map(), this lets you transform the value while staying in the Result context. | ||
| * | ||
| * @example | ||
| * ok(5).map(n => n * 2) // Ok with value 10 | ||
| * | ||
| * @param fn - Function to transform the value | ||
| * @returns A new Ok containing the transformed value | ||
| */ | ||
| map<NewValue>(fn: (v: ValueType) => NewValue): Ok<NewValue>; | ||
| /** | ||
| * Transform the error in an error result. For Ok instances this is a no-op. | ||
| * Provided for API compatibility with Err.mapErr(). | ||
| * | ||
| * @param _fn - Function to transform the error (ignored for Ok) | ||
| * @returns This same Ok instance | ||
| */ | ||
| mapErr<NewError>(_fn: (e: never) => NewError): Ok<ValueType>; | ||
| /** | ||
| * Chain another Result-returning operation after this one. | ||
| * This is useful for sequences of operations that might fail. | ||
| * | ||
| * @example | ||
| * ok(10).flatMap(n => n > 0 ? ok(n) : err('Negative')()) // Ok with value 10 | ||
| * | ||
| * @param fn - Function that takes the value and returns a new Result | ||
| * @returns The Result returned by the function | ||
| */ | ||
| flatMap<NewValue, ErrorType>(fn: (v: ValueType) => Result<NewValue, ErrorType>): Result<NewValue, ErrorType>; | ||
| /** | ||
| * Pattern‑match on this Result. | ||
| * The `err` branch will never run for Ok, but must be provided for exhaustiveness. | ||
| */ | ||
| match<OnOk, OnErr>(arms: { | ||
| ok: (v: ValueType) => OnOk; | ||
| err: (e: never) => OnErr; | ||
| }): OnOk; | ||
| /** | ||
| * Extract the value from this Ok result. | ||
| * Safe to use when you know the Result is Ok. | ||
| * | ||
| * @example | ||
| * ok(42).unwrap() // 42 | ||
| * | ||
| * @returns The contained value | ||
| */ | ||
| unwrap(): ValueType; | ||
| /** | ||
| * Extract the value or use a fallback if this is an Err. | ||
| * For Ok instances, this simply returns the contained value. | ||
| * | ||
| * @example | ||
| * ok(42).or(0) // 42 | ||
| * | ||
| * @param _fallback - Value to use if this is an Err (unused for Ok) | ||
| * @returns The contained value | ||
| */ | ||
| or(_fallback: ValueType): ValueType; | ||
| /** | ||
| * Iteration: `for…of` yields the contained value once. | ||
| */ | ||
| [Symbol.iterator](): Iterator<ValueType>; | ||
| } | ||
| /** | ||
| * Represents a failed computation result. | ||
| * Contains an error value and methods for working with it in a chain. | ||
| * | ||
| * @typeparam ErrorType - The type of the error value | ||
| */ | ||
| export declare class Err<ErrorType = unknown> implements Iterable<never> { | ||
| readonly error?: undefined; | ||
| }; | ||
| export type Err<ErrorType = unknown> = { | ||
| readonly ok: false; | ||
| readonly error: ErrorType; | ||
| /** Discriminant property, always false for Err results. */ | ||
| readonly ok: false; | ||
| /** | ||
| * Creates a new Err result containing the provided error. | ||
| * | ||
| * @param error - The error value | ||
| */ | ||
| constructor(error: ErrorType); | ||
| get raw(): { | ||
| ok: boolean; | ||
| error: ErrorType; | ||
| }; | ||
| get value(): undefined; | ||
| /** | ||
| * Add context to this error, creating a new error with a cause chain. | ||
| * This is particularly useful for wrapping lower-level errors with higher-level context. | ||
| * | ||
| * @example | ||
| * // Create an error chain | ||
| * const ioError = err('IO')({ errno: 'ENOENT' }); | ||
| * const configError = ioError.annotate('ConfigFileMissing', { path: '/etc/app.json' }); | ||
| * | ||
| * @param type - The type for the new error | ||
| * @param payload - Additional context properties for the new error | ||
| * @returns A new Err with this error as its cause | ||
| */ | ||
| annotate<K extends string, P extends Record<string, unknown> = Record<string, unknown>>(type: K, payload?: P): Err<{ | ||
| type: K; | ||
| } & P & { | ||
| cause: ErrorType; | ||
| }>; | ||
| /** | ||
| * Transform the value in a successful result. For Err instances this is a no-op. | ||
| * This allows for chainable operations that safely skip over errors. | ||
| * | ||
| * @param _fn - Function to transform the value (ignored for Err) | ||
| * @returns This same Err instance | ||
| */ | ||
| map<NewValue>(_fn: (v: never) => NewValue): Err<ErrorType>; | ||
| /** | ||
| * Transform the contained error using the provided function. | ||
| * This is useful for adapting or enriching error information. | ||
| * | ||
| * @example | ||
| * err('NotFound')({ id: 123 }).mapErr(e => ({ ...e, message: `Item ${e.id} not found` })) | ||
| * | ||
| * @param fn - Function to transform the error | ||
| * @returns A new Err containing the transformed error | ||
| */ | ||
| mapErr<NewError>(fn: (e: ErrorType) => NewError): Err<NewError>; | ||
| /** | ||
| * Chain another Result-returning operation after this one. | ||
| * For Err instances, this is a no-op that propagates the error. | ||
| * | ||
| * @param _fn - Function that would map the value (ignored for Err) | ||
| * @returns This same Err instance | ||
| */ | ||
| flatMap<NewValue>(_fn: (v: never) => Result<NewValue, ErrorType>): Err<ErrorType>; | ||
| /** | ||
| * Pattern‑match on this Result. | ||
| * The `ok` branch will never run for Err, but must be provided. | ||
| */ | ||
| match<OnOk, OnErr>(arms: { | ||
| ok: (v: never) => OnOk; | ||
| err: (e: ErrorType) => OnErr; | ||
| }): OnErr; | ||
| /** | ||
| * Try to extract a value from this Err result. | ||
| * Since this is an Err, this always throws the contained error. | ||
| * | ||
| * @example | ||
| * err('NotFound')().unwrap() // Throws the error | ||
| * | ||
| * @throws The contained error | ||
| */ | ||
| unwrap(): never; | ||
| /** | ||
| * Extract the value or use a fallback if this is an Err. | ||
| * For Err instances, this returns the provided fallback. | ||
| * | ||
| * @example | ||
| * err('NotFound')().or(0) // 0 | ||
| * | ||
| * @param fallback - Value to use instead of the error | ||
| * @returns The provided fallback value | ||
| */ | ||
| or<Fallback>(fallback: Fallback): Fallback; | ||
| /** | ||
| * Iteration: an error yields nothing, behaving like an empty collection. | ||
| */ | ||
| [Symbol.iterator](): Iterator<never>; | ||
| } | ||
| readonly value?: undefined; | ||
| }; | ||
| /** | ||
@@ -197,8 +22,4 @@ * Union type representing either a successful (Ok) or failed (Err) computation result. | ||
| * @example | ||
| * const success = ok(42); | ||
| * console.log(success.value); // 42 | ||
| * | ||
| * @overload | ||
| * @param value - The value to wrap in an Ok result | ||
| * @returns An Ok result containing the value | ||
| * const success = ok(42) | ||
| * console.log(success.value) // 42 | ||
| */ | ||
@@ -210,111 +31,65 @@ export declare function ok<T>(value: T): Ok<T>; | ||
| * @example | ||
| * const success = ok(); | ||
| * console.log(success.value); // undefined | ||
| * | ||
| * @overload | ||
| * @returns An Ok result containing undefined | ||
| * const success = ok() | ||
| * console.log(success.value) // undefined | ||
| */ | ||
| export declare function ok(): Ok<undefined>; | ||
| /** | ||
| * Construct a typed error Result with a discriminated error type and optional payload. | ||
| * Automatically captures a stack trace frame to help with debugging. | ||
| * Construct an error Result with a type and payload. | ||
| * | ||
| * @example | ||
| * // Create a timeout error with additional context | ||
| * const timeout = err('Timeout')({ ms: 2000 }); | ||
| * | ||
| * // Create an error with just a type | ||
| * const notFound = err('NotFound')(); | ||
| * | ||
| * @param type - The string discriminant type for the error | ||
| * @returns A function that takes an optional payload and returns an Err | ||
| * err('Timeout', { ms: 1000 }) // Err<{ type: 'Timeout', ms: 1000 }> | ||
| */ | ||
| export declare const err: <K extends string>(type: K) => <Payload extends Record<string, unknown> = Record<string, unknown>>(payload?: Payload) => Err<{ | ||
| export declare function err<K extends string, P extends Record<string, unknown>>(type: K, payload: P): Err<{ | ||
| type: K; | ||
| } & Payload>; | ||
| } & P>; | ||
| /** | ||
| * Wrap any error value in an Err result without adding trace information. | ||
| * Useful for working with existing error types or values that aren't in the Result format. | ||
| * Construct an error Result with only a type. | ||
| * | ||
| * @example | ||
| * try { | ||
| * // Some code that might throw | ||
| * } catch (error) { | ||
| * return errAny(error); | ||
| * } | ||
| * | ||
| * @param e - Any value to wrap as an error | ||
| * @returns An Err result containing the error value | ||
| * err('Timeout') // Err<{ type: 'Timeout' }> | ||
| */ | ||
| export declare const errAny: <E = unknown>(e: E) => Err<E>; | ||
| export declare function err<K extends string>(type: K): Err<{ | ||
| type: K; | ||
| }>; | ||
| /** | ||
| * Wraps a synchronous function that might throw, converting exceptions into Result types. | ||
| * Construct an error Result from an arbitrary error value (object, string, etc). | ||
| * Type safety and pattern matching only work if the value is an object with a `type` property. | ||
| * | ||
| * @example | ||
| * const parsed = result(() => JSON.parse(json)); | ||
| * if (parsed.ok) { | ||
| * console.log(parsed.value); | ||
| * } else { | ||
| * console.error('Parse error:', parsed.error); | ||
| * } | ||
| * | ||
| * @param work - A function that might throw an exception | ||
| * @returns A Result containing either the function's return value or the caught error | ||
| * err({ message: 'Something failed' }) | ||
| * err(new Error('fail')) | ||
| */ | ||
| export declare function result<ValueType>(work: () => ValueType): Result<ValueType>; | ||
| export declare function err<K>(payload: K): Err<K>; | ||
| /** | ||
| * Wraps a Promise, converting fulfilled/rejected states into Result types. | ||
| * Helper to embed a cause error payload when constructing a new Err. | ||
| * | ||
| * @example | ||
| * const response = await result(fetch(url)); | ||
| * if (!response.ok) { | ||
| * console.error('Network error:', response.error); | ||
| * return; | ||
| * } | ||
| * | ||
| * const data = await result(response.value.json()); | ||
| * | ||
| * @param work - A Promise or Promise-like object | ||
| * @returns A Promise that resolves to a Result | ||
| * const low = err('Low') | ||
| * const high = err('High', cause(low)) | ||
| */ | ||
| export declare function result<ValueType>(work: PromiseLike<ValueType>): Promise<Result<ValueType>>; | ||
| export declare function cause<T extends { | ||
| error: any; | ||
| }>(e: T): { | ||
| cause: T["error"]; | ||
| }; | ||
| export declare const map: <ValueType, NewValue, ErrorType = unknown>(r: Result<ValueType, ErrorType>, fn: (v: ValueType) => NewValue) => Result<NewValue, ErrorType>; | ||
| export declare const mapErr: <ValueType, ErrorType, NewError>(r: Result<ValueType, ErrorType>, fn: (e: ErrorType) => NewError) => Result<ValueType, NewError>; | ||
| export declare const flatMap: <ValueType, NewValue, ErrorType = unknown>(r: Result<ValueType, ErrorType>, fn: (v: ValueType) => Result<NewValue, ErrorType>) => Result<NewValue, ErrorType>; | ||
| export declare const unwrap: <ValueType, ErrorType = unknown>(r: Result<ValueType, ErrorType>) => ValueType; | ||
| export declare const orElse: <ValueType, ErrorType = unknown>(r: Result<ValueType, ErrorType>, fallback: ValueType) => ValueType; | ||
| export declare function result<ValueType>(work: () => ValueType): Result<ValueType>; | ||
| export declare function result<ValueType, ErrorType = unknown>(work: PromiseLike<ValueType>): Promise<Result<ValueType, ErrorType>>; | ||
| /** | ||
| * Re-hydrates a plain Result-like object with the proper prototype methods. | ||
| * Pattern match on a Result object (Ok/Err) or a discriminant string. | ||
| * | ||
| * @example | ||
| * // After JSON.parse, the Result has lost its methods | ||
| * const raw = JSON.parse(serializedResult); | ||
| * | ||
| * // Re-hydrate to get the methods back | ||
| * const live = result(raw); | ||
| * const value = live.unwrap(); | ||
| * | ||
| * @param work - A plain object with Result shape ({ok: true, value} or {ok: false, error}) | ||
| * @returns A proper Result instance with all methods | ||
| * Overloads: | ||
| * - match(result, { ok, err }) | ||
| * - match(type, { A: fn, B: fn }) | ||
| */ | ||
| export declare function result<ValueType, ErrorType>(work: Result<ValueType, ErrorType>): Result<ValueType, ErrorType>; | ||
| /** | ||
| * Exhaustively pattern‑match on the `type` discriminant of any object | ||
| * (error or otherwise). Works on unions like | ||
| * | ||
| * ```ts | ||
| * type Foo = | ||
| * | { type: "A"; a: number } | ||
| * | { type: "B"; b: string }; | ||
| * | ||
| * declare const foo: Foo; | ||
| * const out = matchType(foo, { | ||
| * A: v => v.a, | ||
| * B: v => v.b.length, | ||
| * }); | ||
| * ``` | ||
| * | ||
| * If you omit a case, TypeScript raises an error. | ||
| */ | ||
| export declare function matchType<T extends { | ||
| type: string; | ||
| }, Cases extends { | ||
| [K in T["type"]]: (val: Extract<T, { | ||
| type: K; | ||
| }>) => unknown; | ||
| }>(value: T, cases: Cases): ReturnType<Cases[keyof Cases]>; | ||
| export declare function match<ValueType, ErrorType, OnOk, OnErr>(r: Result<ValueType, ErrorType>, arms: { | ||
| ok: (v: ValueType) => OnOk; | ||
| err: (e: ErrorType) => OnErr; | ||
| }): OnOk | OnErr; | ||
| export declare function match<T extends string & (string extends T ? never : unknown), Cases extends { | ||
| [K in T]: () => unknown; | ||
| }>(value: T, cases: Cases & Record<Exclude<keyof Cases, T>, never>): ReturnType<Cases[keyof Cases]>; |
+31
-279
@@ -1,268 +0,39 @@ | ||
| /* ── Core classes ─────────────────────────────────────────── */ | ||
| /** | ||
| * Represents a successful computation result. | ||
| * Contains a value and methods for working with it in a chain. | ||
| * | ||
| * @typeparam ValueType - The type of the contained value | ||
| */ | ||
| export class Ok { | ||
| /** | ||
| * Creates a new Ok result containing the provided value. | ||
| * | ||
| * @param value - The successful result value | ||
| */ | ||
| constructor(value) { | ||
| this.value = value; | ||
| /** Discriminant property, always true for Ok results. */ | ||
| this.ok = true; | ||
| } | ||
| get raw() { | ||
| return { ok: true, value: this.value }; | ||
| } | ||
| get error() { | ||
| return undefined; | ||
| } | ||
| /** | ||
| * Transform the contained value using the provided function. | ||
| * Similar to Array.map(), this lets you transform the value while staying in the Result context. | ||
| * | ||
| * @example | ||
| * ok(5).map(n => n * 2) // Ok with value 10 | ||
| * | ||
| * @param fn - Function to transform the value | ||
| * @returns A new Ok containing the transformed value | ||
| */ | ||
| map(fn) { | ||
| return new Ok(fn(this.value)); | ||
| } | ||
| /** | ||
| * Transform the error in an error result. For Ok instances this is a no-op. | ||
| * Provided for API compatibility with Err.mapErr(). | ||
| * | ||
| * @param _fn - Function to transform the error (ignored for Ok) | ||
| * @returns This same Ok instance | ||
| */ | ||
| mapErr(_fn) { | ||
| return this; | ||
| } | ||
| /** | ||
| * Chain another Result-returning operation after this one. | ||
| * This is useful for sequences of operations that might fail. | ||
| * | ||
| * @example | ||
| * ok(10).flatMap(n => n > 0 ? ok(n) : err('Negative')()) // Ok with value 10 | ||
| * | ||
| * @param fn - Function that takes the value and returns a new Result | ||
| * @returns The Result returned by the function | ||
| */ | ||
| flatMap(fn) { | ||
| return fn(this.value); | ||
| } | ||
| /** | ||
| * Pattern‑match on this Result. | ||
| * The `err` branch will never run for Ok, but must be provided for exhaustiveness. | ||
| */ | ||
| match(arms) { | ||
| return arms.ok(this.value); | ||
| } | ||
| /** | ||
| * Extract the value from this Ok result. | ||
| * Safe to use when you know the Result is Ok. | ||
| * | ||
| * @example | ||
| * ok(42).unwrap() // 42 | ||
| * | ||
| * @returns The contained value | ||
| */ | ||
| unwrap() { | ||
| return this.value; | ||
| } | ||
| /** | ||
| * Extract the value or use a fallback if this is an Err. | ||
| * For Ok instances, this simply returns the contained value. | ||
| * | ||
| * @example | ||
| * ok(42).or(0) // 42 | ||
| * | ||
| * @param _fallback - Value to use if this is an Err (unused for Ok) | ||
| * @returns The contained value | ||
| */ | ||
| or(_fallback) { | ||
| return this.value; | ||
| } | ||
| /** | ||
| * Iteration: `for…of` yields the contained value once. | ||
| */ | ||
| *[Symbol.iterator]() { | ||
| yield this.value; | ||
| } | ||
| // ── Core Types ─────────────────────────────────────────── | ||
| export function ok(value) { | ||
| return { ok: true, value: value }; | ||
| } | ||
| /** | ||
| * Represents a failed computation result. | ||
| * Contains an error value and methods for working with it in a chain. | ||
| * | ||
| * @typeparam ErrorType - The type of the error value | ||
| */ | ||
| export class Err { | ||
| /** | ||
| * Creates a new Err result containing the provided error. | ||
| * | ||
| * @param error - The error value | ||
| */ | ||
| constructor(error) { | ||
| this.error = error; | ||
| /** Discriminant property, always false for Err results. */ | ||
| this.ok = false; | ||
| export function err(typeOrPayload, payload) { | ||
| if (payload !== undefined) { | ||
| return { ok: false, error: { type: typeOrPayload, ...payload } }; | ||
| } | ||
| get raw() { | ||
| return { ok: false, error: this.error }; | ||
| } | ||
| get value() { | ||
| return undefined; | ||
| } | ||
| /** | ||
| * Add context to this error, creating a new error with a cause chain. | ||
| * This is particularly useful for wrapping lower-level errors with higher-level context. | ||
| * | ||
| * @example | ||
| * // Create an error chain | ||
| * const ioError = err('IO')({ errno: 'ENOENT' }); | ||
| * const configError = ioError.annotate('ConfigFileMissing', { path: '/etc/app.json' }); | ||
| * | ||
| * @param type - The type for the new error | ||
| * @param payload - Additional context properties for the new error | ||
| * @returns A new Err with this error as its cause | ||
| */ | ||
| annotate(type, payload = {}) { | ||
| return new Err({ | ||
| type, | ||
| ...payload, | ||
| cause: this.error, | ||
| }); | ||
| } | ||
| /** | ||
| * Transform the value in a successful result. For Err instances this is a no-op. | ||
| * This allows for chainable operations that safely skip over errors. | ||
| * | ||
| * @param _fn - Function to transform the value (ignored for Err) | ||
| * @returns This same Err instance | ||
| */ | ||
| map(_fn) { | ||
| return this; | ||
| } | ||
| /** | ||
| * Transform the contained error using the provided function. | ||
| * This is useful for adapting or enriching error information. | ||
| * | ||
| * @example | ||
| * err('NotFound')({ id: 123 }).mapErr(e => ({ ...e, message: `Item ${e.id} not found` })) | ||
| * | ||
| * @param fn - Function to transform the error | ||
| * @returns A new Err containing the transformed error | ||
| */ | ||
| mapErr(fn) { | ||
| return new Err(fn(this.error)); | ||
| } | ||
| /** | ||
| * Chain another Result-returning operation after this one. | ||
| * For Err instances, this is a no-op that propagates the error. | ||
| * | ||
| * @param _fn - Function that would map the value (ignored for Err) | ||
| * @returns This same Err instance | ||
| */ | ||
| flatMap(_fn) { | ||
| return this; | ||
| } | ||
| /** | ||
| * Pattern‑match on this Result. | ||
| * The `ok` branch will never run for Err, but must be provided. | ||
| */ | ||
| match(arms) { | ||
| return arms.err(this.error); | ||
| } | ||
| /** | ||
| * Try to extract a value from this Err result. | ||
| * Since this is an Err, this always throws the contained error. | ||
| * | ||
| * @example | ||
| * err('NotFound')().unwrap() // Throws the error | ||
| * | ||
| * @throws The contained error | ||
| */ | ||
| unwrap() { | ||
| throw this.error; | ||
| } | ||
| /** | ||
| * Extract the value or use a fallback if this is an Err. | ||
| * For Err instances, this returns the provided fallback. | ||
| * | ||
| * @example | ||
| * err('NotFound')().or(0) // 0 | ||
| * | ||
| * @param fallback - Value to use instead of the error | ||
| * @returns The provided fallback value | ||
| */ | ||
| or(fallback) { | ||
| return fallback; | ||
| } | ||
| /** | ||
| * Iteration: an error yields nothing, behaving like an empty collection. | ||
| */ | ||
| *[Symbol.iterator]() { | ||
| // empty | ||
| } | ||
| return { ok: false, error: typeOrPayload }; | ||
| } | ||
| /** | ||
| * Implementation of ok function | ||
| */ | ||
| export function ok(value) { | ||
| return new Ok(value); | ||
| } | ||
| /** | ||
| * Construct a typed error Result with a discriminated error type and optional payload. | ||
| * Automatically captures a stack trace frame to help with debugging. | ||
| * Helper to embed a cause error payload when constructing a new Err. | ||
| * | ||
| * @example | ||
| * // Create a timeout error with additional context | ||
| * const timeout = err('Timeout')({ ms: 2000 }); | ||
| * | ||
| * // Create an error with just a type | ||
| * const notFound = err('NotFound')(); | ||
| * | ||
| * @param type - The string discriminant type for the error | ||
| * @returns A function that takes an optional payload and returns an Err | ||
| * const low = err('Low') | ||
| * const high = err('High', cause(low)) | ||
| */ | ||
| export const err = (type) => (payload = {}) => new Err({ | ||
| type, | ||
| ...payload, | ||
| }); | ||
| /** | ||
| * Wrap any error value in an Err result without adding trace information. | ||
| * Useful for working with existing error types or values that aren't in the Result format. | ||
| * | ||
| * @example | ||
| * try { | ||
| * // Some code that might throw | ||
| * } catch (error) { | ||
| * return errAny(error); | ||
| * } | ||
| * | ||
| * @param e - Any value to wrap as an error | ||
| * @returns An Err result containing the error value | ||
| */ | ||
| export const errAny = (e) => new Err(e); | ||
| /* ── Universal factory / wrapper / rehydrator ─────────────── */ | ||
| export function cause(e) { | ||
| return { cause: e.error }; | ||
| } | ||
| // ── Utility Functions (functional style) ──────────────── | ||
| export const map = (r, fn) => (r.ok ? ok(fn(r.value)) : r); | ||
| export const mapErr = (r, fn) => (r.ok ? r : err(fn(r.error))); | ||
| export const flatMap = (r, fn) => (r.ok ? fn(r.value) : r); | ||
| export const unwrap = (r) => { | ||
| if (r.ok) | ||
| return r.value; | ||
| throw r.error; | ||
| }; | ||
| export const orElse = (r, fallback) => (r.ok ? r.value : fallback); | ||
| // ── Universal factory / wrapper / rehydrator ──────────── | ||
| const isPromiseLike = (v) => typeof v === "object" && v !== null && "then" in v; | ||
| export function result(work) { | ||
| // Rehydrate | ||
| if (typeof work === "object" && work !== null && "ok" in work) { | ||
| return work.ok ? new Ok(work.value) : new Err(work.error); | ||
| } | ||
| // Promise‑like | ||
| if (isPromiseLike(work)) { | ||
| return Promise.resolve(work) | ||
| .then((ok)) | ||
| .catch((e) => errAny(e)); | ||
| .catch((e) => err(e)); | ||
| } | ||
| // Sync fn | ||
| try { | ||
@@ -272,29 +43,10 @@ return ok(work()); | ||
| catch (e) { | ||
| return errAny(e); | ||
| return err(e); | ||
| } | ||
| } | ||
| /* -------------------------------------------------- */ | ||
| /* Universal matchType */ | ||
| /* -------------------------------------------------- */ | ||
| /** | ||
| * Exhaustively pattern‑match on the `type` discriminant of any object | ||
| * (error or otherwise). Works on unions like | ||
| * | ||
| * ```ts | ||
| * type Foo = | ||
| * | { type: "A"; a: number } | ||
| * | { type: "B"; b: string }; | ||
| * | ||
| * declare const foo: Foo; | ||
| * const out = matchType(foo, { | ||
| * A: v => v.a, | ||
| * B: v => v.b.length, | ||
| * }); | ||
| * ``` | ||
| * | ||
| * If you omit a case, TypeScript raises an error. | ||
| */ | ||
| export function matchType(value, cases) { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| return cases[value.type](value); | ||
| export function match(a, b) { | ||
| if (typeof a === "object" && a && "ok" in a) { | ||
| return a.ok ? b.ok(a.value) : b.err(a.error); | ||
| } | ||
| return b[a](); | ||
| } |
+1
-1
| { | ||
| "name": "okay-error", | ||
| "version": "0.2.1", | ||
| "version": "1.0.1", | ||
| "author": "Henry Mao", | ||
@@ -5,0 +5,0 @@ "private": false, |
+111
-85
@@ -13,8 +13,34 @@ # `okay-error` | ||
| * **Type‑level errors** - every possible failure is visible in the function signature (`Result<T, E>`), not thrown from the shadows. Rely on the type checker to ensure you handle every possible failure. | ||
| * **Cause‑chain built‑in** - wrap lower‑level errors with the `.annotate()` method; walk the `cause` links later to see the full logical call stack. | ||
| * **Iterable & ergonomic** - `for (const v of ok(3)) …` works, and helpers `map`, `flatMap`, `or` feel familiar to JS arrays. | ||
| * **Re‑hydration** - after `JSON.parse`, call `result(raw)` to get the fluent API back. | ||
| * **Cause‑chain built‑in** - link any parent error using the `cause()` helper; walk the `cause` links later to see the full logical call stack. | ||
| * **Ergonomic** - helpers `map`, `flatMap`, `or` feel familiar to JS arrays. | ||
| * **Re‑hydration** - after `JSON.parse`, call `result` to get a plain `Result` object. | ||
| --- | ||
| ## Table of Contents | ||
| - [`okay-error`](#okay-error) | ||
| - [Why *okay-error*?](#why-okay-error) | ||
| - [Table of Contents](#table-of-contents) | ||
| - [Install](#install) | ||
| - [Quick tour](#quick-tour) | ||
| - [From try-catch to Result](#from-try-catch-to-result) | ||
| - [Propagating context](#propagating-context) | ||
| - [How cause works](#how-cause-works) | ||
| - [Working with async operations](#working-with-async-operations) | ||
| - [Feature checklist](#feature-checklist) | ||
| - [API reference](#api-reference) | ||
| - [Constructors](#constructors) | ||
| - [Functions](#functions) | ||
| - [Types](#types) | ||
| - [JSON round‑trip example](#json-roundtrip-example) | ||
| - [Error with cause example](#error-with-cause-example) | ||
| - [The `cause()` helper](#the-cause-helper) | ||
| - [Pattern matching example](#pattern-matching-example) | ||
| - [Pattern matching with `match`](#pattern-matching-with-match) | ||
| - [Type Safety and Exhaustiveness](#type-safety-and-exhaustiveness) | ||
| - [License](#license) | ||
| --- | ||
| ## Install | ||
@@ -46,3 +72,3 @@ | ||
| // Alternative approach with Result | ||
| import { ok, err, result } from 'okay-error'; | ||
| import { ok, err, result, annotate } from 'okay-error'; | ||
@@ -53,3 +79,3 @@ // Define functions that return Result types | ||
| if (id <= 0) { | ||
| return err('InvalidId')({ id }); | ||
| return err('InvalidId', { id }); | ||
| } | ||
@@ -61,3 +87,3 @@ // Simulating database lookup | ||
| // Convert any unexpected errors | ||
| return err('DbError')({ cause: error }); | ||
| return err('DbError', { cause: error }); | ||
| } | ||
@@ -80,3 +106,3 @@ } | ||
| ? ok(`Hello ${name}!`) // Return Ok for success | ||
| : err('NameTooShort')({ min: 1 }) // Return Err for failure | ||
| : err('NameTooShort', { min: 1 }) // Return Err for failure | ||
| ) | ||
@@ -93,3 +119,3 @@ .or('Hi stranger!'); // Use fallback if any step failed | ||
| ```ts | ||
| function readConfig(): Result<string, ConfigErr> { … } | ||
| function readConfig(): Result<string, ConfigErr> { /* ... */ } | ||
@@ -100,3 +126,3 @@ function boot(): Result<void, BootErr> { | ||
| // Add higher-level context while preserving the original error | ||
| return cfg.annotate('BootConfig', { phase: 'init' }); | ||
| return err('BootConfig', { phase: 'init', ...cause(cfg) }); | ||
| } | ||
@@ -107,11 +133,6 @@ return ok(); | ||
| #### How annotation works | ||
| #### How cause works | ||
| `.annotate()` creates a new error that wraps the original error: | ||
| `cause` creates a new object `{ cause: error }` that can be spread into your error payload. This creates a discoverable, traceable error chain that's useful for debugging: | ||
| 1. The original error becomes the `cause` property of the new error | ||
| 2. Any additional payload properties are merged into the new error | ||
| This creates a discoverable, traceable error chain that's invaluable for debugging: | ||
| ```plain | ||
@@ -141,3 +162,3 @@ Err { | ||
| if (!response.ok) { | ||
| return response.annotate('NetworkError', { userId }); | ||
| return annotate(response, 'NetworkError', { userId }); | ||
| } | ||
@@ -148,3 +169,3 @@ | ||
| if (!data.ok) { | ||
| return data.annotate('ParseError', { userId }); | ||
| return annotate(data, 'ParseError', { userId }); | ||
| } | ||
@@ -154,3 +175,3 @@ | ||
| if (!data.value.name) { | ||
| return err('ValidationError')({ | ||
| return err('ValidationError', { | ||
| userId, | ||
@@ -193,10 +214,10 @@ message: 'User name is required' | ||
| | ✔ | Feature | Example | | ||
| |---|---------|---------| | ||
| | Typed constructors | `err('Timeout')({ ms: 2000 })` | | ||
| | `map`, `flatMap`, `or` | `ok(1).map(x=>x+1).flatMap(fn).or(0)` | | ||
| | Works with **Promise** | `await result(fetch(url))` | | ||
| | Cause‑chain + optional stack frame | `err(...).annotate('DB', {...})` | | ||
| | JSON serialisable & iterable | `JSON.stringify(err('X')())`, `[...ok(7)]` | | ||
| | Re‑hydrate after JSON | `const live = result(JSON.parse(raw))` | | ||
| | ✔ | Feature | Example | | ||
| | ---------------------------------- | --------------------------------------- | ------- | | ||
| | Typed constructors | `err({ type: 'Timeout', ms: 2000 })` or `err('Timeout', { ms: 2000 })` | | ||
| | `map`, `flatMap`, `or` | `ok(1).map(x=>x+1).flatMap(fn).or(0)` | | ||
| | Works with **Promise** | `await result(fetch(url))` | | ||
| | Cause‑chain + optional stack frame | `annotate(err(...), 'DB', {...})` | | ||
| | JSON serialisable | `JSON.stringify(err('X', {}))` | | ||
| | Re‑hydrate after JSON | `const plain = result(JSON.parse(raw))` | | ||
@@ -209,26 +230,17 @@ --- | ||
| | function | purpose | | ||
| |----------|---------| | ||
| | `ok(value)` | success result | | ||
| | `err(kind)(payload?)` | typed error **+ trace** | | ||
| | `errAny(value)` | error without a discriminant / trace | | ||
| | `result(x)` | wrap a sync fn, a Promise, **or** re‑hydrate a raw object | | ||
| | function | purpose | | ||
| | --------------------- | --------------------------------------------------------- | | ||
| | `ok(value)` | success result | | ||
| | `err(type, payload?)` | typed error, payload is merged with `{ type }` | | ||
| | `err({ ... })` | error from arbitrary value (object, string, etc) | | ||
| | `result(x)` | wrap a sync fn, a Promise, **or** re‑hydrate a raw object | | ||
| ### Instance methods (on `Ok` & `Err`) | ||
| ### Functions | ||
| | method | on `Ok` | on `Err` | | ||
| |--------|---------|----------| | ||
| | `map(fn)` | transform value | no‑op | | ||
| | `mapErr(fn)` | no‑op | transform error | | ||
| | `flatMap(fn)` | chain another `Result` | propagate error | | ||
| | `match(arms)` | run `ok` arm | run `err` arm | | ||
| | `matchType(cases)` | N/A | match on error type | | ||
| | `unwrap()` | get value | throw error | | ||
| | `or(fallback)` | value | fallback | | ||
| | `[Symbol.iterator]()` | yields value | yields nothing | | ||
| | function | purpose | | ||
| | ---------------------------------- | --------------------------------------------------- | | ||
| | `cause(error)` | wrap an error as a cause for another error | | ||
| | `match(result, { ok, err })` | pattern match on Result (success/failure) | | ||
| | `match(type, cases)` | pattern match on a discriminant string (exhaustive) | | ||
| ### Instance Methods | ||
| * `.annotate(kind, payload?)` – add context + cause (on `Err` instances only) | ||
| ### Types | ||
@@ -245,7 +257,6 @@ | ||
| ```ts | ||
| const errOut = err('DbConn')({ host: 'db.local' }); | ||
| const errOut = err('DbConn', { host: 'db.local' }); // preferred | ||
| const raw = JSON.stringify(errOut); | ||
| const back = result(JSON.parse(raw)); // re‑hydrated | ||
| for (const v of back) console.log(v); // nothing, because Err | ||
| ``` | ||
@@ -256,7 +267,16 @@ | ||
| ```ts | ||
| // Create an error chain | ||
| const ioError = err('IO')({ errno: 'ENOENT' }); | ||
| const configError = ioError.annotate('ConfigFileMissing', { path: '/etc/app.json' }); | ||
| const bootError = configError.annotate('BootConfig', { phase: 'init' }); | ||
| import { err, cause } from 'okay-error'; | ||
| // Preferred: use err(type, payload) and cause() | ||
| const ioError = err('IO', { errno: 'ENOENT' }); | ||
| const configError = err('ConfigFileMissing', { path: '/etc/app.json', ...cause(ioError) }); | ||
| const bootError = err('BootConfig', { phase: 'init', ...cause(configError) }); | ||
| // You can also chain inline: | ||
| const chained = err('BootConfig', cause( | ||
| err('ConfigFileMissing', cause( | ||
| err('IO', { errno: 'ENOENT' }) | ||
| )) | ||
| )); | ||
| // Now you can navigate the error chain | ||
@@ -267,17 +287,30 @@ console.log(bootError.error.type); // 'BootConfig' | ||
| --- | ||
| ## The `cause()` helper | ||
| The `cause(error)` function is the idiomatic way to link any parent error as the cause of the current error—this parent could be a lower-level error, a related error, or any error that led to the current one: | ||
| ```ts | ||
| const base = err('Base', { info: 123 }) | ||
| const wrapped = err('Higher', { ...cause(base), context: 'extra' }) | ||
| // wrapped.error.cause === base | ||
| ``` | ||
| This is preferred over annotate, and is composable for deep error chains. | ||
| ## Pattern matching example | ||
| ### Basic pattern matching with `match` | ||
| ### Pattern matching with `match` | ||
| The `match` function is overloaded: | ||
| - Use `match(result, { ok, err })` to branch on Result objects. | ||
| - Use `match(type, { ...cases })` to branch on discriminant string unions (exhaustive, type-safe). | ||
| - `matchType` is now an alias for the discriminant string overload for backwards compatibility. | ||
| ```ts | ||
| // Define a function that returns a Result | ||
| function divide(a: number, b: number): Result<number, { type: string; message: string }> { | ||
| if (b === 0) { | ||
| return err('DivideByZero')({ message: 'Cannot divide by zero' }); | ||
| } | ||
| return ok(a / b); | ||
| } | ||
| // Use match to handle both success and error cases in one expression | ||
| const result = divide(10, 2).match({ | ||
| // Result matching | ||
| const result = divide(10, 2); | ||
| const message = match(result, { | ||
| ok: (value) => `Result: ${value}`, | ||
@@ -298,4 +331,7 @@ err: (error) => `Error: ${error.message}` | ||
| ### Type-based pattern matching with `matchType` | ||
| ### Type Safety and Exhaustiveness | ||
| When using `match` with a discriminant string union, TypeScript will enforce exhaustiveness, ensuring you handle all possible cases. This provides an additional layer of type safety for error handling. | ||
| ```ts | ||
@@ -310,22 +346,13 @@ // Define a discriminated union of error types | ||
| function fetchData(id: string): Result<{ name: string }, ApiError> { | ||
| // Simulate different errors based on input | ||
| if (id === '404') { | ||
| return err('NotFound')({ id }); | ||
| } else if (id === 'slow') { | ||
| return err('Timeout')({ ms: 5000 }); | ||
| } else if (id === 'auth') { | ||
| return err('Unauthorized')({ reason: 'Token expired' }); | ||
| } | ||
| return ok({ name: 'Sample Data' }); | ||
| // ... | ||
| } | ||
| // Use matchType to handle each error type differently | ||
| // Use match to handle each error type differently | ||
| const response = fetchData('slow'); | ||
| if (!response.ok) { | ||
| const errorMessage = response.matchType({ | ||
| NotFound: (e) => `Item ${e.id} could not be found`, | ||
| Timeout: (e) => `Request timed out after ${e.ms}ms`, | ||
| Unauthorized: (e) => `Access denied: ${e.reason}` | ||
| const errorMessage = match(response.error.type, { | ||
| NotFound: () => `Item ${response.error.id} could not be found`, | ||
| Timeout: () => `Request timed out after ${response.error.ms}ms`, | ||
| Unauthorized: () => `Access denied: ${response.error.reason}` | ||
| }); | ||
@@ -335,10 +362,9 @@ | ||
| } | ||
| // Warning: match requires a discriminated union | ||
| // If you're not using a discriminated union, use match instead | ||
| ``` | ||
| The `matchType` method provides exhaustive pattern matching for discriminated union error types, ensuring you handle all possible error cases at compile time. | ||
| --- | ||
| ## License | ||
| MIT |
+101
-376
@@ -1,245 +0,13 @@ | ||
| /* ── Core classes ─────────────────────────────────────────── */ | ||
| // ── Core Types ─────────────────────────────────────────── | ||
| /** | ||
| * Represents a successful computation result. | ||
| * Contains a value and methods for working with it in a chain. | ||
| * | ||
| * @typeparam ValueType - The type of the contained value | ||
| */ | ||
| export class Ok<ValueType> implements Iterable<ValueType> { | ||
| /** Discriminant property, always true for Ok results. */ | ||
| readonly ok = true as const | ||
| /** | ||
| * Creates a new Ok result containing the provided value. | ||
| * | ||
| * @param value - The successful result value | ||
| */ | ||
| constructor(public readonly value: ValueType) {} | ||
| get raw() { | ||
| return { ok: true, value: this.value } | ||
| } | ||
| get error(): undefined { | ||
| return undefined | ||
| } | ||
| /** | ||
| * Transform the contained value using the provided function. | ||
| * Similar to Array.map(), this lets you transform the value while staying in the Result context. | ||
| * | ||
| * @example | ||
| * ok(5).map(n => n * 2) // Ok with value 10 | ||
| * | ||
| * @param fn - Function to transform the value | ||
| * @returns A new Ok containing the transformed value | ||
| */ | ||
| map<NewValue>(fn: (v: ValueType) => NewValue): Ok<NewValue> { | ||
| return new Ok(fn(this.value)) | ||
| } | ||
| /** | ||
| * Transform the error in an error result. For Ok instances this is a no-op. | ||
| * Provided for API compatibility with Err.mapErr(). | ||
| * | ||
| * @param _fn - Function to transform the error (ignored for Ok) | ||
| * @returns This same Ok instance | ||
| */ | ||
| mapErr<NewError>(_fn: (e: never) => NewError): Ok<ValueType> { | ||
| return this | ||
| } | ||
| /** | ||
| * Chain another Result-returning operation after this one. | ||
| * This is useful for sequences of operations that might fail. | ||
| * | ||
| * @example | ||
| * ok(10).flatMap(n => n > 0 ? ok(n) : err('Negative')()) // Ok with value 10 | ||
| * | ||
| * @param fn - Function that takes the value and returns a new Result | ||
| * @returns The Result returned by the function | ||
| */ | ||
| flatMap<NewValue, ErrorType>( | ||
| fn: (v: ValueType) => Result<NewValue, ErrorType>, | ||
| ): Result<NewValue, ErrorType> { | ||
| return fn(this.value) | ||
| } | ||
| /** | ||
| * Pattern‑match on this Result. | ||
| * The `err` branch will never run for Ok, but must be provided for exhaustiveness. | ||
| */ | ||
| match<OnOk, OnErr>(arms: { | ||
| ok: (v: ValueType) => OnOk | ||
| err: (e: never) => OnErr | ||
| }): OnOk { | ||
| return arms.ok(this.value) | ||
| } | ||
| /** | ||
| * Extract the value from this Ok result. | ||
| * Safe to use when you know the Result is Ok. | ||
| * | ||
| * @example | ||
| * ok(42).unwrap() // 42 | ||
| * | ||
| * @returns The contained value | ||
| */ | ||
| unwrap(): ValueType { | ||
| return this.value | ||
| } | ||
| /** | ||
| * Extract the value or use a fallback if this is an Err. | ||
| * For Ok instances, this simply returns the contained value. | ||
| * | ||
| * @example | ||
| * ok(42).or(0) // 42 | ||
| * | ||
| * @param _fallback - Value to use if this is an Err (unused for Ok) | ||
| * @returns The contained value | ||
| */ | ||
| or(_fallback: ValueType): ValueType { | ||
| return this.value | ||
| } | ||
| /** | ||
| * Iteration: `for…of` yields the contained value once. | ||
| */ | ||
| *[Symbol.iterator](): Iterator<ValueType> { | ||
| yield this.value | ||
| } | ||
| export type Ok<ValueType> = { | ||
| readonly ok: true | ||
| readonly value: ValueType | ||
| readonly error?: undefined | ||
| } | ||
| /** | ||
| * Represents a failed computation result. | ||
| * Contains an error value and methods for working with it in a chain. | ||
| * | ||
| * @typeparam ErrorType - The type of the error value | ||
| */ | ||
| export class Err<ErrorType = unknown> implements Iterable<never> { | ||
| /** Discriminant property, always false for Err results. */ | ||
| readonly ok = false as const | ||
| /** | ||
| * Creates a new Err result containing the provided error. | ||
| * | ||
| * @param error - The error value | ||
| */ | ||
| constructor(public readonly error: ErrorType) {} | ||
| get raw() { | ||
| return { ok: false, error: this.error } | ||
| } | ||
| get value(): undefined { | ||
| return undefined | ||
| } | ||
| /** | ||
| * Add context to this error, creating a new error with a cause chain. | ||
| * This is particularly useful for wrapping lower-level errors with higher-level context. | ||
| * | ||
| * @example | ||
| * // Create an error chain | ||
| * const ioError = err('IO')({ errno: 'ENOENT' }); | ||
| * const configError = ioError.annotate('ConfigFileMissing', { path: '/etc/app.json' }); | ||
| * | ||
| * @param type - The type for the new error | ||
| * @param payload - Additional context properties for the new error | ||
| * @returns A new Err with this error as its cause | ||
| */ | ||
| annotate< | ||
| K extends string, | ||
| P extends Record<string, unknown> = Record<string, unknown>, | ||
| >(type: K, payload: P = {} as P) { | ||
| return new Err({ | ||
| type, | ||
| ...payload, | ||
| cause: this.error, | ||
| }) | ||
| } | ||
| /** | ||
| * Transform the value in a successful result. For Err instances this is a no-op. | ||
| * This allows for chainable operations that safely skip over errors. | ||
| * | ||
| * @param _fn - Function to transform the value (ignored for Err) | ||
| * @returns This same Err instance | ||
| */ | ||
| map<NewValue>(_fn: (v: never) => NewValue): Err<ErrorType> { | ||
| return this | ||
| } | ||
| /** | ||
| * Transform the contained error using the provided function. | ||
| * This is useful for adapting or enriching error information. | ||
| * | ||
| * @example | ||
| * err('NotFound')({ id: 123 }).mapErr(e => ({ ...e, message: `Item ${e.id} not found` })) | ||
| * | ||
| * @param fn - Function to transform the error | ||
| * @returns A new Err containing the transformed error | ||
| */ | ||
| mapErr<NewError>(fn: (e: ErrorType) => NewError): Err<NewError> { | ||
| return new Err(fn(this.error)) | ||
| } | ||
| /** | ||
| * Chain another Result-returning operation after this one. | ||
| * For Err instances, this is a no-op that propagates the error. | ||
| * | ||
| * @param _fn - Function that would map the value (ignored for Err) | ||
| * @returns This same Err instance | ||
| */ | ||
| flatMap<NewValue>( | ||
| _fn: (v: never) => Result<NewValue, ErrorType>, | ||
| ): Err<ErrorType> { | ||
| return this | ||
| } | ||
| /** | ||
| * Pattern‑match on this Result. | ||
| * The `ok` branch will never run for Err, but must be provided. | ||
| */ | ||
| match<OnOk, OnErr>(arms: { | ||
| ok: (v: never) => OnOk | ||
| err: (e: ErrorType) => OnErr | ||
| }): OnErr { | ||
| return arms.err(this.error) | ||
| } | ||
| /** | ||
| * Try to extract a value from this Err result. | ||
| * Since this is an Err, this always throws the contained error. | ||
| * | ||
| * @example | ||
| * err('NotFound')().unwrap() // Throws the error | ||
| * | ||
| * @throws The contained error | ||
| */ | ||
| unwrap(): never { | ||
| throw this.error | ||
| } | ||
| /** | ||
| * Extract the value or use a fallback if this is an Err. | ||
| * For Err instances, this returns the provided fallback. | ||
| * | ||
| * @example | ||
| * err('NotFound')().or(0) // 0 | ||
| * | ||
| * @param fallback - Value to use instead of the error | ||
| * @returns The provided fallback value | ||
| */ | ||
| or<Fallback>(fallback: Fallback): Fallback { | ||
| return fallback | ||
| } | ||
| /** | ||
| * Iteration: an error yields nothing, behaving like an empty collection. | ||
| */ | ||
| *[Symbol.iterator](): Iterator<never> { | ||
| // empty | ||
| } | ||
| export type Err<ErrorType = unknown> = { | ||
| readonly ok: false | ||
| readonly error: ErrorType | ||
| readonly value?: undefined | ||
| } | ||
@@ -257,3 +25,3 @@ | ||
| /* ── Constructors ─────────────────────────────────────────── */ | ||
| // ── Constructors ───────────────────────────────────────── | ||
@@ -264,8 +32,4 @@ /** | ||
| * @example | ||
| * const success = ok(42); | ||
| * console.log(success.value); // 42 | ||
| * | ||
| * @overload | ||
| * @param value - The value to wrap in an Ok result | ||
| * @returns An Ok result containing the value | ||
| * const success = ok(42) | ||
| * console.log(success.value) // 42 | ||
| */ | ||
@@ -277,171 +41,132 @@ export function ok<T>(value: T): Ok<T> | ||
| * @example | ||
| * const success = ok(); | ||
| * console.log(success.value); // undefined | ||
| * | ||
| * @overload | ||
| * @returns An Ok result containing undefined | ||
| * const success = ok() | ||
| * console.log(success.value) // undefined | ||
| */ | ||
| export function ok(): Ok<undefined> | ||
| /** | ||
| * Implementation of ok function | ||
| */ | ||
| export function ok<T>(value?: T): Ok<T> { | ||
| return new Ok(value as T) | ||
| return { ok: true, value: value as T } | ||
| } | ||
| /** | ||
| * Construct a typed error Result with a discriminated error type and optional payload. | ||
| * Automatically captures a stack trace frame to help with debugging. | ||
| * Construct an error Result with a type and payload. | ||
| * | ||
| * @example | ||
| * // Create a timeout error with additional context | ||
| * const timeout = err('Timeout')({ ms: 2000 }); | ||
| * | ||
| * // Create an error with just a type | ||
| * const notFound = err('NotFound')(); | ||
| * | ||
| * @param type - The string discriminant type for the error | ||
| * @returns A function that takes an optional payload and returns an Err | ||
| * err('Timeout', { ms: 1000 }) // Err<{ type: 'Timeout', ms: 1000 }> | ||
| */ | ||
| export const err = | ||
| <K extends string>(type: K) => | ||
| <Payload extends Record<string, unknown> = Record<string, unknown>>( | ||
| payload: Payload = {} as Payload, | ||
| ) => | ||
| new Err({ | ||
| type, | ||
| ...payload, | ||
| }) | ||
| export function err<K extends string, P extends Record<string, unknown>>( | ||
| type: K, | ||
| payload: P, | ||
| ): Err<{ type: K } & P> | ||
| /** | ||
| * Wrap any error value in an Err result without adding trace information. | ||
| * Useful for working with existing error types or values that aren't in the Result format. | ||
| * Construct an error Result with only a type. | ||
| * | ||
| * @example | ||
| * try { | ||
| * // Some code that might throw | ||
| * } catch (error) { | ||
| * return errAny(error); | ||
| * } | ||
| * | ||
| * @param e - Any value to wrap as an error | ||
| * @returns An Err result containing the error value | ||
| * err('Timeout') // Err<{ type: 'Timeout' }> | ||
| */ | ||
| export const errAny = <E = unknown>(e: E): Err<E> => new Err(e) | ||
| export function err<K extends string>(type: K): Err<{ type: K }> | ||
| /* ── Universal factory / wrapper / rehydrator ─────────────── */ | ||
| const isPromiseLike = <T>(v: unknown): v is PromiseLike<T> => | ||
| typeof v === "object" && v !== null && "then" in v | ||
| /** | ||
| * Wraps a synchronous function that might throw, converting exceptions into Result types. | ||
| * Construct an error Result from an arbitrary error value (object, string, etc). | ||
| * Type safety and pattern matching only work if the value is an object with a `type` property. | ||
| * | ||
| * @example | ||
| * const parsed = result(() => JSON.parse(json)); | ||
| * if (parsed.ok) { | ||
| * console.log(parsed.value); | ||
| * } else { | ||
| * console.error('Parse error:', parsed.error); | ||
| * } | ||
| * | ||
| * @param work - A function that might throw an exception | ||
| * @returns A Result containing either the function's return value or the caught error | ||
| * err({ message: 'Something failed' }) | ||
| * err(new Error('fail')) | ||
| */ | ||
| export function result<ValueType>(work: () => ValueType): Result<ValueType> | ||
| export function err<K>(payload: K): Err<K> | ||
| export function err(typeOrPayload: any, payload?: any): Err<any> { | ||
| if (payload !== undefined) { | ||
| return { ok: false, error: { type: typeOrPayload, ...payload } } | ||
| } | ||
| return { ok: false, error: typeOrPayload } | ||
| } | ||
| /** | ||
| * Wraps a Promise, converting fulfilled/rejected states into Result types. | ||
| * Helper to embed a cause error payload when constructing a new Err. | ||
| * | ||
| * @example | ||
| * const response = await result(fetch(url)); | ||
| * if (!response.ok) { | ||
| * console.error('Network error:', response.error); | ||
| * return; | ||
| * } | ||
| * | ||
| * const data = await result(response.value.json()); | ||
| * | ||
| * @param work - A Promise or Promise-like object | ||
| * @returns A Promise that resolves to a Result | ||
| * const low = err('Low') | ||
| * const high = err('High', cause(low)) | ||
| */ | ||
| export function result<ValueType>( | ||
| export function cause<T extends { error: any }>(e: T): { cause: T["error"] } { | ||
| return { cause: e.error } | ||
| } | ||
| // ── Utility Functions (functional style) ──────────────── | ||
| export const map = <ValueType, NewValue, ErrorType = unknown>( | ||
| r: Result<ValueType, ErrorType>, | ||
| fn: (v: ValueType) => NewValue, | ||
| ): Result<NewValue, ErrorType> => (r.ok ? ok(fn(r.value)) : r) | ||
| export const mapErr = <ValueType, ErrorType, NewError>( | ||
| r: Result<ValueType, ErrorType>, | ||
| fn: (e: ErrorType) => NewError, | ||
| ): Result<ValueType, NewError> => (r.ok ? r : err(fn(r.error))) | ||
| export const flatMap = <ValueType, NewValue, ErrorType = unknown>( | ||
| r: Result<ValueType, ErrorType>, | ||
| fn: (v: ValueType) => Result<NewValue, ErrorType>, | ||
| ): Result<NewValue, ErrorType> => (r.ok ? fn(r.value) : r) | ||
| export const unwrap = <ValueType, ErrorType = unknown>( | ||
| r: Result<ValueType, ErrorType>, | ||
| ): ValueType => { | ||
| if (r.ok) return r.value | ||
| throw r.error | ||
| } | ||
| export const orElse = <ValueType, ErrorType = unknown>( | ||
| r: Result<ValueType, ErrorType>, | ||
| fallback: ValueType, | ||
| ): ValueType => (r.ok ? r.value : fallback) | ||
| // ── Universal factory / wrapper / rehydrator ──────────── | ||
| const isPromiseLike = <T>(v: unknown): v is PromiseLike<T> => | ||
| typeof v === "object" && v !== null && "then" in v | ||
| export function result<ValueType>(work: () => ValueType): Result<ValueType> | ||
| export function result<ValueType, ErrorType = unknown>( | ||
| work: PromiseLike<ValueType>, | ||
| ): Promise<Result<ValueType>> | ||
| /** | ||
| * Re-hydrates a plain Result-like object with the proper prototype methods. | ||
| * | ||
| * @example | ||
| * // After JSON.parse, the Result has lost its methods | ||
| * const raw = JSON.parse(serializedResult); | ||
| * | ||
| * // Re-hydrate to get the methods back | ||
| * const live = result(raw); | ||
| * const value = live.unwrap(); | ||
| * | ||
| * @param work - A plain object with Result shape ({ok: true, value} or {ok: false, error}) | ||
| * @returns A proper Result instance with all methods | ||
| */ | ||
| ): Promise<Result<ValueType, ErrorType>> | ||
| export function result<ValueType, ErrorType>( | ||
| work: Result<ValueType, ErrorType>, | ||
| ): Result<ValueType, ErrorType> | ||
| export function result<ValueType, ErrorType>( | ||
| work: | ||
| | (() => ValueType) | ||
| | PromiseLike<ValueType> | ||
| | Result<ValueType, ErrorType>, | ||
| work: (() => ValueType) | PromiseLike<ValueType>, | ||
| ): Result<ValueType, ErrorType> | Promise<Result<ValueType, ErrorType>> { | ||
| // Rehydrate | ||
| if (typeof work === "object" && work !== null && "ok" in work) { | ||
| return work.ok ? new Ok(work.value) : new Err(work.error) | ||
| } | ||
| // Promise‑like | ||
| if (isPromiseLike<ValueType>(work)) { | ||
| return Promise.resolve(work) | ||
| .then(ok<ValueType>) | ||
| .catch((e: unknown) => errAny<ErrorType>(e as ErrorType)) | ||
| .catch((e: unknown) => err(e as ErrorType)) | ||
| } | ||
| // Sync fn | ||
| try { | ||
| return ok((work as () => ValueType)()) | ||
| } catch (e) { | ||
| return errAny<ErrorType>(e as ErrorType) | ||
| return err(e as ErrorType) | ||
| } | ||
| } | ||
| /* -------------------------------------------------- */ | ||
| /* Universal matchType */ | ||
| /* -------------------------------------------------- */ | ||
| /** | ||
| * Exhaustively pattern‑match on the `type` discriminant of any object | ||
| * (error or otherwise). Works on unions like | ||
| * Pattern match on a Result object (Ok/Err) or a discriminant string. | ||
| * | ||
| * ```ts | ||
| * type Foo = | ||
| * | { type: "A"; a: number } | ||
| * | { type: "B"; b: string }; | ||
| * | ||
| * declare const foo: Foo; | ||
| * const out = matchType(foo, { | ||
| * A: v => v.a, | ||
| * B: v => v.b.length, | ||
| * }); | ||
| * ``` | ||
| * | ||
| * If you omit a case, TypeScript raises an error. | ||
| * Overloads: | ||
| * - match(result, { ok, err }) | ||
| * - match(type, { A: fn, B: fn }) | ||
| */ | ||
| export function matchType< | ||
| // Discriminated‑union we’re matching on | ||
| T extends { type: string }, | ||
| // Exhaustive handler object – must have a key for every `type` | ||
| Cases extends { | ||
| [K in T["type"]]: (val: Extract<T, { type: K }>) => unknown | ||
| }, | ||
| >(value: T, cases: Cases): ReturnType<Cases[keyof Cases]> { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| return (cases as any)[value.type](value as any) | ||
| export function match<ValueType, ErrorType, OnOk, OnErr>( | ||
| r: Result<ValueType, ErrorType>, | ||
| arms: { ok: (v: ValueType) => OnOk; err: (e: ErrorType) => OnErr }, | ||
| ): OnOk | OnErr | ||
| export function match< | ||
| T extends string & (string extends T ? never : unknown), | ||
| Cases extends { [K in T]: () => unknown }, | ||
| >( | ||
| value: T, | ||
| cases: Cases & Record<Exclude<keyof Cases, T>, never>, | ||
| ): ReturnType<Cases[keyof Cases]> | ||
| export function match(a: any, b: any): any { | ||
| if (typeof a === "object" && a && "ok" in a) { | ||
| return a.ok ? b.ok(a.value) : b.err(a.error) | ||
| } | ||
| return b[a]() | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1
-50%351
8%1
-50%22955
-46.03%294
-71.32%