later

Composable library for defining recurring schedules and calculating their next or previous occurrences.

Library
npm
v4.2.0
150stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
36/100Needs Attention
Development Activity0
Maintenance20
Community44
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
60/100Good
Architecture70
Code Quality65
Innovation60
Learning Curve45

@breejs/later is a maintained fork of the original Later.js scheduling library, built to calculate past and future occurrences of recurring schedules — the same class of problem cron solves, but expressed through composable constraint definitions instead of a single five-field string. It powers recurring-job calculation for Bree, the Node.js job scheduler, and is used anywhere an application needs to answer “when does this recur next?” without relying on OS-level cron.

Schedules can be built three ways — a chainable recurrence API, English text phrases (“every 2 hours starting at 5:00 pm”), or standard cron expressions (with or without a seconds field) — all of which compile down to the same internal schedule definition. That definition is evaluated by composing per-unit constraints (second, minute, hour, day, day-of-week, day-of-week-count, day-of-year, week-of-month, week-of-year, month, year), which is what lets it express schedules cron can’t represent directly, like “the last day of every month except December” or composite schedules with exceptions. It ships as both a Node package and a browser bundle (via jsdelivr/unpkg), with zero runtime dependencies.

What You Get

  • Three schedule input formats - Define schedules via cron expressions (with optional seconds field), human-readable English text phrases, or a chainable JS recurrence builder, all compiled to the same internal representation.
  • Composable constraint engine - Combine second/minute/hour/day/day-of-week/day-of-year/week-of-month/week-of-year/month/year constraints to express schedules plain cron can’t, like “2nd Tuesday of the month” or “every day except December”.
  • Timer helpers - later.setTimeout and later.setInterval act as drop-in replacements that fire on a later.schedule instead of a fixed delay, including cancelable handles.
  • Dual Node/browser builds - Ships a CommonJS build for Node and a minified UMD bundle for <script> tag or bundler use, available via unpkg/jsdelivr.
  • Zero runtime dependencies - Pure JavaScript with no third-party packages required at runtime.

Common Use Cases

  • Recurring job scheduling - Powers Bree’s job scheduler to compute when the next run of a recurring task should fire.
  • Cron-like scheduling without a system crontab - Embed schedule evaluation directly in an app instead of relying on OS-level cron.
  • Human-friendly recurrence UIs - Parse or generate English text schedules (“every 5 minutes”, “on the last day of the month”) for user-facing scheduling settings.
  • Composite/exception schedules - Calculate occurrences for schedules with exceptions, e.g. “every weekday at 9am except holidays”.

Under The Hood

Architecture The library is a single-file engine (src/index.js, ~2,170 lines) built around a compile-then-evaluate model. later.compile() takes a schedule definition (arrays of days, months, hours, etc.) and assembles a composite pipeline from per-unit constraint modules — later.day, later.hour, later.month, later.minute, later.second, later.dayOfWeek, later.dayOfWeekCount, later.dayOfYear, later.weekOfMonth, later.weekOfYear, later.year — each exposing a uniform {name, range, val, extent, start, end, next, prev} interface. later.schedule() walks forward or backward through time using these tick functions, rolling over to higher-order units whenever a constraint invalidates. Parsing is a decoupled front-end layer (later.parse.cron, plus recur/text parsers) that produces the same plain schedule-definition object consumed by compile, so parser front-ends are swappable without touching the constraint engine. Everything hangs off a single later namespace as plain functions rather than classes, with state passed explicitly via a per-tick date-cache object — so every constraint module depends directly on the shared later.date provider (which abstracts UTC vs. local Date access), and a change to that provider’s shape would ripple through all of them.

Tech Stack Pure JavaScript with zero runtime dependencies — devDependencies are all tooling: Babel for transpilation, xo/ESLint plus Prettier for linting, Mocha/nyc/sinon/should for testing, Browserify + tinyify for the browser bundle, and husky/lint-staged/commitlint for repo hygiene. It ships both a Node build (lib/, transpiled from src/) and a browser build (dist/later.js, dist/later.min.js, referenced via the jsdelivr/unpkg package.json fields), plus a plain <script> tag usage example. No database or framework involved — it’s a standalone computation library meant to be embedded, notably by Bree.

Code Quality Tests are substantial: a dedicated test/ tree covers constraints (day, hour, minute, month, second, dayOfWeek, dayOfWeekCount, dayOfYear, weekOfMonth, weekOfYear, year), core behavior (compile, schedule, setInterval, setTimeout), all three parsers (cron, recur, text), modifiers (after/before), and regression cases under test/issues, run via Mocha with nyc coverage (a codecov badge is in the README) and sinon for stubbing timers. Linting is strict (xo plus Prettier, remark for markdown, husky/lint-staged/commitlint enforcing conventional commits pre-commit). Error handling is minimal but explicit — a handful of thrown-string guard clauses (e.g. “Missing schedule definition”) rather than typed/result-based handling; there is no TypeScript and no type annotations anywhere in the codebase.

API Design The public API favors a single global namespace (later) with terse method names mirroring the constraint they configure (later.day, later.hour, later.parse.cron, later.schedule), keeping everyday usage compact — compiling and scheduling is typically a two-line call. But the lack of TypeScript types or inline JSDoc pushes discoverability onto the external documentation site rather than editor autocomplete, and error feedback for malformed cron/text input is limited to a few generic thrown strings rather than descriptive validation.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search