49 lines
3.5 KiB
Markdown
49 lines
3.5 KiB
Markdown
# 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**: `CampaignEngine` with `create`, `schedule`, `send`, `pause`, `resume`, `cancel`, `complete`, `fail`, `duplicate`, `updateStats`, `getById`, `list`. Injectable `Clock`/`IdGenerator`/`LoggerLike`.
|
|
- **State machine**: `CampaignStateMachine` with nine canonical transitions, `transition`/`canTransition`/`getAvailableEvents`.
|
|
- **Batch planner**: `BatchPlanner` with `planBatches` and `calculateTotalDuration`, throttle-aware delay calculation, injectable `Clock`.
|
|
- **Workflow**: `CampaignWorkflow` with `buildSendPipeline` (6 steps) and `run` (sequential executor that wraps step errors in `CampaignsError(WORKFLOW_STEP_FAILED)`).
|
|
- **Store**: `CampaignStore` interface + `InMemoryCampaignStore` reference implementation.
|
|
- **Errors**: `CAMPAIGNS_CODES` (8 codes) + `CampaignsError` with 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 of `core/<feature>/` nested under upstream's `core/`. Aligns with the 3-level package layout rule.
|
|
- Errors thrown by every public method go through `CampaignsError` + `CAMPAIGNS_CODES` instead of raw `Error`.
|
|
- Zod-validated boundaries on `CampaignEngineOptions`, `CreateCampaignInput`, `BatchPlannerOptions`, `BatchConfig`, `ThrottleConfig`.
|
|
- OTel instrumentation added throughout (5 counters + 2 histograms).
|
|
- Injectable `Clock`, `IdGenerator`, `LoggerLike` on engine; injectable `Clock` on batch planner and in-memory store.
|
|
- `CampaignWorkflow.run` added 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` — emits `dist/`.
|
|
- `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.
|