UNPKG

@tempots/std

Version:

Std library for TypeScript. Natural complement to the Tempo libraries.

489 lines (488 loc) 18.4 kB
import { AsyncResult as n } from "./async-result.js"; const i = { /** * Creates a valid `Validation`. * @returns A `Validation` that is `Valid`. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any valid: { type: "valid" }, /** * Creates an invalid `Validation`. * @param error - The error associated with the invalid value. * @returns A `Validation` that is `Invalid`. */ invalid(e) { return { type: "invalid", error: e }; }, /** * Checks if a `Validation` is `Valid`. * @param r - The `Validation` to check. * @returns `true` if the `Validation` is `Valid`, otherwise `false`. */ isValid(e) { return e.type === "valid"; }, /** * Checks if a `Validation` is `Invalid`. * @param r - The `Validation` to check. * @returns `true` if the `Validation` is `Invalid`, otherwise `false`. */ isInvalid(e) { return e.type === "invalid"; }, /** * Maps the value of a `Validation` to a new value. * @param r - The `Validation` to map. * @param valid - The mapping function for a valid value. * @param invalid - The mapping function for an invalid value. * @returns The mapped value. */ match: (e, r, u) => i.isValid(e) ? r() : u(e.error), /** * Maps the value of a `Validation` to a new `Validation`. * @param validation - The `Validation` to map. * @param value - The value to map. * @returns A new `Validation` with the mapped value. */ toResult: (e, r) => i.match( e, () => s.success(r), (u) => s.failure(u) ), /** * Executes side effects based on the state of the validation. * Unlike `match`, all handlers are optional, allowing you to react only to specific states. * The `else` handler is called when no specific handler is provided for the current state. * @param v - The validation. * @param handlers - An object with optional handlers for each state and an optional `else` fallback. * @returns The validation that was passed in, allowing for chaining. * @public */ effect: (e, r) => (e.type === "valid" ? r.valid ? r.valid() : r.else?.() : r.invalid ? r.invalid(e.error) : r.else?.(), e), /** * Execute a function when the `Validation` is valid. * * @param r - The `Validation` to check. * @param apply - The function to execute when the `Validation` is valid. * @returns The `Validation` object. */ whenValid: (e, r) => (i.isValid(e) && r(), e), /** * Execute a function when the `Validation` is invalid. * * @param r - The `Validation` to check. * @param apply - The function to execute when the `Validation` is invalid. * @returns The `Validation` object. */ whenInvalid: (e, r) => (i.isInvalid(e) && r(e.error), e), /** * Maps the error of an invalid `Validation` to a new error using the provided function. * For valid validations, the validation is preserved unchanged. * @param v - The `Validation` to map the error of. * @param f - The mapping function to apply to the error. * @returns A new `Validation` with the mapped error if invalid, otherwise the original valid. * @public */ mapError: (e, r) => e.type === "invalid" ? i.invalid(r(e.error)) : e, /** * Maps the error of an invalid `Validation` to a new `Validation` using the provided function. * This allows recovery from errors by returning a valid validation. * @param v - The `Validation` to recover from. * @param f - The recovery function that returns a new `Validation`. * @returns The result of the recovery function if invalid, otherwise the original valid. * @public */ flatMapError: (e, r) => e.type === "invalid" ? r(e.error) : e, /** * Combines two validations. Both must be valid for the result to be valid. * If both are invalid, errors are combined using the provided function. * @param v1 - The first validation. * @param v2 - The second validation. * @param combineErrors - The function to combine two errors. * @returns A combined validation. * @public */ combine: (e, r, u) => i.isValid(e) && i.isValid(r) ? i.valid : i.isInvalid(e) && i.isInvalid(r) ? i.invalid(u(e.error, r.error)) : i.isInvalid(e) ? e : r, /** * Combines multiple validations into a single validation. * All must be valid for the result to be valid. * Returns the first invalid validation if any. * @param validations - The validations to combine. * @returns A single validation that is valid only if all inputs are valid. * @public */ all: (e) => { for (const r of e) if (i.isInvalid(r)) return r; return i.valid; }, /** * Combines multiple validations, accumulating all errors. * All must be valid for the result to be valid. * If any are invalid, all errors are collected into an array. * @param validations - The validations to combine. * @returns A validation that is valid only if all inputs are valid, otherwise contains all errors. * @public */ allErrors: (e) => { const r = []; for (const u of e) i.isInvalid(u) && r.push(u.error); return r.length > 0 ? i.invalid(r) : i.valid; }, /** * Compares two validations for equality. * @param v1 - The first validation. * @param v2 - The second validation. * @param errorEquals - Optional custom equality function for errors. Defaults to strict equality. * @returns `true` if the validations are equal, `false` otherwise. * @public */ equals: (e, r, u = (t, l) => t === l) => e.type === "valid" && r.type === "valid" ? !0 : e.type === "invalid" && r.type === "invalid" ? u(e.error, r.error) : !1, /** * Recovers from an invalid validation by returning a valid validation. * @param v - The `Validation` to recover from. * @returns A valid validation regardless of the input. * @public */ // eslint-disable-next-line @typescript-eslint/no-unused-vars recover: (e) => i.valid, /** * Gets the error if the validation is invalid, otherwise returns undefined. * @param v - The validation to get the error from. * @returns The error if invalid, otherwise undefined. * @public */ getError: (e) => { if (i.isInvalid(e)) return e.error; }, /** * Gets the error if the validation is invalid, otherwise returns the default value. * @param v - The validation to get the error from. * @param defaultError - The default error to return if valid. * @returns The error if invalid, otherwise the default error. * @public */ getErrorOrElse: (e, r) => i.isInvalid(e) ? e.error : r }, s = { /** * Creates a successful `Result`. * @param value - The value to wrap in a `Success` type. * @returns A `Result` that is a `Success`. * @public */ // eslint-disable-next-line @typescript-eslint/no-explicit-any success(e) { return { type: "Success", value: e }; }, /** * Creates a failure `Result`. * @param error - The error to wrap in a `Failure` type. * @returns A `Result` that is a `Failure`. * @public */ // eslint-disable-next-line @typescript-eslint/no-explicit-any failure(e) { return { type: "Failure", error: e }; }, /** * Maps the value of a `Result` to a new value. * @param r - The `Result` to map. * @param f - The mapping function. * @returns A new `Result` with the mapped value. * @public */ map: (e, r) => e.type === "Success" ? s.success(r(e.value)) : e, /** * Maps the value of a `Result` to a new `Result`. * @param r - The `Result` to map. * @param f - The mapping function. * @returns A new `Result` with the mapped value. * @public */ flatMap: (e, r) => e.type === "Success" ? r(e.value) : e, /** * Converts a `Result` to an `AsyncResult`. * @param r - The `Result` to convert. * @returns An `AsyncResult` that is equivalent to the input `Result`. * @public */ toAsync(e) { return s.match( e, (r) => n.success(r), (r) => n.failure(r) ); }, /** * Converts a `Result` to a `Validation`. * @param r - The `Result` to convert. * @returns A `Validation` that is equivalent to the input `Result`. * @public */ toValidation(e) { return s.match( e, () => i.valid, (r) => i.invalid(r) ); }, /** * Checks if a `Result` is a success. * @param r - The `Result` to check. * @returns `true` if the `Result` is a `Success`, `false` otherwise. * @public */ isSuccess(e) { return e.type === "Success"; }, /** * Checks if a `Result` is a failure. * @param r - The `Result` to check. * @returns `true` if the `Result` is a `Failure`, `false` otherwise. * @public */ isFailure(e) { return e.type === "Failure"; }, /** * Gets the value of a `Result` if it is a `Success`, otherwise returns the provided default value. * @param r - The `Result` to get the value from. * @param alt - The default value to return if the `Result` is a `Failure`. * @returns The value of the `Result` if it is a `Success`, otherwise the default value. * @public */ getOrElse(e, r) { return s.isSuccess(e) ? e.value : r; }, /** * Gets the value of a `Result` if it is a `Success`, otherwise returns the result of the provided function. * @param r - The `Result` to get the value from. * @param altf - The function to call if the `Result` is a `Failure`. * @returns The value of the `Result` if it is a `Success`, otherwise the result of the function. * @public */ getOrElseLazy(e, r) { return s.isSuccess(e) ? e.value : r(); }, /** * Gets the value of a `Result` if it is a `Success`, otherwise returns `null`. * @param r - The `Result` to get the value from. * @returns The value of the `Result` if it is a `Success`, otherwise `null`. * @public */ getOrNull(e) { return s.isSuccess(e) ? e.value : null; }, /** * Gets the value of a `Result` if it is a `Success`, otherwise returns `undefined`. * @param r - The `Result` to get the value from. * @returns The value of the `Result` if it is a `Success`, otherwise `undefined`. * @public */ getOrUndefined(e) { return s.isSuccess(e) ? e.value : void 0; }, /** * Gets the value of a `Result` if it is a `Success`, otherwise it throws the error contained in the `Failure`. * @param r - The `Result` to get the value from. * @returns The value of the `Result` if it is a `Success`. */ getUnsafe: (e) => { if (s.isSuccess(e)) return e.value; throw e.error; }, /** * Based on the state of the result, it picks the appropriate function to call and returns the result. * @param success - The function to call if the result is a success. * @param failure - The function to call if the result is a failure. * @returns The result of calling the appropriate function based on the state of the result. * @public */ match: (e, r, u) => s.isSuccess(e) ? r(e.value) : u(e.error), /** * Executes side effects based on the state of the result. * Unlike `match`, all handlers are optional, allowing you to react only to specific states. * The `else` handler is called when no specific handler is provided for the current state. * @param r - The result. * @param handlers - An object with optional handlers for each state and an optional `else` fallback. * @returns The result that was passed in, allowing for chaining. * @public */ effect: (e, r) => (e.type === "Success" ? r.success ? r.success(e.value) : r.else?.() : r.failure ? r.failure(e.error) : r.else?.(), e), /** * Calls the provided function if the result is a success. * @param apply - The function to call if the result is a success. * @returns A function that takes a `Result` and calls the provided function if the result is a success. * @public */ whenSuccess: (e, r) => (s.isSuccess(e) && r(e.value), e), /** * Calls the provided function if the result is a failure. * @param apply - The function to call if the result is a failure. * @returns The result that was passed in. * @public */ whenFailure: (e, r) => (s.isFailure(e) && r(e.error), e), /** * Combines two results into a single result. * @param r1 - The first result. * @param r2 - The second result. * @param combineV - The function to combine two values. * @param combineE - The function to combine two errors. * @returns The combined result. * @public */ combine: (e, r, u, t) => s.match( e, (l) => s.match( r, (a) => s.success(u(l, a)), (a) => s.failure(a) ), (l) => s.match( r, // eslint-disable-next-line @typescript-eslint/no-unused-vars (a) => s.failure(l), (a) => s.failure(t(l, a)) ) ), /** * Compares two results for equality. * @param r1 - The first result. * @param r2 - The second result. * @param options - The options to use for comparison. By default, uses strict equality. * @returns `true` if the results are equal, `false` otherwise. */ equals: (e, r, u = { valueEquals: (t, l) => t === l, errorEquals: (t, l) => t === l }) => e.type === "Success" && r.type === "Success" ? u.valueEquals(e.value, r.value) : e.type === "Failure" && r.type === "Failure" ? u.errorEquals(e.error, r.error) : !1, /** * Combines multiple results into a single result. * @param results - The results to combine. * @returns A single result that is a success if all the input results are successes, otherwise a failure. */ all: (e) => { const r = []; for (const u of e) if (s.isSuccess(u)) r.push(u.value); else return u; return s.success(r); }, /** * Maps the error of a failed `Result` to a new error using the provided function. * For success results, the result is preserved unchanged. * @param r - The `Result` to map the error of. * @param f - The mapping function to apply to the error. * @returns A new `Result` with the mapped error if failed, otherwise the original success. * @public */ mapError: (e, r) => e.type === "Failure" ? s.failure(r(e.error)) : e, /** * Maps the error of a failed `Result` to a new `Result` using the provided function. * This allows recovery from errors by returning a new successful result. * @param r - The `Result` to recover from. * @param f - The recovery function that returns a new `Result`. * @returns The result of the recovery function if failed, otherwise the original success. * @public */ flatMapError: (e, r) => e.type === "Failure" ? r(e.error) : e, /** * Recovers from a failure by providing an alternative value. * @param r - The `Result` to recover from. * @param f - The function that provides an alternative value given the error. * @returns A successful `Result` with the alternative value if failed, otherwise the original success. * @public */ recover: (e, r) => e.type === "Failure" ? { type: "Success", value: r(e.error) } : e, /** * Applies a function wrapped in a `Result` to a value wrapped in a `Result`. * Useful for applying multiple arguments to a function in a safe way. * @param resultFn - The `Result` containing the function. * @param resultVal - The `Result` containing the value. * @returns A new `Result` with the result of applying the function to the value. * @public */ ap: (e, r) => s.isSuccess(e) && s.isSuccess(r) ? s.success(e.value(r.value)) : s.isFailure(e) ? s.failure(e.error) : s.failure(r.error), /** * Maps two `Result` values using a function. * @param r1 - The first `Result`. * @param r2 - The second `Result`. * @param f - The function to apply to both values. * @returns A new `Result` with the result of applying the function to both values. * @public */ map2: (e, r, u) => s.isSuccess(e) && s.isSuccess(r) ? s.success(u(e.value, r.value)) : s.isFailure(e) ? s.failure(e.error) : s.failure(r.error), /** * Maps three `Result` values using a function. * @param r1 - The first `Result`. * @param r2 - The second `Result`. * @param r3 - The third `Result`. * @param f - The function to apply to all three values. * @returns A new `Result` with the result of applying the function to all three values. * @public */ map3: (e, r, u, t) => s.isSuccess(e) && s.isSuccess(r) && s.isSuccess(u) ? s.success(t(e.value, r.value, u.value)) : s.isFailure(e) ? s.failure(e.error) : s.isFailure(r) ? s.failure(r.error) : s.failure(u.error), /** * Converts a Promise to a Result. * @param p - The Promise to convert. * @returns A Promise that resolves to a Result. * @public */ ofPromise: async (e) => { try { const r = await e; return s.success(r); } catch (r) { return s.failure(r instanceof Error ? r : new Error(String(r))); } }, /** * Swaps the success and failure values of a Result. * A success becomes a failure with the value as the error, * and a failure becomes a success with the error as the value. * @param r - The Result to swap. * @returns A new Result with swapped success and failure. * @public */ swap: (e) => s.isSuccess(e) ? s.failure(e.value) : s.success(e.error), /** * Converts a nullable value to a Result. * @param value - The nullable value. * @param error - The error to use if the value is null or undefined. * @returns A Result containing the value if not null/undefined, otherwise a failure. * @public */ fromNullable: (e, r) => e == null ? s.failure(r) : s.success(e), /** * Converts a nullable value to a Result using a lazy error function. * @param value - The nullable value. * @param errorFn - The function to call to get the error if the value is null or undefined. * @returns A Result containing the value if not null/undefined, otherwise a failure. * @public */ fromNullableLazy: (e, r) => e == null ? s.failure(r()) : s.success(e), /** * Wraps a function that may throw into a function that returns a Result. * @param f - The function that may throw. * @returns A function that returns a Result instead of throwing. * @public */ tryCatch: (e) => { try { return s.success(e()); } catch (r) { return s.failure(r instanceof Error ? r : new Error(String(r))); } } }; export { s as R, i as V };