3.5 KiB
Change: campaigns
- Change ID: campaigns
- Package:
@suaveplan/campaigns - Path:
packages/communication/emails/campaigns/ - Tier: universal
- Date: 2026-05-24
Summary
Land @suaveplan/campaigns on genesis at packages/communication/emails/campaigns/. The package combines audience targeting, template binding, channel dispatch, and schedule management into managed send workflows. It exposes a state-machine-driven CampaignEngine, a throttle-aware BatchPlanner, a composable six-step CampaignWorkflow, and a pluggable CampaignStore interface with an in-memory reference implementation.
Why
@suaveplan/campaigns is the orchestration layer above @suaveplan/emailer, @suaveplan/email-templates, @suaveplan/suppression, and @suaveplan/contacts. Without it, callers have to hand-roll lifecycle FSMs and throttle math, which has caused drift between historical implementations.
Scope
- Engine:
CampaignEnginewithcreate,schedule,send,pause,resume,cancel,complete,fail,duplicate,updateStats,getById,list. InjectableClock/IdGenerator/LoggerLike. - State machine:
CampaignStateMachinewith nine canonical transitions,transition/canTransition/getAvailableEvents. - Batch planner:
BatchPlannerwithplanBatchesandcalculateTotalDuration, throttle-aware delay calculation, injectableClock. - Workflow:
CampaignWorkflowwithbuildSendPipeline(6 steps) andrun(sequential executor that wraps step errors inCampaignsError(WORKFLOW_STEP_FAILED)). - Store:
CampaignStoreinterface +InMemoryCampaignStorereference implementation. - Errors:
CAMPAIGNS_CODES(8 codes) +CampaignsErrorwith explicit public ctor. - Telemetry: 5 counters + 2 histograms via
@opentelemetry/api. - Types: 13 exported types/interfaces including
Campaign,CampaignStatus,CampaignType,AudienceConfig,ThrottleConfig,ABTestConfig,CampaignStats,CreateCampaignInput, etc.
Out of scope (deliberate)
None. The package lands at full functional completeness. Storage adapters (SurrealDB, Postgres) and the HTTP plugin live in sibling packages and are tracked by their own OpenSpec changes.
Deviations from upstream
- Single-folder feature layout (
engine/,state-machine/,batch-planner/,workflow/,store/,types/,error-codes/,telemetry/) instead ofcore/<feature>/nested under upstream'score/. Aligns with the 3-level package layout rule. - Errors thrown by every public method go through
CampaignsError+CAMPAIGNS_CODESinstead of rawError. - Zod-validated boundaries on
CampaignEngineOptions,CreateCampaignInput,BatchPlannerOptions,BatchConfig,ThrottleConfig. - OTel instrumentation added throughout (5 counters + 2 histograms).
- Injectable
Clock,IdGenerator,LoggerLikeon engine; injectableClockon batch planner and in-memory store. CampaignWorkflow.runadded so the package owns its own sequential executor.
Verified by
bunx turbo lint typecheck test --filter=@suaveplan/campaigns— green.bunx turbo test:coverage --filter=@suaveplan/campaigns— 100/100/100/100 (statements/branches/functions/lines).bunx turbo build --filter=@suaveplan/campaigns— emitsdist/.bun scripts/lint/check-feature-layout.ts— no campaigns violations.bun scripts/lint/check-test-runner-imports.ts— no campaigns violations.grep -rn "biome-ignore\|TODO\|FIXME\|XXX" packages/communication/emails/campaigns/src/— empty.