npm package: @locci/scheduler
Description: JavaScript client library for Locci Scheduler platform
Author: MikeTeddyOmondi
Latest version: 1.0.0-beta

# Locci Scheduler - JavaScript/TypeScript SDK

A powerful and easy-to-use JavaScript/TypeScript client library for the Locci Scheduler platform. Schedule webhook executions with flexible timing options including one-time, recurring (cron), and interval-based schedules.

## Installation

```bash
npm install @locci/scheduler
```

Or with other package managers:

```bash
bun add @locci/scheduler
pnpm add @locci/scheduler
yarn add @locci/scheduler
```

## Quick Start

```javascript
import { LocciScheduler } from "@locci/scheduler";

// Initialize the client
const scheduler = new LocciScheduler({
  baseUrl: "https://api.scheduler.locci.cloud",
  apiToken: "your-jwt-token-here",
});

// Schedule a one-time webhook
const task = await scheduler.scheduleOnce({
  name: "Daily Report Generation",
  webhook: {
    url: "https://your-app.com/api/generate-report",
    method: "POST",
    payload: { reportType: "daily" },
  },
  executeAt: new Date("2024-12-25T10:00:00Z"),
});

console.log("Task scheduled:", task.id);
```

## Configuration

```javascript
const scheduler = new LocciScheduler({
  baseUrl: "https://api.scheduler.locci.cloud", // Required: Your server URL
  apiToken: "your-jwt-token", // Required: Your JWT token
  timeout: 30000, // Optional: Request timeout (ms)
  retries: 3, // Optional: Number of retries
});
```

## Usage Examples

### One-time Scheduling

```javascript
// Schedule a task to run once at a specific time
const task = await scheduler.scheduleOnce({
  name: "Welcome Email",
  description: "Send welcome email to new user",
  webhook: {
    url: "https://your-app.com/api/send-welcome-email",
    method: "POST",
    headers: {
      "X-API-Key": "your-publishable-api-key", // We recommend setting .env on the server side
    },
    payload: {
      userId: "12345",
      template: "welcome",
    },
  },
  executeAt: new Date(Date.now() + 3600000), // 1 hour from now
  metadata: {
    userId: "12345",
    campaign: "onboarding",
  },
});
```

### Recurring Schedules (Cron)

```javascript
// Daily at 9:00 AM UTC
const dailyTask = await scheduler.scheduleDaily({
  name: "Daily Backup",
  webhook: {
    url: "https://your-app.com/api/backup",
    method: "POST",
  },
  time: "09:00",
});

// Weekly on Monday at 2:30 PM
const weeklyTask = await scheduler.scheduleWeekly({
  name: "Weekly Report",
  webhook: {
    url: "https://your-app.com/api/weekly-report",
    method: "POST",
  },
  dayOfWeek: 1, // Monday
  time: "14:30",
});

// Custom cron expression
const customTask = await scheduler.scheduleRecurring({
  name: "Custom Schedule",
  webhook: {
    url: "https://your-app.com/api/custom-task",
    method: "POST",
  },
  cronExpression: "0 */4 * * *", // Every 4 hours
  timezone: "America/New_York",
});
```

### Interval-based Scheduling

```javascript
// Every 30 minutes
const intervalTask = await scheduler.scheduleInterval({
  name: "Health Check",
  webhook: {
    url: "https://your-app.com/api/health-check",
    method: "GET",
  },
  intervalSeconds: 1800, // 30 minutes
  startAt: new Date(),
  endDate: new Date(Date.now() + 86400000), // End after 24 hours
});

// Convenience methods
const minuteTask = await scheduler.scheduleEveryMinute({
  name: "Minute Task",
  webhook: { url: "https://your-app.com/api/minute-task", method: "POST" },
});

const hourlyTask = await scheduler.scheduleEveryHour({
  name: "Hourly Task",
  webhook: { url: "https://your-app.com/api/hourly-task", method: "POST" },
});
```

