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

@oribos/croner

v0.9.0

Published

Oribos croner wrapper — cron expressions as a schedules.save() fragment (next + timezone), no dependencies beyond an exact-pinned croner, no core peer.

Readme

@oribos/croner

Cron expressions for Oribos schedules: one cron() call returns exactly the next + timezone fragment schedules.save() takes, so a schedule is registered by spreading it in — and the expression never enters the record.

import { createSchedules } from '@oribos/core/schedules';
import { cron } from '@oribos/croner';

const schedules = createSchedules({ agents: { reporter } });

await schedules.save({
  id: 'daily-report',
  ...cron('0 9 * * *', { timezone: 'Asia/Shanghai' }),
  target: { agent: 'reporter', input: 'write the daily report' },
});

await schedules.tick(); // the platform cron's endpoint calls this

Install

npm install @oribos/croner

The wrapper depends on nothing beyond croner, pinned exactly ([email protected], MIT, engines >=18), and pulls in nothing further. There is no @oribos/core peer: the fragment is structurally just the next + timezone slice of ScheduleSaveInput, so this package stands alone — install @oribos/core separately when you actually call schedules.save().

The fragment

cron(expression, { timezone? }) // → { next: (from: Date) => Date | null, timezone? }
  • Spread or destructure, both work. { ...cron(expr), target } and { next: cron(expr).next, target } are the same to save(). timezone is present only when given — absent, the expression is read in the process's own timezone (croner's default).
  • next is croner's nextRun. The first occurrence strictly after from, milliseconds stripped, null when none is left (an exhausted schedule — e.g. a 7-part pattern whose year has passed). That is the framework's occurrence semantics exactly; no translation layer.
  • A date-time string is croner's once mode, passed through as-is. cron('2030-01-01T00:00:00Z') returns that instant until it has passed, then null — save() records it and the target fires once, then the record is the exhausted nextFireAt: null form. Out of the cron-pattern surface, but the wrapper's stance is upstream behavior, recorded rather than compensated (same as DST).
  • The expression syntax is croner's full set — 5-part, 6-part (seconds first) and 7-part (year last) patterns, @daily-style nicknames, month/weekday names, L / W / #.
  • The option subset is closed: timezone only. croner's other construction options (startAt / stopAt / utcOffset / mode / domAndDow / …) are not exposed. Clamping wants are served by destructuring next and wrapping it yourself.
  • Everything is validated at the cron() call site, synchronously. Structural and range errors are croner's own (TypeError / RangeError) and pass through untouched. Invalid IANA timezones — which croner only rejects lazily, on the first nextRun — are probed once at construction, so cron('0 9 * * *', { timezone: 'Planet/Nowhere' }) throws right there. After that, next is pure computation and never throws.
  • The expression lives on the host side. save() persists the timezone but never the pattern: keep id ↔ expression in your config or code and re-save() each definition at boot — save() re-anchors nextFireAt from now, so the definition you hold is authoritative.

DST

Daylight-saving behavior is croner's, passed through untouched — this package neither compensates nor hides it. Observed on [email protected] with Europe/Stockholm, 2026: on the spring-forward gap day 30 2 * * * returns local 03:30 CEST (the same instant 30 3 * * * returns — an offset mapping, which contradicts croner's README wording "gaps are skipped"; upstream behavior wins and is recorded here), and on the fall-back overlap day the 02:30 pattern runs once, at the first occurrence. Both cases are pinned in this package's tests, so a croner bump that changes DST semantics fails loudly.