2.8 KiB
notification-scheduler — Future-delivery scheduler
(no submodules)
Purpose
notification-scheduler.ts ships the NotificationScheduler class for scheduling notifications for future delivery. The scheduler does NOT call the router itself — callers register an onDue(handler) that receives the original NotificationInput, typically wiring it back into NotificationRouter.send or enqueue. The class exposes both manual processDue() (call from cron / queue worker) and start()/stop() for in-process polling.
Features
schedule(input, deliverAt)returns a stable id.cancel(id),get(id),listPending(),getDue(now?)for inspection.processDue()awaits the handler per entry and removes processed entries.start()/stop()polling driver with idempotent semantics.- Injectable
Clockfor deterministic tests. - Throws
NotificationNotFoundErrorfor unknown ids oncancel/get.
Usage: Basic
import {
NotificationRouter,
NotificationScheduler,
} from "@suaveplan/notification-channel";
const router = new NotificationRouter({ channels, defaultChannel: "email" });
const scheduler = new NotificationScheduler();
scheduler.onDue(async (input) => {
await router.send(input);
});
scheduler.schedule({ recipient: "u@x", body: "Reminder" }, new Date(Date.now() + 60_000));
scheduler.start();
Usage: Advanced
const scheduler = new NotificationScheduler(5_000, () => clock.now());
scheduler.onDue(async (input) => {
try {
await router.send(input);
} catch (err) {
await failureQueue.push({ input, err });
}
});
// Hook into a cron worker instead of in-process polling:
cron.schedule("* * * * *", () => {
void scheduler.processDue();
});
API Reference
NotificationScheduler(checkIntervalMs?, clock?)— constructor.schedule(input, deliverAt),cancel(id),get(id),listPending(),getDue(now?),processDue(),start(),stop(),size.ScheduledNotification—{ id, input, scheduledFor, createdAt, cancelled }.ScheduleHandler—(input: NotificationInput) => Promise<void>.
Implementation Notes
start() is idempotent — calling twice has no effect; the second call is dropped before setInterval runs. stop() is idempotent — calling twice is safe and a leading call before start() is a no-op. processDue() is the single place that mutates the schedule map — both the polling driver and manual / cron callers use it, so behaviour is identical regardless of how processing is triggered. The scheduler does not persist across process restarts — production deployments should layer their own durable store atop schedule/cancel (e.g. snapshot listPending() to disk on shutdown, restore on boot via additional schedule calls).