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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@philiprehberger/cron-kit

v0.3.5

Published

Cron job scheduler with overlap prevention, timezones, and job management

Downloads

1,098

Readme

@philiprehberger/cron-kit

CI npm version Last updated

Cron job scheduler with overlap prevention, timezones, and job management

Installation

npm install @philiprehberger/cron-kit

Usage

Basic

import { createScheduler } from '@philiprehberger/cron-kit';

const scheduler = createScheduler();

scheduler.addJob({
  name: 'cleanup',
  schedule: '0 3 * * *', // daily at 3am
  handler: async () => {
    await db.sessions.deleteExpired();
  },
});

scheduler.start();

With Options

scheduler.addJob({
  name: 'report',
  schedule: '0 9 * * 1', // every Monday at 9am
  timezone: 'Europe/Vienna',
  allowOverlap: false,    // skip if previous run still active
  handler: async () => { /* ... */ },
  onStart: () => console.log('Report started'),
  onComplete: (duration) => console.log(`Done in ${duration}ms`),
  onError: (err) => console.error('Report failed:', err),
  onOverlap: () => console.warn('Skipped — still running'),
});

Cron Expressions

Standard 5-field format: minute hour day-of-month month day-of-week

*     *     *     *     *
│     │     │     │     └── Day of week (0–6, Sun=0)
│     │     │     └──────── Month (1–12)
│     │     └────────────── Day of month (1–31)
│     └──────────────────── Hour (0–23)
└────────────────────────── Minute (0–59)

Supports: *, values (5), ranges (1-5), steps (*/5), lists (1,3,5)

When both day-of-month and day-of-week are specified (not *), the job runs when either field matches (OR logic), following standard cron behavior.

Preview Next Runs

import { nextRuns } from '@philiprehberger/cron-kit';

nextRuns('0 9 * * 1', 3);
// [Date, Date, Date] — next 3 Mondays at 9am

// Or from a job
scheduler.getJob('cleanup').nextRuns(5);

Job Management

scheduler.getJob('cleanup');   // job info
scheduler.getJobs();           // all jobs
scheduler.removeJob('cleanup');

// Update a job's schedule, handler, or other options
scheduler.updateJob('cleanup', {
  schedule: '0 4 * * *',      // change to 4am
  handler: async () => { /* new logic */ },
});

Job Info

const job = scheduler.getJob('cleanup');
job.name;         // 'cleanup'
job.running;      // boolean
job.lastRunAt;    // Date | null
job.lastDuration; // number | null (ms)
job.lastError;    // Error | null
job.runCount;     // number

Graceful Shutdown

scheduler.stop();               // stop scheduling (active jobs keep running)
await scheduler.shutdown();     // stop + wait for active jobs to finish

API

createScheduler(): Scheduler

Creates a new scheduler instance.

| Method | Signature | Description | |--------|-----------|-------------| | addJob | (config: JobConfig) => void | Register a job. Throws if name already exists. | | updateJob | (name: string, updates: Partial<Omit<JobConfig, 'name'>>) => void | Update a job's config. Re-parses schedule if changed. Throws if not found. | | removeJob | (name: string) => void | Remove a job by name. Throws if not found. | | getJob | (name: string) => JobInfo | Get info for a single job. Throws if not found. | | getJobs | () => JobInfo[] | Get info for all registered jobs. | | start | () => void | Start the scheduler tick loop. | | stop | () => void | Stop scheduling (active jobs keep running). | | shutdown | () => Promise<void> | Stop and wait for all active jobs to finish. |

JobConfig

| Property | Type | Required | Description | |----------|------|----------|-------------| | name | string | Yes | Unique job identifier. | | schedule | string | Yes | Cron expression (5 fields). | | handler | () => Promise<void> \| void | Yes | Function to execute. | | timezone | string | No | IANA timezone (e.g. "America/Denver"). | | allowOverlap | boolean | No | Skip execution if previous run is still active. Default: true. | | onStart | () => void | No | Called when job execution begins. | | onComplete | (duration: number) => void | No | Called on success with duration in ms. | | onError | (error: Error) => void | No | Called when handler throws. | | onOverlap | () => void | No | Called when execution is skipped due to overlap. |

JobInfo

| Property | Type | Description | |----------|------|-------------| | name | string | Job name. | | schedule | string | Cron expression. | | timezone | string \| undefined | Configured timezone. | | running | boolean | Whether the job is currently executing. | | lastRunAt | Date \| null | When the job last completed. | | lastDuration | number \| null | Last run duration in ms. | | lastError | Error \| null | Last error thrown by handler. | | runCount | number | Total completed executions. | | nextRuns | (count?: number) => Date[] | Preview upcoming run times. |

nextRuns(expression: string, count?: number, timezone?: string): Date[]

Returns an array of upcoming dates matching the cron expression.

parseCronExpression(expression: string): ParsedCron

Parses a 5-field cron expression into a structured object.

matchesCron(cron: ParsedCron, date: Date): boolean

Tests whether a date matches a parsed cron expression.

Limitations

  • Minute-level precision: The scheduler ticks every 30 seconds to check for matching jobs. All scheduling has minute-level granularity — second-level precision is not supported.
  • Timezone handling: Timezone conversion uses Date.toLocaleString() internally, which may have subtle differences across runtimes.
  • In-memory only: Job state is not persisted. Restarting the process resets all job counters and history.

Development

npm install
npm run build
npm test

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT