/**
 * Cron event source for the skill event router.
 *
 * One process-wide source. Subscriptions register their cron expressions
 * through `register(expr)`; when a matching minute (or second, in croner's
 * 6-field syntax) ticks, the source emits a `schedule.cron` event with
 * `payload.cron` set to the expression that fired. The router then routes
 * to subscriptions whose `filter.cron` matches.
 *
 * The cron job factory is injectable so tests can drive ticks manually
 * without waiting for real time to pass. Production code gets croner's
 * `Cron(expression, onTick)`; tests get a stub that exposes the captured
 * callback.
 *
 * See `agents/2026-05-20-skills-unification-plan.md` step 10.
 */
import { Cron } from 'croner';
const defaultFactory = (expression, onTick) => new Cron(expression, onTick);
export class ScheduleEventSource {
    router;
    factory;
    jobs = new Map();
    constructor(router, factory = defaultFactory) {
        this.router = router;
        this.factory = factory;
    }
    /**
     * Register a cron expression. Subsequent calls with the same expression
     * are no-ops, so multiple subscriptions sharing a cron expression only
     * create one underlying job. The router fans the resulting event out to
     * all of them.
     */
    register(expression) {
        if (this.jobs.has(expression))
            return;
        const job = this.factory(expression, () => {
            void this.router.emit({
                kind: 'schedule.cron',
                payload: { cron: expression },
                at: Date.now(),
            });
        });
        this.jobs.set(expression, job);
    }
    unregister(expression) {
        const job = this.jobs.get(expression);
        if (!job)
            return;
        job.stop();
        this.jobs.delete(expression);
    }
    stop() {
        for (const job of this.jobs.values())
            job.stop();
        this.jobs.clear();
    }
    listRegistered() {
        return [...this.jobs.keys()];
    }
}
//# sourceMappingURL=schedule.js.map