Skip to content

feat(plugins): cron surface — declarations + scheduled() handler (Phase 4)#842

Open
lane711 wants to merge 1 commit into
lane711/plugin-system-issuesfrom
lane711/plugin-system-cron
Open

feat(plugins): cron surface — declarations + scheduled() handler (Phase 4)#842
lane711 wants to merge 1 commit into
lane711/plugin-system-issuesfrom
lane711/plugin-system-cron

Conversation

@lane711

@lane711 lane711 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds the plugin cron surface on top of the foundation (#841). Plugins declare scheduled work as data; a scheduled() handler fans fired Cron Triggers out to the matching plugins.

Stacked on #841 (base = lane711/plugin-system-issues). Review/merge that first.

Infrastructure only — no consumer change yet. Like the other foundation phases, this is live-but-inert: no plugin declares crons yet, and the scheduled export isn't wired into the consumer Worker.

What's here

  • plugins/cron.ts:
    • CronDeclaration { schedule, hookFamily } + structural CronablePlugin (crons[] + async onCronTick).
    • collectCrons() / collectCronSchedules() — flatten declarations (for wrangler-trigger sync + diagnostics).
    • dispatchCronTick() — matches a fired expression to the plugins that declared it, tags each onCronTick with the matched hookFamily (one call per matching declaration), error-isolated.
    • createScheduledHandler({ plugins, getHooks, disabled }) — returns a Cloudflare scheduled(controller, env, ctx) handler that reaches services via the env-independent singletons (cron has no c.env), passes Worker env through, and waitUntils the work.

Design note

Matches Payload's jobs model: declaring a schedule runs nothing by itself — the execution mechanism is the Cloudflare Cron Trigger delivered to scheduled(). The consumer still lists the expressions in wrangler.toml [triggers]; the plugin crons[] are the source of truth for which plugin owns which expression.

Tests

cron.test.ts (12): collect/flatten + malformed-entry skipping, match-only-fired-cron, well-formed event, unmatched reporting, multi-cron fan-out (one call per declaration), error isolation, and the scheduled handler (dispatch + waitUntil + env passthrough + lazy list + disabled no-op).

  • Full core suite: 1552 passed, 0 failed
  • tsc --noEmit: clean

Deferred

Wiring scheduled into the consumer Worker export (export default { fetch, scheduled }) + starter template + wrangler.toml trigger generation — bundled with definePlugin()/docs (Phase 5/7).

🤖 Generated with Claude Code

…se 4)

Let plugins declare scheduled work as data and dispatch it from the Worker's
scheduled() handler. Like Payload's jobs queue, declaring a schedule runs nothing
by itself: on Workers the execution mechanism is a Cron Trigger delivered to
scheduled(), which this fans out to matching plugins.

- plugins/cron.ts: CronDeclaration ({ schedule, hookFamily }) + structural
  CronablePlugin (crons[] + async onCronTick). collectCrons/collectCronSchedules
  flatten declarations (wrangler sync + diagnostics). dispatchCronTick() matches a
  fired expression to plugins, tags each onCronTick with the matched hookFamily
  (one call per matching declaration), error-isolated. createScheduledHandler()
  returns a CF scheduled(controller, env, ctx) handler reaching services via the
  env-independent singletons (cron has no c.env), passing Worker env through and
  waitUntil-ing the work.

Tests: cron.test.ts (12): collect/flatten, match-only-fired-cron, well-formed
event, unmatched reporting, multi-cron fan-out, error isolation, and the scheduled
handler (dispatch + waitUntil + env passthrough + lazy list + disabled no-op).
Full core suite 1552 passed, 0 failed; tsc clean.

Wiring scheduled() into the consumer Worker export + wrangler trigger generation
is a DX change deferred to the definePlugin()/docs bundle (Phase 5/7).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant