# @wentools/simmer

Async control flow primitives: debounce, retry, eventual, sleep.

## Install

```bash
# JSR (Deno)
deno add jsr:@wentools/simmer

# npm
npm install @wentools/simmer
```

## Usage

### Sleep

```typescript
import { sleep } from '@wentools/simmer'

await sleep(1000) // wait 1 second
```

### Debounce

```typescript
import { debounce, debounceAsync } from '@wentools/simmer'

// Synchronous debounce
const debouncedSearch = debounce((query: string) => {
  console.log('Searching:', query)
}, 300)

// Async debounce - cancels stale calls
const debouncedFetch = debounceAsync(async (query: string) => {
  const response = await fetch(`/api/search?q=${query}`)
  return response.json()
}, 300)
```

### Retry

```typescript
import { withRetry, retryPromise } from '@wentools/simmer'

// Retry a Result-returning function
const result = await withRetry(
  () => fetchUser(userId),
  { maxAttempts: 3, initialDelayMs: 1000, backoffStrategy: 'exponential' }
)

// Retry a Promise-returning function
const result = await retryPromise(
  () => fetch('/api/data').then(r => r.json()),
  { maxAttempts: 3, initialDelayMs: 500 }
)
```

### Eventual (Stale-While-Revalidate)

```typescript
import { eventual, mapEventual, combineEventual } from '@wentools/simmer'

// Return cached data immediately, refresh in background
const result = eventual(cachedData, fetchFreshData())

result.current  // cached data (available now)
result.fresh    // Promise<fresh data> | null
result.isStale  // true if fresh !== null

// Transform values
const mapped = mapEventual(result, data => data.map(formatItem))

// Combine multiple
const combined = combineEventual({
  users: eventual(cachedUsers, fetchUsers()),
  count: eventual(cachedCount, fetchCount()),
})
```

## API

### Sleep
- `sleep(ms)` - Promise that resolves after `ms` milliseconds

### Debounce
- `debounce(fn, wait)` - Debounce a synchronous function
- `debounceAsync(fn, wait)` - Debounce an async function with promise cancellation

### Retry
- `withRetry(fn, options?)` - Retry a `Result`-returning function
- `retryPromise(fn, options?)` - Retry a `Promise`-returning function
- `makeRetryable(fn, options?)` - Create a retryable wrapper
- `withRetryMetadata(fn, options?)` - Retry with full execution tracking
- `calculateDelay(attempt, options)` - Calculate delay for a retry attempt
- `defaultRetryOptions` - Default retry configuration

### Eventual
- `eventual(current, fresh)` - Create an `EventualResult`
- `eventualIf(current, isStale, getFresh)` - Lazily create fresh promise
- `mapEventual(result, fn)` - Transform both current and fresh values
- `combineEventual(results)` - Combine multiple `EventualResult`s

## License

MIT
