import { RetryStrategy as RetryStrategy_dwxcm3 } from "@workkit/errors"; type ChannelName = string; type DeliveryStatus = "queued" | "sent" | "delivered" | "read" | "failed" | "bounced" | "skipped" | "duplicate"; type DispatchMode = "live" | "test"; interface ChannelTemplate

{ /** * The template body. Adapters interpret it differently: * - `email`: `string` HTML, or a React Email element rendered via the * optional `@react-email/render` peer. * - `whatsapp`: typically a template id or full template object. * - others: as the adapter documents. * * Typed as `unknown` so adapters can accept their own narrower shape * without forcing every other adapter to deal with it. */ template?: unknown; variables?: (payload: P) => Record; props?: (payload: P) => unknown; attachments?: (payload: P) => Array<{ filename?: string; r2Key: string; type?: string; }>; title?: (payload: P) => string; body?: (payload: P) => string; deepLink?: (payload: P) => string; } interface AdapterSendArgs

{ userId: string; notificationId: string; channel: ChannelName; address: string; template: ChannelTemplate

; payload: P; deliveryId: string; mode: DispatchMode; } interface AdapterSendResult { providerId?: string; status: Exclude; error?: string; /** * Optional. Whether the failure should be retried. Adapters that catch * a `WorkkitError` populate this from `WorkkitError.retryable`; other * adapters can leave it undefined. See ADR-002. */ retryable?: boolean; /** * Optional. Recommended backoff strategy for the failure. Adapters that * catch a `WorkkitError` populate this from `WorkkitError.retryStrategy`. * Consumers / queue policy can opt into reading this field; today it is * not yet acted on by `createNotifyConsumer` (see ADR-002 follow-ups). */ retryStrategy?: RetryStrategy_dwxcm3; } interface WebhookEvent { channel: ChannelName; providerId: string; status: Extract; at: number; raw?: unknown; } interface Adapter

{ send(args: AdapterSendArgs

): Promise; parseWebhook?(req: Request): Promise; verifySignature?(req: Request, secret: string): Promise; } /** Minimal D1-shape we depend on. Matches @cloudflare/workers-types' D1Database. */ interface NotifyD1 { prepare(query: string): NotifyPreparedStatement; batch(statements: NotifyPreparedStatement[]): Promise; } interface NotifyPreparedStatement { bind(...values: unknown[]): NotifyPreparedStatement; first>(): Promise; all>(): Promise<{ results?: T[]; }>; run(): Promise<{ success?: boolean; meta?: { changes?: number; }; }>; } interface SseSubscriber { userId: string; close(): void; push(payload: string): void; } /** * In-memory SSE registry. Single Worker isolate scope — multi-isolate * fan-out belongs to a future Durable-Object-backed adapter. */ declare class SseRegistry { private map; add(sub: SseSubscriber): void; remove(sub: SseSubscriber): void; count(userId: string): number; push(userId: string, event: string): void; disconnectUser(userId: string): void; } interface SseHandlerOptions { db: NotifyD1; registry: SseRegistry; auth: (req: Request) => Promise<{ userId: string; } | null>; originAllowlist?: ReadonlyArray; maxConnPerUser?: number; heartbeatMs?: number; } /** * Construct an SSE handler. `(req: Request) => Promise`. Auth is * **required** at construction — there is no anonymous default. Origin * allowlist defends against cross-origin EventSource scraping when set. */ declare function createSseHandler(opts: SseHandlerOptions): (req: Request) => Promise; interface InAppPayload { [key: string]: unknown; } interface InAppAdapterOptions { db: NotifyD1; registry?: SseRegistry; /** Maximum body length in characters. Default 2000. */ maxBodyChars?: number; /** Allowed deep-link URL schemes. Default `["https:"]`. */ allowedSchemes?: ReadonlyArray; } declare function inAppAdapter(options: InAppAdapterOptions): Adapter; interface InAppNotificationRow { id: string; notificationId: string; title: string; body: string; deepLink: string | null; metadata: Record | null; createdAt: number; readAt: number | null; dismissedAt: number | null; } interface FeedOptions { userId: string; cursor?: string | null; limit?: number; includeRead?: boolean; includeDismissed?: boolean; } interface FeedPage { items: InAppNotificationRow[]; nextCursor: string | null; } declare function feed(db: NotifyD1, opts: FeedOptions): Promise; interface MarkReadOptions { userId: string; ids?: string[]; markAll?: boolean; } declare function markRead(db: NotifyD1, opts: MarkReadOptions, now?: number): Promise<{ updated: number; }>; declare function dismiss(db: NotifyD1, opts: { userId: string; ids: string[]; }, now?: number): Promise<{ updated: number; }>; declare function unreadCount(db: NotifyD1, userId: string): Promise; interface SafeLinkOptions { allowedSchemes?: ReadonlyArray; /** * Allow relative paths that start with `/` (e.g. `/briefs/r1`). Default true. * Protocol-relative URLs (`//host/...`) are always rejected; bare paths * without a leading `/` are not accepted either. */ allowRelative?: boolean; } /** * Sanity-check a deep-link URL: only schemes in the allowlist (default * `https:`) are permitted; relative paths (`/foo`) are allowed by default. * `javascript:`, `data:`, `file:` always rejected. * * Returns the input unchanged on success; throws `UnsafeLinkError` otherwise. */ declare function safeLink(value: string, options?: SafeLinkOptions): string; interface ForgetInAppResult { rowsDeleted: number; } /** * Cascade-delete a user's in-app notification feed AND drop any active * SSE subscriptions for that user. Call alongside `@workkit/notify`'s * `forgetUser` for the full GDPR/DPDP cascade. */ declare function forgetInAppUser(db: NotifyD1, userId: string, registry?: SseRegistry): Promise; /** * D1 schema additions for `@workkit/notify/inapp`. Run once during your * migration setup, alongside `ALL_MIGRATIONS` from `@workkit/notify`. */ declare const INAPP_MIGRATION_SQL: string; import { ValidationError } from "@workkit/errors"; declare class BodyTooLongError extends ValidationError { constructor(actual: number, cap: number); } declare class UnsafeLinkError extends ValidationError { constructor(value: string, reason: string); } export { unreadCount, safeLink, markRead, inAppAdapter, forgetInAppUser, feed, dismiss, createSseHandler, UnsafeLinkError, SseSubscriber, SseRegistry, SseHandlerOptions, SafeLinkOptions, MarkReadOptions, InAppPayload, InAppNotificationRow, InAppAdapterOptions, INAPP_MIGRATION_SQL, ForgetInAppResult, FeedPage, FeedOptions, BodyTooLongError };