npm package: @roboto-studio/utils
Description: Utility functions for Roboto applications
Author: Roboto Studio
Latest version: 0.1.0

# @roboto/utils

A comprehensive collection of utility functions for Roboto applications. Built with TypeScript and designed for reliability, performance, and developer experience.

## Installation

```bash
npm install @roboto/utils
# or
pnpm add @roboto/utils
# or
yarn add @roboto/utils
```

## Features

- **🔄 Retry Logic**: Robust retry mechanism with exponential backoff
- **🛡️ Safe Operations**: Try-catch utilities that never throw
- **⚡ Batch Processing**: Efficient concurrent processing with rate limiting
- **🔗 URL Slugification**: Convert strings to URL-safe slugs
- **📝 TypeScript First**: Full type safety and IntelliSense support
- **🧪 Well Tested**: Comprehensive test coverage (>80%)
- **🚀 Zero Dependencies**: Lightweight and fast

## Quick Start

```typescript
import {
  retryPromise,
  tryCatch,
  processBatch,
  slugify
} from '@roboto/utils'

// Retry failed API calls
const data = await retryPromise(() => fetch('/api/data'))

// Safe JSON parsing
const result = tryCatch(() => JSON.parse(jsonString))

// Process items in batches
const results = await processBatch(urls, async (url) => fetch(url))

// Create URL-safe slugs
const slug = slugify('My Blog Post Title') // 'my-blog-post-title'
```

## API Reference

### Retry Operations

#### `retryPromise<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>`

Retries a promise-returning function with exponential backoff.

```typescript
import { retryPromise } from '@roboto/utils'

// Basic usage
const result = await retryPromise(() => fetch('/api/data'))

// With custom options
const result = await retryPromise(
  () => apiCall(),
  {
    maxAttempts: 5,
    initialDelay: 2000,
    maxDelay: 30000,
    backoffMultiplier: 2,
    shouldRetry: (error) => error.status >= 500,
    onRetry: (error, attempt) => console.log(`Retry ${attempt}:`, error)
  }
)
```

**Options:**
- `maxAttempts` (default: 3): Maximum number of retry attempts
- `initialDelay` (default: 1000): Initial delay between retries in milliseconds
- `maxDelay` (default: 30000): Maximum delay between retries in milliseconds
- `backoffMultiplier` (default: 2): Multiplier for exponential backoff
- `shouldRetry`: Function to determine if an error should trigger a retry
- `onRetry`: Callback function called before each retry attempt

**Error Handling:**
- Throws `RetryExhaustedError` when all attempts are exhausted
- Automatically retries network errors and 5xx HTTP status codes
- Preserves original error information

### Safe Operations

#### `tryCatch<T>(fn: () => T): TryCatchResult<T>`

Safely executes a synchronous function and returns a result object instead of throwing.

```typescript
import { tryCatch, isSuccess, unwrap } from '@roboto/utils'

// Basic usage
const result = tryCatch(() => JSON.parse(jsonString))

if (result.success) {
  console.log('Parsed data:', result.data)
} else {
  console.error('Parse error:', result.error.message)
}

// Using type guards
if (isSuccess(result)) {
  // TypeScript knows result.data is available
  console.log(result.data)
}

// Unwrap with default value
const data = unwrapOr(result, {})
```

#### `tryCatchAsync<T>(fn: () => Promise<T>): Promise<TryCatchResult<T>>`

Safely executes an asynchronous function.

```typescript
import { tryCatchAsync } from '@roboto/utils'

const result = await tryCatchAsync(() => 
  fetch('/api/data').then(res => res.json())
)

if (result.success) {
  console.log('API data:', result.data)
} else {
  console.error('API error:', result.error)
}
```

**Utility Functions:**
- `isSuccess(result)`: Type guard for successful results
- `isError(result)`: Type guard for error results
- `unwrap(result)`: Returns data or throws error
- `unwrapOr(result, defaultValue)`: Returns data or default value
- `unwrapOrElse(result, defaultFn)`: Returns data or computed default value

### Batch Processing

#### `processBatch<T, R>(items: T[], processor: (item: T, index: number) => Promise<R>, options?: BatchProcessOptions<T>): Promise<BatchResult<T>[]>`

Processes an array of items in batches with controlled concurrency.

```typescript
import { processBatch, getSuccessfulResults, getBatchStats } from '@roboto/utils'

// Basic usage
const results = await processBatch(
  urls,
  async (url) => fetch(url).then(res => res.json())
)

// With custom options
const results = await processBatch(
  items,
  async (item, index) => processItem(item),
  {
    concurrency: 3,
    batchDelay: 1000,
    onItemError: (error, item, index) => {
      console.error(`Item ${index} failed:`, error)
    },
    onBatchComplete: (results, batchIndex) => {
      console.log(`Batch ${batchIndex} completed`)
    }
  }
)

// Process results
const successful = getSuccessfulResults(results)
const failed = getFailedResults(results)
const stats = getBatchStats(results)

console.log(`Success rate: ${stats.successRate}%`)
```

