onrails
    Preparing search index...

    onrails

    onrails

    Tagged Result / ResultAsync + railway-oriented utilities for TypeScript. Pure tagged unions, neverthrow-shaped compat shim, FL-friendly. Bun-first.

    Package npm Description
    @onrails/result npm Core Result / ResultAsync + neverthrow compat shim
    @onrails/maybe npm Tagged Maybe for expected absence + Result interop
    @onrails/pattern npm Exhaustive matching for owned unions (ts-pattern-shaped, lighter)
    @onrails/codemod npm Bun script: migrate neverthrow imports + package.json deps to @onrails/result/compat/neverthrow
    @onrails/eslint-plugin npm ESLint rules for @onrails/result boundaries — flags Promise<Result<…>> + _unsafeUnwrap*
    @onrails/biome-plugin npm GritQL plugin: same boundary rules for Biome users

    Docs CI License: MIT

    📚 API reference: alanrsoares.github.io/onrails — generated from tsdoc on every push to main.

    Railway-oriented programming — encode failure as values, chain ok/err down two parallel tracks, never throw across a public boundary.

    @onrails/result is a small, tagged-union take on this pattern. The data is { _tag: "Ok"; value } / { _tag: "Err"; error } — no class wrapper, tree-shake friendly, Fantasy Land-aware. @onrails/maybe models expected absence (Some / None). @onrails/pattern exhaustively matches owned unions. A drop-in compat/neverthrow shim makes migration a regex search-and-replace.

    bun add @onrails/result
    
    import { err, flatMap, isErr, isOk, map, match, ok, trySync } from "@onrails/result";

    const parse = trySync(
    (raw: string) => JSON.parse(raw) as { v: number },
    (e) => ({ kind: "parse" as const, message: String(e) }),
    );

    const out = map(parse('{"v":1}'), (data) => data.v + 1);

    if (isOk(out)) console.log(out.value);
    else console.error(out.error);

    Async, with a thenable ResultAsync:

    import { fromAsync, isOk, ok, err } from "@onrails/result";

    const fetchUser = fromAsync(async () => {
    const res = await fetch("/api/user");
    return res.ok ? ok(await res.json()) : err({ kind: "http" as const, status: res.status });
    });

    const r = await fetchUser(); // bare tagged Result
    if (isOk(r)) console.log(r.value);

    For composition patterns (pipe, flow, dual-form transforms, point-free pipelines) see packages/result/RECIPES.md.

    bunx @onrails/codemod /path/to/your-repo --dry
    bunx @onrails/codemod /path/to/your-repo

    See packages/result/README.md for the compat surface and chain-by-chain mapping.

    Experimental. Versions stay in 0.x until the public API + compat surface settle. Released and tagged per-package via release-please.

    MIT — see LICENSE.