Skip to content

Latest commit

 

History

296 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@sandlada/result

Codecov NPM Downloads NPM Version GitHub License Documentation

Open in StackBlitz

@sandlada/result is a TypeScript library implementing the Result Pattern — a type-safe, exception-free approach to error handling. It makes error flows explicit in the type system so you never wonder whether a function can fail.

Unlike traditional Result libraries that hardcode a single error type, @sandlada/result is fully generic: you bring your own error shapes (discriminated unions, classes, or plain objects).

⚡ Highlights

  • Fully generic TError — define your own error types
  • Pure FP — data-last curried operators (pipe, map, bind) with discriminated union types
  • Option type — IOption<T> (Some / None) with curried operators
  • Async-native — asyncOk/asyncErr factories + pipeAsync for Promise-based railways, plus lazy AsyncResult / AsyncOption thunks
  • Railway Oriented Programming built-in — map, bind, orElse, match, tap, combine
  • Reliability — bounded retry, timeout, race, any, allSettled for production pipelines
  • Observability — breadcrumb withPath / ctx / tapErrContext + format / inspect / installObserver
  • JSON serializable — result and option objects survive JSON.stringify
  • Zero dependencies
  • ESM-only, strict TypeScript
  • Inspired by the C# Result Pattern and Rust's Option<T>

👀 Installation

npm i @sandlada/result

ESM only. This package cannot be used with require(). Your project must use ESM (import) or dynamic import().

🚢 Quick Start

The main barrel @sandlada/result is type-focused. Its only runtime value is an empty default object, used to materialize the entry and sourcemap; functional runtime values come from dedicated subpath packages — pick the one that matches your shape.

import type { IResultOfT } from '@sandlada/result';              // type contracts
import { ok, err } from '@sandlada/result/factories';             // core constructors
import { map, unwrapOr } from '@sandlada/result/operators';       // sync operators
import { pipe } from '@sandlada/result/composition';              // pipe / composeK / safeTry

// Define your error type (discriminated union recommended)
type AppError =
  | { kind: 'NotFound'; id: string }
  | { kind: 'Validation'; fields: Record<string, string> };

function getUser(id: string): IResultOfT<User, AppError> {
  if (!id) {
    return err<AppError>({ kind: 'Validation', fields: { id: 'Required' } }) as IResultOfT<User, AppError>;
  }
  const user = db.find(id);
  if (!user) {
    return err<AppError>({ kind: 'NotFound', id }) as IResultOfT<User, AppError>;
  }
  return ok(user);
}

// FP curried style
const name = pipe(
  getUser('42'),
  map(u => u.name),
  unwrapOr('Unknown'),
);

Why subpath imports? @sandlada/result exposes many types — IResultOfT, IOption, AsyncResult, AsyncOption. Names like map, bind, match exist for both IResultOfT and IOption. The compiler can't disambiguate; the package layout does. Subpath imports make the type explicit at the call site and keep tree-shaking total. See src/types/README.md for the rationale.

📒 API Overview

Each subpath has its own spec — a module README.md with the full export table linked to source files. Full type signatures and JSDoc live in the source.

Export path Contents Spec
@sandlada/result Type-focused barrel — IResult, IResultOfT, IOption, AsyncResult, AsyncOption, plus the runtime empty default marker. Functional runtime values must use a subpath. src/types/README.md
@sandlada/result/factories Core constructors (ok, err, asyncOk, asyncErr, tryCatch, fromPromise, …). src/factories/README.md
@sandlada/result/operators Sync operators on IResultOfT (map, bind, match, pipe, …). src/operators/README.md
@sandlada/result/option Sync IOption<T> operators (ofSome, ofNone, map, bind, okOr, transpose, …). src/option/README.md
@sandlada/result/async-result Lazy AsyncResult<T, E> thunk operators. src/async-result/README.md
@sandlada/result/async-option Lazy AsyncOption<T> thunk operators. src/async-option/README.md
@sandlada/result/promise-result Eager async operators on Promise<IResultOfT>. src/promise-result/README.md
@sandlada/result/promise-option Eager async operators on Promise<IOption>. src/promise-option/README.md
@sandlada/result/composition pipe, composeK, safeTry, pipeAsync, composeKAsync. src/composition/README.md
@sandlada/result/adapters toOption, fromOption, switchFn, liftMap, tee, … src/adapters/README.md
@sandlada/result/combine combine, combineWithAllErrors, all. src/combine/README.md
@sandlada/result/reliability retry, retryLazy, timeout, race, any, allSettled. src/reliability/README.md
@sandlada/result/observability ctx, withPath, format, inspect, installObserver, … src/observability/README.md
@sandlada/result/primitives cond, condErr, sequence, reduce, partitionOption, lift. src/primitives/README.md
@sandlada/result/types Same type contracts as the main barrel, plus its own empty default marker. Kept for backward compatibility. src/types/README.md

📦 Integration Pattern

Bind your error type once and eliminate generic boilerplate:

// app-result.ts
import { ok, err } from '@sandlada/result/factories';
import type { IResultOfT } from '@sandlada/result';
import type { AppError } from './errors.js';

export type AppResult<T = void> = IResultOfT<T, AppError>;

export const AppResult = {
  Success<T>(value?: T): AppResult<T> { return (value === undefined ? ok() : ok(value)) as unknown as AppResult<T>; },
  Failure(error: AppError): AppResult<never> { return err(error) as unknown as AppResult<never>; },
} as const;
// usage — no TError generic anywhere
function getUser(id: string): AppResult<User> {
  if (!id) return AppResult.Failure({ kind: 'Validation', fields: { id: 'Required' } });
  return AppResult.Success({ id, name: 'Alice' });
}

📒 Further Reading

  • result.sandlada.com — published documentation: guides, behavior modes, and the generated API reference
  • AGENTS.md — the module map: every subpath, its module README.md spec, and its behavior-matrix section
  • docs/behavior-modes.md — behavior modes: the fail-fast / accumulate / throw policy matrix for every API

License

MIT

About

Result Pattern & Railway Oriented Programming Library for TypeScript & JavaScript. READY TO PLAY!

Topics

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Used by

Contributors

Languages