**Options:**
- `concurrency` (default: 5): Maximum number of items to process concurrently
- `batchDelay` (default: 0): Delay between batches in milliseconds
- `onItemError`: Function to handle individual item processing errors
- `onBatchComplete`: Function called when a batch completes

**Utility Functions:**
- `getSuccessfulResults(results)`: Filter successful results
- `getFailedResults(results)`: Filter failed results
- `getBatchStats(results)`: Get processing statistics

### Slugification

#### `slugify(input: string, options?: SlugifyOptions): string`

Converts a string into a URL-safe slug.

```typescript
import { slugify, createUniqueSlug, isValidSlug } from '@roboto/utils'

// Basic usage
slugify('Hello World!') // 'hello-world'
slugify('My Blog Post Title') // 'my-blog-post-title'

// With custom options
slugify('My Blog Post', {
  separator: '_',
  maxLength: 20,
  lowercase: false
}) // 'My_Blog_Post'

// Handle accented characters
slugify('Café & Restaurant') // 'cafe-and-restaurant'

// Custom replacements
slugify('Price: $99.99', {
  replacements: { '$': 'USD-' }
}) // 'price-usd-99-dot-99'
```

#### `createUniqueSlug(input: string, existingSlugs: string[], options?: SlugifyOptions): string`

Creates a unique slug by appending a number if the slug already exists.

```typescript
const existingSlugs = ['my-post', 'my-post-1', 'my-post-2']
const uniqueSlug = createUniqueSlug('My Post', existingSlugs)
console.log(uniqueSlug) // 'my-post-3'
```

#### `isValidSlug(slug: string, options?: SlugifyOptions): boolean`

Validates if a string is a valid slug according to the given options.

```typescript
isValidSlug('hello-world') // true
isValidSlug('Hello World!') // false
isValidSlug('hello_world', { separator: '_' }) // true
```

**Options:**
- `separator` (default: '-'): Character to use as separator
- `lowercase` (default: true): Whether to convert to lowercase
- `removeSpecialChars` (default: true): Whether to remove special characters
- `maxLength` (default: 100): Maximum length of the resulting slug
- `replacements`: Custom character replacements

## Error Handling

All utilities follow consistent error handling patterns:

1. **Never throw unexpected errors** - Use result objects or explicit error types
2. **Preserve error information** - Original errors are always accessible
3. **Provide helpful error messages** - Clear indication of what went wrong
4. **Type-safe error handling** - Full TypeScript support for error cases

## Performance Considerations

- **Batch Processing**: Automatically manages concurrency to prevent overwhelming systems
- **Memory Efficient**: Processes items in chunks to handle large datasets
- **Zero Dependencies**: No external dependencies for minimal bundle size
- **Tree Shakeable**: Import only what you need

## Examples

### Real-world API Client

```typescript
import { retryPromise, tryCatch, processBatch } from '@roboto/utils'

class ApiClient {
  async fetchUser(id: string) {
    return retryPromise(
      () => fetch(`/api/users/${id}`).then(res => res.json()),
      {
        maxAttempts: 3,
        shouldRetry: (error) => error.status >= 500
      }
    )
  }

  async fetchMultipleUsers(ids: string[]) {
    const results = await processBatch(
      ids,
      (id) => this.fetchUser(id),
      { concurrency: 5 }
    )

    return {
      users: getSuccessfulResults(results).map(r => r.data),
      errors: getFailedResults(results),
      stats: getBatchStats(results)
    }
  }

  parseConfig(configString: string) {
    const result = tryCatch(() => JSON.parse(configString))
    return unwrapOr(result, { default: 'config' })
  }
}
```

### Content Management System

```typescript
import { slugify, createUniqueSlug } from '@roboto/utils'

class ContentManager {
  private existingSlugs: string[] = []

  createPost(title: string, content: string) {
    const slug = createUniqueSlug(title, this.existingSlugs)
    this.existingSlugs.push(slug)

    return {
      slug,
      title,
      content,
      url: `/posts/${slug}`
    }
  }

  validateSlug(slug: string): boolean {
    return isValidSlug(slug) && !this.existingSlugs.includes(slug)
  }
}
```

## Development

```bash
# Install dependencies
pnpm install

# Run tests
pnpm test

# Run tests in watch mode
pnpm test:watch

# Build the package
pnpm build

# Generate coverage report
pnpm test:coverage
```

## Contributing

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Write tests for your changes
4. Ensure all tests pass (`pnpm test`)
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request

## License

MIT © Roboto Team

## Changelog

See [CHANGELOG.md](./CHANGELOG.md) for details about changes in each version.