import type { DB } from '../db/client.js'; import { type Plan, type PlanDay, type UserPlan } from '../db/schema/plans.js'; import type { Emit } from '../events/emit.js'; export interface SequentialPlanOptions { db: DB; emit?: Emit; } export type PlanRow = Plan; export type DayRow = PlanDay; /** * Enrollment row enriched with plan title / slug / duration when read via * `getActiveEnrollment`. Bare `select().from(userPlans)` returns just UserPlan. */ export interface UserPlanRow extends UserPlan { title?: string; slug?: string; durationDays?: number; } export interface DeliveryUser { whatsapp: string; planId: number; currentDay: number; title: string; durationDays: number; } export type AdvanceResult = { completed: true; day: number; } | { completed: false; nextDay: number; }; export declare class SequentialPlan { readonly db: DB; readonly emit: Emit | null; constructor({ db, emit }: SequentialPlanOptions); listActivePlans(): Promise; getPlanById(id: number): Promise; getPlanBySlug(slug: string): Promise; getDay(planId: number, day: number): Promise; enroll(whatsapp: string, planId: number): Promise; unenroll(whatsapp: string): Promise; getActiveEnrollment(whatsapp: string): Promise; /** * Defer the next delivery for this user's active plan enrollment until * `untilIso` (an ISO datetime string, e.g. `'2026-06-22 10:00:00'`). * * `usersForDelivery` skips users whose `snoozed_until` is in the future. * `markDelivered` clears the snooze, so a snooze never carries past the * first successful delivery — otherwise a one-day snooze becomes a * permanent pause. * * Idempotent. Returns true if the snooze was applied (active enrollment * found), false otherwise. */ snooze(whatsapp: string, planId: number, untilIso: string): Promise; /** Drop any active snooze. Returns true if a snooze was cleared. */ clearSnooze(whatsapp: string, planId: number): Promise; markDone(whatsapp: string, planId: number, day: number): Promise; skipDay(whatsapp: string, planId: number, day: number): Promise; private advance; autoAdvanceStale({ staleHours }?: { staleHours?: number; }): Promise; /** * Cron audience: users due for the next day's delivery. * * Gates: active enrollment, opt-in lead, open Meta message window, * `last_delivered_at` either NULL or older than `minIntervalHours` * (default 20h), AND no active snooze (`snoozed_until` either NULL or * already in the past). */ usersForDelivery({ limit, minIntervalHours }?: { limit?: number; minIntervalHours?: number; }): Promise; /** * Record a successful delivery for cadence + analytics. * * Writes the progress row (idempotent) AND bumps `user_plans.last_delivered_at` * to NOW so `usersForDelivery` won't redeliver until at least `minIntervalHours` * later. */ markDelivered(whatsapp: string, planId: number, day: number): Promise; } //# sourceMappingURL=sequential_plan.d.ts.map