@samveys/scheduler

Cron tasks, overlap and one-server locks, schedule:run

Version
1.0.2Nov 5, 2026
Install
pnpm add @samveys/scheduler
Import
import { createSchedule, cronMatches, nextCronRun, parseCron, scheduler } from '@samveys/scheduler'

Function (5)

createSchedule Function

Creates a schedule. Tasks run when `runDue()` is called once a minute (`svs schedule:run`).

function createSchedule(options?: ScheduleOptions): Schedule;

Exported from packages/scheduler/src/schedule.ts

cronMatches Function

True when the minute of `at` (in its own zone) matches every field.

function cronMatches(cron: CronExpression, at: Temporal.ZonedDateTime): boolean;

Exported from packages/scheduler/src/cron.ts

nextCronRun Function

The first minute strictly after `from` that matches, evaluated in `timeZone` (DST aware: a minute that does not exist on a spring-forward day never matches, one that exists twice matches twice). Skips whole months, days and hours that cannot match.

function nextCronRun(cron: CronExpression, from: Temporal.Instant, timeZone: string): Temporal.ZonedDateTime;

Exported from packages/scheduler/src/cron.ts

parseCron Function

Parses a 5-field cron expression: minute hour day-of-month month day-of-week. Supports lists, ranges and steps.

function parseCron(source: string): CronExpression;

Exported from packages/scheduler/src/cron.ts

scheduler Function

The `@samveys/scheduler` plugin: binds `'schedule'` and adds `schedule:run` and `schedule:list`.

function scheduler(config: SchedulerConfig): import("@samveys/core").Plugin<SchedulerConfig>;

Exported from packages/scheduler/src/plugin.ts

Interface (8)

CronExpression Interface

A parsed 5-field cron expression: the allowed values of each field.

interface CronExpression {
    readonly source: string;
    readonly minutes: ReadonlySet<number>;
    readonly hours: ReadonlySet<number>;
    /** Days of the month, 1-31. */
    readonly daysOfMonth: ReadonlySet<number>;
    /** Months, 1-12. */
    readonly months: ReadonlySet<number>;
    /** Days of the week, 0-6 (0 = Sunday; 7 is accepted in the source and folded to 0). */
    readonly daysOfWeek: ReadonlySet<number>;
  …
Show the whole declaration (15 lines)
interface CronExpression {
    readonly source: string;
    readonly minutes: ReadonlySet<number>;
    readonly hours: ReadonlySet<number>;
    /** Days of the month, 1-31. */
    readonly daysOfMonth: ReadonlySet<number>;
    /** Months, 1-12. */
    readonly months: ReadonlySet<number>;
    /** Days of the week, 0-6 (0 = Sunday; 7 is accepted in the source and folded to 0). */
    readonly daysOfWeek: ReadonlySet<number>;
    /** True when the day-of-month field was not `*`. */
    readonly restrictsDayOfMonth: boolean;
    /** True when the day-of-week field was not `*`. */
    readonly restrictsDayOfWeek: boolean;
}

Exported from packages/scheduler/src/contracts.ts

LockProvider Interface

Minimal lock surface of `@samveys/cache`.

interface LockProvider {
    lock(name: string, ttlMs: number): {
        acquire(): Promise<boolean>;
        release(): Promise<boolean>;
    };
}

Exported from packages/scheduler/src/contracts.ts

Schedule Interface

No documentation comment.

interface Schedule {
    /** Registers a task. Without a cron/frequency call the task is invalid (`tasks()` throws). */
    call(name: string, handler: TaskHandler): TaskBuilder;
    /** Resolved tasks, in registration order. Throws when a task has no frequency. */
    tasks(): readonly ScheduledTask[];
    /** Runs every task due in the minute of `now` once. This is what the one cron entry calls. */
    runDue(now?: Temporal.Instant): Promise<readonly TaskRunResult[]>;
    /** Runs one task now, ignoring its frequency. Throws SVS_E102 while a `withoutOverlapping` task is running. */
    runNow(name: string): Promise<TaskRunResult>;
    /** First minute strictly after `from` at which the task is due. */
    nextRun(name: string, from?: Temporal.Instant): Temporal.ZonedDateTime;
}

Exported from packages/scheduler/src/contracts.ts

ScheduledTask Interface

A task as registered.

interface ScheduledTask {
    readonly name: string;
    readonly handler: TaskHandler;
    readonly expression: CronExpression;
    readonly timezone: string;
    readonly overlapTtlMs: number | undefined;
    readonly onOneServer: boolean;
    readonly pingBefore: string | undefined;
    readonly thenPing: string | undefined;
}

Exported from packages/scheduler/src/contracts.ts

ScheduleOptions Interface

No documentation comment.

interface ScheduleOptions {
    /** Where `withoutOverlapping` and `onOneServer` locks live; resolved lazily, once per use. */
    readonly locks?: () => LockProvider;

Exported from packages/scheduler/src/schedule.ts

SchedulerConfig Interface

Configuration of `scheduler()`.

interface SchedulerConfig {
    /** Declare tasks: `schedule.call('reports', run).dailyAt('02:00')`. Called once, when `'schedule'` is first resolved. */
    readonly define: (schedule: Schedule) => void;

Exported from packages/scheduler/src/plugin.ts

TaskBuilder Interface

Fluent definition of one scheduled task.

interface TaskBuilder {
    /** 5-field cron expression, e.g. every five minutes: five fields separated by spaces. */
    cron(expression: string): TaskBuilder;
    everyMinute(): TaskBuilder;
    hourly(): TaskBuilder;
    /** Every day at `HH:MM` in the task's time zone. */
    dailyAt(time: string): TaskBuilder;
    /** IANA zone the cron fields are evaluated in. Default `UTC`. */
    timezone(zone: string): TaskBuilder;
    /** Skip a run while the previous one is still running (a cache lock, released afterwards). */
  …
Show the whole declaration (18 lines)
interface TaskBuilder {
    /** 5-field cron expression, e.g. every five minutes: five fields separated by spaces. */
    cron(expression: string): TaskBuilder;
    everyMinute(): TaskBuilder;
    hourly(): TaskBuilder;
    /** Every day at `HH:MM` in the task's time zone. */
    dailyAt(time: string): TaskBuilder;
    /** IANA zone the cron fields are evaluated in. Default `UTC`. */
    timezone(zone: string): TaskBuilder;
    /** Skip a run while the previous one is still running (a cache lock, released afterwards). */
    withoutOverlapping(ttlMs?: number): TaskBuilder;
    /** Run on only one server per due minute (a cache lock shared through the cache store). */
    onOneServer(): TaskBuilder;
    /** GET this URL before the task starts. A failed ping never fails the task. */
    pingBefore(url: string): TaskBuilder;
    /** GET this URL after the task succeeded. */
    thenPing(url: string): TaskBuilder;
}

Exported from packages/scheduler/src/contracts.ts

TaskRunResult Interface

No documentation comment.

interface TaskRunResult {
    readonly name: string;
    readonly status: TaskRunStatus;
    readonly error?: Error;
}

Exported from packages/scheduler/src/contracts.ts

Type (2)

TaskHandler Type

No documentation comment.

type TaskHandler = () => void | Promise<void>;

Exported from packages/scheduler/src/contracts.ts

TaskRunStatus Type

No documentation comment.

type TaskRunStatus = 'ran' | 'failed' | 'skipped-overlap' | 'skipped-server';

Exported from packages/scheduler/src/contracts.ts