### Webhook Authentication

```javascript
// API Key authentication
const taskWithApiKey = await scheduler.createTask({
  name: "API Key Task",
  webhook: {
    url: "https://your-app.com/api/secure-endpoint",
    method: "POST",
    authentication: {
      type: "apiKey",
      apiKey: {
        headerName: "X-API-Key",
        key: "your-secret-api-key",
      },
    },
  },
  schedule: { type: "once", executeAt: new Date(Date.now() + 3600000) },
});

// Bearer token authentication
const taskWithBearer = await scheduler.createTask({
  name: "Bearer Token Task",
  webhook: {
    url: "https://your-app.com/api/secure-endpoint",
    method: "POST",
    authentication: {
      type: "bearerToken",
      bearerToken: {
        token: "your-bearer-token",
      },
    },
  },
  schedule: { type: "once", executeAt: new Date(Date.now() + 3600000) },
});

// Basic authentication
const taskWithBasicAuth = await scheduler.createTask({
  name: "Basic Auth Task",
  webhook: {
    url: "https://your-app.com/api/secure-endpoint",
    method: "POST",
    authentication: {
      type: "basicAuth",
      basicAuth: {
        username: "your-username",
        password: "your-password",
      },
    },
  },
  schedule: { type: "once", executeAt: new Date(Date.now() + 3600000) },
});
```

### Task Management

```javascript
// List all tasks
const taskList = await scheduler.listTasks({ page: 1, perPage: 20 });
console.log(`Found ${taskList.total} tasks`);

// Get specific task
const task = await scheduler.getTask("task-id-here");
console.log("Task:", task.name);

// Update a task
const updatedTask = await scheduler.updateTask("task-id-here", {
  name: "Updated Task Name",
  description: "New description",
});

// Pause/Resume/Cancel tasks
await scheduler.pauseTask("task-id-here");
await scheduler.resumeTask("task-id-here");
await scheduler.cancelTask("task-id-here");

// Manual trigger
await scheduler.triggerTask("task-id-here");

// Delete task
await scheduler.deleteTask("task-id-here");
```

### Execution History

```javascript
// Get execution history for a task
const history = await scheduler.getTaskHistory("task-id-here");

console.log(`Task has ${history.total} executions`);
history.executions.forEach((execution) => {
  console.log(
    `${execution.executedAt}: ${execution.status} (${execution.durationMs}ms)`
  );
  if (execution.errorMessage) {
    console.error("Error:", execution.errorMessage);
  }
});
```

### Error Handling

```javascript
import {
  SchedulerError,
  AuthenticationError,
  ValidationError,
  NotFoundError,
  RateLimitError,
} from "@locci/scheduler";

try {
  const task = await scheduler.createTask(taskOptions);
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error("Authentication failed:", error.message);
    // Handle authentication error (e.g., refresh token)
  } else if (error instanceof ValidationError) {
    console.error("Validation error:", error.message, error.details);
    // Handle validation errors
  } else if (error instanceof NotFoundError) {
    console.error("Resource not found:", error.message);
  } else if (error instanceof RateLimitError) {
    console.error("Rate limit exceeded:", error.message);
    // Implement backoff strategy
  } else if (error instanceof SchedulerError) {
    console.error("Scheduler error:", error.message, error.statusCode);
  } else {
    console.error("Unexpected error:", error);
  }
}
```

## Advanced Configuration

### Retry Configuration

```javascript
const taskWithRetries = await scheduler.createTask({
  name: "Resilient Task",
  webhook: {
    url: "https://unreliable-service.com/api/endpoint",
    method: "POST",
    retryConfig: {
      maxAttempts: 5, // Maximum retry attempts
      backoffSeconds: 2, // Initial backoff delay
      backoffMultiplier: 2.0, // Exponential backoff multiplier
      maxBackoffSeconds: 300, // Maximum backoff delay (5 minutes)
    },
    timeoutSeconds: 45, // Custom timeout for this webhook
  },
  schedule: { type: "once", executeAt: new Date(Date.now() + 3600000) },
});
```

