UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

103 lines (91 loc) 3.3 kB
import { $hook } from "alepha"; import type { DateTime } from "alepha/datetime"; import { parseCronExpression } from "cron-schedule"; import { CronProvider } from "./CronProvider.ts"; // --------------------------------------------------------------------------------------------------------------------- declare module "alepha" { interface Hooks { /** * Cloudflare Workers scheduled event. * * Emitted when a cron trigger fires in Cloudflare Workers. */ "cloudflare:scheduled": { cron: string; scheduledTime: number; }; } } // --------------------------------------------------------------------------------------------------------------------- /** * Cloudflare Workers cron provider. * * This provider handles scheduled events from Cloudflare Workers Cron Triggers. * Unlike the Node.js CronProvider, this doesn't use intervals/timeouts - instead, * it reacts to scheduled events triggered by Cloudflare. * * **Usage:** * 1. Define schedulers with `$scheduler({ cron: "0 * * * *", handler: ... })` * 2. Build your app with `alepha build` - cron triggers are automatically added to `wrangler.jsonc` * 3. Deploy to Cloudflare Workers * * **How it works:** * - During build, all registered `$scheduler` cron expressions are collected * - The build generates `wrangler.jsonc` with `triggers.crons` automatically filled * - When Cloudflare fires a cron trigger, the `scheduled` handler emits `cloudflare:scheduled` * - This provider listens to that event and runs matching schedulers * * @see https://developers.cloudflare.com/workers/configuration/cron-triggers/ */ export class WorkerdCronProvider extends CronProvider { /** * Override to avoid creating AbortController in global scope. * Cloudflare Workers doesn't allow this during initialization. */ public override createCronJob( name: string, expression: string, handler: (context: { now: DateTime }) => Promise<void>, ): void { this.cronJobs.push({ name, cron: parseCronExpression(expression), expression, handler, loop: false, }); } /** * Handle a scheduled event from Cloudflare Workers. */ protected readonly onScheduledEvent = $hook({ on: "cloudflare:scheduled", handler: async (event) => { const now = this.dt.of(event.scheduledTime); this.log.info("Received scheduled event", { cron: event.cron, scheduledTime: now.format(), }); // Find jobs that match this cron expression const matchingJobs = this.cronJobs.filter( (job) => job.expression === event.cron, ); if (matchingJobs.length === 0) { // No exact match - try to find jobs that would fire at this time const matchingByTime = this.cronJobs.filter((job) => job.cron.matchDate(now.toDate()), ); if (matchingByTime.length > 0) { this.log.debug( `No exact cron match for '${event.cron}', found ${matchingByTime.length} jobs matching by time`, ); await this.runJobs(matchingByTime, now); return; } this.log.warn(`No cron jobs found for expression '${event.cron}'`); return; } await this.runJobs(matchingJobs, now); }, }); }