npm package: @ortense/attempt
Description: A minimalist TypeScript library for safe error handling using the Result Type pattern
Author: Marcus Ortense
Latest version: 0.3.0

![Attempt banner - the attempt mascot generated by dall-e 2](https://raw.githubusercontent.com/ortense/attempt/main/media/mascot.png)

# @ortense/attempt
![npm](https://img.shields.io/npm/v/@ortense/attempt)
[![Coverage Status](https://coveralls.io/repos/github/ortense/attempt/badge.svg)](https://coveralls.io/github/ortense/attempt)
![Made with Love](https://img.shields.io/badge/Made%20with-%E2%9D%A4-red)

Uma biblioteca TypeScript minimalista para tratamento seguro de erros usando o padrão Result Type.

## Instalação

```bash
npm install @ortense/attempt  # npm
yarn add  @ortense/attempt    # yarn
pnpm add @ortense/attempt     # pnpm
bun add @ortense/attempt      # bun
```

## Por que usar?

- 🎯 **Type-safe**: Tratamento de erros com segurança de tipos
- 🎨 **Elegante**: API simples e expressiva
- 🔒 **Seguro**: Sem exceções não tratadas
- 🪶 **Leve**: Zero dependências
- 📦 **Pequeno**: Bundle size mínimo
- 💪 **Robusto**: 100% testado

## Uso Básico

```typescript
import { attempt, attemptAsync } from '@ortense/attempt'

// Código síncrono
const result = attempt(() => {
  const number = parseInt("123")
  if (isNaN(number)) throw new Error("Número inválido")
  return number
})

if (result.success) {
  console.log(result.value) // 123
} else {
  console.error(result.error) // Error
}

// Código assíncrono
const result = await attemptAsync(
  fetch("https://api.example.com/data")
    .then(response => response.json())
)

if (result.success) {
  console.log(result.value) // dados da API
} else {
  console.error(result.error.message) // "Falha na API"
}
```

## API

### Tipos

```typescript
type Success<T> = {
  success: true
  value: T
}

type Failure<E extends Error> = {
  success: false
  error: E
}

type Result<T, E extends Error> = Success<T> | Failure<E>

type Match<T, E extends Error> = {
  success: (value: T) => void;
  failure: (error: E) => void;
}
```

### Funções

#### `success<T>(value: T): Success<T>`
Cria um resultado de sucesso contendo um valor.

```typescript
const result = success(42)
// { success: true, value: 42 }
```

#### `match<T, E extends Error>(result: Result<T, E>, match: Match<T, E>): void`
Executa diferentes callbacks baseados no estado do resultado.

```typescript
match(result, {
  success: (value) => {
    console.log("Sucesso:", value)
  },
  failure: (error) => {
    console.error("Falha:", error)
  }
})
```

#### `failure<E extends Error>(error: unknown): Failure<E>`
Cria um resultado de falha contendo um erro.

```typescript
const result = failure(new Error("Algo deu errado"))
// { success: false, error: Error("Algo deu errado") }

// Strings são convertidas para Error
const result = failure("Algo deu errado")
// { success: false, error: Error("Algo deu errado") }
```

#### `attempt<T, E extends Error>(fn: () => T): Result<T, E>`
Executa uma função e captura qualquer erro lançado.

```typescript
const result = attempt(() => {
  if (Math.random() > 0.5) throw new Error("Azar!")
  return "Sorte!"
})

if (result.success) {
  console.log(result.value) // "Sorte!"
} else {
  console.error(result.error) // Error("Azar!")
}
```

#### `attemptAsync<T, E extends Error>(Promise<T>): Promise<Result<T, E>>`
Resolve uma promise e captura qualquer erro lançado.

```typescript
const result = await attemptAsync(
  fetch("https://api.example.com/data")
    .then(response.json())
)

if (result.success) {
  console.log(result.value) // dados da API
} else {
  console.error(result.error) // "Falha na API"
}
```

## Exemplos Avançados

### Erros Customizados

```typescript
class ApiError extends Error {
  constructor(
    message: string,
    public statusCode: number
  ) {
    super(message)
    this.name = "ApiError"
  }
}

const result = attempt(() => {
  throw new ApiError("Não autorizado", 401)
})

if (!result.success) {
  console.log(result.error.statusCode) // 401
}
```

### Type Narrowing

```typescript
function processResult(result: Result<number, Error>) {
  if (result.success) {
    // TypeScript sabe que result.value é number
    return result.value * 2
  } else {
    // TypeScript sabe que result.error é Error
    return result.error.message
  }
}
```

### Composição de Operações

```typescript
const getUser = async (id: string) => {
  const result = await attemptAsync(
    fetch(`/api/users/${id}`)
      .then(response => {
        if (!response.ok) throw new Error("Usuário não encontrado")
        return response
      })
      .then(response => response.json())
  )

  if (!result.success) {
    return result // early return com o erro
  }

  // Processa apenas em caso de sucesso
  return attempt(() => {
    const user = result.value
    if (!user.active) throw new Error("Usuário inativo")
    return user
  })
}

// Usando map para uma composição mais limpa
const getUserName = async (id: string) => {
  const result = await attemptAsync(
    fetch(`/api/users/${id}`)
      .then(response => {
        if (!response.ok) throw new Error("Usuário não encontrado")
        return response
      })
      .then(response => response.json())
  )

  return result.map(user => user.name)
}
```

## Licença

MIT © [Marcus Ortense](https://github.com/ortense)