# `repositories/suppression/suppression-internals` ## Purpose This module owns the validation and mapping primitives shared by the two `CampaignSuppressionStore` implementations — `ReasonSchema`, `CreateInputSchema`, `QuerySchema`, the `requireTenant` guard, the `rowToEntry` row mapper, and the `validateCreate` wrapper. Keeping these helpers in a dedicated sibling module is what allows both the SurrealDB implementation (`./suppression.ts`) and the in-memory reference (`./suppression-memory.ts`) to stay under the 300-line hard cap without duplicating their entry-validation logic. ## Features - `ReasonSchema` — Zod enum guard over the five canonical `CampaignSuppressionReason` values (`unsubscribe`, `bounce`, `complaint`, `manual`, `compliance`). - `CreateInputSchema` / `QuerySchema` — strict Zod object guards for the two public input shapes. - `requireTenant(tenantId)` — boundary check that throws `CAMPAIGNS_API.TENANT_REQUIRED` for an empty string. - `validateCreate(input)` — parses with `CreateInputSchema` and throws `CAMPAIGNS_API.INVALID_INPUT` on failure with the underlying `ZodError` attached as `cause`. - `rowToEntry(row)` — maps `CampaignSuppressionRow` ↔ `CampaignSuppressionEntry` with `Date` normalisation and `null` → `undefined` collapsing. ## Usage: Basic ```ts import { validateCreate, requireTenant } from "./suppression-internals.js"; requireTenant("tenant-1"); const data = validateCreate({ tenantId: "tenant-1", channel: "email", channelValue: "user@example.com", reason: "unsubscribe", }); ``` ## Usage: Advanced ```ts import { QuerySchema, rowToEntry } from "./suppression-internals.js"; const parsed = QuerySchema.safeParse({ tenantId: "t1", channel: "sms", limit: 25, }); if (!parsed.success) { // handle ZodError without going through the repository } const entry = rowToEntry({ id: "s1", tenant_id: "t1", channel: "sms", channel_value: "+15551234567", reason: "complaint", metadata: null, created_at: "2026-05-24T00:00:00.000Z", }); ``` ## API Reference - `const ReasonSchema: z.ZodEnum` - `const CreateInputSchema: z.ZodObject` - `const QuerySchema: z.ZodObject` - `function requireTenant(tenantId: string): void` — throws on empty. - `function rowToEntry(row: CampaignSuppressionRow): CampaignSuppressionEntry` - `function validateCreate(input: CreateCampaignSuppressionInput): CreateCampaignSuppressionInput` ## Implementation Notes `CreateInputSchema` and `QuerySchema` use `.strict()` so unknown keys fail validation rather than silently flowing through to SurrealDB — this keeps the repository contract honest against evolving HTTP payloads and stops typos from becoming silent feature requests. The schemas live here (not in `../../schemas/`) because they are implementation details of the suppression repository, not part of the plugin's public option surface. The plugin's `options` schema in `../../schemas/options.ts` is the user-facing configuration contract; the repository schemas are private guards at a different boundary.