@devloops/jcron
v1.5.3
Published
A high-performance, type-safe job scheduler for Node.js and React Native, inspired by Go's jcron.
Maintainers
Readme
@devloops/jcron
A high-performance, type-safe job scheduler for Node.js, React Native, and browsers
Features • Installation • Quick Start • Documentation • Examples • API Reference
🌟 Why JCRON?
JCRON is a next-generation cron scheduler that combines the power of Go's performance with the flexibility of TypeScript. Unlike other schedulers that rely on polling or setInterval, JCRON uses a mathematical scheduling algorithm for precise, CPU-efficient execution.
Key Highlights
- ⚡ Blazing Fast: Mathematical scheduling, not polling-based (~0.002ms per calculation)
- 🎯 100% Cron Compatible: All standard Unix/Linux patterns work perfectly
- 🌍 Universal: Node.js, React Native, Browsers (UMD/ESM/CJS)
- 🔒 Type-Safe: Full TypeScript support with strict typing
- 🌐 i18n Ready: Human-readable descriptions in 10+ languages
- 🚀 Zero Idle CPU: Near-zero CPU usage when no jobs are running
- 📦 Tiny Bundle: < 50KB gzipped, tree-shakeable
- 🧪 Battle-Tested: 175+ test cases, 91.6% coverage
🎯 Features
Core Scheduling
- ✅ Classic Cron Syntax: Full support for Unix/Linux cron patterns
- ✅ Shortcuts:
@yearly,@monthly,@weekly,@daily,@hourly,@minutely - ✅ Advanced Patterns:
L(last day/weekday of month)#(nth weekday occurrence, e.g.,1#2= 2nd Monday)W(week-based, e.g.,1W3= Monday of week 3)- Multiple patterns (e.g.,
1#1,5#3= 1st Monday and 3rd Friday)
- ✅ Week of Year:
WOY:33for ISO week-based scheduling - ✅ End of Duration (EOD): Schedule tasks relative to period ends
Timezone & Internationalization
- 🌍 Timezone Support: Full IANA timezone database support
- 🔄 DST Handling: Automatic daylight saving time adjustments
- 🗣️ Human-Readable: Convert cron expressions to natural language
- 🌐 10+ Languages: English, Turkish, Spanish, French, German, Polish, Portuguese, Italian, Czech, Dutch
Performance & Reliability
- ⚡ Optimized Engine: Timezone caching (41x → 1.68x overhead)
- 📊 Smart Caching: nthWeekDay caching, validation optimization
- 🛡️ Error Recovery: Built-in retry policies and error handling
- 🔍 Logging: Compatible with popular Node.js loggers (Winston, Pino, Bunyan)
Developer Experience
- 📘 TypeScript-First: Full type definitions with IntelliSense
- 🧪 Well-Tested: Comprehensive test suite with edge cases
- 📖 Rich Documentation: API reference, examples, migration guides
- 🔧 Flexible API: Function-based, object-based, or class-based usage
📦 Installation
# npm
npm install @devloops/jcron
# yarn
yarn add @devloops/jcron
# pnpm
pnpm add @devloops/jcron
# bun
bun add @devloops/jcronPlatform Support
| Platform | Support | Entry Point |
| ------------- | ------- | ------------------- |
| Node.js (CJS) | ✅ Full | dist/index.cjs |
| Node.js (ESM) | ✅ Full | dist/index.mjs |
| React Native | ✅ Full | dist/index.mjs |
| Browser (UMD) | ✅ Full | dist/jcron.umd.js |
| Browser (ESM) | ✅ Full | dist/index.mjs |
| Bun | ✅ Full | dist/index.mjs |
| Deno | ✅ Full | dist/index.mjs |
🚀 Quick Start
Basic Usage
import { Runner } from "@devloops/jcron";
const runner = new Runner();
// Run every day at 9 AM
runner.addFuncCron("0 9 * * *", () => {
console.log("Daily morning task executed!");
});
runner.start();Calculate Next Run Time
import { getNext, toString } from "@devloops/jcron";
const nextRun = getNext("0 9 * * *");
console.log("Next execution:", nextRun);
const humanReadable = toString("0 9 * * *");
console.log("Description:", humanReadable); // "Daily at 9:00 AM"Timezone-Aware Scheduling
import { Schedule, getNext } from "@devloops/jcron";
const schedule = new Schedule({
h: "9",
m: "0",
tz: "America/New_York",
});
const nextRun = getNext(schedule);
console.log("Next run in NYC timezone:", nextRun);📚 Documentation
Core Concepts
- API Reference - Complete API documentation
- Build System - Build configuration and outputs
- React Native Compatibility - Mobile development guide
Advanced Topics
- Performance Optimization - Performance benchmarks and optimizations
- Examples & Recipes - Real-world use cases
- Migration Guide - Upgrading from older versions
- Contributing - How to contribute
💡 Examples
1. Classic Cron Patterns
import { Runner } from "@devloops/jcron";
const runner = new Runner();
// Every 15 minutes
runner.addFuncCron("*/15 * * * *", () => {
console.log("Runs every 15 minutes");
});
// Every weekday at 9 AM
runner.addFuncCron("0 9 * * 1-5", () => {
console.log("Weekday morning task");
});
// First Monday of every month at midnight
runner.addFuncCron("0 0 * * 1#1", () => {
console.log("First Monday task");
});
runner.start();2. Advanced Patterns
import { Runner } from "@devloops/jcron";
const runner = new Runner();
// Multiple nth weekdays: 1st Monday and 3rd Friday
runner.addFuncCron("0 0 * * 1#1,5#3", () => {
console.log("Bi-monthly specific weekdays");
});
// Week-based pattern: Monday of week 4
runner.addFuncCron("0 0 * * 1W4", () => {
console.log("Monday of the 4th week");
});
// Last day of the month
runner.addFuncCron("0 0 L * *", () => {
console.log("Last day of month");
});
// Last Friday of the month
runner.addFuncCron("0 0 * * 5L", () => {
console.log("Last Friday");
});
runner.start();3. End of Duration (EOD)
import { Schedule, Runner } from "@devloops/jcron";
const runner = new Runner();
// Run at 9 AM daily, until the end of the day
const schedule = new Schedule({
h: "9",
m: "0",
eod: "E1D", // End of 1 Day
});
runner.addScheduleCron(schedule, () => {
console.log("Daily task with EOD");
});
runner.start();4. Human-Readable Descriptions
import { toHumanize } from "@devloops/jcron";
// Natural language descriptions
console.log(toHumanize("0 9 * * *")); // "Daily at 9:00 AM"
console.log(toHumanize("0 0 * * 0")); // "Weekly on Sunday"
console.log(toHumanize("0 0 1 * *")); // "Monthly on 1st"
console.log(toHumanize("0 0 1 1 *")); // "Yearly on January 1st"
console.log(toHumanize("0 0 * * 1-5")); // "at midnight, on weekdays"
console.log(toHumanize("0 0 * * 6,0")); // "at midnight, on weekends"
console.log(toHumanize("0 0 * * 1#2")); // "at midnight, on 2nd Monday of the month"
console.log(toHumanize("0 0 * * 1W4")); // "at midnight, on Monday of week 4"
console.log(toHumanize("*/15 9-17 * * 1-5")); // Smart time range formatting
// Multi-language support
console.log(toHumanize("0 9 * * *", { locale: "tr" })); // "Günlük saat 9:00"
console.log(toHumanize("0 9 * * *", { locale: "de" })); // "Täglich um 9:00"
console.log(toHumanize("0 9 * * *", { locale: "fr" })); // "Quotidien à 9:00"5. Week of Year Scheduling
import { Schedule, Runner } from "@devloops/jcron";
const runner = new Runner();
// Run on week 33 of the year
const schedule = new Schedule({
h: "9",
m: "0",
woy: "33",
});
runner.addScheduleCron(schedule, () => {
console.log("Week 33 task");
});
runner.start();6. React Native Usage
import { Runner } from "@devloops/jcron";
import { useEffect } from "react";
function useScheduler() {
useEffect(() => {
const runner = new Runner();
// Background sync every 15 minutes
runner.addFuncCron("*/15 * * * *", async () => {
await syncDataWithServer();
});
// Daily cleanup at midnight
runner.addFuncCron("0 0 * * *", async () => {
await cleanupOldCache();
});
runner.start();
return () => {
runner.stop();
};
}, []);
}7. Error Handling & Logging
import { Runner } from "@devloops/jcron";
const runner = new Runner();
// With error handling
runner.addFuncCron("0 9 * * *", async () => {
try {
await riskyOperation();
} catch (error) {
console.error("Task failed:", error);
// Implement retry logic or alerting
}
});
// With custom logging
runner.setLogger({
error: (msg: string, data?: any) => console.error(msg, data),
warn: (msg: string, data?: any) => console.warn(msg, data),
info: (msg: string, data?: any) => console.info(msg, data),
debug: (msg: string, data?: any) => console.debug(msg, data),
});
runner.start();🎨 API Reference
Core Functions
getNext(schedule: Schedule | string, from?: Date): Date
Calculate the next run time for a schedule.
import { getNext } from "@devloops/jcron";
const next = getNext("0 9 * * *");
const nextFromDate = getNext("0 9 * * *", new Date("2024-12-25"));getPrev(schedule: Schedule | string, from?: Date): Date
Calculate the previous run time for a schedule.
import { getPrev } from "@devloops/jcron";
const prev = getPrev("0 9 * * *");isMatch(schedule: Schedule | string, date: Date): boolean
Check if a date matches a schedule.
import { isMatch } from "@devloops/jcron";
const matches = isMatch("0 9 * * *", new Date("2024-12-25 09:00:00"));toString(schedule: Schedule | string, options?: HumanizeOptions): string
Convert a schedule to a human-readable string.
import { toString } from "@devloops/jcron";
const description = toString("0 9 * * *");
// "Daily at 9:00 AM"
const turkish = toString("0 9 * * *", { locale: "tr" });
// "Günlük saat 9:00"Runner Class
The Runner class manages scheduled tasks.
import { Runner } from "@devloops/jcron";
const runner = new Runner();
// Add a function-based cron job
const jobId = runner.addFuncCron("0 9 * * *", () => {
console.log("Task executed");
});
// Add a Schedule-based cron job
runner.addScheduleCron(schedule, callback);
// Control runner
runner.start();
runner.stop();
// Remove a job
runner.remove(jobId);Schedule Class
Create schedules using the Schedule class.
import { Schedule } from "@devloops/jcron";
const schedule = new Schedule({
s: "0", // Seconds (0-59)
m: "0", // Minutes (0-59)
h: "9", // Hours (0-23)
D: "*", // Day of month (1-31, L for last)
M: "*", // Month (1-12 or JAN-DEC)
dow: "1-5", // Day of week (0-7 or SUN-SAT, # for nth, L for last)
Y: "*", // Year (1970-3000)
woy: "*", // Week of year (1-53)
tz: "UTC", // Timezone (IANA timezone)
eod: null, // End of duration (e.g., "E1D")
});Humanize Options
Customize human-readable output.
interface HumanizeOptions {
locale?: string; // Language code (default: 'en')
use24HourTime?: boolean; // Use 24-hour format (default: false)
dayFormat?: "long" | "short" | "narrow";
monthFormat?: "long" | "short" | "narrow" | "numeric";
caseStyle?: "lower" | "upper" | "title";
verbose?: boolean; // Include verbose descriptions
includeTimezone?: boolean; // Include timezone info
includeYear?: boolean; // Include year info
includeWeekOfYear?: boolean; // Include week of year
includeSeconds?: boolean; // Include seconds in time
useShorthand?: boolean; // Use "weekdays"/"weekends" (default: true)
}🚀 Performance
JCRON delivers exceptional performance through mathematical scheduling and smart caching:
| Operation | Performance | Notes | | ----------------------- | -------------- | ------------------------------------ | | Simple patterns | ~0.002ms | Basic cron patterns | | Complex patterns | ~0.008ms | Advanced patterns with nthWeekDay | | Timezone conversion | 1.68x overhead | With caching (was 41x without) | | Humanization | ~0.5ms | With locale caching | | Memory usage | < 50KB | Per Runner instance | | CPU idle | ~0% | Mathematical scheduling, not polling |
Optimization Highlights
- ✅ Timezone Cache: Reduces overhead from 41x to 1.68x
- ✅ nthWeekDay Cache: 3.2x speedup for nth weekday patterns
- ✅ Validation Optimization: 1.2x improvement
- ✅ EOD Parsing Optimization: 3.3x faster
For detailed benchmarks, see HUMANIZE_FINAL_REPORT.md.
🌍 Supported Locales
JCRON supports humanization in 10+ languages:
| Code | Language | Example |
| ---- | ---------- | ----------------------- |
| en | English | "Daily at 9:00 AM" |
| tr | Turkish | "Günlük saat 9:00" |
| es | Spanish | "Diario a las 9:00" |
| fr | French | "Quotidien à 9:00" |
| de | German | "Täglich um 9:00" |
| pl | Polish | "Codziennie o 9:00" |
| pt | Portuguese | "Diário às 9:00" |
| it | Italian | "Giornaliero alle 9:00" |
| cz | Czech | "Denně v 9:00" |
| nl | Dutch | "Dagelijks om 9:00" |
🧪 Testing
# Run all tests
bun test
# Run with coverage
bun test --coverage
# Run specific test suite
bun test tests/01-core-engine.test.ts
# Run linter
npm run lint
# Build and test
npm run build && npm testTest Coverage
- ✅ 175+ passing tests
- ✅ 91.6% coverage
- ✅ Edge cases covered (DST, leap years, month boundaries)
- ✅ Performance benchmarks
📊 Bundle Size
JCRON is optimized for minimal bundle size:
| Format | Size (Gzipped) | Use Case | | ----------- | -------------- | --------------- | | ESM | ~45 KB | Modern bundlers | | CJS | ~46 KB | Node.js | | UMD | ~48 KB | Browsers | | UMD Min | ~22 KB | CDN usage |
Tree-shaking enabled for all formats.
🛠️ Build System
JCRON uses a modern build system with multiple output formats:
# Build all formats
npm run build
# Build specific format
npm run build:rollup # ESM, CJS, UMD
npm run build:types # TypeScript declarations
# Watch mode
npm run build:watch
# Analyze bundle size
npm run size
npm run analyzeFor detailed build information, see BUILD_SYSTEM.md.
🤝 Contributing
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Development Setup
# Clone the repository
git clone https://github.com/meftunca/jcron.git
cd jcron/node-port
# Install dependencies
bun install
# Run tests
bun test
# Build
npm run build📄 License
MIT © AI Assistant (Ported from Go)
🙏 Acknowledgments
- Inspired by the robust Go library jcron
- Built with TypeScript, Rollup, and modern tooling
- Community-driven with contributions from developers worldwide
📞 Support
- 📖 Documentation
- 🐛 Issue Tracker
- 💬 Discussions
- 📧 Email: [email protected]
Made with ❤️ by the JCRON Team
