@chamberu/scheduler
Fires chamberu cron triggers once per tick across replicas, on hubwire. The public API of packages/scheduler/src/index.ts.
bash
npm install @chamberu/scheduler @chamberu/core --save@chamberu/core is a peer dependency. See Cron Scheduling for the behaviour.
createScheduler
ts
function createScheduler(opts: SchedulerOptions): IScheduler;
const SCHEDULE_LOCK_TTL_MS: 600_000;Throws InvalidAgentDefinitionError for a duplicate agent name or a cron trigger the scheduler cannot use (expression, timezone, lockTtlMs).
ts
/*
* Copyright (c) 2026.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/
import type { Agent, IStore, RunEngine } from '@chamberu/core';
export type SchedulerErrorContext = {
agent: string;
trigger: string;
};
export type SchedulerOptions = {
engine: RunEngine;
/**
* The agents whose cron triggers are scheduled. Must be the list the engine
* was built with: a tick for an agent the engine does not know fails with
* InvalidAgentDefinitionError, reported to onError.
*/
agents: Agent[];
store: IStore;
/** Lock owner. Default `${hostname()}-${pid}`. */
owner?: string;
/**
* How long a tick's lock key is held; a tick whose timer fires this late or
* later is skipped. Default SCHEDULE_LOCK_TTL_MS.
*/
lockTtlMs?: number;
/**
* Receives a failing tick (lock store, overlap check or dispatch). Ticks
* another replica won are not errors and are not reported.
* Default console.error('[chamberu]', error, ctx).
*/
onError?: (error: unknown, ctx: SchedulerErrorContext) => void;
};
export interface IScheduler {
/** Schedules every cron trigger; never fires on start. Idempotent. */
start(): Promise<void>;
/** Unschedules every trigger, then waits for ticks in flight. */
stop(): Promise<void>;
}runJob
ts
function runJob(opts: RunJobOptions): Promise<RunJobResult>;Rejects with InvalidAgentDefinitionError when the trigger is missing or ambiguous.
ts
/*
* Copyright (c) 2026.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/
import type {
Agent,
IClock,
IStore,
RunEngine,
RunStatusLiteral,
} from '@chamberu/core';
export type RunJobOptions = {
/** Not started yet; runJob starts and drains it, also when the tick is skipped. */
engine: RunEngine;
store: IStore;
agent: Agent;
/** Cron trigger id; optional when the agent has exactly one cron trigger. */
trigger?: string;
/** Default systemClock; gives scheduledAt and drives the poll. */
clock?: IClock;
/** Fallback poll of engine.getRun, default 1_000. */
pollMs?: number;
/** Passed to engine.drain, default 30_000. */
drainTimeoutMs?: number;
};
export type RunJobResult = {
runId?: string;
/**
* The run's status when the job returned: `completed`, `failed`,
* `cancelled`, `waiting`, `queued` (behind a run of its concurrency key
* that waits for an approval), or 'skipped' (overlap).
*/
status: RunStatusLiteral | 'skipped';
/** 0 for completed, waiting, queued and skipped; 1 for failed and cancelled. */
exitCode: 0 | 1;
};Building blocks
ts
function fireCronTrigger(ctx: CronTickContext): Promise<CronTickResult>;
function readCronOptions(agent: Agent, trigger: TriggerDefinition): CronTriggerOptions;
function createStoreScheduleLock(store: ILockStore, owner: string): IScheduleLock;fireCronTrigger is one tick (overlap check and dispatch) for an explicit scheduledAt; createScheduler and runJob both call it. createStoreScheduleLock adapts core's ILockStore to hubwire's IScheduleLock.
ts
/*
* Copyright (c) 2026.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/
import type {
Agent,
IRunStore,
OverlapPolicy,
RunEngine,
TriggerDefinition,
} from '@chamberu/core';
export type CronTriggerOptions = {
expression: string;
timezone?: string;
overlap: OverlapPolicy;
payload?: unknown;
};
export type CronTickContext = {
engine: RunEngine;
store: Pick<IRunStore, 'listRuns'>;
agent: Agent;
trigger: TriggerDefinition;
/** The tick's planned fire time; forms the idempotency key. */
scheduledAt: Date;
};
export type CronTickDispatched = {
dispatched: true;
runId: string;
deduplicated: boolean;
};
export type CronTickSkipped = {
dispatched: false;
reason: 'overlap';
};
export type CronTickResult = CronTickDispatched | CronTickSkipped;