Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:

- run: pnpm install --frozen-lockfile

- run: turbo run test:coverage
- run: pnpm test:coverage

build:
name: Build
Expand Down
68 changes: 64 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,72 @@
# @cometloop/safe

Type-safe error handling for TypeScript using the Result pattern. Zero dependencies.
Type-safe error handling library for TypeScript using the Result pattern. Zero dependencies.

Instead of `try/catch`, functions return a `SafeResult` tuple `[value, null]` or `[null, error]` — with full type inference and tagged access via `.ok`, `.value`, and `.error`.
## **Wrap any function**

- Flexible wrapping — single function, domain scope, or app-wide
- Type-safe results — tuples or objects
- Flexible parsing — transform results and errors with full type inference
- Built in hooks — run side effects automatically
- Async utils included — retry, timeout, abort, all, allSettled
- No try/catch clutter — clean, concise call sites

## Documentation

Full API reference, examples, and guides:

[https://cometloop.github.io/safe/](https://cometloop.github.io/safe/)

How we recommend using this library:

[https://cometloop.github.io/safe/docs/recommended-patterns](https://cometloop.github.io/safe/docs/recommended-patterns)

## Example usage:

```typescript
import { createSafe, safe } from '@cometloop/safe'

// Wrap any function — zero config
const safeParse = safe.wrap(JSON.parse)
const [data, err] = safeParse(rawInput)

// Shared config for a whole domain
const apiSafe = createSafe({
parseError: errorParser,
defaultError: fallbackError,
onError: errorHook,
})

const fetchUser = apiSafe.wrapAsync(fetchUserAsync)
const fetchPosts = apiSafe.wrapAsync(fetchPostsAsync)

// Same config. Full type narrowing.
const [user, userErr] = await fetchUser('123')
if (userErr) return

const [posts, postsErr] = await fetchPosts(user.id)
console.log(user.name, posts.length)

// Prefer objects? One call to switch.
const objSafe = withObjects(apiSafe)
const fetchPostsObj = objSafe.wrapAsync(fetchPostsAsync)
const { ok, data, error } = await fetchPostsObj('123')
```

## Installation

```bash
pnpm add @cometloop/safe
```

```bash
bun add @cometloop/safe
```

```bash
yarn add @cometloop/safe
```

```bash
npm install @cometloop/safe
```
Expand Down Expand Up @@ -65,14 +126,13 @@ All methods support optional `parseError` for custom error types, `parseResult`

- **Clean call sites** — configure once with `createSafe`, then call functions normally
- **Result tuples** — `[value, null]` or `[null, error]` with TypeScript narrowing
- **Tagged results** — `.ok`, `.value`, `.error` properties for pattern matching
- **Object results** — prefer `{ ok, data, error }` over tuples? Use `withObjects`
- **Custom error types** — `parseError` maps caught errors to your domain types
- **Result transformation** — `parseResult` transforms successful values
- **Lifecycle hooks** — `onSuccess`, `onError`, `onSettled`, `onRetry`, `onHookError`
- **Retry with backoff** — configurable retry for async operations
- **Timeout/abort** — `abortAfter` with `AbortSignal` integration
- **Error normalization** — non-`Error` thrown values are automatically normalized
- **Object results** — prefer `{ ok, data, error }` over tuples? Use `withObjects`
- **Zero dependencies**

## Documentation
Expand Down