@locci/scheduler
v1.0.0-beta
Published
JavaScript client library for Locci Scheduler platform
Maintainers
Readme
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
npm install @locci/schedulerOr with other package managers:
bun add @locci/scheduler
pnpm add @locci/scheduler
yarn add @locci/schedulerQuick Start
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
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
// 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)
// 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
// 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
// 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
// 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
// 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
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
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
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:
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
// 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
// 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
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
// 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
// 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
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT License - see the LICENSE file for details.
Support
- Documentation: https://docs.scheduler.locci.cloud
- Issues: GitHub Issues
- Email: [email protected]
