Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
# Campaign action routes
## Purpose
This module owns the campaign action routes — state-machine
transitions (`/send`, `/pause`, `/cancel`), the `/duplicate`
shallow-copy endpoint, the `/test` ad-hoc send, and the two
delivery-tracking-backed read endpoints (`/stats` and `/events`).
State transitions are authoritative: invalid transitions raise
`CAMPAIGNS_API.INVALID_STATE_TRANSITION` mapped to HTTP 409 so clients
can drive UI affordances (enable/disable a "Pause" button) off a
stable code rather than parsing error messages.
The action routes consume the in-domain `CampaignStateMachine` from
`@suaveplan/campaigns`. Every transition decision flows through that
single source of truth — no string-comparisons of `status` are
duplicated at the route layer, eliminating drift between the engine
and the HTTP surface.
## Features
- 3 transition routes: `buildSendCampaignRoute`,
`buildPauseCampaignRoute`, `buildCancelCampaignRoute`.
- 1 clone route: `buildDuplicateCampaignRoute` — copies metadata and
audience/template config, resets stats, clears `sentAt`/`completedAt`.
- 1 test-send route: `buildTestSendRoute` — validates a recipient list
of 1–10 email addresses and echoes them back.
- 2 reporting routes consuming `DeliveryEventStore`:
`buildCampaignStatsRoute`, `buildCampaignEventsRoute`.
- Transition guard raises `INVALID_STATE_TRANSITION` (HTTP 409) when
`CampaignStateMachine.transition` rejects the (status, event) pair.
## Usage: Basic
```ts
import { buildSendCampaignRoute } from "@suaveplan/api-plugin-campaigns";
const entry = buildSendCampaignRoute();
app.post(entry.route.path, (c) => entry.handler(c));
```
## Usage: Advanced
```ts
import {
buildCampaignEventsRoute,
buildCampaignStatsRoute,
buildCancelCampaignRoute,
buildDuplicateCampaignRoute,
buildPauseCampaignRoute,
buildSendCampaignRoute,
buildTestSendRoute,
} from "@suaveplan/api-plugin-campaigns";
const actionEntries = [
buildSendCampaignRoute(),
buildPauseCampaignRoute(),
buildCancelCampaignRoute(),
buildDuplicateCampaignRoute(),
buildTestSendRoute(),
buildCampaignStatsRoute(),
buildCampaignEventsRoute(),
];
```
## API Reference
- `buildSendCampaignRoute(): PluginRouteEntry`
- `buildPauseCampaignRoute(): PluginRouteEntry`
- `buildCancelCampaignRoute(): PluginRouteEntry`
- `buildDuplicateCampaignRoute(): PluginRouteEntry`
- `buildTestSendRoute(): PluginRouteEntry`
- `buildCampaignStatsRoute(): PluginRouteEntry`
- `buildCampaignEventsRoute(): PluginRouteEntry`
- `TestSendSchema`, `EventQuerySchema`, `DeliveryStatsResponseSchema`
— Zod schemas re-exported for documentation generators and tests.
## Implementation Notes
`applyTransition` wraps `CampaignStateMachine.transition` in a
`CampaignsApiError` so a failed transition surfaces as
`CAMPAIGNS_API.INVALID_STATE_TRANSITION` rather than the
domain-package's `CampaignsError` (which carries an unrelated
namespace). Wrapping at this seam keeps the route layer's error
vocabulary self-contained: every error a client sees from the
campaigns plugin carries a `CAMPAIGNS_API.*` code.
The `/duplicate` handler intentionally copies all configuration
fields (template, audience, throttle, abTestConfig) but resets the
volatile fields (`id`, `status`, `stats`, `sentAt`, `completedAt`,
`createdAt`, `updatedAt`). The `(copy)` suffix is appended to the
name so the duplicate is visually distinct in listing UIs without
forcing the operator to provide a new name up front. The route
handlers' OTel attributes record `route` plus `outcome` so an
operator can chart per-route success/error counters without parsing
log messages.