npm package: @firebreak/lookout
Description: TypeScript client for Lookout deployment notifications
Author: Firebreak
Latest version: 1.0.0

# Lookout

TypeScript/JavaScript client for sending deployment notifications to Lookout with smart step tracking and automatic timing.

## Features

- 🚀 **Simple API** - Start tracking deployments in just a few lines of code
- ⏱️ **Automatic Timing** - SDK automatically tracks step duration
- 🔄 **Auto Retry** - Built-in retry logic with exponential backoff
- 📝 **TypeScript** - Full type definitions for excellent IDE support
- ✅ **Validation** - Input validation prevents bad API calls
- 🎯 **Smart State Management** - Tracks deployment and step state automatically

## Installation

```bash
npm install @firebreak/lookout
```

## Quick Start

```typescript
import { LookoutClient } from '@firebreak/lookout';

// Create client
const client = new LookoutClient({
  webhookUrl: process.env.LOOKOUT_WEBHOOK_URL,
  apiToken: process.env.LOOKOUT_API_TOKEN,
  teamId: process.env.SLACK_TEAM_ID,
});

// Start deployment
const deployment = await client.startDeployment({
  deploymentId: 'deploy-123',
  version: 'v1.2.0',
  environment: 'production',
  steps: ['Build', 'Test', 'Deploy'],
});

// Run steps with automatic timing
await deployment.runStep('Build', async () => {
  // Your build logic here
});

await deployment.runStep('Test', async () => {
  // Your test logic here
});

await deployment.runStep('Deploy', async () => {
  // Your deploy logic here
});

// Mark deployment as successful
await deployment.success();
```

## GitHub Actions Integration

### Step 1: Add Secrets

Add these secrets to your GitHub repository:

- `LOOKOUT_WEBHOOK_URL` - Your Lookout webhook URL
- `LOOKOUT_API_TOKEN` - Your Lookout API token
- `SLACK_TEAM_ID` - Your Slack workspace ID
- `SLACK_RELEASE_MANAGER_ID` - Slack user ID for release manager

### Step 2: Create Deployment Script

Create `deploy-with-lookout.js`:

```javascript
const { LookoutClient } = require('@firebreak/lookout');

async function deploy() {
  const client = new LookoutClient({
    webhookUrl: process.env.LOOKOUT_WEBHOOK_URL,
    apiToken: process.env.LOOKOUT_API_TOKEN,
    teamId: process.env.SLACK_TEAM_ID,
  });

  const deployment = await client.startDeployment({
    deploymentId: `${process.env.GITHUB_RUN_ID}-${process.env.GITHUB_RUN_NUMBER}`,
    version: process.env.GITHUB_REF_NAME || 'unknown',
    environment: 'production',
    releaseManager: process.env.SLACK_RELEASE_MANAGER_ID,
    steps: ['Build', 'Test', 'Deploy', 'Verify'],
  });

  try {
    await deployment.runStep('Build', async () => {
      // Build commands
      console.log('Building...');
    });

    await deployment.runStep('Test', async () => {
      // Test commands
      console.log('Testing...');
    });

    await deployment.runStep('Deploy', async () => {
      // Deploy commands
      console.log('Deploying...');
    });

    await deployment.runStep('Verify', async () => {
      // Verification commands
      console.log('Verifying...');
    });

    await deployment.success();
  } catch (error) {
    await deployment.fail(error.message);
    throw error;
  }
}

deploy();
```

### Step 3: Update GitHub Actions Workflow

```yaml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-node@v3
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm install @firebreak/lookout

      - name: Deploy with Lookout notifications
        run: node deploy-with-lookout.js
        env:
          LOOKOUT_WEBHOOK_URL: ${{ secrets.LOOKOUT_WEBHOOK_URL }}
          LOOKOUT_API_TOKEN: ${{ secrets.LOOKOUT_API_TOKEN }}
          SLACK_TEAM_ID: ${{ secrets.SLACK_TEAM_ID }}
          SLACK_RELEASE_MANAGER_ID: ${{ secrets.SLACK_RELEASE_MANAGER_ID }}
```

## API Reference

### LookoutClient

Main entry point for the SDK.

#### Constructor

```typescript
new LookoutClient(config: LookoutConfig)
```

**Config Options:**

- `webhookUrl` (required) - Lookout webhook URL
- `apiToken` (required) - API authentication token
- `teamId` (required) - Slack team/workspace ID
- `retry` (optional) - Retry configuration
  - `maxAttempts` - Max retry attempts (default: 3)
  - `initialDelay` - Initial delay in ms (default: 1000)
  - `maxDelay` - Max delay in ms (default: 10000)
  - `backoff` - 'linear' or 'exponential' (default: 'exponential')
