Package Exports
- @gianstack/result
Readme
@gianstack/result
Transport-agnostic primitives for modeling expected failures without using throw as normal control flow.
What this package gives you
- a minimal
Resultunion built aroundok/err; - a generic
AppErrorshape with stablecode: string; - one explicit
normalizeUnknownError(cause, fallback)entrypoint; - small
tryResultandtryResultAsynchelpers for throw-based APIs; - ESM-only output that is safe to consume in Next.js on both server and client.
What this package does not do
This package does not provide:
- a built-in error-code catalog;
- transport mapping for HTTP, GraphQL, tRPC, or Next.js boundaries;
- logging, telemetry, or status-code policies;
- monadic helpers like
map,andThen,match, orunwrap.
The goal is to stay small, explicit, and reusable. Repository-specific error catalogs and transport adapters belong in higher layers.
Install
pnpm add @gianstack/resultCore API
import {
appError,
err,
normalizeUnknownError,
ok,
tryResultAsync,
type AppError,
type Result,
} from "@gianstack/result";
type SaveUserResult = Result<{ id: string }, AppError>;
export async function saveUser(input: string): Promise<SaveUserResult> {
if (input.length === 0) {
return err(appError({ code: "validation_failed" }));
}
const persisted = await tryResultAsync(
async () => ({ id: input.toLowerCase() }),
(cause) => appError({ code: "unexpected", cause }),
);
if (!persisted.ok) {
return persisted;
}
return ok(persisted.value);
}
export function toAppError(cause: unknown): AppError {
return normalizeUnknownError(cause, { code: "unexpected" });
}Design constraints
- Expected failures should travel through
Result, notthrow. - Invariant violations should still fail loudly.
AppError.messageis developer-oriented only.- Call sites should branch on
error.code, not onmessage. normalizeUnknownErrorrequires an explicit fallback so the package never invents repository-specific semantics.
Support matrix
- ESM only
- browser-safe runtime
- server-safe runtime
- no framework peer dependencies
Development contract
Before publishing a new version, this package must pass:
pnpm --filter @gianstack/result lintpnpm --filter @gianstack/result check-typespnpm --filter @gianstack/result testpnpm --filter @gianstack/result buildpnpm --filter @gianstack/result pack:checkpnpm --filter @gianstack/result smoke:consumer
For repository-level release notes and contributor workflow, see CONTRIBUTING.md and docs/result.md.