npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@locci-scheduler/client

v1.0.0-beta

Published

JavaScript client library for Locci Scheduler platform

Readme

Locci Scheduler - JavaScript Client

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-scheduler/client

Quick Start

import { LocciScheduler } from '@locci-scheduler/client';

// 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/client';

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/client';

// 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/client');

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 file for details.

Support