### Complex Payloads and Headers

```javascript
const complexTask = await scheduler.createTask({
  name: "Complex API Call",
  webhook: {
    url: "https://api.example.com/v1/process",
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Version": "2.1",
      "X-Client-ID": "webhook-scheduler",
      "User-Agent": "WebhookScheduler/1.0",
    },
    payload: {
      event: "scheduled_task",
      data: {
        timestamp: new Date().toISOString(),
        source: "webhook-scheduler",
        payload: {
          type: "batch_process",
          items: ["item1", "item2", "item3"],
          options: {
            priority: "high",
            async: true,
          },
        },
      },
      metadata: {
        version: "1.0",
        environment: "production",
      },
    },
  },
  schedule: {
    type: "recurring",
    cronExpression: "0 2 * * 1-5", // Weekdays at 2 AM
    timezone: "UTC",
  },
});
```

## TypeScript Support

The library is built with TypeScript and provides full type safety:

```typescript
import {
  LocciScheduler,
  Task,
  TaskExecution,
  CreateTaskOptions,
  ScheduleConfig,
} from "@locci/scheduler";

// Fully typed configurations
const config: SchedulerConfig = {
  baseUrl: "https://api.scheduler.locci.cloud",
  apiToken: process.env.LOCCI_SCHEDULER_TOKEN!,
  timeout: 30000,
};

const scheduler = new LocciScheduler(config);

// Type-safe task creation
const taskOptions: CreateTaskOptions = {
  name: "Typed Task",
  webhook: {
    url: "https://api.example.com/webhook",
    method: "POST", // Typed as HttpMethod
    payload: {
      // Any JSON-serializable data
      message: "Hello from TypeScript!",
    },
  },
  schedule: {
    type: "once", // Typed schedule configuration
    executeAt: new Date(Date.now() + 3600000),
  },
};

const task: Task = await scheduler.createTask(taskOptions);
```

## Best Practices

### 1. Environment Configuration

```javascript
// Use environment variables for configuration
const scheduler = new LocciScheduler({
  baseUrl:
    process.env.LOCCI_SCHEDULER_URL || "https://api.scheduler.locci.cloud",
  apiToken: process.env.LOCCI_SCHEDULER_TOKEN,
  timeout: parseInt(process.env.DEFAULT_WEBHOOK_TIMEOUT || "30000"),
  retries: parseInt(process.env.MAX_CONCURRENT_EXECUTIONS || "3"),
});
```

### 2. Idempotent Operations

```javascript
// Use metadata to track operations and avoid duplicates
const task = await scheduler.createTask({
  name: "User Onboarding Email",
  webhook: {
    url: "https://your-app.com/api/send-email",
    method: "POST",
    payload: { userId: "12345", template: "onboarding" },
  },
  schedule: { type: "once", executeAt: new Date(Date.now() + 3600000) },
  metadata: {
    userId: "12345",
    operation: "user_onboarding",
    idempotencyKey: "user-12345-onboarding-2024-01-01",
  },
});
```

### 3. Graceful Error Handling

```javascript
async function scheduleWithRetry(taskOptions, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await scheduler.createTask(taskOptions);
    } catch (error) {
      if (error instanceof RateLimitError && attempt < maxRetries) {
        // Exponential backoff for rate limits
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise((resolve) => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
}
```

### 4. Batch Operations

