diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5bb2ee..5fcb1ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: - run: pnpm install --frozen-lockfile - - run: turbo run test:coverage + - run: pnpm test:coverage build: name: Build diff --git a/README.md b/README.md index 58ae519..1fe5256 100644 --- a/README.md +++ b/README.md @@ -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 ``` @@ -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