- `timeout` (optional) - Request timeout in ms (default: 30000)
- `defaultEnvironment` (optional) - Default environment name
- `defaultVersion` (optional) - Default version string

#### Methods

**startDeployment(options: DeploymentOptions): Promise<DeploymentTracker>**

Create and start a deployment, sending initial notification.

```typescript
const deployment = await client.startDeployment({
  deploymentId: 'deploy-123',
  version: 'v1.0.0',
  environment: 'production',
  releaseManager: 'U123456',
  releaseNotes: '## Changes\n- Bug fixes',
  steps: ['Build', 'Test', 'Deploy'],
});
```

**createDeployment(options: DeploymentOptions): DeploymentTracker**

Create deployment tracker without sending initial notification.

### DeploymentTracker

Manages individual deployment lifecycle.

#### Methods

**runStep(name: string, fn: () => Promise<T>): Promise<T>**

Run a step with automatic timing and error handling.

```typescript
await deployment.runStep('Build', async () => {
  // Build logic
});
```

**startStep(name: string): Promise<void>**

Manually start a step.

```typescript
await deployment.startStep('Build');
```

**completeStep(name: string): Promise<void>**

Manually complete a step.

```typescript
await deployment.completeStep('Build');
```

**failStep(name: string, error?: string): Promise<void>**

Mark a step as failed.

```typescript
await deployment.failStep('Build', 'Compilation failed');
```

**success(): Promise<void>**

Mark deployment as successful.

```typescript
await deployment.success();
```

**fail(error?: string): Promise<void>**

Mark deployment as failed.

```typescript
await deployment.fail('Deployment timed out');
```

**update(): Promise<void>**

Manually send current state to Lookout.

```typescript
await deployment.update();
```

**getState(): DeploymentState**

Get current deployment state (read-only).

**getDeploymentId(): string**

Get deployment ID.

**getStatus(): DeploymentStatus**

Get current deployment status.

**isComplete(): boolean**

Check if deployment is complete (success or failed).

## Error Handling

The SDK provides typed errors for different failure scenarios:

```typescript
import { LookoutClient, ApiError, ValidationError, LookoutError } from '@firebreak/lookout';

try {
  await deployment.success();
} catch (error) {
  if (error instanceof ApiError) {
    if (error.is('UNAUTHORIZED')) {
      console.error('Invalid API token');
    } else if (error.is('NOT_FOUND')) {
      console.error('Team not configured');
    } else if (error.statusCode >= 500) {
      console.error('Server error - retrying...');
    }
  } else if (error instanceof ValidationError) {
    console.error('Invalid input:', error.message);
    console.error('Field:', error.field);
  } else if (error instanceof LookoutError) {
    console.error('SDK error:', error.code, error.message);
  }
}
```

## Advanced Usage

### Manual Step Control

```typescript
const deployment = client.createDeployment({
  deploymentId: 'deploy-123',
  version: 'v1.0.0',
  steps: [
    { name: 'Build', status: 'pending' },
    { name: 'Test', status: 'pending' },
    { name: 'Deploy', status: 'pending' },
  ],
});

// Send initial notification
await deployment.update();

// Manual control
await deployment.startStep('Build');
// ... build logic ...
await deployment.completeStep('Build');

await deployment.startStep('Test');
// ... if test fails ...
await deployment.failStep('Test', 'Tests failed');
await deployment.fail('Deployment aborted due to test failures');
```

### Custom Retry Configuration

```typescript
const client = new LookoutClient({
  webhookUrl: process.env.LOOKOUT_WEBHOOK_URL,
  apiToken: process.env.LOOKOUT_API_TOKEN,
  teamId: process.env.SLACK_TEAM_ID,
  retry: {
    maxAttempts: 5,
    initialDelay: 2000,
    maxDelay: 30000,
    backoff: 'exponential',
  },
  timeout: 60000, // 60 seconds
});
```

## TypeScript Support

The SDK is written in TypeScript and provides full type definitions:

```typescript
import type {
  LookoutConfig,
  DeploymentOptions,
  DeploymentStatus,
  StepStatus,
  DeploymentState,
} from '@firebreak/lookout';

const options: DeploymentOptions = {
  deploymentId: 'deploy-123',
  version: 'v1.0.0',
  environment: 'production',
  status: 'in_progress', // Type-safe
  steps: ['Build', 'Test', 'Deploy'],
};
```

## Examples

See the [examples/](examples/) directory for more usage examples:

- [basic-usage.ts](examples/basic-usage.ts) - Simple deployment tracking
- [github-actions.ts](examples/github-actions.ts) - GitHub Actions integration
- [error-handling.ts](examples/error-handling.ts) - Error handling patterns

## License

MIT

## Support

For issues and questions, please [open an issue](https://github.com/firebreak/lookout/issues).