notifkit 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +179 -152
  2. package/dist/index.d.mts +193 -129
  3. package/dist/index.d.mts.map +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/{main-DtHWhueo.mjs → main-40zwq6b0.mjs} +28 -3
  7. package/dist/{main-DtHWhueo.mjs.map → main-40zwq6b0.mjs.map} +1 -1
  8. package/dist/{main-DyfbnJc3.mjs → main-BFre2-HQ.mjs} +2 -2
  9. package/dist/{main-DyfbnJc3.mjs.map → main-BFre2-HQ.mjs.map} +1 -1
  10. package/dist/{main-CAH0_Q6d.mjs → main-BNJtzY61.mjs} +3 -3
  11. package/dist/main-BNJtzY61.mjs.map +1 -0
  12. package/dist/{main-B561M1d3.mjs → main-BOPMYqsW.mjs} +2 -2
  13. package/dist/{main-B561M1d3.mjs.map → main-BOPMYqsW.mjs.map} +1 -1
  14. package/dist/{main-CCfc45ev.mjs → main-CiigNpsP.mjs} +7 -4
  15. package/dist/main-CiigNpsP.mjs.map +1 -0
  16. package/dist/{main-Ce9dcrsg.mjs → main-DeNFQ-UL.mjs} +6 -3
  17. package/dist/{main-Ce9dcrsg.mjs.map → main-DeNFQ-UL.mjs.map} +1 -1
  18. package/dist/{main-B-jwm8ED.mjs → main-DmCPcxOc.mjs} +2 -2
  19. package/dist/{main-B-jwm8ED.mjs.map → main-DmCPcxOc.mjs.map} +1 -1
  20. package/dist/{main-C45e7grq.mjs → main-DvgJSm11.mjs} +2 -2
  21. package/dist/{main-C45e7grq.mjs.map → main-DvgJSm11.mjs.map} +1 -1
  22. package/dist/{src-C-PfEDMY.mjs → src-vG79L-8m.mjs} +57 -26
  23. package/dist/src-vG79L-8m.mjs.map +1 -0
  24. package/drizzle/0002_wide_colleen_wing.sql +2 -0
  25. package/drizzle/0003_skinny_daimon_hellstrom.sql +1 -0
  26. package/drizzle/0004_pretty_bruce_banner.sql +1 -0
  27. package/drizzle/meta/0002_snapshot.json +1460 -0
  28. package/drizzle/meta/0003_snapshot.json +1460 -0
  29. package/drizzle/meta/0004_snapshot.json +1470 -0
  30. package/drizzle/meta/_journal.json +21 -0
  31. package/package.json +7 -1
  32. package/scripts/create-project.mjs +61 -0
  33. package/src/client.ts +412 -0
  34. package/src/config/index.ts +107 -0
  35. package/src/contracts/common.ts +28 -0
  36. package/src/contracts/envelope.ts +31 -0
  37. package/src/contracts/events/notification-ai-pending.ts +18 -0
  38. package/src/contracts/events/notification-canceled.ts +7 -0
  39. package/src/contracts/events/notification-created.ts +14 -0
  40. package/src/contracts/events/notification-delivered.ts +17 -0
  41. package/src/contracts/events/notification-dispatched.ts +45 -0
  42. package/src/contracts/events/notification-enriched.ts +46 -0
  43. package/src/contracts/events/notification-failed.ts +19 -0
  44. package/src/contracts/events/notification-requested.ts +36 -0
  45. package/src/contracts/events/notification-scheduled.ts +9 -0
  46. package/src/contracts/events/notification-skipped.ts +9 -0
  47. package/src/contracts/helpers.ts +21 -0
  48. package/src/contracts/index.ts +46 -0
  49. package/src/contracts/metadata.ts +10 -0
  50. package/src/contracts/registry.ts +88 -0
  51. package/src/contracts/sdk.ts +242 -0
  52. package/src/contracts/streams.ts +62 -0
  53. package/src/db/index.ts +69 -0
  54. package/src/db/schema.ts +412 -0
  55. package/src/idempotency/index.ts +50 -0
  56. package/src/index.ts +19 -0
  57. package/src/logger/index.ts +60 -0
  58. package/src/metrics/index.ts +53 -0
  59. package/src/queue/index.ts +501 -0
  60. package/src/rate-limiter/index.ts +210 -0
  61. package/src/redis/index.ts +89 -0
  62. package/src/repositories/index.ts +1246 -0
  63. package/src/server.ts +277 -0
  64. package/src/services/ai/main.ts +404 -0
  65. package/src/services/api/handlers.ts +1734 -0
  66. package/src/services/api/http.ts +64 -0
  67. package/src/services/api/main.ts +693 -0
  68. package/src/services/api/router.ts +82 -0
  69. package/src/services/delivery/main.ts +842 -0
  70. package/src/services/delivery/throttle.ts +71 -0
  71. package/src/services/engine/main.ts +827 -0
  72. package/src/services/enricher/main.ts +594 -0
  73. package/src/services/events/main.ts +365 -0
  74. package/src/services/scheduler/main.ts +319 -0
  75. package/src/services/workflow/main.ts +627 -0
  76. package/src/shared/batch-processor.ts +67 -0
  77. package/src/shared/cache.ts +47 -0
  78. package/src/shared/circuit-breaker.ts +74 -0
  79. package/src/shared/dataloader.ts +41 -0
  80. package/src/shared/events.ts +3 -0
  81. package/src/shared/index.ts +39 -0
  82. package/src/shared/semaphore.ts +33 -0
  83. package/src/shared/utils.ts +64 -0
  84. package/src/templates/cache.ts +32 -0
  85. package/src/templates/index.ts +69 -0
  86. package/src/templates/render.ts +128 -0
  87. package/src/transport/index.ts +96 -0
  88. package/src/unsubscribe/index.ts +127 -0
  89. package/src/workers/health.ts +31 -0
  90. package/src/workers/index.ts +266 -0
  91. package/src/workflows/index.ts +2 -0
  92. package/src/workflows/registry.ts +21 -0
  93. package/src/workflows/sdk.ts +106 -0
  94. package/dist/main-CAH0_Q6d.mjs.map +0 -1
  95. package/dist/main-CCfc45ev.mjs.map +0 -1
  96. package/dist/src-C-PfEDMY.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"src-C-PfEDMY.mjs","names":["randomUUID"],"sources":["../src/config/index.ts","../src/contracts/common.ts","../src/contracts/metadata.ts","../src/contracts/registry.ts","../src/contracts/envelope.ts","../src/contracts/streams.ts","../src/contracts/helpers.ts","../src/contracts/sdk.ts","../src/contracts/events/notification-requested.ts","../src/contracts/events/notification-created.ts","../src/contracts/events/notification-enriched.ts","../src/contracts/events/notification-scheduled.ts","../src/contracts/events/notification-dispatched.ts","../src/contracts/events/notification-delivered.ts","../src/contracts/events/notification-failed.ts","../src/contracts/events/notification-skipped.ts","../src/contracts/events/notification-canceled.ts","../src/contracts/events/notification-ai-pending.ts","../src/contracts/index.ts","../src/db/schema.ts","../src/db/index.ts","../src/idempotency/index.ts","../src/logger/index.ts","../src/metrics/index.ts","../src/queue/index.ts","../src/shared/events.ts","../src/shared/cache.ts","../src/shared/utils.ts","../src/shared/semaphore.ts","../src/shared/batch-processor.ts","../src/shared/circuit-breaker.ts","../src/shared/dataloader.ts","../src/shared/index.ts","../src/rate-limiter/index.ts","../src/redis/index.ts","../src/repositories/index.ts","../src/templates/render.ts","../src/templates/cache.ts","../src/templates/index.ts","../src/unsubscribe/index.ts","../src/workers/health.ts","../src/workers/index.ts","../src/workflows/sdk.ts","../src/workflows/registry.ts","../src/client.ts","../src/server.ts"],"sourcesContent":["import { config } from \"dotenv\";\nimport { resolve } from \"node:path\";\nimport { z, type ZodTypeAny } from \"zod\";\nimport type { LanguageModel } from \"ai\";\n\nimport { ValidationError } from \"@/index.js\";\n\nexport function loadEnv(path?: string): void {\n const envPath = path ?? resolve(process.cwd(), \".env\");\n config({ path: envPath, override: false });\n}\n\nexport function parseConfig<TSchema extends ZodTypeAny>(\n schema: TSchema,\n data: unknown,\n): z.output<TSchema> {\n const result = schema.safeParse(data);\n\n if (!result.success) {\n const fields: Record<string, string[]> = {};\n\n for (const issue of result.error.issues) {\n const key = issue.path.join(\".\");\n fields[key] ??= [];\n fields[key].push(issue.message);\n }\n\n throw new ValidationError(\"Configuration validation failed\", fields);\n }\n\n return result.data;\n}\n\nexport const baseConfigSchema = z.object({\n NODE_ENV: z.enum([\"development\", \"test\", \"production\"]).default(\"development\"),\n LOG_LEVEL: z.enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\", \"silent\"]).default(\"info\"),\n PORT: z.coerce.number().int().min(1).max(65_535).default(3000),\n HOST: z.string().default(\"127.0.0.1\"),\n REDIS_URL: z.string().url().default(\"redis://localhost:6379\"),\n DATABASE_URL: z.string().url().default(\"postgres://platform:platform@localhost:5432/notifkit\"),\n ADMIN_API_KEY: z.string().optional(),\n WORKER_CONCURRENCY: z.coerce.number().int().min(1).default(10),\n QUEUE_MAX_LEN: z.coerce.number().int().min(1).default(10000000),\n DB_MAX_CONNECTIONS: z.coerce.number().int().min(1).default(2),\n LOG_FLUSH_INTERVAL_MS: z.coerce.number().int().min(50).default(500),\n LOG_BUFFER_MAX_SIZE: z.coerce.number().int().min(100).default(5000),\n SEGMENT_MAX_USERS: z.coerce.number().int().min(1).default(10000),\n /**\n * Externally reachable base URL of this API. Unsubscribe links are built from\n * it, so it must be what an inbox can actually reach — not `HOST`/`PORT`,\n * which describe the bind address behind your proxy.\n */\n PUBLIC_URL: z.string().url().optional(),\n /**\n * Signing key for unsubscribe tokens. Rotating it invalidates every\n * unsubscribe link already sitting in someone's inbox, so treat it as\n * permanent: a dead link means the recipient reaches for the spam button\n * instead, which costs far more than the key ever protected.\n */\n UNSUBSCRIBE_SECRET: z.string().min(16).optional(),\n});\n\nexport type BaseConfig = z.infer<typeof baseConfigSchema>;\n\nlet globalConfig: BaseConfig | null = null;\n\nexport function setGlobalConfig(config: BaseConfig) {\n globalConfig = config;\n}\n\nexport function readBaseConfig(data: NodeJS.ProcessEnv = process.env): BaseConfig {\n if (globalConfig) {\n return globalConfig;\n }\n return parseConfig(baseConfigSchema, data);\n}\n\nexport interface RateLimitConfig {\n limit: number;\n windowSeconds: number;\n}\n\nexport interface AiConfig {\n aiModel?: LanguageModel;\n /** Hard cap on generated tokens per prompt. Bounds cost and email size. */\n maxOutputTokens?: number;\n /** Wall-clock budget for a single generation before it is aborted. */\n timeoutMs?: number;\n /** Max prompts executed for one notification. */\n maxPromptsPerNotification?: number;\n}\n\nexport const AI_DEFAULTS = {\n maxOutputTokens: 1_000,\n timeoutMs: 30_000,\n maxPromptsPerNotification: 5,\n} as const;\n\nlet globalAiConfig: AiConfig = {};\n\nexport function setAiConfig(config: AiConfig) {\n globalAiConfig = config;\n}\n\nexport function getAiConfig(): AiConfig {\n return globalAiConfig;\n}\n","import { z } from \"zod\";\n\nexport const NotificationChannelSchema = z.enum([\"email\", \"sms\", \"push\", \"webhook\", \"in-app\"]);\nexport type NotificationChannel = z.infer<typeof NotificationChannelSchema>;\n\nexport const NotificationPrioritySchema = z.enum([\"low\", \"normal\", \"high\", \"critical\"]);\nexport type NotificationPriority = z.infer<typeof NotificationPrioritySchema>;\n\nexport const NotificationStatusSchema = z.enum([\n \"pending\",\n \"queued\",\n \"processing\",\n \"delivered\",\n \"failed\",\n \"bounced\",\n \"suppressed\",\n]);\nexport type NotificationStatus = z.infer<typeof NotificationStatusSchema>;\n","import { z } from \"zod\";\n\nexport const EventMetadataSchema = z.object({\n traceId: z.string(),\n source: z.string(),\n retryCount: z.number().int().nonnegative().default(0),\n correlationId: z.string().optional(),\n causationId: z.string().optional(),\n});\nexport type EventMetadata = z.infer<typeof EventMetadataSchema>;\n","import { z, type ZodTypeAny, type ZodError } from \"zod\";\nimport type { NotificationRequestedPayload } from \"./events/notification-requested.js\";\nimport type { NotificationCreatedPayload } from \"./events/notification-created.js\";\nimport type { NotificationEnrichedPayload } from \"./events/notification-enriched.js\";\nimport type { NotificationScheduledPayload } from \"./events/notification-scheduled.js\";\nimport type { NotificationDispatchedPayload } from \"./events/notification-dispatched.js\";\nimport type { NotificationDeliveredPayload } from \"./events/notification-delivered.js\";\nimport type { NotificationFailedPayload } from \"./events/notification-failed.js\";\nimport type { NotificationSkippedPayload } from \"./events/notification-skipped.js\";\nimport type { NotificationCanceledPayload } from \"./events/notification-canceled.js\";\nimport type { NotificationAiPendingPayload } from \"./events/notification-ai-pending.js\";\n/**\n * Built-in event payload types.\n * External packages extend this interface via declaration merging:\n *\n * declare module \"../index.js\" {\n * interface EventPayloadMap {\n * \"my.custom.event\": { field: string };\n * }\n * }\n *\n * Then call registry.define(\"my.custom.event\", MySchema) at app startup.\n */\nexport interface EventPayloadMap {\n \"notification.requested\": NotificationRequestedPayload;\n \"notification.created\": NotificationCreatedPayload;\n \"notification.enriched\": NotificationEnrichedPayload;\n \"notification.scheduled\": NotificationScheduledPayload;\n \"notification.dispatched\": NotificationDispatchedPayload;\n \"notification.delivered\": NotificationDeliveredPayload;\n \"notification.failed\": NotificationFailedPayload;\n \"notification.skipped\": NotificationSkippedPayload;\n \"notification.canceled\": NotificationCanceledPayload;\n \"notification.ai_pending\": NotificationAiPendingPayload;\n}\n\nexport type KnownEventType = keyof EventPayloadMap & string;\n\nexport type ParseResult<T> = { success: true; data: T } | { success: false; error: ZodError };\n\nexport class EventRegistry {\n private readonly schemas = new Map<string, ZodTypeAny>();\n\n define(type: string, schema: ZodTypeAny): void {\n if (this.schemas.has(type)) {\n throw new Error(`Event type \"${type}\" is already registered`);\n }\n this.schemas.set(type, schema);\n }\n\n getSchema(type: string): ZodTypeAny | undefined {\n return this.schemas.get(type);\n }\n\n has(type: string): boolean {\n return this.schemas.has(type);\n }\n\n types(): string[] {\n return [...this.schemas.keys()];\n }\n\n parsePayload<K extends KnownEventType>(type: K, payload: unknown): EventPayloadMap[K] {\n const schema = this.schemas.get(type);\n if (!schema) throw new Error(`Unknown event type: \"${type}\"`);\n return schema.parse(payload) as EventPayloadMap[K];\n }\n\n safeParsePayload<K extends KnownEventType>(\n type: K,\n payload: unknown,\n ): ParseResult<EventPayloadMap[K]> {\n const schema = this.schemas.get(type);\n if (!schema) {\n return {\n success: false,\n error: new z.ZodError([\n { code: \"custom\", message: `Unknown event type: \"${type}\"`, path: [] },\n ]),\n };\n }\n const result = schema.safeParse(payload);\n if (result.success) return { success: true, data: result.data as EventPayloadMap[K] };\n return { success: false, error: result.error };\n }\n}\n\nexport const registry = new EventRegistry();\n","import { z } from \"zod\";\nimport { EventMetadataSchema, type EventMetadata } from \"./metadata.js\";\nimport type { EventPayloadMap, KnownEventType } from \"./registry.js\";\n\n/**\n * Wire format written to Redis Streams. The payload field is an opaque record\n * at the envelope level — use registry.parsePayload() to get a typed payload.\n */\nexport const EventEnvelopeSchema = z.object({\n id: z.string().uuid(),\n type: z.string(),\n timestamp: z.string().datetime(),\n payload: z.record(z.string(), z.unknown()),\n metadata: EventMetadataSchema,\n});\nexport type EventEnvelope = z.infer<typeof EventEnvelopeSchema>;\n\n/** Typed envelope where the payload is strongly typed via EventPayloadMap. */\nexport type TypedEventEnvelope<K extends KnownEventType> = Omit<\n EventEnvelope,\n \"type\" | \"payload\"\n> & {\n type: K;\n payload: EventPayloadMap[K];\n};\n\n// Backward-compatible alias used by the queue / workers packages.\nexport type StreamEvent = EventEnvelope;\nexport const StreamEventSchema = EventEnvelopeSchema;\nexport type StreamEventMetadata = EventMetadata;\nexport const StreamEventMetadataSchema = EventMetadataSchema;\n","export const STREAMS = {\n INBOUND_CRITICAL: \"notifkit:stream:inbound:critical\",\n INBOUND_NORMAL: \"notifkit:stream:inbound:normal\",\n INBOUND_LOW: \"notifkit:stream:inbound:low\",\n ENRICHED_CRITICAL: \"notifkit:stream:enriched:critical\",\n ENRICHED_NORMAL: \"notifkit:stream:enriched:normal\",\n ENRICHED_LOW: \"notifkit:stream:enriched:low\",\n AI_PENDING: \"notifkit:stream:ai:pending\",\n SCHEDULED: \"notifkit:stream:scheduled\",\n OUTBOUND_CRITICAL: \"notifkit:stream:outbound:critical\",\n OUTBOUND_NORMAL: \"notifkit:stream:outbound:normal\",\n OUTBOUND_LOW: \"notifkit:stream:outbound:low\",\n DEAD_LETTER: \"notifkit:stream:dlq\",\n WORKFLOW_INBOUND: \"notifkit:stream:workflow:inbound\",\n EVENTS_INBOUND: \"notifkit:stream:events:inbound\",\n} as const;\n\nexport const INBOUND_STREAMS = [\n STREAMS.INBOUND_CRITICAL,\n STREAMS.INBOUND_NORMAL,\n STREAMS.INBOUND_LOW,\n] as const;\n\nexport const ENRICHED_STREAMS = [\n STREAMS.ENRICHED_CRITICAL,\n STREAMS.ENRICHED_NORMAL,\n STREAMS.ENRICHED_LOW,\n] as const;\n\nexport const OUTBOUND_STREAMS = [\n STREAMS.OUTBOUND_CRITICAL,\n STREAMS.OUTBOUND_NORMAL,\n STREAMS.OUTBOUND_LOW,\n] as const;\nexport type StreamName = (typeof STREAMS)[keyof typeof STREAMS];\n\n/**\n * Redis pub/sub channels used to drop cached state across every process.\n *\n * Each cache also carries a TTL, so these only shorten the window in which a\n * worker can act on stale data — they are not the sole correctness mechanism.\n */\nexport const PUBSUB_CHANNELS = {\n /** Payload: `{projectId}:{templateId}`. */\n TEMPLATE_INVALIDATED: \"template.invalidated\",\n /** Payload: `{projectId}`. Published when project settings change. */\n PROJECT_INVALIDATED: \"project.invalidated\",\n /** Payload: a token hash, or `*` for the whole cache. */\n API_KEY_INVALIDATED: \"apikey.invalidated\",\n} as const;\nexport type PubSubChannel = (typeof PUBSUB_CHANNELS)[keyof typeof PUBSUB_CHANNELS];\n\nexport const CONSUMER_GROUPS = {\n ENRICHER: \"notifkit:group:enricher\",\n ENGINE: \"notifkit:group:engine\",\n DELIVERY: \"notifkit:group:delivery\",\n SCHEDULER: \"notifkit:group:scheduler\",\n AI: \"notifkit:group:ai\",\n WORKFLOW: \"notifkit:group:workflow\",\n EVENTS: \"notifkit:group:events\",\n} as const;\nexport type ConsumerGroup = (typeof CONSUMER_GROUPS)[keyof typeof CONSUMER_GROUPS];\n","import { randomUUID } from \"node:crypto\";\nimport { type EventEnvelope } from \"./envelope.js\";\n\n// Legacy factory helpers — backward compatible with the queue package's publish() API,\n// which accepts Omit<StreamEvent, \"id\" | \"timestamp\">.\nexport function buildStreamEvent(\n type: string,\n payload: Record<string, unknown>,\n source: string,\n traceId?: string,\n): Omit<EventEnvelope, \"id\" | \"timestamp\"> {\n return {\n type,\n payload,\n metadata: {\n traceId: traceId ?? randomUUID(),\n source,\n retryCount: 0,\n },\n };\n}\n","import { z } from \"zod\";\nimport { NotificationChannelSchema, NotificationPrioritySchema } from \"./common.js\";\n\n// ─── Preferences ──────────────────────────────────────────────────────────────\n//\n// Preferences are stored as JSONB and are intentionally open-ended so new\n// channels/topics can be added without a migration. `channels` and `topics`\n// are boolean opt-in maps; `quietHours` is a list of UTC HH:MM windows.\n\nexport const QuietHoursSchema = z.object({\n start: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"expected HH:MM (24h, UTC)\"),\n end: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"expected HH:MM (24h, UTC)\"),\n});\nexport type QuietHours = z.infer<typeof QuietHoursSchema>;\n\nexport const PreferencesSchema = z.object({\n channels: z.record(z.string(), z.boolean()).optional(),\n topics: z.record(z.string(), z.boolean()).optional(),\n quietHours: z.array(QuietHoursSchema).optional(),\n});\nexport type Preferences = z.infer<typeof PreferencesSchema>;\n\n// ─── Contacts ─────────────────────────────────────────────────────────────────\n\n/** Channels that carry an addressable target (email address, phone, push token, url). */\nexport const ContactChannelSchema = z.enum([\"email\", \"sms\", \"push\", \"webhook\"]);\nexport type ContactChannel = z.infer<typeof ContactChannelSchema>;\n\n/** Accept a single value or an array; always normalise to a non-empty array. */\nconst stringOrArray = z\n .union([z.string().min(1), z.array(z.string().min(1))])\n .transform((v) => (Array.isArray(v) ? v : [v]));\n\n// ─── Users ────────────────────────────────────────────────────────────────────\n\n/**\n * addUser({ id, email, phone, pushToken, segments, preferences })\n * email / phone / pushToken accept a single string or an array.\n */\nexport const AddUserSchema = z.object({\n id: z.string().min(1),\n language: z.string().optional(),\n timezone: z.string().optional(),\n email: stringOrArray.optional(),\n phone: stringOrArray.optional(),\n pushToken: stringOrArray.optional(),\n segments: z.array(z.string().min(1)).optional(),\n preferences: PreferencesSchema.optional(),\n});\nexport type AddUserInput = z.input<typeof AddUserSchema>;\n\n/** updateUser(id, patch) — every field optional; id comes from the path. */\nexport const UpdateUserSchema = z.object({\n language: z.string().optional(),\n timezone: z.string().optional(),\n email: stringOrArray.optional(),\n phone: stringOrArray.optional(),\n pushToken: stringOrArray.optional(),\n segments: z.array(z.string().min(1)).optional(),\n preferences: PreferencesSchema.optional(),\n});\nexport type UpdateUserInput = z.input<typeof UpdateUserSchema>;\n\n/** addUserContact(userId, channel, { target, preferences }) — channel carried in body. */\nexport const AddContactSchema = z.object({\n channel: ContactChannelSchema,\n target: z.string().min(1),\n preferences: PreferencesSchema.optional(),\n});\nexport type AddContactInput = z.infer<typeof AddContactSchema>;\n\n// ─── Templates ────────────────────────────────────────────────────────────────\n\nexport const TemplateSchema = z.object({\n id: z.string().min(1),\n channel: NotificationChannelSchema,\n topic: z\n .union([z.string().min(1), z.array(z.string().min(1))])\n .transform((v) => (Array.isArray(v) ? v : [v]))\n .optional(),\n content: z.record(z.string(), z.unknown()),\n aiPrompts: z.record(z.string(), z.string()).optional(),\n});\nexport type TemplateInput = z.infer<typeof TemplateSchema>;\n\nexport const SyncTemplatesSchema = z.object({\n templates: z.array(TemplateSchema).min(1),\n});\nexport type SyncTemplatesInput = z.infer<typeof SyncTemplatesSchema>;\n\n// ─── notify() ─────────────────────────────────────────────────────────────────\n\n/** Inline user object accepted by notify({ user: {...} }). */\nexport const InlineUserSchema = z.object({\n id: z.string().min(1),\n language: z.string().optional(),\n timezone: z.string().optional(),\n email: stringOrArray.optional(),\n phone: stringOrArray.optional(),\n pushToken: stringOrArray.optional(),\n segments: z.array(z.string().min(1)).optional(),\n preferences: PreferencesSchema.optional(),\n});\nexport type InlineUser = z.infer<typeof InlineUserSchema>;\n\n/**\n * notify(...) request body.\n *\n * Exactly one of `user` / `segment` / `topic` must be provided.\n */\n/**\n * The field set shared by `notify()` and a workflow's `notify` step. The two\n * differ only in whether naming a target is mandatory, so the fields live here\n * once and each schema layers its own target rule on top.\n */\nconst NotifyRequestFields = z.object({\n user: z\n .union([\n z.string().min(1),\n InlineUserSchema,\n z.array(z.union([z.string().min(1), InlineUserSchema])).nonempty(),\n ])\n .optional(),\n segment: z.string().min(1).optional(),\n topic: z.string().min(1).optional(),\n template: z.string().min(1),\n data: z.record(z.string(), z.unknown()).optional(),\n aiPrompts: z.record(z.string(), z.string()).optional(),\n priority: NotificationPrioritySchema.optional(),\n channels: z.array(NotificationChannelSchema).nonempty().optional(),\n fallback: z.boolean().optional(),\n sendAt: z.string().datetime().optional(),\n /**\n * A label grouping every message this call produces, so the send can be\n * reported on later via `/v1/campaigns/:id/stats`. Free-form, but reusing one\n * label across calls merges them into a single campaign — which is either\n * what you want (a send split into batches) or a reporting bug.\n */\n campaign: z.string().min(1).max(128).optional(),\n});\n\nfunction countTargets(val: { user?: unknown; segment?: unknown; topic?: unknown }): number {\n return [val.user, val.segment, val.topic].filter((t) => t !== undefined).length;\n}\n\nexport const NotifyRequestSchema = NotifyRequestFields.superRefine((val, ctx) => {\n const targets = countTargets(val);\n if (targets === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"one of `user`, `segment`, or `topic` is required\",\n path: [\"user\"],\n });\n } else if (targets > 1) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"provide exactly one of `user`, `segment`, or `topic`\",\n path: [\"user\"],\n });\n }\n});\nexport type NotifyRequestInput = z.input<typeof NotifyRequestSchema>;\n\n/**\n * The payload of a workflow `notify` step — every field `notify()` takes.\n *\n * The one difference is that a target is optional here: a step naming none\n * inherits the instance's own user, which is the ordinary case. Naming one\n * overrides that, so a step can notify a different user, a segment, or a topic.\n */\nexport const WorkflowNotifyPayloadSchema = NotifyRequestFields.superRefine((val, ctx) => {\n if (countTargets(val) > 1) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"provide at most one of `user`, `segment`, or `topic`\",\n path: [\"user\"],\n });\n }\n});\nexport type WorkflowNotifyInput = z.input<typeof WorkflowNotifyPayloadSchema>;\n\n// ─── Workflows ────────────────────────────────────────────────────────────────\n\nexport const TriggerWorkflowSchema = z.object({\n name: z.string().min(1),\n input: z.record(z.string(), z.unknown()).optional(),\n user: z.union([z.string().min(1), InlineUserSchema]).optional(),\n});\nexport type TriggerWorkflowInput = z.infer<typeof TriggerWorkflowSchema>;\n\n// ─── Events ───────────────────────────────────────────────────────────────────\n\nexport const IngestEventSchema = z.object({\n name: z.string().min(1),\n properties: z.record(z.string(), z.unknown()),\n});\nexport type IngestEventInput = z.infer<typeof IngestEventSchema>;\n\nexport const WorkflowStepSchema = z.discriminatedUnion(\"action\", [\n z.object({\n action: z.literal(\"notify\"),\n payload: WorkflowNotifyPayloadSchema,\n }),\n z.object({\n action: z.literal(\"wait\"),\n duration: z.string(), // e.g. \"1h\", \"5m\"\n }),\n z.object({\n action: z.literal(\"waitForEvent\"),\n event: z.string(),\n options: z\n .object({\n timeout: z.string().optional(), // e.g. \"24h\"\n })\n .optional(),\n }),\n]);\nexport type WorkflowStepDef = z.infer<typeof WorkflowStepSchema>;\n\nexport const CreateWorkflowSchema = z.object({\n name: z.string().min(1),\n steps: z.array(WorkflowStepSchema).min(1),\n});\nexport type CreateWorkflowInput = z.infer<typeof CreateWorkflowSchema>;\n\n// ─── Projects ─────────────────────────────────────────────────────────────────\n\nexport const UpdateProjectSchema = z.object({\n rateLimitRpm: z.number().nullable().optional(),\n throttleLimit: z.number().nullable().optional(),\n throttleWindowHours: z.number().nullable().optional(),\n});\nexport type UpdateProjectInput = z.infer<typeof UpdateProjectSchema>;\n","import { z } from \"zod\";\nimport { NotificationChannelSchema, NotificationPrioritySchema } from \"@/contracts/common.js\";\n\n/**\n * A high-level notification request as issued by the SDK's `notify()` call.\n *\n * Unlike `notification.created` (which targets a single resolved recipient),\n * this event carries the *unresolved* target — a user id, a segment, or a\n * topic. A downstream resolver stage fans it out into one\n * `notification.created` per matching recipient, applying preference filters.\n */\nexport const NotificationTargetSchema = z.discriminatedUnion(\"type\", [\n z.object({ type: z.literal(\"user\"), userId: z.string().min(1) }),\n z.object({ type: z.literal(\"segment\"), segment: z.string().min(1) }),\n z.object({ type: z.literal(\"topic\"), topic: z.string().min(1) }),\n]);\nexport type NotificationTarget = z.infer<typeof NotificationTargetSchema>;\n\nexport const NotificationRequestedPayloadSchema = z.object({\n projectId: z.string().uuid(),\n target: NotificationTargetSchema,\n templateId: z.string().min(1),\n priority: NotificationPrioritySchema.default(\"normal\"),\n channels: z.array(NotificationChannelSchema).nonempty().optional(),\n data: z.record(z.string(), z.unknown()).default({}),\n fallback: z.boolean().default(false),\n aiPrompts: z.record(z.string(), z.string()).optional(),\n scheduledAt: z.string().datetime().optional(),\n idempotencyKey: z.string().optional(),\n /**\n * Groups every message this request fans out into, so the send can be\n * reported on afterwards. Carried unchanged to the delivery log.\n */\n campaignId: z.string().min(1).max(128).optional(),\n});\nexport type NotificationRequestedPayload = z.infer<typeof NotificationRequestedPayloadSchema>;\n","import { z } from \"zod\";\nimport { NotificationChannelSchema, NotificationPrioritySchema } from \"@/contracts/common.js\";\n\nexport const NotificationCreatedPayloadSchema = z.object({\n projectId: z.string().uuid(),\n recipientId: z.string().min(1),\n channel: NotificationChannelSchema,\n priority: NotificationPrioritySchema.default(\"normal\"),\n templateId: z.string().min(1).optional(),\n payload: z.record(z.string(), z.unknown()),\n scheduledAt: z.string().datetime().optional(),\n idempotencyKey: z.string().optional(),\n});\nexport type NotificationCreatedPayload = z.infer<typeof NotificationCreatedPayloadSchema>;\n","import { z } from \"zod\";\nimport { NotificationChannelSchema, NotificationPrioritySchema } from \"@/contracts/common.js\";\n\nexport const RecipientProfileSchema = z.object({\n id: z.string(),\n email: z.string().email().optional(),\n phone: z.string().optional(),\n webhook: z.string().url().optional(),\n pushTokens: z.array(z.string()).optional(),\n pushToken: z.string().optional(),\n locale: z.string().default(\"en\"),\n timezone: z.string().default(\"UTC\"),\n preferences: z.object({\n optedOut: z.boolean().default(false),\n channels: z.array(NotificationChannelSchema).default([]),\n quietHours: z\n .array(\n z.object({\n start: z.string(),\n end: z.string(),\n }),\n )\n .optional(),\n }),\n});\nexport type RecipientProfile = z.infer<typeof RecipientProfileSchema>;\n\nexport const NotificationEnrichedPayloadSchema = z.object({\n projectId: z.string().uuid(),\n rawEventId: z.string().uuid(),\n recipientId: z.string().min(1),\n channel: NotificationChannelSchema,\n priority: NotificationPrioritySchema,\n templateId: z.string().min(1).optional(),\n templateVariables: z.record(z.string(), z.unknown()),\n recipient: RecipientProfileSchema,\n aiPrompts: z.record(z.string(), z.string()).optional(),\n scheduledAt: z.string().datetime().optional(),\n fallbackChain: z.array(NotificationChannelSchema).optional(),\n /** Campaign this message belongs to, carried from the originating request. */\n campaignId: z.string().min(1).max(128).optional(),\n});\nexport type NotificationEnrichedPayload = z.infer<typeof NotificationEnrichedPayloadSchema>;\n","import { z } from \"zod\";\n\nexport const NotificationScheduledPayloadSchema = z.object({\n projectId: z.string().uuid(),\n enrichedEventId: z.string().uuid(),\n taskId: z.string().min(1),\n scheduledAt: z.string().datetime(),\n});\nexport type NotificationScheduledPayload = z.infer<typeof NotificationScheduledPayloadSchema>;\n","import { z } from \"zod\";\nimport { NotificationChannelSchema, NotificationPrioritySchema } from \"@/contracts/common.js\";\nimport { RecipientProfileSchema } from \"./notification-enriched.js\";\n\nexport const RenderedContentSchema = z.object({\n content: z.record(z.string(), z.unknown()),\n attachments: z\n .array(\n z.object({\n name: z.string(),\n contentType: z.string(),\n url: z.string().url(),\n }),\n )\n .optional(),\n});\nexport type RenderedContent = z.infer<typeof RenderedContentSchema>;\n\nexport const DeliveryOptionsSchema = z.object({\n maxAttempts: z.number().int().positive().default(3),\n timeoutMs: z.number().int().positive().default(10_000),\n headers: z.record(z.string(), z.string()).optional(),\n});\nexport type DeliveryOptions = z.infer<typeof DeliveryOptionsSchema>;\n\nexport const NotificationDispatchedPayloadSchema = z.object({\n projectId: z.string().uuid(),\n taskId: z.string().min(1),\n enrichedEventId: z.string().uuid(),\n recipientId: z.string().min(1),\n channel: NotificationChannelSchema,\n priority: NotificationPrioritySchema,\n templateId: z.string().min(1).optional(),\n templateVariables: z.record(z.string(), z.unknown()).default({}),\n aiPrompts: z.record(z.string(), z.string()).optional(),\n recipient: RecipientProfileSchema.optional(),\n renderedContent: RenderedContentSchema,\n destination: z.string().min(1).optional(),\n deliveryOptions: DeliveryOptionsSchema,\n fallbackChain: z.array(NotificationChannelSchema).optional(),\n throttleAttemptCount: z.number().int().nonnegative().optional(),\n /** Campaign this message belongs to, carried from the originating request. */\n campaignId: z.string().min(1).max(128).optional(),\n});\nexport type NotificationDispatchedPayload = z.infer<typeof NotificationDispatchedPayloadSchema>;\n","import { z } from \"zod\";\nimport { NotificationChannelSchema } from \"@/contracts/common.js\";\n\nexport const NotificationDeliveredPayloadSchema = z.object({\n projectId: z.string().uuid(),\n taskId: z.string().min(1),\n enrichedEventId: z.string().uuid(),\n channel: NotificationChannelSchema,\n deliveredAt: z.string().datetime(),\n providerMessageId: z.string().optional(),\n providerResponse: z.record(z.string(), z.unknown()).optional(),\n templateId: z.string().uuid().optional(),\n workflowInstanceId: z.string().uuid().optional(),\n /** Campaign this message belongs to, carried from the originating request. */\n campaignId: z.string().min(1).max(128).optional(),\n});\nexport type NotificationDeliveredPayload = z.infer<typeof NotificationDeliveredPayloadSchema>;\n","import { z } from \"zod\";\nimport { NotificationChannelSchema } from \"@/contracts/common.js\";\n\nexport const NotificationFailedPayloadSchema = z.object({\n projectId: z.string().uuid(),\n taskId: z.string().min(1),\n enrichedEventId: z.string().uuid(),\n channel: NotificationChannelSchema,\n failureReason: z.string(),\n failureCode: z.string(),\n retryable: z.boolean(),\n attempt: z.number().int().positive(),\n providerResponse: z.record(z.string(), z.unknown()).optional(),\n templateId: z.string().uuid().optional(),\n workflowInstanceId: z.string().uuid().optional(),\n /** Campaign this message belongs to, carried from the originating request. */\n campaignId: z.string().min(1).max(128).optional(),\n});\nexport type NotificationFailedPayload = z.infer<typeof NotificationFailedPayloadSchema>;\n","import { z } from \"zod\";\n\nexport const NotificationSkippedPayloadSchema = z.object({\n projectId: z.string().uuid(),\n eventId: z.string().uuid(),\n recipientId: z.string(),\n reason: z.string(),\n});\nexport type NotificationSkippedPayload = z.infer<typeof NotificationSkippedPayloadSchema>;\n","import { z } from \"zod\";\n\nexport const NotificationCanceledPayloadSchema = z.object({\n projectId: z.string().uuid(),\n taskId: z.string().min(1),\n});\nexport type NotificationCanceledPayload = z.infer<typeof NotificationCanceledPayloadSchema>;\n","import { z } from \"zod\";\nimport { NotificationChannelSchema, NotificationPrioritySchema } from \"@/contracts/common.js\";\nimport { RecipientProfileSchema } from \"./notification-enriched.js\";\n\nexport const NotificationAiPendingPayloadSchema = z.object({\n projectId: z.string().uuid(),\n enrichedEventId: z.string().uuid(),\n recipientId: z.string().min(1),\n channel: NotificationChannelSchema,\n priority: NotificationPrioritySchema,\n templateId: z.string().min(1).optional(),\n templateVariables: z.record(z.string(), z.unknown()),\n recipient: RecipientProfileSchema,\n aiPrompts: z.record(z.string(), z.string()),\n scheduledAt: z.string().datetime().optional(),\n fallbackChain: z.array(NotificationChannelSchema).optional(),\n});\nexport type NotificationAiPendingPayload = z.infer<typeof NotificationAiPendingPayloadSchema>;\n","// Core building blocks\nexport * from \"./common.js\";\nexport * from \"./metadata.js\";\nexport * from \"./registry.js\";\nexport * from \"./envelope.js\";\nexport * from \"./streams.js\";\nexport * from \"./helpers.js\";\nexport * from \"./sdk.js\";\n\n// Event payload schemas and types\nexport * from \"./events/notification-requested.js\";\nexport * from \"./events/notification-created.js\";\nexport * from \"./events/notification-enriched.js\";\nexport * from \"./events/notification-scheduled.js\";\nexport * from \"./events/notification-dispatched.js\";\nexport * from \"./events/notification-delivered.js\";\nexport * from \"./events/notification-failed.js\";\nexport * from \"./events/notification-skipped.js\";\nexport * from \"./events/notification-canceled.js\";\nexport * from \"./events/notification-ai-pending.js\";\n\n// Register all built-in event schemas in the global registry.\n// This runs once at module load time; call registry.define() in your own\n// module to add new event types without modifying this file.\nimport { registry } from \"./registry.js\";\nimport { NotificationRequestedPayloadSchema } from \"./events/notification-requested.js\";\nimport { NotificationCreatedPayloadSchema } from \"./events/notification-created.js\";\nimport { NotificationEnrichedPayloadSchema } from \"./events/notification-enriched.js\";\nimport { NotificationScheduledPayloadSchema } from \"./events/notification-scheduled.js\";\nimport { NotificationDispatchedPayloadSchema } from \"./events/notification-dispatched.js\";\nimport { NotificationDeliveredPayloadSchema } from \"./events/notification-delivered.js\";\nimport { NotificationFailedPayloadSchema } from \"./events/notification-failed.js\";\nimport { NotificationSkippedPayloadSchema } from \"./events/notification-skipped.js\";\nimport { NotificationCanceledPayloadSchema } from \"./events/notification-canceled.js\";\nimport { NotificationAiPendingPayloadSchema } from \"./events/notification-ai-pending.js\";\n\nregistry.define(\"notification.requested\", NotificationRequestedPayloadSchema);\nregistry.define(\"notification.created\", NotificationCreatedPayloadSchema);\nregistry.define(\"notification.enriched\", NotificationEnrichedPayloadSchema);\nregistry.define(\"notification.scheduled\", NotificationScheduledPayloadSchema);\nregistry.define(\"notification.dispatched\", NotificationDispatchedPayloadSchema);\nregistry.define(\"notification.delivered\", NotificationDeliveredPayloadSchema);\nregistry.define(\"notification.failed\", NotificationFailedPayloadSchema);\nregistry.define(\"notification.skipped\", NotificationSkippedPayloadSchema);\nregistry.define(\"notification.canceled\", NotificationCanceledPayloadSchema);\nregistry.define(\"notification.ai_pending\", NotificationAiPendingPayloadSchema);\n","import {\n pgTable,\n varchar,\n text,\n jsonb,\n boolean,\n timestamp,\n primaryKey,\n unique,\n uuid,\n pgEnum,\n time,\n check,\n index,\n integer,\n} from \"drizzle-orm/pg-core\";\nimport { sql } from \"drizzle-orm\";\nimport { createInsertSchema, createSelectSchema } from \"drizzle-zod\";\n\nexport const channelEnum = pgEnum(\"channel\", [\"email\", \"sms\", \"push\", \"webhook\", \"in-app\"]);\n\nexport const projects = pgTable(\"projects\", {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n name: varchar(\"name\").notNull(),\n rateLimitRpm: integer(\"rate_limit_rpm\"),\n throttleLimit: integer(\"throttle_limit\"),\n throttleWindowHours: integer(\"throttle_window_hours\"),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n});\n\nexport const users = pgTable(\n \"users\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n projectId: uuid(\"project_id\").notNull(),\n externalId: text(\"external_id\").notNull(),\n attributes: jsonb(\"attributes\").notNull().default({}),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n updatedAt: timestamp(\"updated_at\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n unq: unique().on(table.projectId, table.externalId),\n }),\n);\n\nexport const apiKeyRoleEnum = pgEnum(\"api_key_role\", [\"admin\", \"read_only\"]);\n\nexport const projectApiKeys = pgTable(\"project_api_keys\", {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n projectId: uuid(\"project_id\")\n .notNull()\n .references(() => projects.id, { onDelete: \"cascade\" }),\n keyHash: varchar(\"key_hash\").notNull().unique(),\n role: apiKeyRoleEnum(\"role\").notNull().default(\"admin\"),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n});\n\nexport const userSegments = pgTable(\n \"user_segments\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n userId: uuid(\"user_id\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n segment: varchar(\"segment\").notNull(),\n },\n (table) => ({\n unq: unique().on(table.userId, table.segment),\n segmentIdx: index(\"segment_idx\").on(table.segment),\n }),\n);\n\nexport const userContacts = pgTable(\n \"user_contacts\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n userId: uuid(\"user_id\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n channel: channelEnum(\"channel\").notNull(),\n target: text(\"target\").notNull(),\n label: text(\"label\"),\n isPrimary: boolean(\"is_primary\").notNull().default(false),\n enabled: boolean(\"enabled\").notNull().default(true),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n unq: unique().on(table.userId, table.channel, table.target),\n }),\n);\n\nexport const userChannelPreferences = pgTable(\n \"user_channel_preferences\",\n {\n userId: uuid(\"user_id\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n channel: channelEnum(\"channel\").notNull(),\n enabled: boolean(\"enabled\").notNull(),\n },\n (table) => ({\n pk: primaryKey({ columns: [table.userId, table.channel] }),\n }),\n);\n\nexport const userTopicPreferences = pgTable(\n \"user_topic_preferences\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n userId: uuid(\"user_id\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n topic: varchar(\"topic\").notNull(),\n enabled: boolean(\"enabled\").notNull(),\n },\n (table) => ({\n unq: unique().on(table.userId, table.topic),\n }),\n);\n\nexport const contactTopicPreferences = pgTable(\n \"contact_topic_preferences\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n contactId: uuid(\"contact_id\")\n .notNull()\n .references(() => userContacts.id, { onDelete: \"cascade\" }),\n topic: varchar(\"topic\").notNull(),\n enabled: boolean(\"enabled\").notNull(),\n },\n (table) => ({\n unq: unique().on(table.contactId, table.topic),\n }),\n);\n\nexport const quietHours = pgTable(\n \"quiet_hours\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n userId: uuid(\"user_id\").references(() => users.id, { onDelete: \"cascade\" }),\n contactId: uuid(\"contact_id\").references(() => userContacts.id, { onDelete: \"cascade\" }),\n startTime: time(\"start_time\").notNull(),\n endTime: time(\"end_time\").notNull(),\n },\n (table) => ({\n checkOwner: check(\"check_owner\", sql`num_nonnulls(user_id, contact_id) = 1`),\n userIdIdx: index(\"quiet_hours_user_idx\").on(table.userId),\n }),\n);\n\nexport const templates = pgTable(\n \"templates\",\n {\n projectId: uuid(\"project_id\")\n .notNull()\n .references(() => projects.id, { onDelete: \"cascade\" }),\n id: varchar(\"id\").notNull(),\n channel: channelEnum(\"channel\").notNull(),\n topics: text(\"topics\").array().notNull(),\n content: jsonb(\"content\").notNull(),\n aiPrompts: jsonb(\"ai_prompts\"),\n updatedAt: timestamp(\"updated_at\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n pk: primaryKey({ columns: [table.projectId, table.id] }),\n }),\n);\n\nexport const messageLogs = pgTable(\n \"message_logs\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n projectId: uuid(\"project_id\").notNull(),\n taskId: varchar(\"task_id\").notNull(),\n providerMessageId: varchar(\"provider_message_id\"),\n templateId: varchar(\"template_id\"),\n workflowInstanceId: uuid(\"workflow_instance_id\"),\n channel: channelEnum(\"channel\").notNull(),\n // Delivery attempts are numbered from 1; provider engagement events are not\n // attempts and use 0.\n attempt: integer(\"attempt\").default(1).notNull(),\n /**\n * Discriminates a delivery attempt (\"attempt\") from a provider engagement\n * event (\"opened\", \"clicked\", \"bounced\", …). Without it an engagement row\n * collides with the delivery row for the same (task, channel, attempt).\n */\n kind: varchar(\"kind\").notNull().default(\"attempt\"),\n status: varchar(\"status\").notNull(),\n /**\n * Groups every message produced by one `notify()` call. Null for sends that\n * did not name a campaign, which is every send made before this column\n * existed — treat null as \"unattributed\", not as a campaign of its own.\n */\n campaignId: varchar(\"campaign_id\"),\n /**\n * Provider-specific detail that would otherwise be discarded: the clicked\n * URL on a click event, the bounce subtype on a bounce. Deliberately loose\n * — every provider reports these differently.\n */\n metadata: jsonb(\"metadata\"),\n timestamp: timestamp(\"timestamp\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n projectIdx: index(\"message_logs_project_idx\").on(table.projectId),\n taskIdx: index(\"task_idx\").on(table.taskId),\n projectIdTaskIdIdx: index(\"message_logs_project_task_idx\").on(table.projectId, table.taskId),\n providerMsgIdx: index(\"provider_msg_idx\").on(table.providerMessageId),\n projectTimeIdx: index(\"msg_log_proj_time_idx\").on(table.projectId, table.timestamp),\n templateIdx: index(\"msg_log_template_idx\").on(table.projectId, table.templateId),\n workflowIdx: index(\"msg_log_workflow_idx\").on(table.projectId, table.workflowInstanceId),\n campaignIdx: index(\"msg_log_campaign_idx\").on(table.projectId, table.campaignId),\n taskChannelAttemptUidx: unique(\"task_channel_attempt_uidx\").on(\n table.taskId,\n table.channel,\n table.attempt,\n table.kind,\n ),\n }),\n);\n\n/**\n * Addresses that must not be contacted again on a given channel.\n *\n * Rows are written from provider webhooks (an unsubscribe, a spam complaint, a\n * hard bounce) and by hand through the API. The engine consults this table\n * before dispatching, so a suppression is a hard stop rather than a preference\n * — `priority: \"critical\"` does not override it. Removing a row is the only way\n * back, and that is deliberately a manual act.\n */\nexport const suppressions = pgTable(\n \"suppressions\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n projectId: uuid(\"project_id\").notNull(),\n channel: channelEnum(\"channel\").notNull(),\n /** The address itself, normalised: email is lower-cased, others stored verbatim. */\n target: varchar(\"target\").notNull(),\n /** `unsubscribed` | `complained` | `bounced` | `manual`. */\n reason: varchar(\"reason\").notNull(),\n /** Where it came from — a provider name, or `api` for a manual entry. */\n source: varchar(\"source\"),\n /** The delivery that triggered it, when a provider webhook is the origin. */\n taskId: varchar(\"task_id\"),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n // One row per address per channel; a second complaint must not error.\n projectChannelTargetUidx: unique(\"suppression_project_channel_target_uidx\").on(\n table.projectId,\n table.channel,\n table.target,\n ),\n projectIdx: index(\"suppression_project_idx\").on(table.projectId),\n // The engine's hot path: \"is this address suppressed on this channel?\"\n lookupIdx: index(\"suppression_lookup_idx\").on(table.projectId, table.channel, table.target),\n }),\n);\n\nexport const workflowStatusEnum = pgEnum(\"workflow_status\", [\n \"pending\",\n \"running\",\n \"completed\",\n \"failed\",\n]);\n\nexport const workflowDefinitions = pgTable(\n \"workflow_definitions\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n projectId: uuid(\"project_id\").notNull(),\n name: varchar(\"name\").notNull(),\n steps: jsonb(\"steps\").notNull(),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n nameUnq: unique().on(table.projectId, table.name),\n }),\n);\n\nexport const workflowInstances = pgTable(\n \"workflow_instances\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n projectId: uuid(\"project_id\").notNull(),\n name: varchar(\"name\").notNull(),\n status: workflowStatusEnum(\"status\").notNull().default(\"pending\"),\n input: jsonb(\"input\").default({}),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n updatedAt: timestamp(\"updated_at\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n nameIdx: index(\"workflow_name_idx\").on(table.name),\n }),\n);\n\nexport const workflowSteps = pgTable(\n \"workflow_steps\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n projectId: uuid(\"project_id\").notNull(),\n instanceId: uuid(\"instance_id\")\n .notNull()\n .references(() => workflowInstances.id, { onDelete: \"cascade\" }),\n stepIndex: varchar(\"step_index\").notNull(), // We can use string format for index (e.g. \"0\", \"1\", \"0.1\") or a deterministic id\n action: varchar(\"action\").notNull(), // e.g. \"notify\", \"wait\", \"run\"\n output: jsonb(\"output\"),\n error: text(\"error\"),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n unq: unique().on(table.instanceId, table.stepIndex),\n }),\n);\n\nexport const workflowWaiters = pgTable(\n \"workflow_waiters\",\n {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n projectId: uuid(\"project_id\").notNull(),\n instanceId: uuid(\"instance_id\")\n .notNull()\n .references(() => workflowInstances.id, { onDelete: \"cascade\" }),\n eventName: varchar(\"event_name\").notNull(),\n matchCriteria: jsonb(\"match_criteria\").notNull(), // { userId: \"123\" }\n expiresAt: timestamp(\"expires_at\", { withTimezone: true }).notNull(),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n eventIdx: index(\"waiter_event_idx\").on(table.eventName),\n instanceIdx: index(\"waiter_instance_idx\").on(table.instanceId),\n compositeWaitIdx: index(\"waiter_comp_idx\").on(\n table.eventName,\n table.projectId,\n table.expiresAt,\n ),\n matchCriteriaIdx: index(\"waiter_match_idx\").using(\"gin\", table.matchCriteria),\n }),\n);\n\nexport const deliveryOutbox = pgTable(\n \"delivery_outbox\",\n {\n taskId: varchar(\"task_id\").notNull(),\n channel: channelEnum(\"channel\").notNull(),\n destination: text(\"destination\").notNull(),\n providerMessageId: varchar(\"provider_message_id\"),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n },\n (table) => ({\n pk: primaryKey({ columns: [table.taskId, table.channel, table.destination] }),\n }),\n);\n\nexport const scheduledPayloads = pgTable(\"scheduled_payloads\", {\n taskId: varchar(\"task_id\").primaryKey(),\n payload: jsonb(\"payload\").notNull(),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n});\n\n// ─── Generated Zod Schemas ──────────────────────────────────────────────────\n\nexport const insertProjectSchema = createInsertSchema(projects);\nexport const selectProjectSchema = createSelectSchema(projects);\n\nexport const insertProjectApiKeySchema = createInsertSchema(projectApiKeys);\nexport const selectProjectApiKeySchema = createSelectSchema(projectApiKeys);\n\nexport const insertUserSchema = createInsertSchema(users);\nexport const selectUserSchema = createSelectSchema(users);\n\nexport const insertUserSegmentSchema = createInsertSchema(userSegments);\nexport const selectUserSegmentSchema = createSelectSchema(userSegments);\n\nexport const insertUserContactSchema = createInsertSchema(userContacts);\nexport const selectUserContactSchema = createSelectSchema(userContacts);\n\nexport const insertUserChannelPreferenceSchema = createInsertSchema(userChannelPreferences);\nexport const selectUserChannelPreferenceSchema = createSelectSchema(userChannelPreferences);\n\nexport const insertUserTopicPreferenceSchema = createInsertSchema(userTopicPreferences);\nexport const selectUserTopicPreferenceSchema = createSelectSchema(userTopicPreferences);\n\nexport const insertMessageLogSchema = createInsertSchema(messageLogs);\nexport const selectMessageLogSchema = createSelectSchema(messageLogs);\n\nexport const insertSuppressionSchema = createInsertSchema(suppressions);\nexport const selectSuppressionSchema = createSelectSchema(suppressions);\n\nexport const insertWorkflowInstanceSchema = createInsertSchema(workflowInstances);\nexport const selectWorkflowInstanceSchema = createSelectSchema(workflowInstances);\n\nexport const insertWorkflowStepSchema = createInsertSchema(workflowSteps);\nexport const selectWorkflowStepSchema = createSelectSchema(workflowSteps);\n\nexport const insertWorkflowWaiterSchema = createInsertSchema(workflowWaiters);\nexport const selectWorkflowWaiterSchema = createSelectSchema(workflowWaiters);\n\nexport const insertDeliveryOutboxSchema = createInsertSchema(deliveryOutbox);\nexport const selectDeliveryOutboxSchema = createSelectSchema(deliveryOutbox);\n\nexport const insertScheduledPayloadSchema = createInsertSchema(scheduledPayloads);\nexport const selectScheduledPayloadSchema = createSelectSchema(scheduledPayloads);\n","import postgres from \"postgres\";\nimport { drizzle, type PostgresJsDatabase } from \"drizzle-orm/postgres-js\";\nimport type { Logger } from \"@/index.js\";\nimport * as schema from \"./schema.js\";\nimport { readBaseConfig } from \"@/index.js\";\n\nexport type Sql = postgres.Sql;\nexport type Db = PostgresJsDatabase<typeof schema>;\n\nexport interface DatabaseOptions {\n url: string;\n applicationName?: string;\n maxConnections?: number;\n idleTimeoutSeconds?: number;\n logger?: Logger;\n}\n\nexport interface DatabaseClients {\n sql: Sql;\n db: Db;\n}\n\n/**\n * Create a postgres.js connection pool and initialize Drizzle ORM.\n * Call once at application startup; pass db into repositories.\n * Call sql.end() during graceful shutdown.\n */\nexport function createDatabase({\n url,\n applicationName = \"notifkit\",\n maxConnections,\n idleTimeoutSeconds = 30,\n logger,\n}: DatabaseOptions): DatabaseClients {\n const finalMaxConnections = maxConnections ?? readBaseConfig().DB_MAX_CONNECTIONS;\n\n const sql = postgres(url, {\n max: finalMaxConnections,\n idle_timeout: idleTimeoutSeconds,\n connection: {\n application_name: applicationName,\n statement_timeout: 10000 as any, // prevent TS issues with postgres.js types\n },\n onnotice: (notice) => {\n logger?.debug({ notice }, \"postgres notice\");\n },\n });\n\n const db = drizzle(sql, { schema });\n\n return { sql, db };\n}\n\nimport { migrate } from \"drizzle-orm/postgres-js/migrator\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\nimport fs from \"fs\";\n\nexport async function runMigrations(db: Db) {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n\n let migrationsFolder = path.resolve(__dirname, \"../../drizzle\");\n if (!fs.existsSync(migrationsFolder)) {\n migrationsFolder = path.resolve(__dirname, \"../drizzle\");\n }\n\n await migrate(db, { migrationsFolder });\n}\n","import type { Redis } from \"@/index.js\";\n\nexport interface IdempotencyOptions {\n redis: Redis;\n keyPrefix: string;\n ttlSeconds?: number;\n}\n\n/**\n * SETNX-based idempotency guard.\n * Returns true from checkAndMark() only the first time a given ID is seen\n * within the TTL window; subsequent calls return false (duplicate / retry).\n */\nexport class IdempotencyGuard {\n private readonly redis: Redis;\n private readonly keyPrefix: string;\n private readonly ttlSeconds: number;\n\n constructor({ redis, keyPrefix, ttlSeconds = 86_400 }: IdempotencyOptions) {\n this.redis = redis;\n this.keyPrefix = keyPrefix;\n this.ttlSeconds = ttlSeconds;\n }\n\n private key(id: string): string {\n return `${this.keyPrefix}:${id}`;\n }\n\n /** Atomically mark id as processed. Returns true on first call; false if already seen. */\n async checkAndMark(id: string, customTtlSeconds?: number): Promise<boolean> {\n const ttl = customTtlSeconds ?? this.ttlSeconds;\n const result = await this.redis.set(this.key(id), \"1\", \"EX\", ttl, \"NX\");\n return result === \"OK\";\n }\n\n /** Unconditionally mark id as processed (e.g. to upgrade a short-lived lock). */\n async markProcessed(id: string, customTtlSeconds?: number): Promise<void> {\n const ttl = customTtlSeconds ?? this.ttlSeconds;\n await this.redis.set(this.key(id), \"1\", \"EX\", ttl);\n }\n\n async isProcessed(id: string): Promise<boolean> {\n return (await this.redis.get(this.key(id))) !== null;\n }\n\n /** Remove the idempotency marker (useful in tests or manual rollbacks). */\n async unmark(id: string): Promise<void> {\n await this.redis.del(this.key(id));\n }\n}\n","import pino, { type Logger as PinoLogger, type LoggerOptions as PinoOptions } from \"pino\";\n\nexport type LogLevel = \"fatal\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"trace\" | \"silent\";\n\nexport type Logger = PinoLogger;\n\nexport interface LoggerOptions {\n name: string;\n level?: LogLevel;\n pretty?: boolean;\n context?: Record<string, unknown>;\n}\n\nexport function createLogger({ name, level = \"info\", pretty, context }: LoggerOptions): Logger {\n const usePretty = pretty ?? process.env[\"NODE_ENV\"] !== \"production\";\n\n const options: PinoOptions = {\n name,\n level,\n formatters: {\n level(label) {\n return { level: label };\n },\n },\n timestamp: pino.stdTimeFunctions.isoTime,\n serializers: {\n err: pino.stdSerializers.err,\n error: pino.stdSerializers.err,\n req: pino.stdSerializers.req,\n res: pino.stdSerializers.res,\n },\n base: { service: name, ...context },\n };\n\n if (usePretty) {\n options.transport = {\n target: \"pino-pretty\",\n options: {\n colorize: true,\n translateTime: \"SYS:standard\",\n ignore: \"pid,hostname\",\n messageFormat: \"{service} | {msg}\",\n },\n };\n }\n\n return pino(options);\n}\n\nexport function withRequestId(logger: Logger, requestId: string): Logger {\n return logger.child({ requestId });\n}\n\nexport function withContext(logger: Logger, context: Record<string, unknown>): Logger {\n return logger.child(context);\n}\n\nexport function childLogger(logger: Logger, bindings: Record<string, unknown>): Logger {\n return logger.child(bindings);\n}\n","import promClient from \"prom-client\";\n\nconst register = new promClient.Registry();\npromClient.collectDefaultMetrics({ register });\n\nexport const metrics = {\n messagesPublished: new promClient.Counter({\n name: \"notifkit_messages_published_total\",\n help: \"Total messages published to inbound streams\",\n labelNames: [\"channel\", \"priority\"],\n registers: [register],\n }),\n messagesProcessed: new promClient.Counter({\n name: \"notifkit_messages_processed_total\",\n help: \"Total messages processed by workers\",\n labelNames: [\"worker\", \"status\"],\n registers: [register],\n }),\n deliverySuccess: new promClient.Counter({\n name: \"notifkit_delivery_success_total\",\n help: \"Total successful deliveries\",\n labelNames: [\"channel\"],\n registers: [register],\n }),\n deliveryFailed: new promClient.Counter({\n name: \"notifkit_delivery_failed_total\",\n help: \"Total failed deliveries\",\n labelNames: [\"channel\", \"reason\"],\n registers: [register],\n }),\n workerActiveTasks: new promClient.Gauge({\n name: \"notifkit_worker_active_tasks\",\n help: \"Number of currently active tasks per worker\",\n labelNames: [\"worker\"],\n registers: [register],\n }),\n queueSize: new promClient.Gauge({\n name: \"notifkit_queue_size\",\n help: \"Current size of streams\",\n labelNames: [\"stream\"],\n registers: [register],\n }),\n pendingAcks: new promClient.Gauge({\n name: \"notifkit_pending_acks\",\n help: \"Number of pending un-acked messages per group\",\n labelNames: [\"group\"],\n registers: [register],\n }),\n};\n\nexport function getMetricsRegistry() {\n return register;\n}\n","import type { Redis } from \"@/index.js\";\nimport type { Logger } from \"@/index.js\";\nimport {\n StreamEventSchema,\n type StreamEvent,\n type StreamName,\n type ConsumerGroup,\n readBaseConfig,\n STREAMS,\n} from \"@/index.js\";\nimport { metrics } from \"@/metrics/index.js\";\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\nexport interface StreamMessage {\n id: string;\n event: StreamEvent;\n /** Stream this message was read from. Required to ack/claim against the right one. */\n stream?: string;\n}\n\nexport interface PendingEntry {\n id: string;\n consumer: string;\n idleMs: number;\n deliveryCount: number;\n /** Stream this entry is pending on. Stream ids are not unique across streams. */\n stream: StreamName;\n}\n\n// ─── Internal helpers ──────────────────────────────────────────────────────\n\nfunction parseMessage(id: string, fields: string[] | null, logger?: Logger): StreamMessage | null {\n if (!fields) return null;\n\n const dataIndex = fields.indexOf(\"data\");\n if (dataIndex === -1) return null;\n\n const raw = fields[dataIndex + 1];\n if (!raw) return null;\n\n let decoded: unknown;\n try {\n decoded = JSON.parse(raw);\n } catch (err) {\n logger?.warn({ id, err }, \"failed to parse stream event JSON\");\n return null;\n }\n\n const parsed = StreamEventSchema.safeParse(decoded);\n if (!parsed.success) {\n logger?.warn({ id, error: parsed.error.issues }, \"failed to parse stream event\");\n return null;\n }\n\n return { id, event: parsed.data };\n}\n\n// ─── StreamProducer ────────────────────────────────────────────────────────\n\nexport interface StreamProducerOptions {\n redis: Redis;\n stream: StreamName;\n logger?: Logger;\n maxLen?: number;\n}\n\nexport class StreamProducer {\n private readonly redis: Redis;\n private readonly stream: StreamName;\n private readonly logger?: Logger;\n private readonly maxLen: number;\n\n constructor({ redis, stream, logger, maxLen }: StreamProducerOptions) {\n this.redis = redis;\n this.stream = stream;\n this.logger = logger;\n this.maxLen = maxLen ?? readBaseConfig().QUEUE_MAX_LEN;\n }\n\n async publish(partial: Omit<StreamEvent, \"id\" | \"timestamp\">): Promise<string> {\n const event: StreamEvent = {\n ...partial,\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n };\n\n const messageId = await this.redis.xadd(\n this.stream,\n \"MAXLEN\",\n \"~\",\n String(this.maxLen),\n \"*\",\n \"data\",\n JSON.stringify(event),\n );\n\n if (!messageId) throw new Error(`XADD to ${this.stream} returned null`);\n\n this.logger?.debug(\n { stream: this.stream, messageId, eventType: event.type, eventId: event.id },\n \"event published\",\n );\n\n return messageId;\n }\n\n private async monitorMaxLen(stream: string) {\n if (Math.random() < 0.05) {\n // Check ~5% of the time to avoid overhead\n try {\n const len = await this.redis.xlen(stream);\n metrics.queueSize.set({ stream }, len);\n\n if (len > this.maxLen * 0.8) {\n this.logger?.warn(\n { stream, len, maxLen: this.maxLen },\n \"stream is nearing MAXLEN limit (80%+)\",\n );\n }\n\n const dlqLen = await this.redis.xlen(STREAMS.DEAD_LETTER);\n metrics.queueSize.set({ stream: STREAMS.DEAD_LETTER }, dlqLen);\n } catch (err) {\n this.logger?.debug({ err }, \"failed to monitor stream length\");\n }\n }\n }\n\n async publishBatch(\n partials: Omit<StreamEvent, \"id\" | \"timestamp\">[],\n ): Promise<{ messageIds: string[]; eventIds: string[] }> {\n if (partials.length === 0) return { messageIds: [], eventIds: [] };\n\n const pipeline = this.redis.pipeline();\n const timestamp = new Date().toISOString();\n\n const eventIds: string[] = [];\n\n for (const partial of partials) {\n const id = crypto.randomUUID();\n eventIds.push(id);\n const event: StreamEvent = {\n ...partial,\n id,\n timestamp,\n };\n\n pipeline.xadd(\n this.stream,\n \"MAXLEN\",\n \"~\",\n String(this.maxLen),\n \"*\",\n \"data\",\n JSON.stringify(event),\n );\n }\n\n const results = await pipeline.exec();\n if (!results) throw new Error(`Pipeline execution failed for ${this.stream}`);\n\n const messageIds: string[] = [];\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (!result) throw new Error(\"Pipeline result is undefined\");\n const [err, msgId] = result;\n if (err) throw err;\n messageIds.push(msgId as string);\n }\n\n this.logger?.debug({ stream: this.stream, count: partials.length }, \"batch events published\");\n\n this.monitorMaxLen(this.stream).catch(() => {});\n\n return { messageIds, eventIds };\n }\n}\n\n// ─── StreamConsumer ────────────────────────────────────────────────────────\n\nexport interface StreamConsumerOptions {\n redis: Redis;\n stream: StreamName | StreamName[];\n group: ConsumerGroup;\n consumer: string;\n dlqStream?: StreamName;\n logger?: Logger;\n batchSize?: number;\n blockMs?: number;\n}\n\ntype XReadGroupResult = Array<[string, Array<[string, string[] | null]>]> | null;\n\nexport class StreamConsumer {\n readonly redis: Redis;\n private readonly blockingRedis: Redis;\n private readonly streams: StreamName[];\n private readonly group: ConsumerGroup;\n private readonly consumer: string;\n private readonly dlqStream?: StreamName;\n private readonly logger?: Logger;\n private readonly batchSize: number;\n private readonly blockMs: number;\n private running = false;\n\n constructor({\n redis,\n stream,\n group,\n consumer,\n dlqStream,\n logger,\n batchSize = 10,\n blockMs = 5_000,\n }: StreamConsumerOptions) {\n this.redis = redis;\n this.blockingRedis = redis.duplicate();\n this.streams = Array.isArray(stream) ? stream : [stream];\n this.group = group;\n this.consumer = consumer;\n this.dlqStream = dlqStream;\n this.logger = logger;\n this.batchSize = batchSize;\n this.blockMs = blockMs;\n }\n\n async ensureGroup(): Promise<void> {\n for (const s of this.streams) {\n try {\n // Start from the beginning so events published before the first worker\n // comes online are not silently skipped. Retention is controlled by the\n // producer's MAXLEN policy rather than consumer-group creation time.\n await this.redis.xgroup(\"CREATE\", s, this.group, \"0\", \"MKSTREAM\");\n this.logger?.info({ stream: s, group: this.group }, \"consumer group created\");\n } catch (err) {\n if (err instanceof Error && err.message.includes(\"BUSYGROUP\")) {\n this.logger?.debug({ stream: s, group: this.group }, \"consumer group already exists\");\n continue;\n }\n throw err;\n }\n }\n }\n\n async *readBatch(): AsyncGenerator<StreamMessage[], void, unknown> {\n this.running = true;\n let retryDelay = 1000;\n\n while (this.running) {\n try {\n let currentStreams = [...this.streams];\n // Weighted fair queuing: 10% of the time, rotate the priority order\n // to prevent starvation of low priority queues.\n if (currentStreams.length > 1 && Math.random() < 0.1) {\n const offset = Math.floor(Math.random() * (currentStreams.length - 1)) + 1;\n for (let i = 0; i < offset; i++) {\n currentStreams.push(currentStreams.shift()!);\n }\n }\n\n let results;\n let deadConnectionTimer: ReturnType<typeof setTimeout> | undefined;\n try {\n results = (await Promise.race([\n this.blockingRedis.xreadgroup(\n \"GROUP\",\n this.group,\n this.consumer,\n \"COUNT\",\n String(this.batchSize),\n \"BLOCK\",\n String(this.blockMs),\n \"STREAMS\",\n ...currentStreams,\n ...currentStreams.map(() => \">\"),\n ),\n new Promise((_, reject) => {\n deadConnectionTimer = setTimeout(\n () => reject(new Error(\"XREADGROUP_TIMEOUT_DEAD_CONNECTION\")),\n this.blockMs + 5000,\n );\n }),\n ])) as XReadGroupResult;\n } catch (err: any) {\n if (err.message === \"XREADGROUP_TIMEOUT_DEAD_CONNECTION\") {\n this.logger?.warn(\n \"XREADGROUP took too long, assuming dead connection. Disconnecting...\",\n );\n this.blockingRedis.disconnect();\n throw err;\n }\n throw err;\n } finally {\n // Whichever side of the race loses stays pending, so the guard timer\n // outlives the read it was guarding. Normally the loop turns over once\n // per `blockMs` and only a couple accumulate — but whenever the read\n // returns straight away, one timer per iteration piles up unbounded.\n clearTimeout(deadConnectionTimer);\n }\n\n retryDelay = 1000; // reset on success\n\n if (!results) continue;\n\n const batch: StreamMessage[] = [];\n for (const [streamName, messages] of results) {\n for (const [id, fields] of messages) {\n const msg = parseMessage(id, fields, this.logger);\n if (!msg) {\n await this.redis.xack(streamName, this.group, id);\n continue;\n }\n // Attach original stream name for dynamic acking\n msg.stream = streamName as StreamName;\n batch.push(msg);\n }\n }\n if (batch.length > 0) yield batch;\n } catch (err) {\n if (\n !this.running &&\n err instanceof Error &&\n err?.message?.toLowerCase?.().includes(\"connection is closed\")\n ) {\n break; // Expected during graceful shutdown\n }\n this.logger?.error({ err }, \"error reading from stream\");\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n retryDelay = Math.min(retryDelay * 2, 30_000); // Exponential backoff up to 30s\n }\n }\n }\n\n async ack(messageId: string | string[], stream?: string): Promise<void> {\n const s = stream ?? this.streams[0]!;\n const ids = Array.isArray(messageId) ? messageId : [messageId];\n if (ids.length === 0) return;\n await this.redis.xack(s, this.group, ...ids);\n this.logger?.debug({ stream: s, count: ids.length }, \"messages acknowledged\");\n }\n\n async nack(messageId: string, event: StreamEvent, stream?: string): Promise<void> {\n const s = stream ?? this.streams[0]!;\n if (this.dlqStream) {\n // Sequential rather than pipelined, and in this order: the ack is what\n // makes the drop final, so it must never run against a DLQ write that\n // did not land. A pipeline is not a transaction — both commands execute\n // regardless — so inspecting its results afterwards would be too late.\n // Throwing here leaves the message pending for the recovery loop, which\n // is the recoverable end of the trade.\n const dlqId = await this.redis.xadd(\n this.dlqStream,\n \"*\",\n \"data\",\n JSON.stringify({\n ...event,\n dlq: { originalStream: s, ackedAt: new Date().toISOString() },\n }),\n );\n\n if (!dlqId) {\n throw new Error(`XADD to dead-letter stream ${this.dlqStream} returned null`);\n }\n\n await this.redis.xack(s, this.group, messageId);\n this.logger?.warn(\n { stream: s, dlqStream: this.dlqStream, messageId, eventId: event.id, dlqId },\n \"message moved to dead-letter queue and acked\",\n );\n } else {\n await this.ack(messageId, s);\n }\n }\n\n async stop(): Promise<void> {\n this.running = false;\n try {\n await this.blockingRedis.quit();\n } catch (err: any) {\n if (!err?.message?.toLowerCase?.().includes(\"connection is closed\")) {\n throw err;\n }\n }\n }\n}\n\n// ─── PendingMessageScanner ─────────────────────────────────────────────────\n\nexport interface PendingMessageScannerOptions {\n redis: Redis;\n stream: StreamName | StreamName[];\n group: ConsumerGroup;\n consumer: string;\n logger?: Logger;\n}\n\nexport class PendingMessageScanner {\n private readonly redis: Redis;\n private readonly streams: StreamName[];\n private readonly group: ConsumerGroup;\n private readonly consumer: string;\n private readonly logger?: Logger;\n\n constructor({ redis, stream, group, consumer, logger }: PendingMessageScannerOptions) {\n this.redis = redis;\n this.streams = Array.isArray(stream) ? stream : [stream];\n this.group = group;\n this.consumer = consumer;\n this.logger = logger;\n }\n\n async getPendingCount(): Promise<number> {\n let total = 0;\n for (const s of this.streams) {\n const summary = await this.redis.xpending(s, this.group);\n if (Array.isArray(summary) && summary.length > 0) {\n const count = summary[0];\n if (typeof count === \"number\") total += count;\n }\n }\n return total;\n }\n\n /** Pending entries for one stream, or across all of them when `stream` is omitted. */\n async getPendingEntries(limit = 100, stream?: StreamName): Promise<PendingEntry[]> {\n const streams = stream ? [stream] : this.streams;\n const allEntries: PendingEntry[] = [];\n\n for (const s of streams) {\n const result = await this.redis.xpending(s, this.group, \"-\", \"+\", limit);\n if (Array.isArray(result)) {\n for (const item of result) {\n if (Array.isArray(item)) {\n allEntries.push({\n id: item[0],\n consumer: item[1],\n idleMs: item[2],\n deliveryCount: item[3],\n stream: s,\n });\n }\n }\n }\n if (allEntries.length >= limit) break;\n }\n return allEntries.slice(0, limit);\n }\n\n async autoclaim(minIdleMs: number, limit = 10): Promise<StreamMessage[]> {\n const recovered: StreamMessage[] = [];\n\n for (const s of this.streams) {\n if (recovered.length >= limit) break;\n\n // Scope the scan to THIS stream. Message ids are `<ms>-<seq>` and are not\n // unique across streams, so claiming an id gathered from another stream\n // can silently claim an unrelated message.\n const pending = await this.getPendingEntries(limit * 2, s);\n const toClaim = pending\n .filter((p) => p.idleMs > minIdleMs * Math.pow(2, p.deliveryCount - 1))\n .slice(0, limit - recovered.length);\n\n if (toClaim.length === 0) continue;\n\n const ids = toClaim.map((p) => p.id);\n // Let Redis arbitrate rather than claiming unconditionally. Two scanners\n // routinely list the same entry, and with a min-idle of 0 both claims\n // succeed and the message is processed twice; with the threshold applied\n // server-side the loser sees an entry whose idle time the winner has just\n // reset, and gets nothing back. The filter above is stricter than this,\n // so nothing it selected is excluded here for being too fresh.\n const result = (await this.redis.xclaim(\n s,\n this.group,\n this.consumer,\n minIdleMs,\n ...ids,\n )) as Array<[string, string[] | null]>;\n\n for (const raw of result) {\n if (!raw) continue;\n const [id, fields] = raw;\n const msg = parseMessage(id, fields, this.logger);\n if (msg) {\n msg.stream = s;\n recovered.push(msg);\n }\n }\n }\n\n if (recovered.length > 0) {\n this.logger?.info(\n { group: this.group, count: recovered.length },\n \"autoclaimed pending messages\",\n );\n }\n\n return recovered;\n }\n}\n","import { EventEmitter } from \"node:events\";\n\nexport const globalEmitter = new EventEmitter();\n","export class LRUCache<K, V> {\n private cache = new Map<K, { value: V; expiresAt: number }>();\n private readonly maxSize: number;\n private readonly defaultTtlMs: number;\n\n constructor(maxSize: number = 1000, defaultTtlMs: number = 5 * 60 * 1000) {\n this.maxSize = maxSize;\n this.defaultTtlMs = defaultTtlMs;\n }\n\n get(key: K): V | undefined {\n const item = this.cache.get(key);\n if (!item) return undefined;\n\n if (Date.now() > item.expiresAt) {\n this.cache.delete(key);\n return undefined;\n }\n\n // Refresh position to make it most recently used\n this.cache.delete(key);\n this.cache.set(key, item);\n return item.value;\n }\n\n set(key: K, value: V, ttlMs: number = this.defaultTtlMs): void {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxSize) {\n // Delete the oldest item (first inserted)\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.cache.delete(oldestKey);\n }\n }\n\n this.cache.set(key, { value, expiresAt: Date.now() + ttlMs });\n }\n\n delete(key: K): void {\n this.cache.delete(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n}\n","export function getPriorityBucket(\n priority: string | undefined,\n): \"critical\" | \"high\" | \"normal\" | \"low\" {\n const p = priority || \"normal\";\n return p === \"critical\" || p === \"high\" ? \"critical\" : p === \"low\" ? \"low\" : \"normal\";\n}\n\n/**\n * Canonical form of a destination, for suppression lookups.\n *\n * Both the writer (the provider webhook) and the reader (the engine's\n * pre-dispatch gate) must agree on this, or an unsubscribe recorded as\n * `Bob@Example.com` will not match a send addressed to `bob@example.com` and\n * the person keeps receiving mail. Case folding is safe for email domains and\n * for the local part in every mailbox provider in practice; phone numbers and\n * push tokens are case-sensitive and are only trimmed.\n */\nexport function normaliseTarget(target: string): string {\n const trimmed = target.trim();\n return trimmed.includes(\"@\") ? trimmed.toLowerCase() : trimmed;\n}\n\nexport const LUA_SCHEDULER_POLL = `\n local key = KEYS[1]\n local maxScore = tonumber(ARGV[1])\n local limit = tonumber(ARGV[2])\n local visibilityTimeout = tonumber(ARGV[3]) or 0\n local tasks = redis.call('ZRANGE', key, 0, maxScore, 'BYSCORE', 'LIMIT', 0, limit)\n if #tasks > 0 then\n for i, task in ipairs(tasks) do\n redis.call('ZADD', key, maxScore + visibilityTimeout, task)\n end\n end\n return tasks\n`;\n\nexport const LUA_SCHEDULER_CLAIM = `\n local payloadKey = KEYS[1]\n local claimedKey = KEYS[2]\n \n if redis.call('EXISTS', payloadKey) == 1 then\n redis.call('RENAME', payloadKey, claimedKey)\n return redis.call('GET', claimedKey)\n elseif redis.call('EXISTS', claimedKey) == 1 then\n return redis.call('GET', claimedKey)\n else\n return nil\n end\n`;\n/** Release a lock only if we still hold it (value matches our token). */\nexport const LUA_RELEASE_LOCK = `\n if redis.call('GET', KEYS[1]) == ARGV[1] then\n return redis.call('DEL', KEYS[1])\n end\n return 0\n`;\n\n/** Extend a lock's TTL only if we still hold it. */\nexport const LUA_RENEW_LOCK = `\n if redis.call('GET', KEYS[1]) == ARGV[1] then\n return redis.call('EXPIRE', KEYS[1], ARGV[2])\n end\n return 0\n`;\n","export class AsyncSemaphore {\n private count = 0;\n private queue: Array<() => void> = [];\n\n constructor(private readonly max: number) {}\n\n async acquire(): Promise<void> {\n if (this.count < this.max) {\n this.count++;\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n this.queue.push(resolve);\n });\n }\n\n release(): void {\n if (this.queue.length > 0) {\n // Hand the permit straight over rather than decrementing and letting the\n // next acquire take it back — occupancy is unchanged either way.\n const next = this.queue.shift()!;\n next();\n } else if (this.count > 0) {\n this.count--;\n }\n // A release with nothing held is a caller bug, but silently going negative\n // turns it into over-admission later, which is far harder to trace back.\n }\n\n get activeCount(): number {\n return this.count;\n }\n}\n","export interface BatchItem<T, R> {\n item: T;\n resolve: (value: R) => void;\n reject: (reason?: any) => void;\n}\n\nexport class BatchProcessor<T, R = void> {\n private buffer: BatchItem<T, R>[] = [];\n private timer: NodeJS.Timeout | null = null;\n private isFlushing = false;\n\n constructor(\n private readonly maxSize: number,\n private readonly maxWaitMs: number,\n private readonly flushFn: (items: T[]) => Promise<R[]>,\n ) {}\n\n add(item: T): Promise<R> {\n return new Promise((resolve, reject) => {\n this.buffer.push({ item, resolve, reject });\n if (this.buffer.length >= this.maxSize && !this.isFlushing) {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n void this.flush();\n } else if (!this.timer && !this.isFlushing) {\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.flush();\n }, this.maxWaitMs);\n }\n });\n }\n\n async flush(): Promise<void> {\n if (this.isFlushing || this.buffer.length === 0) return;\n\n this.isFlushing = true;\n const batch = this.buffer;\n this.buffer = [];\n\n try {\n const items = batch.map((b) => b.item);\n const results = await this.flushFn(items);\n for (let i = 0; i < batch.length; i++) {\n batch[i]!.resolve(results[i] as R);\n }\n } catch (err) {\n for (const b of batch) {\n b.reject(err);\n }\n } finally {\n this.isFlushing = false;\n if (this.buffer.length > 0 && !this.timer) {\n if (this.buffer.length >= this.maxSize) {\n void this.flush();\n } else {\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.flush();\n }, this.maxWaitMs);\n }\n }\n }\n }\n}\n","export interface CircuitBreakerOptions {\n failureThreshold: number;\n resetTimeoutMs: number;\n}\n\ntype State = \"CLOSED\" | \"OPEN\" | \"HALF_OPEN\";\n\nexport class CircuitBreaker {\n private state: State = \"CLOSED\";\n private failures = 0;\n private nextAttemptAt = 0;\n /** True while one caller is testing whether the dependency has recovered. */\n private probeInFlight = false;\n private readonly threshold: number;\n private readonly timeout: number;\n\n constructor(options: CircuitBreakerOptions) {\n this.threshold = options.failureThreshold;\n this.timeout = options.resetTimeoutMs;\n }\n\n async execute<T>(action: () => Promise<T>): Promise<T> {\n // Only one caller gets to find out whether the dependency is back. Letting\n // the whole waiting crowd through on the first tick after the timeout is\n // how a struggling provider gets knocked over again the moment it recovers.\n let isProbe = false;\n\n if (this.state === \"OPEN\") {\n if (Date.now() > this.nextAttemptAt && !this.probeInFlight) {\n this.state = \"HALF_OPEN\";\n this.probeInFlight = true;\n isProbe = true;\n } else {\n throw new Error(\"Circuit breaker is OPEN\");\n }\n } else if (this.state === \"HALF_OPEN\") {\n if (this.probeInFlight) {\n throw new Error(\"Circuit breaker is OPEN\");\n }\n this.probeInFlight = true;\n isProbe = true;\n }\n\n try {\n const result = await action();\n this.onSuccess();\n return result;\n } catch (err) {\n this.onFailure();\n throw err;\n } finally {\n if (isProbe) this.probeInFlight = false;\n }\n }\n\n private onSuccess() {\n this.failures = 0;\n this.state = \"CLOSED\";\n }\n\n private onFailure() {\n this.failures++;\n if (this.failures >= this.threshold) {\n this.state = \"OPEN\";\n // Restart the clock so the next probe waits a full timeout rather than\n // firing immediately off the previous deadline.\n this.nextAttemptAt = Date.now() + this.timeout;\n }\n }\n\n getState(): State {\n return this.state;\n }\n}\n","export class DataLoader<K, V> {\n private keys: K[] = [];\n private promises: Array<{ resolve: (value: V | Error) => void }> = [];\n private currentTick: Promise<void> | null = null;\n\n constructor(private readonly batchLoadFn: (keys: K[]) => Promise<(V | Error)[]>) {}\n\n load(key: K): Promise<V> {\n return new Promise((resolve, reject) => {\n this.keys.push(key);\n this.promises.push({\n resolve: (value) => {\n if (value instanceof Error) reject(value);\n else resolve(value);\n },\n });\n\n if (!this.currentTick) {\n this.currentTick = Promise.resolve().then(() => {\n const keysToLoad = this.keys;\n const currentPromises = this.promises;\n this.keys = [];\n this.promises = [];\n this.currentTick = null;\n\n this.batchLoadFn(keysToLoad)\n .then((results) => {\n for (let i = 0; i < currentPromises.length; i++) {\n currentPromises[i]!.resolve(results[i] as V | Error);\n }\n })\n .catch((err) => {\n for (const p of currentPromises) {\n p.resolve(err);\n }\n });\n });\n }\n });\n }\n}\n","import { randomUUID } from \"node:crypto\";\n\nexport function generateId(): string {\n return randomUUID();\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class AppError extends Error {\n readonly code: string;\n\n constructor(message: string, code: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"AppError\";\n this.code = code;\n }\n}\n\nexport class ValidationError extends AppError {\n readonly fields?: Record<string, string[]>;\n\n constructor(message: string, fields?: Record<string, string[]>, options?: ErrorOptions) {\n super(message, \"VALIDATION_ERROR\", options);\n this.name = \"ValidationError\";\n this.fields = fields;\n }\n}\n\nexport * from \"./events.js\";\nexport * from \"./cache.js\";\nexport * from \"./utils.js\";\nexport * from \"./semaphore.js\";\nexport * from \"./batch-processor.js\";\nexport * from \"./cache.js\";\nexport * from \"./circuit-breaker.js\";\nexport * from \"./dataloader.js\";\nexport { type WorkerOptions } from \"@/workers/index.js\";\n","import type { Redis } from \"@/index.js\";\nimport { LRUCache } from \"@/shared/index.js\";\n\nimport { randomUUID } from \"crypto\";\n\nexport interface ThrottleResult {\n allowed: boolean;\n count: number;\n limit: number;\n}\n\n// ─── Per-project overrides ──────────────────────────────────────────────────\n\n/**\n * Throttle overrides stored on the project row. `null` on either field means\n * \"no override\" — fall back to the process-wide default.\n */\nexport interface ProjectThrottleSettings {\n throttleLimit: number | null;\n throttleWindowHours: number | null;\n}\n\nconst NO_OVERRIDES: ProjectThrottleSettings = {\n throttleLimit: null,\n throttleWindowHours: null,\n};\n\nexport interface ProjectSettingsCacheOptions {\n maxSize?: number;\n ttlMs?: number;\n}\n\n/**\n * Caches per-project throttle overrides for the engine.\n *\n * The throttle check runs once per notification, so an uncached lookup here\n * would put a Postgres round trip on the hot path. Projects with no overrides\n * are cached as well — the common case must not cost a query per message.\n *\n * The TTL bounds staleness on its own; `invalidate()` exists so a settings\n * change published over pub/sub applies immediately rather than at expiry.\n */\nexport class ProjectSettingsCache {\n private readonly cache: LRUCache<string, ProjectThrottleSettings>;\n /**\n * Lookups already on the wire, keyed by project.\n *\n * The cache only fills once a query has come back, so without this a cold\n * project at the start of a campaign puts one query per in-flight message on\n * Postgres before the first answer lands — exactly when the database is\n * busiest. Followers wait on the leader's promise instead.\n */\n private readonly inFlight = new Map<string, Promise<ProjectThrottleSettings>>();\n\n constructor(\n private readonly load: (projectId: string) => Promise<ProjectThrottleSettings | null>,\n { maxSize = 1000, ttlMs = 60_000 }: ProjectSettingsCacheOptions = {},\n ) {\n this.cache = new LRUCache<string, ProjectThrottleSettings>(maxSize, ttlMs);\n }\n\n /**\n * Throws whatever the loader throws. The caller decides whether a settings\n * lookup failure should drop the message or fall back to defaults.\n */\n async get(projectId: string): Promise<ProjectThrottleSettings> {\n const cached = this.cache.get(projectId);\n if (cached) return cached;\n\n const existing = this.inFlight.get(projectId);\n if (existing) return existing;\n\n // Assigned before any callback below can run, since `load` cannot settle\n // within this synchronous block.\n let pending!: Promise<ProjectThrottleSettings>;\n pending = this.load(projectId)\n .then((settings) => {\n const resolved = settings ?? NO_OVERRIDES;\n // Only cache while this is still the current lookup: an invalidate()\n // that landed while the query was on the wire means the answer in hand\n // already describes the old settings.\n if (this.inFlight.get(projectId) === pending) {\n this.cache.set(projectId, resolved);\n }\n return resolved;\n })\n .finally(() => {\n // Cleared on failure too, so one bad lookup does not pin every later\n // caller to the same rejection.\n if (this.inFlight.get(projectId) === pending) {\n this.inFlight.delete(projectId);\n }\n });\n\n this.inFlight.set(projectId, pending);\n return pending;\n }\n\n invalidate(projectId: string): void {\n this.cache.delete(projectId);\n // A lookup that started before the change would write a stale value on\n // arrival; dropping it here sends the next caller back to the database.\n this.inFlight.delete(projectId);\n }\n\n clear(): void {\n this.cache.clear();\n this.inFlight.clear();\n }\n}\n\n// ─── UserThrottle ──────────────────────────────────────────────────────────\n// True sliding window counter using Redis ZSET: max N sends per window per user.\n\nexport interface UserThrottleOptions {\n redis: Redis;\n maxPerHour?: number;\n windowHours?: number;\n}\n\nexport interface ThrottleCheckOptions {\n /** Per-project cap for this window. `0` blocks every non-critical send. */\n limit?: number | null;\n /** Per-project window length in hours. */\n windowHours?: number | null;\n /** Future send time. The window is evaluated at that instant, not at now. */\n scheduledAt?: string;\n}\n\n/** Reject stored values that would make the window meaningless. */\nfunction positiveOrNull(value: number | null | undefined): number | null {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0 ? value : null;\n}\n\n/** A limit of 0 is a legitimate kill switch, so zero is allowed here. */\nfunction nonNegativeOrNull(value: number | null | undefined): number | null {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : null;\n}\n\nexport class UserThrottle {\n private readonly redis: Redis;\n private readonly maxPerHour: number;\n private readonly windowHours: number;\n\n constructor({ redis, maxPerHour = 3, windowHours = 1 }: UserThrottleOptions) {\n this.redis = redis;\n this.maxPerHour = maxPerHour;\n this.windowHours = windowHours;\n }\n\n /**\n * @param projectId Tenant that owns `userId`. User ids are caller-supplied\n * external ids, so they collide across tenants and MUST be namespaced —\n * otherwise one tenant's traffic throttles another's.\n * @param options Per-project overrides. Values that are absent, null, or\n * nonsensical fall back to this instance's defaults.\n */\n async check(\n projectId: string,\n userId: string,\n priority?: string,\n options: ThrottleCheckOptions = {},\n ): Promise<ThrottleResult> {\n const limit = nonNegativeOrNull(options.limit) ?? this.maxPerHour;\n const windowHours = positiveOrNull(options.windowHours) ?? this.windowHours;\n\n if (priority === \"critical\") {\n return { allowed: true, count: 0, limit };\n }\n\n const windowMs = windowHours * 3600_000;\n const key = `throttle:${projectId}:user:${userId}`;\n const targetTime = options.scheduledAt ? new Date(options.scheduledAt).getTime() : Date.now();\n const windowStart = targetTime - windowMs;\n const memberId = randomUUID();\n\n const LUA_THROTTLE = `\n redis.call(\"ZREMRANGEBYSCORE\", KEYS[1], \"-inf\", ARGV[1])\n local count = redis.call(\"ZCARD\", KEYS[1])\n if tonumber(count) < tonumber(ARGV[2]) then\n redis.call(\"ZADD\", KEYS[1], tonumber(ARGV[3]), ARGV[4])\n redis.call(\"EXPIRE\", KEYS[1], tonumber(ARGV[5]))\n return tonumber(count) + 1\n end\n return tonumber(count) + 1\n `;\n\n // The key must outlive the window it is counting. For a future-dated send\n // that means surviving until targetTime plus one more window, so a task\n // scheduled for next week still counts against the right bucket.\n const windowSeconds = Math.ceil(windowMs / 1000);\n const ttlSeconds = Math.max(\n windowSeconds,\n Math.ceil((targetTime - Date.now()) / 1000) + windowSeconds,\n );\n\n const count = (await this.redis.eval(\n LUA_THROTTLE,\n 1,\n key,\n windowStart,\n limit,\n targetTime,\n memberId,\n ttlSeconds,\n )) as number;\n\n return { allowed: count <= limit, count, limit };\n }\n}\n","import { Redis, type RedisOptions } from \"ioredis\";\nimport type { Logger } from \"@/index.js\";\n\nexport interface RedisClientOptions {\n url: string;\n name?: string;\n logger?: Logger;\n redisOptions?: Partial<RedisOptions>;\n}\n\nexport class RedisClient {\n readonly native: Redis;\n\n private readonly logger?: Logger;\n private isClosing = false;\n\n constructor({ url, name = \"notifkit\", logger, redisOptions }: RedisClientOptions) {\n this.logger = logger;\n\n this.native = new Redis(url, {\n maxRetriesPerRequest: null,\n enableReadyCheck: true,\n lazyConnect: false,\n connectionName: name,\n ...redisOptions,\n });\n\n this.native.on(\"connect\", () => {\n this.logger?.info({ url: redactUrl(url) }, \"redis connected\");\n });\n\n this.native.on(\"ready\", () => {\n this.logger?.debug(\"redis ready\");\n });\n\n this.native.on(\"error\", (err: Error) => {\n this.logger?.error({ err }, \"redis client error\");\n });\n\n this.native.on(\"close\", () => {\n if (!this.isClosing) {\n this.logger?.warn(\"redis connection closed unexpectedly\");\n }\n });\n\n this.native.on(\"reconnecting\", () => {\n this.logger?.warn(\"redis reconnecting\");\n });\n }\n\n async healthCheck(): Promise<boolean> {\n try {\n const pong = await this.native.ping();\n return pong === \"PONG\";\n } catch {\n return false;\n }\n }\n\n async disconnect(): Promise<void> {\n this.isClosing = true;\n this.logger?.info(\"disconnecting redis\");\n await this.native.quit();\n this.logger?.info(\"redis disconnected\");\n }\n}\n\nfunction redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (parsed.password) parsed.password = \"***\";\n return parsed.toString();\n } catch {\n return \"[invalid-url]\";\n }\n}\n\nexport { Redis, type RedisOptions };\n","import { eq, and, sql as drizzleSql, inArray, desc } from \"drizzle-orm\";\nimport type { Db } from \"@/index.js\";\nimport type { Preferences, ContactChannel } from \"@/contracts/index.js\";\nimport {\n users,\n userSegments,\n userContacts,\n userChannelPreferences,\n userTopicPreferences,\n contactTopicPreferences,\n quietHours,\n templates,\n projects,\n workflowInstances,\n workflowSteps,\n workflowWaiters,\n workflowDefinitions,\n projectApiKeys,\n suppressions,\n messageLogs,\n} from \"@/db/schema.js\";\n\n// ─── Domain types ───────────────────────────────────────────────────────────\n\nexport interface UserProfile {\n userId: string; // Maps to externalId\n language?: string;\n timezone?: string;\n email?: string | null;\n}\n\nexport interface UserRecord extends UserProfile {\n segments: string[];\n preferences: Preferences;\n}\n\nexport interface UserContact {\n id: string; // uuid\n userId: string; // externalId\n channel: ContactChannel;\n target: string;\n preferences: Preferences;\n active: boolean;\n}\n\nexport interface TemplateRecord {\n id: string;\n channel: string;\n topics: string[];\n content: unknown;\n /** Template-level AI prompts, merged with per-request ones by the engine. */\n aiPrompts?: Record<string, string> | null;\n}\n\nexport type DevicePlatform = \"fcm\" | \"apns\" | \"web\";\n\nexport interface DeviceToken {\n id: string;\n userId: string;\n deviceToken: string;\n platform: DevicePlatform;\n active: boolean;\n}\n\nexport interface NotificationPreference {\n userId: string;\n eventType: string;\n optedIn: boolean;\n}\n\n// ─── UserRepository ─────────────────────────────────────────────────────────\n\nexport class UserRepository {\n constructor(private readonly db: Db) {}\n\n async findById(projectId: string, userId: string): Promise<UserProfile | null> {\n const rows = await this.db\n .select()\n .from(users)\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)))\n .limit(1);\n\n if (!rows[0]) return null;\n const attrs = rows[0].attributes as any;\n return {\n userId: rows[0].externalId,\n language: attrs.language,\n timezone: attrs.timezone,\n email: attrs.email,\n };\n }\n\n async findRecordById(projectId: string, userId: string): Promise<UserRecord | null> {\n const userRows = await this.db\n .select()\n .from(users)\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));\n if (!userRows[0]) return null;\n\n const userRow = userRows[0];\n const internalId = userRow.id;\n const attrs = userRow.attributes as any;\n\n const [segmentRows, topicRows, channelRows, qhRows] = await Promise.all([\n this.db.select().from(userSegments).where(eq(userSegments.userId, internalId)),\n this.db\n .select()\n .from(userTopicPreferences)\n .where(eq(userTopicPreferences.userId, internalId)),\n this.db\n .select()\n .from(userChannelPreferences)\n .where(eq(userChannelPreferences.userId, internalId)),\n this.db.select().from(quietHours).where(eq(quietHours.userId, internalId)),\n ]);\n\n const segments = segmentRows.map((r) => r.segment);\n\n const topics: Record<string, boolean> = {};\n for (const r of topicRows) {\n topics[r.topic] = r.enabled;\n }\n\n const channels: Record<string, boolean> = {};\n for (const r of channelRows) {\n channels[r.channel] = r.enabled;\n }\n\n const quietHoursList = qhRows.map((r) => ({\n start: r.startTime.slice(0, 5),\n end: r.endTime.slice(0, 5),\n }));\n\n return {\n userId: userRow.externalId,\n language: attrs.language,\n timezone: attrs.timezone,\n email: attrs.email,\n segments,\n preferences: {\n channels,\n topics,\n quietHours: quietHoursList.length > 0 ? quietHoursList : undefined,\n },\n };\n }\n\n async findRecordsByIds(projectId: string, userIds: string[]): Promise<UserRecord[]> {\n if (userIds.length === 0) return [];\n\n const usersRows = await this.db\n .select()\n .from(users)\n .where(and(inArray(users.externalId, userIds), eq(users.projectId, projectId)));\n\n if (usersRows.length === 0) return [];\n\n const internalIds = usersRows.map((r) => r.id);\n\n const [segmentRows, topicRows, channelRows, qhRows] = await Promise.all([\n this.db.select().from(userSegments).where(inArray(userSegments.userId, internalIds)),\n this.db\n .select()\n .from(userTopicPreferences)\n .where(inArray(userTopicPreferences.userId, internalIds)),\n this.db\n .select()\n .from(userChannelPreferences)\n .where(inArray(userChannelPreferences.userId, internalIds)),\n this.db.select().from(quietHours).where(inArray(quietHours.userId, internalIds)),\n ]);\n\n const segmentsByUserId = new Map<string, string[]>();\n for (const r of segmentRows) {\n if (!segmentsByUserId.has(r.userId)) segmentsByUserId.set(r.userId, []);\n segmentsByUserId.get(r.userId)!.push(r.segment);\n }\n\n const topicsByUserId = new Map<string, Record<string, boolean>>();\n for (const r of topicRows) {\n if (!topicsByUserId.has(r.userId)) topicsByUserId.set(r.userId, {});\n topicsByUserId.get(r.userId)![r.topic] = r.enabled;\n }\n\n const channelsByUserId = new Map<string, Record<string, boolean>>();\n for (const r of channelRows) {\n if (!channelsByUserId.has(r.userId)) channelsByUserId.set(r.userId, {});\n channelsByUserId.get(r.userId)![r.channel] = r.enabled;\n }\n\n const qhByUserId = new Map<string, any[]>();\n for (const r of qhRows) {\n if (r.userId == null) continue;\n if (!qhByUserId.has(r.userId)) qhByUserId.set(r.userId, []);\n qhByUserId\n .get(r.userId)!\n .push({ start: r.startTime.slice(0, 5), end: r.endTime.slice(0, 5) });\n }\n\n const userRecords: UserRecord[] = [];\n\n for (const userRow of usersRows) {\n const attrs = userRow.attributes as any;\n const internalId = userRow.id;\n\n userRecords.push({\n userId: userRow.externalId,\n language: attrs.language,\n timezone: attrs.timezone,\n email: attrs.email,\n segments: segmentsByUserId.get(internalId) || [],\n preferences: {\n channels: channelsByUserId.get(internalId) || {},\n topics: topicsByUserId.get(internalId) || {},\n quietHours: qhByUserId.get(internalId),\n },\n });\n }\n\n return userRecords;\n }\n\n async upsertFull(\n projectId: string,\n user: {\n userId: string;\n language?: string;\n timezone?: string;\n email?: string | null;\n segments: string[];\n preferences: Preferences;\n },\n ): Promise<void> {\n await this.db.transaction(async (tx) => {\n await tx\n .insert(users)\n .values({\n projectId,\n externalId: user.userId,\n attributes: {\n language: user.language,\n timezone: user.timezone,\n email: user.email,\n },\n })\n .onConflictDoUpdate({\n target: [users.projectId, users.externalId],\n set: {\n attributes: {\n language: user.language,\n timezone: user.timezone,\n email: user.email,\n },\n updatedAt: new Date(),\n },\n });\n\n const internalIdRows = await tx\n .select({ id: users.id })\n .from(users)\n .where(and(eq(users.externalId, user.userId), eq(users.projectId, projectId)));\n const internalId = internalIdRows[0]?.id;\n if (!internalId) return;\n\n if (user.segments && user.segments.length > 0) {\n await tx\n .insert(userSegments)\n .values(user.segments.map((s) => ({ userId: internalId, segment: s })))\n .onConflictDoNothing();\n }\n\n if (user.preferences && user.preferences.topics) {\n const topicInserts = Object.entries(user.preferences.topics).map(([topic, enabled]) => ({\n userId: internalId,\n topic,\n enabled,\n }));\n if (topicInserts.length > 0) {\n await tx\n .insert(userTopicPreferences)\n .values(topicInserts)\n .onConflictDoUpdate({\n target: [userTopicPreferences.userId, userTopicPreferences.topic],\n set: { enabled: drizzleSql`excluded.enabled` },\n });\n }\n }\n\n if (user.preferences?.channels) {\n const channelInserts = Object.entries(user.preferences.channels).map(\n ([channel, enabled]) => ({\n userId: internalId,\n channel: channel as ContactChannel,\n enabled,\n }),\n );\n if (channelInserts.length > 0) {\n await tx\n .insert(userChannelPreferences)\n .values(channelInserts)\n .onConflictDoUpdate({\n target: [userChannelPreferences.userId, userChannelPreferences.channel],\n set: { enabled: drizzleSql`excluded.enabled` },\n });\n }\n }\n\n if (user.preferences?.quietHours !== undefined) {\n await tx.delete(quietHours).where(eq(quietHours.userId, internalId));\n if (user.preferences.quietHours.length > 0) {\n await tx.insert(quietHours).values(\n user.preferences.quietHours.map((window) => ({\n userId: internalId,\n startTime: window.start,\n endTime: window.end,\n })),\n );\n }\n }\n });\n }\n\n async upsertManyFull(\n projectId: string,\n usersList: Array<{\n userId: string;\n language?: string;\n timezone?: string;\n email?: string | null;\n segments: string[];\n preferences: Preferences;\n }>,\n ): Promise<void> {\n if (usersList.length === 0) return;\n\n let attempts = 0;\n while (attempts < 3) {\n try {\n await this.db.transaction(async (tx) => {\n await tx\n .insert(users)\n .values(\n usersList.map((u) => ({\n projectId,\n externalId: u.userId,\n attributes: {\n language: u.language,\n timezone: u.timezone,\n email: u.email,\n },\n })),\n )\n .onConflictDoUpdate({\n target: [users.projectId, users.externalId],\n set: {\n attributes: drizzleSql`excluded.attributes`,\n updatedAt: new Date(),\n },\n });\n\n const internalIdRows = await tx\n .select({ id: users.id, externalId: users.externalId })\n .from(users)\n .where(\n and(\n inArray(\n users.externalId,\n usersList.map((u) => u.userId),\n ),\n eq(users.projectId, projectId),\n ),\n );\n\n const idMap = new Map(internalIdRows.map((r) => [r.externalId, r.id]));\n\n const segmentInserts: any[] = [];\n const topicInserts: any[] = [];\n const channelInserts: any[] = [];\n const quietHoursInserts: any[] = [];\n\n for (const u of usersList) {\n const internalId = idMap.get(u.userId);\n if (!internalId) continue;\n\n if (u.segments && u.segments.length > 0) {\n for (const s of u.segments) {\n segmentInserts.push({ userId: internalId, segment: s });\n }\n }\n\n if (u.preferences?.topics) {\n for (const [topic, enabled] of Object.entries(u.preferences.topics)) {\n topicInserts.push({ userId: internalId, topic, enabled });\n }\n }\n\n if (u.preferences?.channels) {\n for (const [channel, enabled] of Object.entries(u.preferences.channels)) {\n channelInserts.push({\n userId: internalId,\n channel: channel as ContactChannel,\n enabled,\n });\n }\n }\n\n if (u.preferences?.quietHours && u.preferences.quietHours.length > 0) {\n for (const window of u.preferences.quietHours) {\n quietHoursInserts.push({\n userId: internalId,\n startTime: window.start,\n endTime: window.end,\n });\n }\n }\n }\n\n const segmentSet = new Set<string>();\n const dedupedSegmentInserts: any[] = [];\n for (const s of segmentInserts) {\n const key = `${s.userId}:${s.segment}`;\n if (!segmentSet.has(key)) {\n segmentSet.add(key);\n dedupedSegmentInserts.push(s);\n }\n }\n\n const topicMap = new Map<string, any>();\n for (const t of topicInserts) {\n topicMap.set(`${t.userId}:${t.topic}`, t);\n }\n const dedupedTopicInserts = Array.from(topicMap.values());\n\n const channelMap = new Map<string, any>();\n for (const c of channelInserts) {\n channelMap.set(`${c.userId}:${c.channel}`, c);\n }\n const dedupedChannelInserts = Array.from(channelMap.values());\n\n if (dedupedSegmentInserts.length > 0) {\n await tx.insert(userSegments).values(dedupedSegmentInserts).onConflictDoNothing();\n }\n\n if (dedupedTopicInserts.length > 0) {\n await tx\n .insert(userTopicPreferences)\n .values(dedupedTopicInserts)\n .onConflictDoUpdate({\n target: [userTopicPreferences.userId, userTopicPreferences.topic],\n set: { enabled: drizzleSql`excluded.enabled` },\n });\n }\n\n if (dedupedChannelInserts.length > 0) {\n await tx\n .insert(userChannelPreferences)\n .values(dedupedChannelInserts)\n .onConflictDoUpdate({\n target: [userChannelPreferences.userId, userChannelPreferences.channel],\n set: { enabled: drizzleSql`excluded.enabled` },\n });\n }\n\n const usersWithQuietHours = usersList.filter(\n (u) => u.preferences?.quietHours !== undefined,\n );\n const internalIdsToClearQuietHours = usersWithQuietHours\n .map((u) => idMap.get(u.userId))\n .filter(Boolean) as string[];\n\n if (internalIdsToClearQuietHours.length > 0) {\n await tx\n .delete(quietHours)\n .where(inArray(quietHours.userId, internalIdsToClearQuietHours));\n }\n if (quietHoursInserts.length > 0) {\n await tx.insert(quietHours).values(quietHoursInserts);\n }\n });\n return;\n } catch (err: any) {\n attempts++;\n if (attempts >= 3 || (err.code !== \"40001\" && err.code !== \"40P01\")) {\n throw err;\n }\n await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempts) * 100));\n }\n }\n }\n\n async updatePartial(\n projectId: string,\n userId: string,\n patch: {\n language?: string;\n timezone?: string;\n email?: string | null;\n segments?: string[];\n preferences?: Preferences;\n },\n ): Promise<boolean> {\n const existing = await this.findById(projectId, userId);\n if (!existing) return false;\n\n const attrs = {\n language: patch.language ?? existing.language,\n timezone: patch.timezone ?? existing.timezone,\n email: patch.email ?? existing.email,\n };\n\n await this.db.transaction(async (tx) => {\n await tx\n .update(users)\n .set({ attributes: attrs, updatedAt: new Date() })\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));\n\n const internalIdRows = await tx\n .select({ id: users.id })\n .from(users)\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));\n const internalId = internalIdRows[0]?.id;\n if (!internalId) return;\n\n if (patch.segments) {\n await tx.delete(userSegments).where(eq(userSegments.userId, internalId));\n if (patch.segments.length > 0) {\n await tx\n .insert(userSegments)\n .values(patch.segments.map((s) => ({ userId: internalId, segment: s })))\n .onConflictDoNothing();\n }\n }\n\n if (patch.preferences && patch.preferences.topics) {\n const topicInserts = Object.entries(patch.preferences.topics).map(([topic, enabled]) => ({\n userId: internalId,\n topic,\n enabled,\n }));\n if (topicInserts.length > 0) {\n await tx\n .insert(userTopicPreferences)\n .values(topicInserts)\n .onConflictDoUpdate({\n target: [userTopicPreferences.userId, userTopicPreferences.topic],\n set: { enabled: drizzleSql`excluded.enabled` },\n });\n }\n }\n\n if (patch.preferences?.channels) {\n const channelInserts = Object.entries(patch.preferences.channels).map(\n ([channel, enabled]) => ({\n userId: internalId,\n channel: channel as ContactChannel,\n enabled,\n }),\n );\n if (channelInserts.length > 0) {\n await tx\n .insert(userChannelPreferences)\n .values(channelInserts)\n .onConflictDoUpdate({\n target: [userChannelPreferences.userId, userChannelPreferences.channel],\n set: { enabled: drizzleSql`excluded.enabled` },\n });\n }\n }\n\n if (patch.preferences?.quietHours !== undefined) {\n await tx.delete(quietHours).where(eq(quietHours.userId, internalId));\n if (patch.preferences.quietHours.length > 0) {\n await tx.insert(quietHours).values(\n patch.preferences.quietHours.map((window) => ({\n userId: internalId,\n startTime: window.start,\n endTime: window.end,\n })),\n );\n }\n }\n });\n\n return true;\n }\n\n async delete(projectId: string, userId: string): Promise<boolean> {\n const result = await this.db\n .delete(users)\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)))\n .returning();\n return result.length > 0;\n }\n\n async list(\n projectId: string,\n limit: number,\n cursor?: string,\n filters?: {\n search?: string;\n segment?: string;\n language?: string;\n timezone?: string;\n channel?: string;\n },\n ): Promise<{ users: UserProfile[]; nextCursor: string | null }> {\n const conditions = [eq(users.projectId, projectId)];\n\n if (cursor) {\n const cursorDate = new Date(parseInt(cursor, 10));\n if (!isNaN(cursorDate.getTime())) {\n conditions.push(drizzleSql`${users.createdAt} < ${cursorDate.toISOString()}`);\n }\n }\n\n if (filters?.language) {\n conditions.push(drizzleSql`(${users.attributes}->>'language') = ${filters.language}`);\n }\n\n if (filters?.timezone) {\n conditions.push(drizzleSql`(${users.attributes}->>'timezone') = ${filters.timezone}`);\n }\n\n if (filters?.search) {\n const term = `%${filters.search.trim()}%`;\n conditions.push(\n drizzleSql`(${users.externalId} ILIKE ${term} OR (${users.attributes}->>'email') ILIKE ${term})`,\n );\n }\n\n if (filters?.segment) {\n conditions.push(\n drizzleSql`EXISTS (SELECT 1 FROM ${userSegments} WHERE ${userSegments.userId} = ${users.id} AND ${userSegments.segment} = ${filters.segment})`,\n );\n }\n\n if (filters?.channel) {\n conditions.push(\n drizzleSql`EXISTS (SELECT 1 FROM ${userContacts} WHERE ${userContacts.userId} = ${users.id} AND ${userContacts.channel} = ${filters.channel})`,\n );\n }\n\n const rows = await this.db\n .select()\n .from(users)\n .where(and(...conditions))\n .orderBy(desc(users.createdAt))\n .limit(limit);\n\n const items = rows.map((r) => {\n const attrs = r.attributes as any;\n return {\n userId: r.externalId,\n language: attrs.language,\n timezone: attrs.timezone,\n email: attrs.email,\n createdAt: r.createdAt.getTime(),\n };\n });\n\n const nextCursor =\n items.length === limit ? items[items.length - 1]!.createdAt.toString() : null;\n return { users: items, nextCursor };\n }\n\n async findUsersBySegment(projectId: string, segmentName: string): Promise<string[]> {\n const rows = await this.db\n .select({ externalId: users.externalId })\n .from(users)\n .innerJoin(userSegments, eq(users.id, userSegments.userId))\n .where(and(eq(userSegments.segment, segmentName), eq(users.projectId, projectId)));\n\n return rows.map((r) => r.externalId);\n }\n\n async findUsersByTopic(projectId: string, topicName: string): Promise<string[]> {\n const rows = await this.db\n .select({ externalId: users.externalId })\n .from(users)\n .innerJoin(userTopicPreferences, eq(users.id, userTopicPreferences.userId))\n .where(\n and(\n eq(userTopicPreferences.topic, topicName),\n eq(userTopicPreferences.enabled, true),\n eq(users.projectId, projectId),\n ),\n );\n\n return rows.map((r) => r.externalId);\n }\n}\n\n// ─── PreferenceRepository ────────────────────────────────────────────────────\n\nexport class PreferenceRepository {\n constructor(private readonly db: Db) {}\n\n async isOptedIn(projectId: string, userId: string, eventType: string): Promise<boolean> {\n const prefs = await this.findByUserId(projectId, userId);\n const pref = prefs.find((p) => p.eventType === eventType);\n return pref ? pref.optedIn : true;\n }\n\n async findByUserId(projectId: string, userId: string): Promise<NotificationPreference[]> {\n const internalUserIdRows = await this.db\n .select({ id: users.id })\n .from(users)\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));\n const internalId = internalUserIdRows[0]?.id;\n if (!internalId) return [];\n\n const rows = await this.db\n .select()\n .from(userTopicPreferences)\n .where(eq(userTopicPreferences.userId, internalId));\n\n return rows.map((r) => ({\n userId,\n eventType: r.topic,\n optedIn: r.enabled,\n }));\n }\n}\n\n// ─── ContactRepository ───────────────────────────────────────────────────────\n\nexport class ContactRepository {\n constructor(private readonly db: Db) {}\n\n async findByUserId(projectId: string, userId: string): Promise<UserContact[]> {\n const internalUserIdRows = await this.db\n .select({ id: users.id })\n .from(users)\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));\n const internalId = internalUserIdRows[0]?.id;\n if (!internalId) return [];\n\n const rows = await this.db\n .select()\n .from(userContacts)\n .where(eq(userContacts.userId, internalId));\n if (rows.length === 0) return [];\n\n const topicRows = await this.db\n .select()\n .from(contactTopicPreferences)\n .where(\n inArray(\n contactTopicPreferences.contactId,\n rows.map((r) => r.id),\n ),\n );\n\n const topicsByContact = new Map<string, Record<string, boolean>>();\n for (const t of topicRows) {\n if (!topicsByContact.has(t.contactId)) topicsByContact.set(t.contactId, {});\n topicsByContact.get(t.contactId)![t.topic] = t.enabled;\n }\n\n return rows.map((r) => ({\n id: r.id,\n userId,\n channel: r.channel as ContactChannel,\n target: r.target,\n preferences: { topics: topicsByContact.get(r.id) ?? {} },\n active: r.enabled,\n }));\n }\n\n /** Resolve active addressable contacts for a batch without an N+1 query. */\n async findActiveByUserIds(\n projectId: string,\n userIds: string[],\n ): Promise<Map<string, UserContact[]>> {\n const byUser = new Map<string, UserContact[]>();\n if (userIds.length === 0) return byUser;\n\n const rows = await this.db\n .select({\n userId: users.externalId,\n id: userContacts.id,\n channel: userContacts.channel,\n target: userContacts.target,\n enabled: userContacts.enabled,\n })\n .from(users)\n .innerJoin(userContacts, eq(users.id, userContacts.userId))\n .where(\n and(\n eq(users.projectId, projectId),\n inArray(users.externalId, userIds),\n eq(userContacts.enabled, true),\n ),\n );\n\n for (const row of rows) {\n const contacts = byUser.get(row.userId) ?? [];\n contacts.push({\n id: row.id,\n userId: row.userId,\n channel: row.channel as ContactChannel,\n target: row.target,\n preferences: {},\n active: row.enabled,\n });\n byUser.set(row.userId, contacts);\n }\n return byUser;\n }\n\n async upsert(\n projectId: string,\n userId: string,\n channel: ContactChannel,\n target: string,\n preferences: Preferences = {},\n ): Promise<void> {\n const internalUserIdRows = await this.db\n .select({ id: users.id })\n .from(users)\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));\n const internalId = internalUserIdRows[0]?.id;\n if (!internalId) return;\n\n const inserted = await this.db\n .insert(userContacts)\n .values({\n userId: internalId,\n channel: channel as any,\n target,\n enabled: true,\n })\n .onConflictDoUpdate({\n target: [userContacts.userId, userContacts.channel, userContacts.target],\n // Re-adding a contact re-enables it; this is also how a device token\n // deactivated by an invalid-token response comes back.\n set: { enabled: true },\n })\n .returning({ id: userContacts.id });\n\n const contactId = inserted[0]?.id;\n if (!contactId) return;\n\n // Contact-level topic preferences were previously accepted by the API and\n // silently discarded.\n const topics = Object.entries(preferences.topics ?? {});\n if (topics.length > 0) {\n await this.db\n .insert(contactTopicPreferences)\n .values(topics.map(([topic, enabled]) => ({ contactId, topic, enabled })))\n .onConflictDoUpdate({\n target: [contactTopicPreferences.contactId, contactTopicPreferences.topic],\n set: { enabled: drizzleSql`excluded.enabled` },\n });\n }\n }\n\n async upsertMany(\n projectId: string,\n contactsList: Array<{\n userId: string;\n channel: ContactChannel;\n target: string;\n preferences?: Preferences;\n }>,\n ): Promise<void> {\n if (contactsList.length === 0) return;\n\n const internalUserIdRows = await this.db\n .select({ id: users.id, externalId: users.externalId })\n .from(users)\n .where(\n and(\n inArray(\n users.externalId,\n contactsList.map((c) => c.userId),\n ),\n eq(users.projectId, projectId),\n ),\n );\n\n const idMap = new Map(internalUserIdRows.map((r) => [r.externalId, r.id]));\n\n const validContacts = contactsList.filter((c) => idMap.has(c.userId));\n if (validContacts.length === 0) return;\n\n const inserted = await this.db\n .insert(userContacts)\n .values(\n validContacts.map((c) => ({\n userId: idMap.get(c.userId)!,\n channel: c.channel as any,\n target: c.target,\n enabled: true,\n })),\n )\n .onConflictDoUpdate({\n target: [userContacts.userId, userContacts.channel, userContacts.target],\n set: { enabled: true },\n })\n .returning({\n id: userContacts.id,\n userId: userContacts.userId,\n channel: userContacts.channel,\n target: userContacts.target,\n });\n\n const contactIdMap = new Map();\n for (const row of inserted) {\n contactIdMap.set(`${row.userId}:${row.channel}:${row.target}`, row.id);\n }\n\n const topicInserts: any[] = [];\n for (const c of validContacts) {\n const internalId = idMap.get(c.userId)!;\n const contactId = contactIdMap.get(`${internalId}:${c.channel}:${c.target}`);\n if (!contactId || !c.preferences?.topics) continue;\n\n for (const [topic, enabled] of Object.entries(c.preferences.topics)) {\n topicInserts.push({ contactId, topic, enabled });\n }\n }\n\n if (topicInserts.length > 0) {\n await this.db\n .insert(contactTopicPreferences)\n .values(topicInserts)\n .onConflictDoUpdate({\n target: [contactTopicPreferences.contactId, contactTopicPreferences.topic],\n set: { enabled: drizzleSql`excluded.enabled` },\n });\n }\n }\n\n /**\n * Mark a contact unusable without destroying it — used when a provider\n * reports an invalid push token. Deleting the row would lose the user's\n * device permanently on what is often a transient provider response.\n */\n async deactivate(\n projectId: string,\n userId: string,\n channel: ContactChannel,\n target: string,\n ): Promise<boolean> {\n const internalUserIdRows = await this.db\n .select({ id: users.id })\n .from(users)\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));\n const internalId = internalUserIdRows[0]?.id;\n if (!internalId) return false;\n\n const result = await this.db\n .update(userContacts)\n .set({ enabled: false })\n .where(\n and(\n eq(userContacts.userId, internalId),\n eq(userContacts.channel, channel as any),\n eq(userContacts.target, target),\n ),\n )\n .returning();\n return result.length > 0;\n }\n\n async delete(\n projectId: string,\n userId: string,\n channel: ContactChannel,\n target: string,\n ): Promise<boolean> {\n const internalUserIdRows = await this.db\n .select({ id: users.id })\n .from(users)\n .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));\n const internalId = internalUserIdRows[0]?.id;\n if (!internalId) return false;\n\n const result = await this.db\n .delete(userContacts)\n .where(\n and(\n eq(userContacts.userId, internalId),\n eq(userContacts.channel, channel as any),\n eq(userContacts.target, target),\n ),\n )\n .returning();\n return result.length > 0;\n }\n}\n\n// ─── TemplateRepository ──────────────────────────────────────────────────────\n\nexport class TemplateRepository {\n constructor(private readonly db: Db) {}\n\n async findById(projectId: string, id: string): Promise<TemplateRecord | null> {\n const rows = await this.db\n .select()\n .from(templates)\n .where(and(eq(templates.id, id), eq(templates.projectId, projectId)))\n .limit(1);\n if (!rows[0]) return null;\n return {\n id: rows[0].id,\n channel: rows[0].channel,\n content: rows[0].content,\n topics: (rows[0].topics ?? []) as string[],\n aiPrompts: rows[0].aiPrompts as Record<string, string> | null,\n };\n }\n\n async list(projectId: string): Promise<TemplateRecord[]> {\n const rows = await this.db.select().from(templates).where(eq(templates.projectId, projectId));\n return rows.map((r) => ({\n id: r.id,\n channel: r.channel,\n content: r.content,\n topics: (r.topics ?? []) as string[],\n aiPrompts: r.aiPrompts as Record<string, string> | null,\n }));\n }\n\n async upsertMany(projectId: string, templateList: any[]): Promise<number> {\n if (templateList.length === 0) return 0;\n\n const values = templateList.map((t) => ({\n projectId,\n id: t.id,\n channel: t.channel as any,\n topics: t.topics ?? [],\n content: t.content,\n aiPrompts: t.aiPrompts,\n }));\n\n await this.db\n .insert(templates)\n .values(values)\n .onConflictDoUpdate({\n target: [templates.projectId, templates.id],\n set: {\n channel: drizzleSql`excluded.channel`,\n topics: drizzleSql`excluded.topics`,\n content: drizzleSql`excluded.content`,\n aiPrompts: drizzleSql`excluded.ai_prompts`,\n updatedAt: new Date(),\n },\n });\n\n return templateList.length;\n }\n\n async delete(projectId: string, id: string): Promise<boolean> {\n const result = await this.db\n .delete(templates)\n .where(and(eq(templates.id, id), eq(templates.projectId, projectId)))\n .returning();\n return result.length > 0;\n }\n}\n\n// ─── ProjectRepository ───────────────────────────────────────────────────────\n\nexport class ProjectRepository {\n constructor(private readonly db: Db) {}\n\n async list(): Promise<any[]> {\n return this.db\n .select({\n id: projects.id,\n name: projects.name,\n rateLimitRpm: projects.rateLimitRpm,\n throttleLimit: projects.throttleLimit,\n throttleWindowHours: projects.throttleWindowHours,\n createdAt: projects.createdAt,\n })\n .from(projects)\n .orderBy(desc(projects.createdAt));\n }\n\n async delete(id: string): Promise<boolean> {\n return await this.db.transaction(async (tx) => {\n const userRows = await tx.select({ id: users.id }).from(users).where(eq(users.projectId, id));\n const userIds = userRows.map((u) => u.id);\n if (userIds.length > 0) {\n await tx.delete(userContacts).where(inArray(userContacts.userId, userIds));\n await tx.delete(userSegments).where(inArray(userSegments.userId, userIds));\n await tx.delete(userTopicPreferences).where(inArray(userTopicPreferences.userId, userIds));\n await tx\n .delete(userChannelPreferences)\n .where(inArray(userChannelPreferences.userId, userIds));\n await tx.delete(quietHours).where(inArray(quietHours.userId, userIds));\n await tx.delete(users).where(eq(users.projectId, id));\n }\n await tx.delete(suppressions).where(eq(suppressions.projectId, id));\n await tx.delete(messageLogs).where(eq(messageLogs.projectId, id));\n await tx.delete(workflowInstances).where(eq(workflowInstances.projectId, id));\n\n const result = await tx.delete(projects).where(eq(projects.id, id)).returning();\n return result.length > 0;\n });\n }\n\n /**\n * Throttle overrides only. Kept narrow because the engine calls this once per\n * notification (behind a cache) and has no use for the rest of the row.\n */\n async findThrottleSettings(\n id: string,\n ): Promise<{ throttleLimit: number | null; throttleWindowHours: number | null } | null> {\n const rows = await this.db\n .select({\n throttleLimit: projects.throttleLimit,\n throttleWindowHours: projects.throttleWindowHours,\n })\n .from(projects)\n .where(eq(projects.id, id))\n .limit(1);\n\n return rows[0] ?? null;\n }\n\n async updateSettings(\n id: string,\n settings: {\n rateLimitRpm?: number | null;\n throttleLimit?: number | null;\n throttleWindowHours?: number | null;\n },\n ): Promise<boolean> {\n const result = await this.db\n .update(projects)\n .set(settings)\n .where(eq(projects.id, id))\n .returning();\n return result.length > 0;\n }\n\n async createApiKey(\n projectId: string,\n keyHash: string,\n role: \"admin\" | \"read_only\" = \"admin\",\n ): Promise<{ id: string }> {\n const result = await this.db\n .insert(projectApiKeys)\n .values({ projectId, keyHash, role })\n .returning();\n return { id: result[0]!.id };\n }\n\n async listApiKeys(projectId: string): Promise<any[]> {\n return this.db\n .select({\n id: projectApiKeys.id,\n role: projectApiKeys.role,\n createdAt: projectApiKeys.createdAt,\n })\n .from(projectApiKeys)\n .where(eq(projectApiKeys.projectId, projectId))\n .orderBy(desc(projectApiKeys.createdAt));\n }\n\n async deleteApiKey(projectId: string, keyId: string): Promise<boolean> {\n const result = await this.db\n .delete(projectApiKeys)\n .where(and(eq(projectApiKeys.id, keyId), eq(projectApiKeys.projectId, projectId)))\n .returning();\n return result.length > 0;\n }\n}\n\n// ─── WorkflowRepository ──────────────────────────────────────────────────────\n\nexport class WorkflowRepository {\n constructor(private readonly db: Db) {}\n\n async listDefinitions(projectId: string): Promise<any[]> {\n return this.db\n .select()\n .from(workflowDefinitions)\n .where(eq(workflowDefinitions.projectId, projectId))\n .orderBy(desc(workflowDefinitions.createdAt));\n }\n\n async getInstance(projectId: string, instanceId: string): Promise<any | null> {\n const instances = await this.db\n .select()\n .from(workflowInstances)\n .where(and(eq(workflowInstances.id, instanceId), eq(workflowInstances.projectId, projectId)))\n .limit(1);\n if (!instances[0]) return null;\n\n const steps = await this.db\n .select()\n .from(workflowSteps)\n .where(eq(workflowSteps.instanceId, instanceId))\n .orderBy(workflowSteps.createdAt);\n const waiters = await this.db\n .select()\n .from(workflowWaiters)\n .where(eq(workflowWaiters.instanceId, instanceId));\n\n return {\n ...instances[0],\n steps,\n waiters,\n };\n }\n\n async cancelInstance(projectId: string, instanceId: string): Promise<boolean> {\n return await this.db.transaction(async (tx) => {\n const result = await tx\n .update(workflowInstances)\n .set({ status: \"canceled\" as any })\n .where(\n and(\n eq(workflowInstances.id, instanceId),\n eq(workflowInstances.projectId, projectId),\n inArray(workflowInstances.status, [\"pending\", \"running\"]),\n ),\n )\n .returning();\n if (result.length === 0) return false;\n await tx.delete(workflowWaiters).where(eq(workflowWaiters.instanceId, instanceId));\n return true;\n });\n }\n}\n\n// ─── SegmentRepository ───────────────────────────────────────────────────────\n\nexport class SegmentRepository {\n constructor(private readonly db: Db) {}\n\n async listSegments(projectId: string): Promise<string[]> {\n const rows = await this.db.execute(drizzleSql`\n SELECT DISTINCT s.segment\n FROM user_segments s\n JOIN users u ON u.id = s.user_id\n WHERE u.project_id = ${projectId}\n `);\n return (rows as any[]).map((r) => r.segment);\n }\n}\n","export function escapeHtml(unsafe: string): string {\n return String(unsafe)\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\n/** Strip CR/LF so an interpolated value cannot inject extra headers. */\nexport function escapeHeader(unsafe: string): string {\n return String(unsafe)\n .replace(/[\\r\\n]+/g, \" \")\n .trim();\n}\n\nexport function interpolate(\n tmpl: string,\n variables: Record<string, unknown>,\n sanitize = true,\n): string {\n return tmpl\n .replace(/\\{\\{\\{(\\w+)\\}\\}\\}/g, (_, k: string) => {\n return String(variables[k] ?? \"\");\n })\n .replace(/\\{\\{(\\w+)\\}\\}/g, (_, k: string) => {\n const val = String(variables[k] ?? \"\");\n return sanitize ? escapeHtml(val) : val;\n });\n}\n\n/**\n * How an interpolated value must be escaped, decided by the field it lands in.\n *\n * html — rendered as markup, so values are HTML-escaped\n * header — single-line headers (subject, from, …), so CR/LF are stripped\n * text — plain text body, no escaping needed\n */\nexport type EscapeMode = \"html\" | \"header\" | \"text\";\n\nconst HTML_FIELDS = new Set([\"html\", \"htmlbody\", \"bodyhtml\", \"htmlcontent\"]);\nconst HEADER_FIELDS = new Set([\n \"subject\",\n \"title\",\n \"from\",\n \"replyto\",\n \"cc\",\n \"bcc\",\n \"preheader\",\n \"preview\",\n]);\n\nfunction escapeModeFor(key: string, inherited: EscapeMode): EscapeMode {\n const k = key.toLowerCase().replace(/[-_]/g, \"\");\n if (HTML_FIELDS.has(k)) return \"html\";\n if (HEADER_FIELDS.has(k)) return \"header\";\n return inherited;\n}\n\nfunction applyEscape(value: string, mode: EscapeMode): string {\n if (mode === \"html\") return escapeHtml(value);\n if (mode === \"header\") return escapeHeader(value);\n return value;\n}\n\n/**\n * Interpolate `{{var}}` placeholders in a single leaf string.\n *\n * `{{{var}}}` (triple braces) interpolates raw unescaped values.\n * `{{var}}` (double braces) applies contextual escaping to the substituted value.\n */\nfunction interpolateLeaf(\n tmpl: string,\n variables: Record<string, unknown>,\n mode: EscapeMode,\n): string {\n return tmpl\n .replace(/\\{\\{\\{(\\w+)\\}\\}\\}/g, (_, k: string) => {\n const raw = variables[k];\n if (raw === undefined || raw === null) return \"\";\n return typeof raw === \"string\" ? raw : JSON.stringify(raw);\n })\n .replace(/\\{\\{(\\w+)\\}\\}/g, (_, k: string) => {\n const raw = variables[k];\n if (raw === undefined || raw === null) return \"\";\n return applyEscape(typeof raw === \"string\" ? raw : JSON.stringify(raw), mode);\n });\n}\n\n/**\n * Walk a template content tree and interpolate every leaf string in place.\n *\n * Values are substituted into the already-parsed structure. Interpolating into\n * serialised JSON and re-parsing (the previous approach) let a value containing\n * a quote either break JSON.parse outright or forge sibling fields such as\n * `htmlBody`.\n */\nfunction renderNode(node: unknown, variables: Record<string, unknown>, mode: EscapeMode): unknown {\n if (typeof node === \"string\") return interpolateLeaf(node, variables, mode);\n if (Array.isArray(node)) return node.map((item) => renderNode(item, variables, mode));\n if (node && typeof node === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(node as Record<string, unknown>)) {\n out[key] = renderNode(value, variables, escapeModeFor(key, mode));\n }\n return out;\n }\n return node;\n}\n\nexport function renderWithTemplate(\n dbTemplate: { content?: any } | null | undefined,\n templateVariables: Record<string, unknown>,\n): { content: Record<string, unknown> } {\n const vars = templateVariables ?? {};\n\n if (dbTemplate) {\n const content = (dbTemplate.content ?? {}) as Record<string, unknown>;\n return { content: renderNode(content, vars, \"text\") as Record<string, unknown> };\n }\n\n return {\n content: {\n subject: \"Notification\",\n body: JSON.stringify(vars, null, 2),\n },\n };\n}\n","import { LRUCache } from \"@/shared/index.js\";\nimport type { TemplateRepository } from \"@/repositories/index.js\";\n\nexport class TemplateCache {\n private cache = new LRUCache<string, any>(1000, 5 * 60 * 1000);\n\n constructor(private readonly templateRepo: TemplateRepository) {}\n\n async getCachedTemplate(projectId: string, id: string) {\n const key = `${projectId}:${id}`;\n const cached = this.cache.get(key);\n if (cached) return cached;\n\n const dbTemplate = await this.templateRepo.findById(projectId, id);\n if (dbTemplate) {\n this.cache.set(key, dbTemplate);\n }\n return dbTemplate;\n }\n\n invalidate(projectId: string, id: string) {\n this.cache.delete(`${projectId}:${id}`);\n }\n\n invalidateKey(key: string) {\n this.cache.delete(key);\n }\n\n clear() {\n this.cache.clear();\n }\n}\n","import type { RenderedContent } from \"@/contracts/index.js\";\nexport * from \"./render.js\";\nexport * from \"./cache.js\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface TemplateContext {\n eventType: string;\n templateVariables: Record<string, unknown>;\n locale: string;\n timezone: string;\n deeplinkScheme: string;\n}\n\nexport type TemplateRenderer = (ctx: TemplateContext) => RenderedContent;\n\n/** Coerce a template variable to a non-empty string, or undefined. */\nfunction asText(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\n// ─── TemplateRegistry ────────────────────────────────────────────────────────\n//\n// Open registry — new event types register a renderer without touching this file.\n// The default renderer falls back to the i18n translation table, so most event\n// types work without a custom renderer.\n\nclass TemplateRegistry {\n private readonly renderers = new Map<string, TemplateRenderer>();\n\n /**\n * Register a custom renderer for an event type.\n * Overwrites any previous registration for the same type.\n */\n register(eventType: string, renderer: TemplateRenderer): void {\n this.renderers.set(eventType, renderer);\n }\n\n /** Render content for the given context, falling back to the i18n table. */\n render(ctx: TemplateContext): RenderedContent {\n const renderer = this.renderers.get(ctx.eventType);\n if (renderer) return renderer(ctx);\n return defaultRenderer(ctx);\n }\n\n has(eventType: string): boolean {\n return this.renderers.has(eventType);\n }\n\n registeredTypes(): string[] {\n return [...this.renderers.keys()];\n }\n}\n\nfunction defaultRenderer(ctx: TemplateContext): RenderedContent {\n const vars = ctx.templateVariables;\n\n // Fall back to any caller-supplied title/body in the payload, then to a generic label.\n const subject = asText(vars.subject ?? vars.title);\n const body = asText(vars.body ?? vars.message) ?? `Notification: ${ctx.eventType}`;\n\n return { content: { subject, body } };\n}\n\nexport const templateRegistry = new TemplateRegistry();\n\nexport function renderTemplate(ctx: TemplateContext): RenderedContent {\n return templateRegistry.render(ctx);\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/**\n * One-click unsubscribe (RFC 8058).\n *\n * Gmail and Yahoo have required `List-Unsubscribe` + `List-Unsubscribe-Post` on\n * bulk mail since early 2024; without them bulk sends get throttled or junked,\n * and because deliverability is reputation on the *sending domain*, that\n * eventually drags transactional mail down with it.\n *\n * The link has to work from an inbox, years later, with no session — so the\n * token carries its own claim and is signed rather than looked up. Nothing is\n * stored per message, and there is no expiry: an unsubscribe link that has\n * stopped working is worse than useless, because the recipient's next move is\n * the spam button.\n */\n\nexport interface UnsubscribeClaim {\n /** Project the send belonged to. */\n projectId: string;\n /** The user's external id, as supplied by the caller. */\n userId: string;\n channel: string;\n /** The address itself — used when there is no topic to opt out of. */\n target: string;\n /** Topics the template belonged to. Empty means \"suppress the address\". */\n topics: string[];\n}\n\ninterface WireClaim {\n p: string;\n u: string;\n c: string;\n t: string;\n k: string[];\n}\n\nfunction b64url(input: Buffer | string): string {\n return Buffer.from(input).toString(\"base64url\");\n}\n\n/**\n * Sign a claim into a URL-safe token.\n *\n * The signature covers the exact encoded payload rather than a re-serialisation\n * of it, so a verifier never has to reproduce this function's JSON key order to\n * get a matching MAC.\n */\nexport function signUnsubscribeToken(claim: UnsubscribeClaim, secret: string): string {\n const wire: WireClaim = {\n p: claim.projectId,\n u: claim.userId,\n c: claim.channel,\n t: claim.target,\n k: claim.topics,\n };\n const payload = b64url(JSON.stringify(wire));\n const mac = b64url(createHmac(\"sha256\", secret).update(payload).digest());\n return `${payload}.${mac}`;\n}\n\n/**\n * Verify and decode a token. Returns null for anything not signed by `secret`.\n *\n * Every failure returns the same null rather than a reason: the caller is an\n * unauthenticated endpoint, and distinguishing \"malformed\" from \"bad signature\"\n * hands an attacker a probe.\n */\nexport function verifyUnsubscribeToken(token: string, secret: string): UnsubscribeClaim | null {\n const dot = token.indexOf(\".\");\n if (dot <= 0 || dot === token.length - 1) return null;\n\n const payload = token.slice(0, dot);\n const provided = Buffer.from(token.slice(dot + 1), \"base64url\");\n const expected = createHmac(\"sha256\", secret).update(payload).digest();\n\n // timingSafeEqual throws on a length mismatch, which is itself a signal.\n if (provided.length !== expected.length) return null;\n if (!timingSafeEqual(provided, expected)) return null;\n\n try {\n const wire = JSON.parse(Buffer.from(payload, \"base64url\").toString(\"utf8\")) as WireClaim;\n if (\n typeof wire.p !== \"string\" ||\n typeof wire.u !== \"string\" ||\n typeof wire.c !== \"string\" ||\n typeof wire.t !== \"string\" ||\n !Array.isArray(wire.k)\n ) {\n return null;\n }\n return {\n projectId: wire.p,\n userId: wire.u,\n channel: wire.c,\n target: wire.t,\n topics: wire.k.filter((t): t is string => typeof t === \"string\"),\n };\n } catch {\n return null;\n }\n}\n\nexport interface UnsubscribeHeaderOptions {\n claim: UnsubscribeClaim;\n secret: string;\n /** Externally reachable base URL of the API, e.g. https://notify.example.com */\n publicUrl: string;\n}\n\n/**\n * The two headers that make an inbox render a real unsubscribe button.\n *\n * `List-Unsubscribe-Post` is what upgrades the link from \"open this URL\" to\n * one-click: the mail client POSTs directly and never shows the recipient a\n * landing page. Sending the URL without it means the recipient has to click\n * through and confirm, which mailbox providers do not count as compliant.\n */\nexport function buildUnsubscribeHeaders(options: UnsubscribeHeaderOptions): Record<string, string> {\n const token = signUnsubscribeToken(options.claim, options.secret);\n const base = options.publicUrl.replace(/\\/$/, \"\");\n const url = `${base}/v1/unsubscribe?token=${encodeURIComponent(token)}`;\n return {\n \"List-Unsubscribe\": `<${url}>`,\n \"List-Unsubscribe-Post\": \"List-Unsubscribe=One-Click\",\n };\n}\n","import type { Logger } from \"@/index.js\";\nimport type { BaseWorker } from \"./index.js\";\n\nexport function startHealthReporter(\n serviceName: string,\n worker: BaseWorker,\n redis: { healthCheck: () => Promise<boolean>; native: any },\n logger: Logger,\n intervalMs = 1000,\n): NodeJS.Timeout {\n return setInterval(() => {\n void (async () => {\n try {\n const redisOk = await redis.healthCheck();\n await redis.native.set(\n `notif:health:${serviceName}`,\n JSON.stringify({\n service: serviceName,\n redis: redisOk,\n ...worker.health(),\n updatedAt: new Date().toISOString(),\n }),\n \"EX\",\n 15,\n );\n } catch {\n // A failed health write is not worth failing the worker over.\n }\n })();\n }, intervalMs);\n}\n","import type { Logger } from \"@/index.js\";\nimport type { StreamConsumer, PendingMessageScanner, StreamMessage } from \"@/index.js\";\nimport { globalEmitter, AsyncSemaphore } from \"@/shared/index.js\";\nimport { metrics } from \"@/metrics/index.js\";\nexport * from \"./health.js\";\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\nexport class NonRetryableError extends Error {\n readonly nonRetryable = true;\n constructor(message: string) {\n super(message);\n this.name = \"NonRetryableError\";\n }\n}\n\nexport type WorkerState = \"idle\" | \"running\" | \"stopping\" | \"stopped\" | \"error\";\n\nexport interface WorkerHealth {\n state: WorkerState;\n processedCount: number;\n errorCount: number;\n lastProcessedAt: string | null;\n lastErrorAt: string | null;\n pendingCount: number | null;\n}\n\nexport interface WorkerOptions {\n consumer: StreamConsumer;\n pendingScanner: PendingMessageScanner;\n logger: Logger;\n concurrency?: number;\n recoveryIntervalMs?: number;\n maxRetriesBeforeDlq?: number;\n}\n\n// ─── BaseWorker ────────────────────────────────────────────────────────────\n\nexport abstract class BaseWorker {\n protected readonly logger: Logger;\n\n private readonly consumer: StreamConsumer;\n private readonly pendingScanner: PendingMessageScanner;\n private readonly concurrency: number;\n private readonly recoveryIntervalMs: number;\n private readonly maxRetriesBeforeDlq: number;\n\n private state: WorkerState = \"idle\";\n private stopping = false;\n private processedCount = 0;\n private errorCount = 0;\n private lastProcessedAt: string | null = null;\n private lastErrorAt: string | null = null;\n private recoveryTimer: ReturnType<typeof setInterval> | null = null;\n private lastPendingCount: number | null = null;\n private readonly active = new Set<Promise<void>>();\n private readonly semaphore: AsyncSemaphore;\n private runLoop?: Promise<void>;\n\n constructor({\n consumer,\n pendingScanner,\n logger,\n concurrency = 10,\n recoveryIntervalMs = 60_000,\n maxRetriesBeforeDlq = 3,\n }: WorkerOptions) {\n this.consumer = consumer;\n this.pendingScanner = pendingScanner;\n this.logger = logger.child({ component: this.constructor.name });\n this.concurrency = concurrency;\n this.recoveryIntervalMs = recoveryIntervalMs;\n this.maxRetriesBeforeDlq = maxRetriesBeforeDlq;\n this.semaphore = new AsyncSemaphore(concurrency);\n }\n\n protected abstract process(message: StreamMessage, attempt?: number): Promise<void>;\n\n async start(): Promise<void> {\n if (this.state !== \"idle\") {\n throw new Error(`Worker cannot start from state: ${this.state}`);\n }\n\n this.state = \"running\";\n this.logger.info({ concurrency: this.concurrency }, \"worker starting\");\n\n await this.consumer.ensureGroup();\n this.startRecoveryLoop();\n\n this.runLoop = this.consume();\n }\n\n private async consume(): Promise<void> {\n for await (const batch of this.consumer.readBatch()) {\n if (this.stopping) break;\n\n for (const message of batch) {\n if (this.stopping) break;\n\n await this.semaphore.acquire();\n\n const task = this.processWithTracking(message).finally(() => {\n this.active.delete(task);\n this.semaphore.release();\n });\n\n this.active.add(task);\n }\n }\n\n await Promise.allSettled([...this.active]);\n this.state = \"stopped\";\n this.logger.info(\"worker stopped\");\n }\n\n async stop(): Promise<void> {\n if (this.stopping || this.state !== \"running\") return;\n\n this.logger.info(\"worker stopping\");\n this.stopping = true;\n this.state = \"stopping\";\n await this.consumer.stop();\n this.stopRecoveryLoop();\n\n if (this.runLoop) {\n await Promise.race([\n this.runLoop,\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error(\"Worker stop timeout\")), 30_000),\n ),\n ]).catch((err) => this.logger.warn({ err }, \"Worker shutdown timeout or error\"));\n }\n\n this.logger.info(\"worker shutdown complete\");\n }\n\n async recover(): Promise<void> {\n this.logger.debug(\"scanning for stale pending messages\");\n\n const pendingCount = await this.pendingScanner.getPendingCount();\n this.lastPendingCount = pendingCount;\n if (pendingCount === 0) return;\n\n this.logger.info({ pendingCount }, \"found pending messages, attempting autoclaim\");\n\n const BATCH_SIZE = 1000;\n while (!this.stopping) {\n const messages = await this.pendingScanner.autoclaim(this.recoveryIntervalMs, BATCH_SIZE);\n if (messages.length === 0) {\n break; // No more eligible messages to claim\n }\n\n for (const message of messages) {\n if (this.stopping) break;\n\n await this.semaphore.acquire();\n\n const task = this.processWithTracking(message).finally(() => {\n this.active.delete(task);\n this.semaphore.release();\n });\n\n this.active.add(task);\n }\n }\n }\n\n health(): WorkerHealth {\n return {\n state: this.state,\n processedCount: this.processedCount,\n errorCount: this.errorCount,\n lastProcessedAt: this.lastProcessedAt,\n lastErrorAt: this.lastErrorAt,\n // Refreshed by the recovery loop; health() stays synchronous so the\n // reporter never blocks on Redis.\n pendingCount: this.lastPendingCount,\n };\n }\n\n private async processWithTracking(message: StreamMessage): Promise<void> {\n const start = Date.now();\n const stream = message.stream;\n const retryKey = `notif:worker:retries:${this.constructor.name}:${stream ?? \"default\"}:${message.id}`;\n\n try {\n const results = await this.consumer.redis\n .multi()\n .incr(retryKey)\n .expire(retryKey, 7200)\n .exec();\n const retryCount = (results?.[0]?.[1] as number) ?? 1;\n\n if (retryCount > this.maxRetriesBeforeDlq) {\n this.logger.warn(\n { messageId: message.id, retryCount },\n \"max retries exceeded, moving to dead-letter queue\",\n );\n await this.consumer.nack(message.id, message.event, stream);\n await this.consumer.redis.del(retryKey);\n globalEmitter.emit(\n \"notification:failed\",\n message.id,\n \"Poison pill: max retries exceeded\",\n message.event.type,\n );\n return;\n }\n\n await this.process(message, retryCount);\n\n await this.consumer.ack(message.id, stream);\n await this.consumer.redis.del(retryKey);\n this.processedCount += 1;\n this.lastProcessedAt = new Date().toISOString();\n metrics.messagesProcessed.inc({ worker: this.constructor.name, status: \"success\" });\n\n this.logger.debug(\n { messageId: message.id, eventType: message.event.type, durationMs: Date.now() - start },\n \"message processed\",\n );\n } catch (err) {\n this.errorCount += 1;\n this.lastErrorAt = new Date().toISOString();\n metrics.messagesProcessed.inc({ worker: this.constructor.name, status: \"error\" });\n\n if (err instanceof NonRetryableError || (err as any)?.nonRetryable) {\n this.logger.warn(\n { err, messageId: message.id },\n \"non-retryable error encountered, immediately moving to dead-letter queue without retry loop\",\n );\n await this.consumer.nack(message.id, message.event, stream);\n await this.consumer.redis.del(retryKey);\n globalEmitter.emit(\n \"notification:failed\",\n message.id,\n (err as Error).message,\n message.event.type,\n );\n return;\n }\n\n this.logger.error(\n { err, messageId: message.id, eventType: message.event.type },\n \"failed to process message\",\n );\n }\n }\n\n private startRecoveryLoop(): void {\n this.recoveryTimer = setInterval(() => {\n if (this.state === \"running\") {\n this.recover().catch((err: unknown) => {\n this.logger.error({ err }, \"recovery loop error\");\n });\n }\n }, this.recoveryIntervalMs);\n }\n\n private stopRecoveryLoop(): void {\n if (this.recoveryTimer) {\n clearInterval(this.recoveryTimer);\n this.recoveryTimer = null;\n }\n }\n}\n","import type { WorkflowNotifyInput } from \"@/contracts/sdk.js\";\nimport type {\n NotificationRequestedPayload,\n NotificationTarget,\n} from \"@/contracts/events/notification-requested.js\";\n\nexport class SuspendExecutionError extends Error {\n constructor(\n public reason: \"wait\" | \"waitForEvent\",\n public payload: any,\n ) {\n super(`Execution suspended for ${reason}`);\n this.name = \"SuspendExecutionError\";\n }\n}\n\nexport interface WorkflowEvent {\n user: { id: string };\n [key: string]: any;\n}\n\nexport interface WorkflowContext {\n step: WorkflowStepContext;\n event: WorkflowEvent;\n}\n\n/** What a completed `step.notify()` records and returns. */\nexport interface WorkflowNotifyResult {\n success: boolean;\n messageId: string;\n notificationId: string;\n}\n\nexport interface WorkflowStepContext {\n notify(payload: WorkflowNotifyInput): Promise<WorkflowNotifyResult>;\n wait(duration: string): Promise<void>;\n waitForEvent(\n eventName: string,\n options?: { timeout?: string; match?: Record<string, any> },\n ): Promise<any | null>;\n run<T>(name: string, fn: () => Promise<T> | T): Promise<T>;\n}\n\n/**\n * Works out who a `step.notify()` call is for.\n *\n * A target named in the step payload wins. Only when the step names none does\n * the notification fall back to the instance's own user — the common case, and\n * the reason most steps carry no target at all.\n */\nexport function resolveStepTarget(\n args: WorkflowNotifyInput,\n instanceInput: unknown,\n): NotificationTarget {\n if (args.segment !== undefined) return { type: \"segment\", segment: args.segment };\n if (args.topic !== undefined) return { type: \"topic\", topic: args.topic };\n\n if (args.user !== undefined) {\n if (Array.isArray(args.user)) {\n throw new Error(\n \"step.notify() takes a single `user`. To reach several people from one workflow, \" +\n \"use one notify step each, or target a `segment`.\",\n );\n }\n return {\n type: \"user\",\n userId: typeof args.user === \"string\" ? args.user : args.user.id,\n };\n }\n\n const inherited = (instanceInput as { user?: { id?: string } } | null)?.user?.id;\n if (!inherited) {\n throw new Error(\n \"step.notify() has no target: the step payload names no `user`, `segment` or `topic`, \" +\n \"and the workflow instance was triggered without `input.user.id`.\",\n );\n }\n return { type: \"user\", userId: inherited };\n}\n\n/**\n * Maps a `step.notify()` payload onto the wire event the pipeline consumes.\n *\n * The field names differ either side of the boundary — `template` becomes\n * `templateId`, `sendAt` becomes `scheduledAt` — so this translation is\n * deliberate rather than a spread, and the return type keeps it honest.\n */\nexport function buildStepNotifyPayload(\n args: WorkflowNotifyInput,\n instanceInput: unknown,\n projectId: string,\n idempotencyKey?: string,\n): NotificationRequestedPayload {\n return {\n projectId,\n target: resolveStepTarget(args, instanceInput),\n templateId: args.template,\n priority: args.priority ?? \"normal\",\n channels: args.channels,\n data: args.data ?? {},\n aiPrompts: args.aiPrompts,\n fallback: args.fallback ?? false,\n scheduledAt: args.sendAt,\n idempotencyKey,\n };\n}\n","import type { WorkflowContext } from \"./sdk.js\";\n\ntype WorkflowHandler = (ctx: WorkflowContext) => Promise<void>;\n\nclass WorkflowRegistry {\n private workflows = new Map<string, WorkflowHandler>();\n\n register(name: string, handler: WorkflowHandler) {\n this.workflows.set(name, handler);\n }\n\n get(name: string): WorkflowHandler | undefined {\n return this.workflows.get(name);\n }\n}\n\nexport const workflowRegistry = new WorkflowRegistry();\n\nexport function workflow(name: string, handler: WorkflowHandler) {\n workflowRegistry.register(name, handler);\n}\n","import type {\n AddUserInput,\n UpdateUserInput,\n AddContactInput,\n SyncTemplatesInput,\n NotifyRequestInput,\n TriggerWorkflowInput,\n CreateWorkflowInput,\n IngestEventInput,\n UpdateProjectInput,\n} from \"./contracts/sdk.js\";\n\nexport interface NotifkitClientOptions {\n baseUrl: string;\n headers?: Record<string, string>;\n templates?: SyncTemplatesInput[\"templates\"];\n apiKey?: string;\n}\n\nexport class NotifkitClient {\n private readonly options: NotifkitClientOptions;\n private readonly baseUrl: string;\n private readonly headers: Record<string, string>;\n\n constructor(options: NotifkitClientOptions) {\n this.options = options;\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n this.headers = {\n \"Content-Type\": \"application/json\",\n ...(options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {}),\n ...options.headers,\n };\n }\n\n private async request<T>(path: string, method: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const res = await fetch(url, {\n method,\n headers: this.headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (res.status === 204) {\n return undefined as T;\n }\n\n const data = await res.json();\n if (!res.ok) {\n const errorMsg =\n (data as any).message || (data as any).error || `Request failed with status ${res.status}`;\n throw new Error(errorMsg);\n }\n return data as T;\n }\n\n /** Sync templates with the server. */\n async syncTemplates(input: SyncTemplatesInput): Promise<{ synced: number }> {\n return this.request(\"/v1/templates\", \"PUT\", input);\n }\n\n /** Create/upsert a user profile and contacts. */\n async addUser(input: AddUserInput): Promise<{ id: string }> {\n return this.request(\"/v1/users\", \"POST\", input);\n }\n\n /** Update user profile. */\n async updateUser(id: string, input: UpdateUserInput): Promise<{ id: string }> {\n return this.request(`/v1/users/${id}`, \"PATCH\", input);\n }\n\n /** Delete user profile. */\n async deleteUser(id: string): Promise<void> {\n return this.request(`/v1/users/${id}`, \"DELETE\");\n }\n\n /** Add contact targets to user profile. */\n async addContact(\n userId: string,\n input: AddContactInput,\n ): Promise<{ userId: string; channel: string; target: string }> {\n return this.request(`/v1/users/${userId}/contacts`, \"POST\", input);\n }\n\n /** Delete a specific contact channel target. */\n async deleteContact(userId: string, channel: string, target: string): Promise<void> {\n return this.request(`/v1/users/${userId}/contacts/${channel}/${target}`, \"DELETE\");\n }\n\n /** Request a notification dispatch. */\n async notify(\n input: NotifyRequestInput,\n ): Promise<{ messageId: string; notificationId: string; target: unknown }> {\n return this.request(\"/v1/notify\", \"POST\", input);\n }\n\n /** Trigger a registered background workflow. */\n async triggerWorkflow(\n input: TriggerWorkflowInput,\n ): Promise<{ messageId: string; instanceId: string }> {\n return this.request(\"/v1/workflows/trigger\", \"POST\", input);\n }\n\n /** Create a dynamic JSON workflow definition. */\n async createWorkflow(input: CreateWorkflowInput): Promise<{ name: string }> {\n return this.request(\"/v1/workflows\", \"POST\", input);\n }\n\n /** Ingest an external event into the system to resume workflows or trigger automations. */\n async ingestEvent(input: IngestEventInput): Promise<{ messageId: string; eventId: string }> {\n return this.request(\"/v1/events\", \"POST\", input);\n }\n\n /** Sync templates configured on the client options to the server. */\n async sync(): Promise<{ synced: number }> {\n if (!this.options.templates || this.options.templates.length === 0) {\n return { synced: 0 };\n }\n return this.syncTemplates({ templates: this.options.templates });\n }\n\n // ─── Missing Endpoints additions ─────────────────────────────────────────────\n\n /** List registered workflow definitions. */\n async listWorkflows(options?: {\n limit?: number;\n search?: string;\n }): Promise<{ workflows: any[] }> {\n const params = new URLSearchParams();\n if (options?.limit) params.set(\"limit\", options.limit.toString());\n if (options?.search) params.set(\"search\", options.search);\n const qs = params.toString();\n return this.request(`/v1/workflows${qs ? `?${qs}` : \"\"}`, \"GET\");\n }\n\n /** Get a workflow instance by ID. */\n async getWorkflow(instanceId: string): Promise<any> {\n return this.request(`/v1/workflows/instances/${instanceId}`, \"GET\");\n }\n\n /** Cancel a running/suspended workflow instance. */\n async cancelWorkflow(instanceId: string): Promise<void> {\n return this.request(`/v1/workflows/instances/${instanceId}`, \"DELETE\");\n }\n\n /** Get notification logs for the project. */\n async getNotificationLogs(options?: {\n limit?: number;\n cursor?: string;\n templateId?: string;\n workflowInstanceId?: string;\n channel?: string;\n status?: string;\n taskId?: string;\n campaign?: string;\n search?: string;\n }): Promise<{ logs: any[]; nextCursor: string | null }> {\n let url = \"/v1/notifications/logs\";\n if (options) {\n const params = new URLSearchParams();\n if (options.limit !== undefined) params.append(\"limit\", options.limit.toString());\n if (options.cursor) params.append(\"cursor\", options.cursor);\n if (options.templateId) params.append(\"templateId\", options.templateId);\n if (options.workflowInstanceId)\n params.append(\"workflowInstanceId\", options.workflowInstanceId);\n if (options.channel) params.append(\"channel\", options.channel);\n if (options.status) params.append(\"status\", options.status);\n if (options.taskId) params.append(\"taskId\", options.taskId);\n if (options.campaign) params.append(\"campaign\", options.campaign);\n if (options.search) params.append(\"search\", options.search);\n const str = params.toString();\n if (str) url += `?${str}`;\n }\n return this.request(url, \"GET\");\n }\n\n /** List/paginate users. */\n async listUsers(options?: {\n limit?: number;\n cursor?: string;\n search?: string;\n segment?: string;\n language?: string;\n timezone?: string;\n channel?: string;\n }): Promise<{ users: any[]; nextCursor: string | null }> {\n const params = new URLSearchParams();\n if (options?.limit) params.set(\"limit\", options.limit.toString());\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n if (options?.search) params.set(\"search\", options.search);\n if (options?.segment) params.set(\"segment\", options.segment);\n if (options?.language) params.set(\"language\", options.language);\n if (options?.timezone) params.set(\"timezone\", options.timezone);\n if (options?.channel) params.set(\"channel\", options.channel);\n const qs = params.toString();\n return this.request(`/v1/users${qs ? `?${qs}` : \"\"}`, \"GET\");\n }\n\n /** Delete a template. */\n async deleteTemplate(id: string): Promise<void> {\n return this.request(`/v1/templates/${id}`, \"DELETE\");\n }\n\n /** Get a user's contacts. */\n async getUserContacts(userId: string): Promise<{ contacts: any[] }> {\n return this.request(`/v1/users/${userId}/contacts`, \"GET\");\n }\n\n /** List projects (Admin only). */\n async listProjects(): Promise<{ projects: any[] }> {\n return this.request(\"/v1/projects\", \"GET\");\n }\n\n /** Delete a project (Admin only). */\n async deleteProject(id: string): Promise<void> {\n return this.request(`/v1/projects/${id}`, \"DELETE\");\n }\n\n /** Create a new project API key (Admin only). */\n async createProjectKey(\n id: string,\n input?: { role?: \"admin\" | \"read_only\" },\n ): Promise<{ id: string; apiKey: string; role: string }> {\n return this.request(`/v1/projects/${id}/keys`, \"POST\", input || {});\n }\n\n /** List project API keys (Admin only). */\n async listProjectKeys(id: string): Promise<{ keys: any[] }> {\n return this.request(`/v1/projects/${id}/keys`, \"GET\");\n }\n\n /** Delete a project API key (Admin only). */\n async deleteProjectKey(id: string, keyId: string): Promise<void> {\n return this.request(`/v1/projects/${id}/keys/${keyId}`, \"DELETE\");\n }\n\n /** Update project settings (Admin only). */\n async updateProject(id: string, input: UpdateProjectInput): Promise<{ id: string }> {\n return this.request(`/v1/projects/${id}`, \"PATCH\", input);\n }\n\n /** List unique segment tags. */\n async listSegments(): Promise<{ segments: string[] }> {\n return this.request(\"/v1/segments\", \"GET\");\n }\n\n // ─── Campaigns ───────────────────────────────────────────────────────────────\n\n /** List campaign labels seen in the delivery log, most recent activity first. */\n async listCampaigns(options?: {\n limit?: number;\n search?: string;\n channel?: string;\n since?: string | Date;\n until?: string | Date;\n minMessages?: number;\n }): Promise<{\n campaigns: {\n campaign: string;\n messages: number;\n firstSentAt: string;\n lastActivityAt: string;\n }[];\n }> {\n const params = new URLSearchParams();\n if (options?.limit) params.set(\"limit\", String(options.limit));\n if (options?.search) params.set(\"search\", options.search);\n if (options?.channel) params.set(\"channel\", options.channel);\n if (options?.since) {\n params.set(\n \"since\",\n options.since instanceof Date ? options.since.toISOString() : options.since,\n );\n }\n if (options?.until) {\n params.set(\n \"until\",\n options.until instanceof Date ? options.until.toISOString() : options.until,\n );\n }\n if (options?.minMessages) params.set(\"minMessages\", String(options.minMessages));\n\n const qs = params.toString();\n return this.request(`/v1/campaigns${qs ? `?${qs}` : \"\"}`, \"GET\");\n }\n\n /** Delivery and engagement funnel for one campaign. */\n async getCampaignStats(campaign: string): Promise<{\n campaign: string;\n totals: Record<string, number | null>;\n byChannel: Record<string, Record<string, number>>;\n engagementTracked: boolean;\n warnings: string[];\n }> {\n return this.request(`/v1/campaigns/${encodeURIComponent(campaign)}/stats`, \"GET\");\n }\n\n // ─── Suppressions ────────────────────────────────────────────────────────────\n\n /** List suppressed destinations. */\n async listSuppressions(options?: {\n limit?: number;\n channel?: string;\n reason?: string;\n target?: string;\n }): Promise<{ suppressions: any[] }> {\n const params = new URLSearchParams();\n if (options?.limit) params.set(\"limit\", options.limit.toString());\n if (options?.channel) params.set(\"channel\", options.channel);\n if (options?.reason) params.set(\"reason\", options.reason);\n if (options?.target) params.set(\"target\", options.target);\n const qs = params.toString();\n return this.request(`/v1/suppressions${qs ? `?${qs}` : \"\"}`, \"GET\");\n }\n\n /** Suppress a destination by hand. */\n async createSuppression(input: {\n channel: string;\n target: string;\n reason?: \"unsubscribed\" | \"complained\" | \"bounced\" | \"manual\";\n }): Promise<{ channel: string; target: string; reason: string }> {\n return this.request(\"/v1/suppressions\", \"POST\", input);\n }\n\n /** Remove a suppression, re-enabling sends to that destination. */\n async deleteSuppression(channel: string, target: string): Promise<void> {\n return this.request(\n `/v1/suppressions/${encodeURIComponent(channel)}/${encodeURIComponent(target)}`,\n \"DELETE\",\n );\n }\n\n // ─── Notification Status & Cancellation ─────────────────────────────────────\n\n /** Get real-time status and delivery logs for a specific notification task. */\n async getNotificationStatus(taskId: string): Promise<{ status: string; logs: any[] }> {\n return this.request(`/v1/notifications/${encodeURIComponent(taskId)}`, \"GET\");\n }\n\n /** Cancel a scheduled notification task. */\n async cancelNotification(taskId: string): Promise<{ success: boolean }> {\n return this.request(`/v1/notifications/${encodeURIComponent(taskId)}`, \"DELETE\");\n }\n\n /** List pending scheduled messages. */\n async getScheduledMessages(): Promise<{ scheduled: any[] }> {\n return this.request(\"/v1/notifications/scheduled\", \"GET\");\n }\n\n // ─── User Profile & Preferences ─────────────────────────────────────────────\n\n /** Get user profile and contacts by ID. */\n async getUser(id: string): Promise<any> {\n return this.request(`/v1/users/${encodeURIComponent(id)}`, \"GET\");\n }\n\n /** Get user details including contacts and recent message logs. */\n async getUserDetails(id: string): Promise<any> {\n return this.request(`/v1/users/${encodeURIComponent(id)}/details`, \"GET\");\n }\n\n /** Get user preferences. */\n async getUserPreferences(id: string): Promise<any> {\n return this.request(`/v1/users/${encodeURIComponent(id)}/preferences`, \"GET\");\n }\n\n /** Update user preferences. */\n async updateUserPreferences(\n id: string,\n preferences: Record<string, any>,\n ): Promise<{ id: string; preferences: any }> {\n return this.request(`/v1/users/${encodeURIComponent(id)}/preferences`, \"PATCH\", preferences);\n }\n\n // ─── Templates Querying ─────────────────────────────────────────────────────\n\n /** List all templates for the project. */\n async listTemplates(): Promise<{ templates: any[] }> {\n return this.request(\"/v1/templates\", \"GET\");\n }\n\n /** Get a template by ID. */\n async getTemplate(id: string): Promise<any> {\n return this.request(`/v1/templates/${encodeURIComponent(id)}`, \"GET\");\n }\n\n // ─── System Health, Metrics & DLQ ───────────────────────────────────────────\n\n /** Get system health and worker status. */\n async getSystemHealth(): Promise<any> {\n return this.request(\"/v1/system/health\", \"GET\");\n }\n\n /** Get system metrics and queue lengths. */\n async getSystemMetrics(): Promise<any> {\n return this.request(\"/v1/system/metrics\", \"GET\");\n }\n\n /** Get dead-letter queue messages. */\n async getDLQMessages(): Promise<{ messages: any[] }> {\n return this.request(\"/v1/dlq\", \"GET\");\n }\n\n /** Replay a dead-letter queue message. */\n async replayDLQMessage(id: string): Promise<{ success: boolean; replayedId: string }> {\n return this.request(\"/v1/dlq/replay\", \"POST\", { id });\n }\n\n /** Delete a dead-letter queue message. */\n async deleteDLQMessage(id: string): Promise<{ success: boolean }> {\n return this.request(`/v1/dlq/${encodeURIComponent(id)}`, \"DELETE\");\n }\n}\n","import { EventEmitter } from \"node:events\";\nimport { registerTransport, type Transport } from \"./transport/index.js\";\nimport { globalEmitter } from \"./shared/index.js\";\nimport { createLogger, type Logger } from \"./logger/index.js\";\nimport type { LanguageModel } from \"ai\";\n\nexport interface NotifkitOptions {\n redisUrl?: string;\n databaseUrl?: string;\n port?: number;\n logLevel?: \"fatal\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"trace\" | \"silent\";\n nodeEnv?: \"development\" | \"test\" | \"production\";\n services: (\n \"api\" | \"delivery\" | \"engine\" | \"enricher\" | \"scheduler\" | \"ai\" | \"workflow\" | \"events\" | \"all\"\n )[];\n providers?: Transport[];\n autoMigrate?: boolean;\n aiModel?: LanguageModel;\n workerConcurrency?: number;\n redisOptions?: {\n maxQueueLength?: number;\n };\n dbOptions?: {\n maxConnections?: number;\n };\n}\n\nexport class NotifkitServer extends EventEmitter {\n private options: NotifkitOptions;\n private pgContainer: any = null;\n private redisContainer: any = null;\n private eventCleanupFns: (() => void)[] = [];\n private signalHandlersAttached = false;\n private logger: Logger;\n\n constructor(options: NotifkitOptions) {\n super();\n this.options = options;\n this.logger = createLogger({\n name: \"server\",\n level: options.logLevel || (process.env.LOG_LEVEL as any) || \"info\",\n });\n\n // Forward worker/API events to the server instance\n const eventNames = [\n \"delivery:delivered\",\n \"delivery:failed\",\n \"notification:throttled\",\n \"notification:failed\",\n \"notification:skipped\",\n \"notification:canceled\",\n ];\n for (const name of eventNames) {\n const listener = (...args: any[]) => {\n this.emit(name, ...args);\n };\n globalEmitter.on(name, listener);\n this.eventCleanupFns.push(() => {\n globalEmitter.off(name, listener);\n });\n }\n }\n\n async start() {\n // 1. Initial configuration setup for environment overrides\n const { setGlobalConfig, readBaseConfig } = await import(\"./config/index.js\");\n if (this.options.port) process.env.PORT = String(this.options.port);\n if (this.options.logLevel) process.env.LOG_LEVEL = this.options.logLevel;\n if (this.options.nodeEnv) process.env.NODE_ENV = this.options.nodeEnv;\n if (this.options.workerConcurrency)\n process.env.WORKER_CONCURRENCY = String(this.options.workerConcurrency);\n if (this.options.redisOptions?.maxQueueLength)\n process.env.QUEUE_MAX_LEN = String(this.options.redisOptions.maxQueueLength);\n if (this.options.dbOptions?.maxConnections)\n process.env.DB_MAX_CONNECTIONS = String(this.options.dbOptions.maxConnections);\n\n const isProduction = process.env.NODE_ENV === \"production\";\n\n // 2. Spin up test containers if needed (only in development/test)\n if (!this.options.redisUrl) {\n if (process.env.REDIS_URL) {\n this.options.redisUrl = process.env.REDIS_URL;\n } else if (!isProduction) {\n this.logger.info(\n \"No redisUrl provided, spinning up Redis container for development/testing...\",\n );\n const { RedisContainer } = await import(\"@testcontainers/redis\");\n this.redisContainer = await new RedisContainer(\"redis:alpine\").start();\n this.options.redisUrl = this.redisContainer.getConnectionUrl();\n } else {\n throw new Error(\n \"Missing required configuration: REDIS_URL must be provided when running in production mode.\",\n );\n }\n }\n\n if (!this.options.databaseUrl) {\n if (process.env.DATABASE_URL) {\n this.options.databaseUrl = process.env.DATABASE_URL;\n } else if (!isProduction) {\n this.logger.info(\n \"No databaseUrl provided, spinning up PostgreSQL container for development/testing...\",\n );\n const { PostgreSqlContainer } = await import(\"@testcontainers/postgresql\");\n this.pgContainer = await new PostgreSqlContainer(\"postgres:15-alpine\").start();\n this.options.databaseUrl = this.pgContainer.getConnectionUri();\n } else {\n throw new Error(\n \"Missing required configuration: DATABASE_URL must be provided when running in production mode.\",\n );\n }\n }\n\n if (this.options.redisUrl) process.env.REDIS_URL = this.options.redisUrl;\n if (this.options.databaseUrl) process.env.DATABASE_URL = this.options.databaseUrl;\n\n // 3. Set global config overrides\n const finalConfig = readBaseConfig();\n setGlobalConfig(finalConfig);\n if (this.options.aiModel) {\n const { setAiConfig } = await import(\"./config/index.js\");\n setAiConfig({ aiModel: this.options.aiModel });\n }\n\n // 4. Run database migrations if enabled\n if (this.options.autoMigrate !== false) {\n this.logger.info(\"Running database migrations...\");\n const { createDatabase, runMigrations } = await import(\"./db/index.js\");\n const { db, sql } = createDatabase({ url: this.options.databaseUrl! });\n await runMigrations(db);\n await sql.end();\n this.logger.info(\"Database migrations complete\");\n }\n\n const services = this.options.services.includes(\"all\")\n ? [\"api\", \"delivery\", \"engine\", \"enricher\", \"scheduler\", \"ai\", \"workflow\", \"events\"]\n : this.options.services;\n\n if (this.options.providers) {\n for (const provider of this.options.providers) {\n registerTransport(provider);\n }\n this.logger.info(`Registered ${this.options.providers.length} custom providers`);\n }\n\n const startupPromises: Promise<any>[] = [];\n\n if (services.includes(\"api\")) {\n const { startApiServer } = await import(\"./services/api/main.js\");\n startupPromises.push(startApiServer());\n }\n\n if (services.includes(\"delivery\")) {\n const { startDeliveryWorker } = await import(\"./services/delivery/main.js\");\n startupPromises.push(startDeliveryWorker());\n }\n\n if (services.includes(\"engine\")) {\n const { startEngineWorker } = await import(\"./services/engine/main.js\");\n startupPromises.push(startEngineWorker());\n }\n\n if (services.includes(\"enricher\")) {\n const { startEnricherWorker } = await import(\"./services/enricher/main.js\");\n startupPromises.push(startEnricherWorker());\n }\n\n if (services.includes(\"scheduler\")) {\n const { startSchedulerWorker } = await import(\"./services/scheduler/main.js\");\n startupPromises.push(startSchedulerWorker());\n }\n\n if (services.includes(\"ai\")) {\n const { startAiWorker } = await import(\"./services/ai/main.js\");\n startupPromises.push(startAiWorker());\n }\n\n if (services.includes(\"workflow\")) {\n const { startWorkflowWorker } = await import(\"./services/workflow/main.js\");\n startupPromises.push(startWorkflowWorker());\n }\n\n if (services.includes(\"events\")) {\n const { startEventWorker } = await import(\"./services/events/main.js\");\n startupPromises.push(startEventWorker());\n }\n\n const handleSignal = async (signal: string) => {\n this.logger.info(`Received ${signal}, starting graceful shutdown...`);\n await this.stop();\n process.exit(0);\n };\n\n if (!this.signalHandlersAttached) {\n process.once(\"SIGINT\", () => {\n void handleSignal(\"SIGINT\");\n });\n process.once(\"SIGTERM\", () => {\n void handleSignal(\"SIGTERM\");\n });\n this.signalHandlersAttached = true;\n }\n\n await Promise.all(startupPromises);\n }\n\n async stop() {\n // Cleanup event listeners to prevent memory leaks\n for (const cleanup of this.eventCleanupFns) {\n cleanup();\n }\n this.eventCleanupFns = [];\n\n const services = this.options.services.includes(\"all\")\n ? [\"api\", \"delivery\", \"engine\", \"enricher\", \"scheduler\", \"ai\", \"workflow\", \"events\"]\n : this.options.services;\n\n if (services.includes(\"api\")) {\n const { stopApiServer } = await import(\"./services/api/main.js\");\n await stopApiServer();\n }\n\n if (services.includes(\"delivery\")) {\n const { stopDeliveryWorker } = await import(\"./services/delivery/main.js\");\n await stopDeliveryWorker();\n }\n\n if (services.includes(\"engine\")) {\n const { stopEngineWorker } = await import(\"./services/engine/main.js\");\n await stopEngineWorker();\n }\n\n if (services.includes(\"enricher\")) {\n const { stopEnricherWorker } = await import(\"./services/enricher/main.js\");\n await stopEnricherWorker();\n }\n\n if (services.includes(\"scheduler\")) {\n const { stopSchedulerWorker } = await import(\"./services/scheduler/main.js\");\n await stopSchedulerWorker();\n }\n\n if (services.includes(\"ai\")) {\n const { stopAiWorker } = await import(\"./services/ai/main.js\");\n await stopAiWorker();\n }\n\n if (services.includes(\"workflow\")) {\n const { stopWorkflowWorker } = await import(\"./services/workflow/main.js\");\n await stopWorkflowWorker();\n }\n\n if (services.includes(\"events\")) {\n const { stopEventWorker } = await import(\"./services/events/main.js\");\n await stopEventWorker();\n }\n\n if (this.pgContainer) {\n this.logger.info(\"Stopping PostgreSQL container...\");\n await this.pgContainer.stop();\n }\n if (this.redisContainer) {\n this.logger.info(\"Stopping Redis container...\");\n await this.redisContainer.stop();\n }\n }\n}\n"],"mappings":"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"}