Files
2026-05-27 23:29:53 +01:00

196 lines
5.7 KiB
Markdown

# OpenSpec Change: enterprise-delivery-tracking
- **Change ID**: enterprise-delivery-tracking
- **Package**: `@suaveplan/delivery-tracking`
- **Category**: universal
- **Target path**: `universal/email/delivery-tracking`
- **Status**: IN PROGRESS
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/delivery-tracking.md`
---
## Summary
Deliver `@suaveplan/delivery-tracking` on the genesis branch from scratch at enterprise quality. The package provides channel-agnostic delivery event tracking: a typed `DeliveryEvent` model covering all SMTP/webhook status transitions (`queued`, `sent`, `delivered`, `bounced`, `complained`, `unsubscribed`, `opened`, `clicked`), a `DeliveryTracker` facade with pluggable store adapters, webhook payload normalization for major ESP providers (generic interface — no provider SDK deps), bounce classification (hard/soft/transient), unsubscribe propagation to `@suaveplan/suppression`, and per-campaign/per-recipient analytics aggregation. Genesis adds structured errors, Zod schemas, OTel instrumentation, and property-based tests.
---
## Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/error` (`workspace:^`) | `DeliveryTrackingError`; `DLVR.*` error codes |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock`, `EventBusLike`, `StorageLike` |
| `@suaveplan/events` (`workspace:^`) | Delivery event propagation |
| `@suaveplan/suppression` (`workspace:^`) | Unsubscribe/bounce → suppression list write |
| `@suaveplan/crypto` (`workspace:^`) | HMAC-SHA256 webhook signature verification |
| `zod` (`catalog:forms`) | Schema validation |
| `@opentelemetry/api` (`catalog:observability`) | Spans + counters |
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`).
---
## Public API surface
### Core types
```ts
type DeliveryEventType =
| 'queued' | 'sent' | 'delivered' | 'deferred'
| 'bounced' | 'complained' | 'unsubscribed'
| 'opened' | 'clicked'
type BounceType = 'hard' | 'soft' | 'transient'
interface DeliveryEvent {
id: string
messageId: string
campaignId?: string
recipientEmail: string
type: DeliveryEventType
timestamp: Date
bounceType?: BounceType
bounceCode?: string
clickUrl?: string
userAgent?: string
ipAddress?: string
metadata: Record<string, unknown>
}
interface DeliveryAnalytics {
campaignId: string
total: number
sent: number
delivered: number
bounced: number
complained: number
unsubscribed: number
opened: number
clicked: number
deliveryRate: number
openRate: number
clickRate: number
bounceRate: number
}
```
### `DeliveryTracker`
```ts
interface DeliveryTracker {
record(event: DeliveryEventInput): Promise<DeliveryEvent>
get(eventId: string): Promise<DeliveryEvent | undefined>
listByMessage(messageId: string): Promise<DeliveryEvent[]>
listByRecipient(email: string, options?: ListOptions): Promise<PaginatedResult<DeliveryEvent>>
analytics(campaignId: string): Promise<DeliveryAnalytics>
}
function createMemoryDeliveryTracker(options?: MemoryTrackerOptions): DeliveryTracker
```
### Webhook normalizer
```ts
interface WebhookNormalizeOptions {
secret?: string // HMAC secret for signature verification
providerHint?: string // 'sendgrid' | 'mailgun' | 'ses' | 'postmark' | 'generic'
}
function normalizeWebhookPayload(
body: unknown,
headers: Record<string, string>,
options?: WebhookNormalizeOptions
): DeliveryEventInput[]
```
### Error codes
```ts
type DeliveryTrackingErrorCode =
| 'DLVR_EVENT_NOT_FOUND'
| 'DLVR_INVALID_PAYLOAD'
| 'DLVR_SIGNATURE_FAILED'
| 'DLVR_STORE_FAILED'
| 'DLVR_ANALYTICS_FAILED'
```
---
## Genesis additions
1. **`DeliveryTrackingError`** extends `SuaveplanError` with five stable codes.
2. **Zod schemas** for `DeliveryEvent`, `DeliveryAnalytics`, `WebhookNormalizeOptions`, all provider payload shapes.
3. **Suppression auto-propagation** — `bounced` (hard) and `complained` events automatically call `@suaveplan/suppression` to add the recipient.
4. **Webhook signature verification** — constant-time HMAC via `@suaveplan/crypto`; configurable clock-skew window.
5. **OTel instrumentation** — counter `delivery.event_total` with attribute `delivery.event_type`; histogram `delivery.analytics_duration_ms`.
6. **Property-based tests** — analytics rate calculations for arbitrary event sets.
---
## File layout
```text
universal/email/delivery-tracking/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
types/
common.ts
common.md
core/
delivery-tracker/
delivery-tracker.ts
delivery-tracker.md
delivery-tracker.types.ts
delivery-tracker.test.ts
index.ts
memory-tracker/
memory-tracker.ts
memory-tracker.md
memory-tracker.test.ts
index.ts
analytics/
analytics-aggregator/
analytics-aggregator.ts
analytics-aggregator.md
analytics-aggregator.test.ts
index.ts
webhook/
webhook-normalizer/
webhook-normalizer.ts
webhook-normalizer.md
webhook-normalizer.test.ts
index.ts
signature-verifier/
signature-verifier.ts
signature-verifier.md
signature-verifier.test.ts
index.ts
__benchmarks__/
delivery-tracking.bench.ts
tests/
integration.test.ts
property.test.ts
```
---
## Impact
**Downstream consumers**: `@suaveplan/campaigns`, any server-side email dispatch service.
**Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/suppression`, `@suaveplan/crypto`, `@suaveplan/pagination`.