```javascript
// Schedule multiple related tasks
async function scheduleUserOnboarding(userId) {
  const baseTime = new Date(Date.now() + 3600000); // 1 hour from now

  const tasks = await Promise.all([
    // Welcome email immediately
    scheduler.scheduleOnce({
      name: `Welcome Email - User ${userId}`,
      webhook: {
        url: "https://your-app.com/api/send-email",
        method: "POST",
        payload: { userId, template: "welcome" },
      },
      executeAt: baseTime,
      metadata: { userId, step: "welcome" },
    }),

    // Follow-up email after 3 days
    scheduler.scheduleOnce({
      name: `Follow-up Email - User ${userId}`,
      webhook: {
        url: "https://your-app.com/api/send-email",
        method: "POST",
        payload: { userId, template: "followup" },
      },
      executeAt: new Date(baseTime.getTime() + 3 * 24 * 60 * 60 * 1000),
      metadata: { userId, step: "followup" },
    }),

    // Survey after 7 days
    scheduler.scheduleOnce({
      name: `Survey Email - User ${userId}`,
      webhook: {
        url: "https://your-app.com/api/send-email",
        method: "POST",
        payload: { userId, template: "survey" },
      },
      executeAt: new Date(baseTime.getTime() + 7 * 24 * 60 * 60 * 1000),
      metadata: { userId, step: "survey" },
    }),
  ]);

  return tasks;
}
```

## API Reference

### WebhookScheduler Class

#### Constructor

- `new LocciScheduler(config: SchedulerConfig)`

#### Methods

**Task Management:**

- `createTask(options: CreateTaskOptions): Promise<Task>`
- `getTask(taskId: string): Promise<Task>`
- `listTasks(options?: PaginationOptions): Promise<TaskListResponse>`
- `updateTask(taskId: string, options: UpdateTaskOptions): Promise<Task>`
- `deleteTask(taskId: string): Promise<void>`

**Task Control:**

- `triggerTask(taskId: string): Promise<void>`
- `pauseTask(taskId: string): Promise<Task>`
- `resumeTask(taskId: string): Promise<Task>`
- `cancelTask(taskId: string): Promise<Task>`

**Scheduling Helpers:**

- `scheduleOnce(options): Promise<Task>`
- `scheduleRecurring(options): Promise<Task>`
- `scheduleInterval(options): Promise<Task>`
- `scheduleDaily(options): Promise<Task>`
- `scheduleWeekly(options): Promise<Task>`
- `scheduleEveryMinute(options): Promise<Task>`
- `scheduleEveryHour(options): Promise<Task>`

**History:**

- `getTaskHistory(taskId: string): Promise<TaskHistoryResponse>`

## Testing

```javascript
// examples/test.js
const { LocciScheduler } = require("@locci/scheduler");

async function runTests() {
  const scheduler = new LocciScheduler({
    baseUrl: "http://localhost:9696",
    apiToken: "your-test-token",
  });

  try {
    // Test creating a simple task
    console.log("Creating test task...");
    const task = await scheduler.scheduleOnce({
      name: "Test Task",
      webhook: {
        url: "https://httpbin.org/post",
        method: "POST",
        payload: { test: true, timestamp: new Date().toISOString() },
      },
      executeAt: new Date(Date.now() + 60000), // 1 minute from now
    });

    console.log("Task created:", task.id);

    // Test getting the task
    const retrievedTask = await scheduler.getTask(task.id);
    console.log("Task retrieved:", retrievedTask.name);

    // Test listing tasks
    const taskList = await scheduler.listTasks();
    console.log(`Total tasks: ${taskList.total}`);

    // Test manual trigger
    console.log("Triggering task manually...");
    await scheduler.triggerTask(task.id);

    // Wait a moment then check history
    setTimeout(async () => {
      const history = await scheduler.getTaskHistory(task.id);
      console.log(`Executions: ${history.total}`);

      // Clean up
      await scheduler.deleteTask(task.id);
      console.log("Test completed successfully!");
    }, 2000);
  } catch (error) {
    console.error("Test failed:", error.message);
  }
}

runTests();
```

## Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

MIT License - see the [LICENSE](LICENSE) file for details.

## Support

- Documentation: [https://docs.scheduler.locci.cloud](https://docs.scheduler.locci.cloud)
- Issues: [GitHub Issues](https://github.com/MikeTeddyOmondi/locci-scheduler/issues)
- Email: support@locci.cloud