← All @molecule/* packages · App templates
@molecule/api-scheduler-cloudflareProvider bond · scheduler · API (Node) · v1.0.2 · Apache-2.0
Scheduler provider for Cloudflare Workers Cron Triggers — the platform owns the clock, so tasks run from the scheduled() handler instead of an in-process timer.
npm install @molecule/api-scheduler-cloudflarenpm · Source on GitHub · Implements @molecule/api-scheduler
@molecule/api-scheduler-cloudflare is a provider bond on the API (Node) side: it implements the scheduler core interface (@molecule/api-scheduler) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { schedule, setProvider, start } from '@molecule/api-scheduler'
import { createProvider } from '@molecule/api-scheduler-cloudflare'
const scheduler = createProvider()
setProvider(scheduler)
schedule({
name: 'monitor-sweep',
intervalMs: 60000,
async handler() {
// ...
},
})
// REQUIRED, same as the default provider: nothing runs until start().
// Unlike it, start() begins no timers — a Worker has no process to hold one.
start()
// Then, from the Worker's scheduled() handler, wrapped in ctx.waitUntil():
// export default { async scheduled(event, env, ctx) {
// ctx.waitUntil(scheduler.runDueTasks())
// } }
// and in wrangler.toml: [triggers] crons = ["* * * * *"]
void scheduler.runDueTasks()Works with: @molecule/api-bond, @molecule/api-scheduler
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
@molecule/api-scheduler-cloudflare — a @molecule/api-scheduler provider
for Cloudflare Workers, where the platform owns the clock.
@molecule/api-scheduler-default keeps tasks running with setInterval,
which needs a long-lived process. A Worker has none: it is an isolate that
exists for one invocation. So this provider registers tasks and runs them
when a Cron Trigger fires, via runDueTasks() from the Worker's
scheduled() handler. The application code that calls schedule() does not
change — only the bond wired in bonds/.
import { schedule, setProvider, start } from '@molecule/api-scheduler'
import { createProvider } from '@molecule/api-scheduler-cloudflare'
const scheduler = createProvider()
setProvider(scheduler)
schedule({
name: 'monitor-sweep',
intervalMs: 60000,
async handler() {
// ...
},
})
// REQUIRED, same as the default provider: nothing runs until start().
// Unlike it, start() begins no timers — a Worker has no process to hold one.
start()
// Then, from the Worker's scheduled() handler, wrapped in ctx.waitUntil():
// export default { async scheduled(event, env, ctx) {
// ctx.waitUntil(scheduler.runDueTasks())
// } }
// and in wrangler.toml: [triggers] crons = ["* * * * *"]
void scheduler.runDueTasks()
provider
npm install @molecule/api-scheduler-cloudflare @molecule/api-bond @molecule/api-scheduler
CloudflareSchedulerA scheduler provider whose tasks are driven by Cloudflare Cron Triggers rather than by an in-process timer.
start() and stop() gate whether {@link CloudflareScheduler.runDueTasks}
will execute anything; they start no timers, because a Worker has no
long-lived process to hold one. Nothing runs until the Worker's scheduled()
handler calls runDueTasks().
interface CloudflareScheduler extends SchedulerProvider {
/**
* Run the scheduled tasks. Call this from the Worker's `scheduled()` handler.
*
* Tasks run SEQUENTIALLY and every rejection is captured, so one failing task
* can neither abort the sweep nor reject the caller's promise — a Cron
* Trigger invocation that throws is retried by the platform, which would
* re-run the tasks that had already succeeded.
*
* @returns The status of every task after the run.
*/
runDueTasks(): Promise<TaskStatus[]>
}
CloudflareSchedulerOptionsOptions for the Cloudflare Workers scheduler provider.
interface CloudflareSchedulerOptions {
/**
* Honour each task's `intervalMs` as a floor, using an in-isolate record of
* when it last ran.
*
* Defaults to `false`, and false is almost always what you want. A Worker
* isolate is short-lived and there may be many of them, so "when did this last
* run" is NOT reliably known — a task skipped on that basis may simply never
* run. With the default, every Cron Trigger runs every enabled task and the
* trigger schedule IS the schedule, which is the only interpretation the
* platform can actually guarantee.
*
* Set this to `true` only when a duplicate run is more expensive than a missed
* one, and even then treat it as best-effort.
*/
respectIntervalWithinIsolate?: boolean
}
createProvider(options)Creates a Cloudflare Workers scheduler provider.
function createProvider(options?: CloudflareSchedulerOptions): CloudflareScheduler
options — Configuration options.Returns: A SchedulerProvider driven by Cron Triggers.
Implements @molecule/api-scheduler interface.
Peer dependencies:
@molecule/api-scheduler ^1.0.1@molecule/api-bond ^1.0.1@molecule/api-bond
@molecule/api-scheduler
intervalMs is not honoured by default, and that is deliberate. The
Cron Trigger cadence is the real schedule. A Worker isolate is short-lived
and there may be many, so "when did this task last run" is not reliably
known in-process; skipping a task on that basis can mean it never runs.
Set the interval you want in wrangler.toml, not in intervalMs. The
respectIntervalWithinIsolate option exists for the case where a duplicate
run costs more than a missed one, and even then it is best-effort.
Nothing runs until start() is called, exactly as with the default
provider. runDueTasks() on a stopped scheduler logs a warning and returns
an empty array rather than silently doing nothing — a Cron Trigger firing
into a stopped scheduler otherwise looks identical to having no work.
TaskStatus.nextRunAt is always null. Only the platform knows when
the next trigger fires, and this code cannot read the cron expression.
Computing a plausible-looking time would be a guess presented as a fact.
Status counters live in the isolate and do not persist. They are useful for the current invocation, not as a run history — persist to D1/KV if you need that.
Wrap runDueTasks() in ctx.waitUntil() so the invocation is not
cut short. The scheduled handler has a 15-minute budget; a sweep that
exceeds it is killed mid-task.